mirror of https://github.com/kubernetes/kops.git
dep: update github.com/pkg/sftp
This picks up changes to use go errors, instead of the (deprecated?) pkg/errors.
This commit is contained in:
parent
43027b9998
commit
b2cfc5c4c4
8
go.mod
8
go.mod
|
@ -60,7 +60,7 @@ require (
|
|||
github.com/jetstack/cert-manager v1.6.1
|
||||
github.com/mitchellh/mapstructure v1.4.1
|
||||
github.com/pelletier/go-toml v1.9.3
|
||||
github.com/pkg/sftp v1.13.0
|
||||
github.com/pkg/sftp v1.13.4
|
||||
github.com/prometheus/client_golang v1.11.0
|
||||
github.com/sergi/go-diff v1.2.0
|
||||
github.com/spf13/cobra v1.2.1
|
||||
|
@ -70,10 +70,10 @@ require (
|
|||
github.com/stretchr/testify v1.7.0
|
||||
github.com/weaveworks/mesh v0.0.0-20191105120815-58dbcc3e8e63
|
||||
github.com/zclconf/go-cty v1.8.2
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
|
||||
golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e
|
||||
google.golang.org/api v0.57.0
|
||||
gopkg.in/gcfg.v1 v1.2.3
|
||||
gopkg.in/inf.v0 v0.9.1
|
||||
|
|
15
go.sum
15
go.sum
|
@ -1060,8 +1060,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pkg/sftp v1.13.0 h1:Riw6pgOKK41foc1I1Uu03CjvbLZDXeGpInycM4shXoI=
|
||||
github.com/pkg/sftp v1.13.0/go.mod h1:41g+FIPlQUTDCveupEmEA65IoiQFrtgCeDopC4ajGIM=
|
||||
github.com/pkg/sftp v1.13.4 h1:Lb0RYJCmgUcBgZosfoi9Y9sbl6+LJgOIgk/2Y4YjMFg=
|
||||
github.com/pkg/sftp v1.13.4/go.mod h1:LzqnAvaD5TWeNBsZpfKxSYn1MbjWwOsCIAFFJbpIsK8=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
@ -1358,10 +1358,12 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP
|
|||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
@ -1465,8 +1467,9 @@ golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw=
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -1597,6 +1600,7 @@ golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -1612,8 +1616,9 @@ golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8 h1:M69LAlWZCshgp0QSzyDcSsSIejIEeuaCVpmwcKwyLMk=
|
||||
golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
|
|
@ -84,7 +84,7 @@ require (
|
|||
github.com/opencontainers/image-spec v1.0.2-0.20200206005212-79b036d80240 // indirect
|
||||
github.com/pierrec/lz4 v2.5.2+incompatible // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/sftp v1.13.0 // indirect
|
||||
github.com/pkg/sftp v1.13.4 // indirect
|
||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||
github.com/sergi/go-diff v1.2.0 // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.20.12 // indirect
|
||||
|
@ -94,11 +94,11 @@ require (
|
|||
github.com/ulikunitz/xz v0.5.8 // indirect
|
||||
github.com/xanzy/ssh-agent v0.2.1 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8 // indirect
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||
|
|
|
@ -1331,8 +1331,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pkg/sftp v1.13.0 h1:Riw6pgOKK41foc1I1Uu03CjvbLZDXeGpInycM4shXoI=
|
||||
github.com/pkg/sftp v1.13.0/go.mod h1:41g+FIPlQUTDCveupEmEA65IoiQFrtgCeDopC4ajGIM=
|
||||
github.com/pkg/sftp v1.13.4 h1:Lb0RYJCmgUcBgZosfoi9Y9sbl6+LJgOIgk/2Y4YjMFg=
|
||||
github.com/pkg/sftp v1.13.4/go.mod h1:LzqnAvaD5TWeNBsZpfKxSYn1MbjWwOsCIAFFJbpIsK8=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
@ -1698,9 +1698,11 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP
|
|||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
@ -1815,8 +1817,9 @@ golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw=
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -1963,6 +1966,7 @@ golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -1977,8 +1981,9 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8 h1:M69LAlWZCshgp0QSzyDcSsSIejIEeuaCVpmwcKwyLMk=
|
||||
golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
|
|
@ -5,3 +5,6 @@ server_standalone/server_standalone
|
|||
|
||||
examples/*/id_rsa
|
||||
examples/*/id_rsa.pub
|
||||
|
||||
memprofile.out
|
||||
memprofile.svg
|
||||
|
|
|
@ -9,10 +9,15 @@ go_library(
|
|||
"attrs_unix.go",
|
||||
"client.go",
|
||||
"conn.go",
|
||||
"ls_formatting.go",
|
||||
"ls_plan9.go",
|
||||
"ls_stub.go",
|
||||
"ls_unix.go",
|
||||
"match.go",
|
||||
"packet.go",
|
||||
"packet-manager.go",
|
||||
"packet-typing.go",
|
||||
"pool.go",
|
||||
"release.go",
|
||||
"request.go",
|
||||
"request-attrs.go",
|
||||
|
@ -29,8 +34,6 @@ go_library(
|
|||
"server_statvfs_linux.go",
|
||||
"server_statvfs_plan9.go",
|
||||
"server_statvfs_stubs.go",
|
||||
"server_stubs.go",
|
||||
"server_unix.go",
|
||||
"sftp.go",
|
||||
"stat_plan9.go",
|
||||
"stat_posix.go",
|
||||
|
@ -42,7 +45,7 @@ go_library(
|
|||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/kr/fs:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
"//vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer:go_default_library",
|
||||
"//vendor/golang.org/x/crypto/ssh:go_default_library",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
.PHONY: integration integration_w_race benchmark
|
||||
|
||||
integration:
|
||||
go test -integration -v ./...
|
||||
go test -testserver -v ./...
|
||||
|
@ -14,4 +16,12 @@ integration_w_race:
|
|||
go test -race -testserver -allocator -v ./...
|
||||
go test -race -integration -allocator -testserver -v ./...
|
||||
|
||||
COUNT ?= 1
|
||||
BENCHMARK_PATTERN ?= "."
|
||||
|
||||
benchmark:
|
||||
go test -integration -run=NONE -bench=$(BENCHMARK_PATTERN) -benchmem -count=$(COUNT)
|
||||
|
||||
benchmark_w_memprofile:
|
||||
go test -integration -run=NONE -bench=$(BENCHMARK_PATTERN) -benchmem -count=$(COUNT) -memprofile memprofile.out
|
||||
go tool pprof -svg -output=memprofile.svg memprofile.out
|
||||
|
|
|
@ -13,37 +13,34 @@ const (
|
|||
sshFileXferAttrUIDGID = 0x00000002
|
||||
sshFileXferAttrPermissions = 0x00000004
|
||||
sshFileXferAttrACmodTime = 0x00000008
|
||||
sshFileXferAttrExtented = 0x80000000
|
||||
sshFileXferAttrExtended = 0x80000000
|
||||
|
||||
sshFileXferAttrAll = sshFileXferAttrSize | sshFileXferAttrUIDGID | sshFileXferAttrPermissions |
|
||||
sshFileXferAttrACmodTime | sshFileXferAttrExtented
|
||||
sshFileXferAttrACmodTime | sshFileXferAttrExtended
|
||||
)
|
||||
|
||||
// fileInfo is an artificial type designed to satisfy os.FileInfo.
|
||||
type fileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
mtime time.Time
|
||||
sys interface{}
|
||||
name string
|
||||
stat *FileStat
|
||||
}
|
||||
|
||||
// Name returns the base name of the file.
|
||||
func (fi *fileInfo) Name() string { return fi.name }
|
||||
|
||||
// Size returns the length in bytes for regular files; system-dependent for others.
|
||||
func (fi *fileInfo) Size() int64 { return fi.size }
|
||||
func (fi *fileInfo) Size() int64 { return int64(fi.stat.Size) }
|
||||
|
||||
// Mode returns file mode bits.
|
||||
func (fi *fileInfo) Mode() os.FileMode { return fi.mode }
|
||||
func (fi *fileInfo) Mode() os.FileMode { return toFileMode(fi.stat.Mode) }
|
||||
|
||||
// ModTime returns the last modification time of the file.
|
||||
func (fi *fileInfo) ModTime() time.Time { return fi.mtime }
|
||||
func (fi *fileInfo) ModTime() time.Time { return time.Unix(int64(fi.stat.Mtime), 0) }
|
||||
|
||||
// IsDir returns true if the file is a directory.
|
||||
func (fi *fileInfo) IsDir() bool { return fi.Mode().IsDir() }
|
||||
|
||||
func (fi *fileInfo) Sys() interface{} { return fi.sys }
|
||||
func (fi *fileInfo) Sys() interface{} { return fi.stat }
|
||||
|
||||
// FileStat holds the original unmarshalled values from a call to READDIR or
|
||||
// *STAT. It is exported for the purposes of accessing the raw values via
|
||||
|
@ -65,25 +62,21 @@ type StatExtended struct {
|
|||
ExtData string
|
||||
}
|
||||
|
||||
func fileInfoFromStat(st *FileStat, name string) os.FileInfo {
|
||||
fs := &fileInfo{
|
||||
name: name,
|
||||
size: int64(st.Size),
|
||||
mode: toFileMode(st.Mode),
|
||||
mtime: time.Unix(int64(st.Mtime), 0),
|
||||
sys: st,
|
||||
func fileInfoFromStat(stat *FileStat, name string) os.FileInfo {
|
||||
return &fileInfo{
|
||||
name: name,
|
||||
stat: stat,
|
||||
}
|
||||
return fs
|
||||
}
|
||||
|
||||
func fileStatFromInfo(fi os.FileInfo) (uint32, FileStat) {
|
||||
func fileStatFromInfo(fi os.FileInfo) (uint32, *FileStat) {
|
||||
mtime := fi.ModTime().Unix()
|
||||
atime := mtime
|
||||
var flags uint32 = sshFileXferAttrSize |
|
||||
sshFileXferAttrPermissions |
|
||||
sshFileXferAttrACmodTime
|
||||
|
||||
fileStat := FileStat{
|
||||
fileStat := &FileStat{
|
||||
Size: uint64(fi.Size()),
|
||||
Mode: fromFileMode(fi.Mode()),
|
||||
Mtime: uint32(mtime),
|
||||
|
@ -91,83 +84,7 @@ func fileStatFromInfo(fi os.FileInfo) (uint32, FileStat) {
|
|||
}
|
||||
|
||||
// os specific file stat decoding
|
||||
fileStatFromInfoOs(fi, &flags, &fileStat)
|
||||
fileStatFromInfoOs(fi, &flags, fileStat)
|
||||
|
||||
return flags, fileStat
|
||||
}
|
||||
|
||||
func unmarshalAttrs(b []byte) (*FileStat, []byte) {
|
||||
flags, b := unmarshalUint32(b)
|
||||
return getFileStat(flags, b)
|
||||
}
|
||||
|
||||
func getFileStat(flags uint32, b []byte) (*FileStat, []byte) {
|
||||
var fs FileStat
|
||||
if flags&sshFileXferAttrSize == sshFileXferAttrSize {
|
||||
fs.Size, b, _ = unmarshalUint64Safe(b)
|
||||
}
|
||||
if flags&sshFileXferAttrUIDGID == sshFileXferAttrUIDGID {
|
||||
fs.UID, b, _ = unmarshalUint32Safe(b)
|
||||
}
|
||||
if flags&sshFileXferAttrUIDGID == sshFileXferAttrUIDGID {
|
||||
fs.GID, b, _ = unmarshalUint32Safe(b)
|
||||
}
|
||||
if flags&sshFileXferAttrPermissions == sshFileXferAttrPermissions {
|
||||
fs.Mode, b, _ = unmarshalUint32Safe(b)
|
||||
}
|
||||
if flags&sshFileXferAttrACmodTime == sshFileXferAttrACmodTime {
|
||||
fs.Atime, b, _ = unmarshalUint32Safe(b)
|
||||
fs.Mtime, b, _ = unmarshalUint32Safe(b)
|
||||
}
|
||||
if flags&sshFileXferAttrExtented == sshFileXferAttrExtented {
|
||||
var count uint32
|
||||
count, b, _ = unmarshalUint32Safe(b)
|
||||
ext := make([]StatExtended, count)
|
||||
for i := uint32(0); i < count; i++ {
|
||||
var typ string
|
||||
var data string
|
||||
typ, b, _ = unmarshalStringSafe(b)
|
||||
data, b, _ = unmarshalStringSafe(b)
|
||||
ext[i] = StatExtended{typ, data}
|
||||
}
|
||||
fs.Extended = ext
|
||||
}
|
||||
return &fs, b
|
||||
}
|
||||
|
||||
func marshalFileInfo(b []byte, fi os.FileInfo) []byte {
|
||||
// attributes variable struct, and also variable per protocol version
|
||||
// spec version 3 attributes:
|
||||
// uint32 flags
|
||||
// uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
|
||||
// uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID
|
||||
// uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID
|
||||
// uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
|
||||
// uint32 atime present only if flag SSH_FILEXFER_ACMODTIME
|
||||
// uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME
|
||||
// uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
|
||||
// string extended_type
|
||||
// string extended_data
|
||||
// ... more extended data (extended_type - extended_data pairs),
|
||||
// so that number of pairs equals extended_count
|
||||
|
||||
flags, fileStat := fileStatFromInfo(fi)
|
||||
|
||||
b = marshalUint32(b, flags)
|
||||
if flags&sshFileXferAttrSize != 0 {
|
||||
b = marshalUint64(b, fileStat.Size)
|
||||
}
|
||||
if flags&sshFileXferAttrUIDGID != 0 {
|
||||
b = marshalUint32(b, fileStat.UID)
|
||||
b = marshalUint32(b, fileStat.GID)
|
||||
}
|
||||
if flags&sshFileXferAttrPermissions != 0 {
|
||||
b = marshalUint32(b, fileStat.Mode)
|
||||
}
|
||||
if flags&sshFileXferAttrACmodTime != 0 {
|
||||
b = marshalUint32(b, fileStat.Atime)
|
||||
b = marshalUint32(b, fileStat.Mtime)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// +build !cgo plan9 windows android
|
||||
// +build plan9 windows android
|
||||
|
||||
package sftp
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris aix
|
||||
// +build cgo
|
||||
// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris aix js
|
||||
|
||||
package sftp
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package sftp
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
|
@ -13,7 +14,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/kr/fs"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
|
@ -43,10 +43,10 @@ type ClientOption func(*Client) error
|
|||
func MaxPacketChecked(size int) ClientOption {
|
||||
return func(c *Client) error {
|
||||
if size < 1 {
|
||||
return errors.Errorf("size must be greater or equal to 1")
|
||||
return errors.New("size must be greater or equal to 1")
|
||||
}
|
||||
if size > 32768 {
|
||||
return errors.Errorf("sizes larger than 32KB might not work with all servers")
|
||||
return errors.New("sizes larger than 32KB might not work with all servers")
|
||||
}
|
||||
c.maxPacket = size
|
||||
return nil
|
||||
|
@ -65,7 +65,7 @@ func MaxPacketChecked(size int) ClientOption {
|
|||
func MaxPacketUnchecked(size int) ClientOption {
|
||||
return func(c *Client) error {
|
||||
if size < 1 {
|
||||
return errors.Errorf("size must be greater or equal to 1")
|
||||
return errors.New("size must be greater or equal to 1")
|
||||
}
|
||||
c.maxPacket = size
|
||||
return nil
|
||||
|
@ -90,7 +90,7 @@ func MaxPacket(size int) ClientOption {
|
|||
func MaxConcurrentRequestsPerFile(n int) ClientOption {
|
||||
return func(c *Client) error {
|
||||
if n < 1 {
|
||||
return errors.Errorf("n must be greater or equal to 1")
|
||||
return errors.New("n must be greater or equal to 1")
|
||||
}
|
||||
c.maxConcurrentRequests = n
|
||||
return nil
|
||||
|
@ -273,7 +273,10 @@ func (c *Client) recvVersion() error {
|
|||
return &unexpectedPacketErr{sshFxpVersion, typ}
|
||||
}
|
||||
|
||||
version, data := unmarshalUint32(data)
|
||||
version, data, err := unmarshalUint32Safe(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if version != sftpProtocolVersion {
|
||||
return &unexpectedVersionErr{sftpProtocolVersion, version}
|
||||
}
|
||||
|
@ -384,27 +387,11 @@ func (c *Client) opendir(path string) (string, error) {
|
|||
// Stat returns a FileInfo structure describing the file specified by path 'p'.
|
||||
// If 'p' is a symbolic link, the returned FileInfo structure describes the referent file.
|
||||
func (c *Client) Stat(p string) (os.FileInfo, error) {
|
||||
id := c.nextID()
|
||||
typ, data, err := c.sendPacket(nil, &sshFxpStatPacket{
|
||||
ID: id,
|
||||
Path: p,
|
||||
})
|
||||
fs, err := c.stat(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch typ {
|
||||
case sshFxpAttrs:
|
||||
sid, data := unmarshalUint32(data)
|
||||
if sid != id {
|
||||
return nil, &unexpectedIDErr{id, sid}
|
||||
}
|
||||
attr, _ := unmarshalAttrs(data)
|
||||
return fileInfoFromStat(attr, path.Base(p)), nil
|
||||
case sshFxpStatus:
|
||||
return nil, normaliseError(unmarshalStatus(id, data))
|
||||
default:
|
||||
return nil, unimplementedPacketErr(typ)
|
||||
}
|
||||
return fileInfoFromStat(fs, path.Base(p)), nil
|
||||
}
|
||||
|
||||
// Lstat returns a FileInfo structure describing the file specified by path 'p'.
|
||||
|
@ -560,8 +547,12 @@ func (c *Client) Chown(path string, uid, gid int) error {
|
|||
}
|
||||
|
||||
// Chmod changes the permissions of the named file.
|
||||
//
|
||||
// Chmod does not apply a umask, because even retrieving the umask is not
|
||||
// possible in a portable way without causing a race condition. Callers
|
||||
// should mask off umask bits, if desired.
|
||||
func (c *Client) Chmod(path string, mode os.FileMode) error {
|
||||
return c.setstat(path, sshFileXferAttrPermissions, uint32(mode))
|
||||
return c.setstat(path, sshFileXferAttrPermissions, toChmodPerm(mode))
|
||||
}
|
||||
|
||||
// Truncate sets the size of the named file. Although it may be safely assumed
|
||||
|
@ -631,6 +622,30 @@ func (c *Client) close(handle string) error {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *Client) stat(path string) (*FileStat, error) {
|
||||
id := c.nextID()
|
||||
typ, data, err := c.sendPacket(nil, &sshFxpStatPacket{
|
||||
ID: id,
|
||||
Path: path,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch typ {
|
||||
case sshFxpAttrs:
|
||||
sid, data := unmarshalUint32(data)
|
||||
if sid != id {
|
||||
return nil, &unexpectedIDErr{id, sid}
|
||||
}
|
||||
attr, _ := unmarshalAttrs(data)
|
||||
return attr, nil
|
||||
case sshFxpStatus:
|
||||
return nil, normaliseError(unmarshalStatus(id, data))
|
||||
default:
|
||||
return nil, unimplementedPacketErr(typ)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) fstat(handle string) (*FileStat, error) {
|
||||
id := c.nextID()
|
||||
typ, data, err := c.sendPacket(nil, &sshFxpFstatPacket{
|
||||
|
@ -864,12 +879,12 @@ func (c *Client) MkdirAll(path string) error {
|
|||
|
||||
// Slow path: make sure parent exists and then call Mkdir for path.
|
||||
i := len(path)
|
||||
for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator.
|
||||
for i > 0 && path[i-1] == '/' { // Skip trailing path separator.
|
||||
i--
|
||||
}
|
||||
|
||||
j := i
|
||||
for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element.
|
||||
for j > 0 && path[j-1] != '/' { // Scan backward over element.
|
||||
j--
|
||||
}
|
||||
|
||||
|
@ -1013,7 +1028,17 @@ func (f *File) ReadAt(b []byte, off int64) (int, error) {
|
|||
|
||||
cancel := make(chan struct{})
|
||||
|
||||
concurrency := len(b)/f.c.maxPacket + 1
|
||||
if concurrency > f.c.maxConcurrentRequests || concurrency < 1 {
|
||||
concurrency = f.c.maxConcurrentRequests
|
||||
}
|
||||
|
||||
resPool := newResChanPool(concurrency)
|
||||
|
||||
type work struct {
|
||||
id uint32
|
||||
res chan result
|
||||
|
||||
b []byte
|
||||
off int64
|
||||
}
|
||||
|
@ -1033,8 +1058,18 @@ func (f *File) ReadAt(b []byte, off int64) (int, error) {
|
|||
rb = rb[:chunkSize]
|
||||
}
|
||||
|
||||
id := f.c.nextID()
|
||||
res := resPool.Get()
|
||||
|
||||
f.c.dispatchRequest(res, &sshFxpReadPacket{
|
||||
ID: id,
|
||||
Handle: f.handle,
|
||||
Offset: uint64(offset),
|
||||
Len: uint32(chunkSize),
|
||||
})
|
||||
|
||||
select {
|
||||
case workCh <- work{rb, offset}:
|
||||
case workCh <- work{id, res, rb, offset}:
|
||||
case <-cancel:
|
||||
return
|
||||
}
|
||||
|
@ -1050,11 +1085,6 @@ func (f *File) ReadAt(b []byte, off int64) (int, error) {
|
|||
}
|
||||
errCh := make(chan rErr)
|
||||
|
||||
concurrency := len(b)/f.c.maxPacket + 1
|
||||
if concurrency > f.c.maxConcurrentRequests {
|
||||
concurrency = f.c.maxConcurrentRequests
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(concurrency)
|
||||
for i := 0; i < concurrency; i++ {
|
||||
|
@ -1062,10 +1092,40 @@ func (f *File) ReadAt(b []byte, off int64) (int, error) {
|
|||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
ch := make(chan result, 1) // reusable channel per mapper.
|
||||
|
||||
for packet := range workCh {
|
||||
n, err := f.readChunkAt(ch, packet.b, packet.off)
|
||||
var n int
|
||||
|
||||
s := <-packet.res
|
||||
resPool.Put(packet.res)
|
||||
|
||||
err := s.err
|
||||
if err == nil {
|
||||
switch s.typ {
|
||||
case sshFxpStatus:
|
||||
err = normaliseError(unmarshalStatus(packet.id, s.data))
|
||||
|
||||
case sshFxpData:
|
||||
sid, data := unmarshalUint32(s.data)
|
||||
if packet.id != sid {
|
||||
err = &unexpectedIDErr{packet.id, sid}
|
||||
|
||||
} else {
|
||||
l, data := unmarshalUint32(data)
|
||||
n = copy(packet.b, data[:l])
|
||||
|
||||
// For normal disk files, it is guaranteed that this will read
|
||||
// the specified number of bytes, or up to end of file.
|
||||
// This implies, if we have a short read, that means EOF.
|
||||
if n < len(packet.b) {
|
||||
err = io.EOF
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
err = unimplementedPacketErr(s.typ)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// return the offset as the start + how much we read before the error.
|
||||
errCh <- rErr{packet.off + int64(n), err}
|
||||
|
@ -1153,30 +1213,32 @@ func (f *File) WriteTo(w io.Writer) (written int64, err error) {
|
|||
}
|
||||
|
||||
// For concurrency, we want to guess how many concurrent workers we should use.
|
||||
var fileSize uint64
|
||||
var fileStat *FileStat
|
||||
if f.c.useFstat {
|
||||
fileStat, err := f.c.fstat(f.handle)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
fileSize = fileStat.Size
|
||||
fileStat, err = f.c.fstat(f.handle)
|
||||
} else {
|
||||
fi, err := f.c.Stat(f.path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
fileSize = uint64(fi.Size())
|
||||
fileStat, err = f.c.stat(f.path)
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if fileSize <= uint64(f.c.maxPacket) {
|
||||
// We should be able to handle this in one Read.
|
||||
fileSize := fileStat.Size
|
||||
if fileSize <= uint64(f.c.maxPacket) || !isRegular(fileStat.Mode) {
|
||||
// only regular files are guaranteed to return (full read) xor (partial read, next error)
|
||||
return f.writeToSequential(w)
|
||||
}
|
||||
|
||||
concurrency := int(fileSize/uint64(f.c.maxPacket) + 1) // a bad guess, but better than no guess
|
||||
if concurrency > f.c.maxConcurrentRequests {
|
||||
concurrency = f.c.maxConcurrentRequests
|
||||
concurrency64 := fileSize/uint64(f.c.maxPacket) + 1 // a bad guess, but better than no guess
|
||||
if concurrency64 > uint64(f.c.maxConcurrentRequests) || concurrency64 < 1 {
|
||||
concurrency64 = uint64(f.c.maxConcurrentRequests)
|
||||
}
|
||||
// Now that concurrency64 is saturated to an int value, we know this assignment cannot possibly overflow.
|
||||
concurrency := int(concurrency64)
|
||||
|
||||
chunkSize := f.c.maxPacket
|
||||
pool := newBufPool(concurrency, chunkSize)
|
||||
resPool := newResChanPool(concurrency)
|
||||
|
||||
cancel := make(chan struct{})
|
||||
var wg sync.WaitGroup
|
||||
|
@ -1191,7 +1253,6 @@ func (f *File) WriteTo(w io.Writer) (written int64, err error) {
|
|||
|
||||
type writeWork struct {
|
||||
b []byte
|
||||
n int
|
||||
off int64
|
||||
err error
|
||||
|
||||
|
@ -1200,7 +1261,10 @@ func (f *File) WriteTo(w io.Writer) (written int64, err error) {
|
|||
writeCh := make(chan writeWork)
|
||||
|
||||
type readWork struct {
|
||||
off int64
|
||||
id uint32
|
||||
res chan result
|
||||
off int64
|
||||
|
||||
cur, next chan writeWork
|
||||
}
|
||||
readCh := make(chan readWork)
|
||||
|
@ -1210,53 +1274,78 @@ func (f *File) WriteTo(w io.Writer) (written int64, err error) {
|
|||
defer close(readCh)
|
||||
|
||||
off := f.offset
|
||||
chunkSize := int64(f.c.maxPacket)
|
||||
|
||||
cur := writeCh
|
||||
for {
|
||||
id := f.c.nextID()
|
||||
res := resPool.Get()
|
||||
|
||||
next := make(chan writeWork)
|
||||
readWork := readWork{
|
||||
off: off,
|
||||
id: id,
|
||||
res: res,
|
||||
off: off,
|
||||
|
||||
cur: cur,
|
||||
next: next,
|
||||
}
|
||||
|
||||
f.c.dispatchRequest(res, &sshFxpReadPacket{
|
||||
ID: id,
|
||||
Handle: f.handle,
|
||||
Offset: uint64(off),
|
||||
Len: uint32(chunkSize),
|
||||
})
|
||||
|
||||
select {
|
||||
case readCh <- readWork:
|
||||
case <-cancel:
|
||||
return
|
||||
}
|
||||
|
||||
off += chunkSize
|
||||
off += int64(chunkSize)
|
||||
cur = next
|
||||
}
|
||||
}()
|
||||
|
||||
pool := sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, f.c.maxPacket)
|
||||
},
|
||||
}
|
||||
|
||||
wg.Add(concurrency)
|
||||
for i := 0; i < concurrency; i++ {
|
||||
// Map_i: each worker gets readWork, and does the Read into a buffer at the given offset.
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
ch := make(chan result, 1) // reusable channel
|
||||
|
||||
for readWork := range readCh {
|
||||
b := pool.Get().([]byte)
|
||||
var b []byte
|
||||
var n int
|
||||
|
||||
n, err := f.readChunkAt(ch, b, readWork.off)
|
||||
if n < 0 {
|
||||
panic("sftp.File: returned negative count from readChunkAt")
|
||||
s := <-readWork.res
|
||||
resPool.Put(readWork.res)
|
||||
|
||||
err := s.err
|
||||
if err == nil {
|
||||
switch s.typ {
|
||||
case sshFxpStatus:
|
||||
err = normaliseError(unmarshalStatus(readWork.id, s.data))
|
||||
|
||||
case sshFxpData:
|
||||
sid, data := unmarshalUint32(s.data)
|
||||
if readWork.id != sid {
|
||||
err = &unexpectedIDErr{readWork.id, sid}
|
||||
|
||||
} else {
|
||||
l, data := unmarshalUint32(data)
|
||||
b = pool.Get()[:l]
|
||||
n = copy(b, data[:l])
|
||||
b = b[:n]
|
||||
}
|
||||
|
||||
default:
|
||||
err = unimplementedPacketErr(s.typ)
|
||||
}
|
||||
}
|
||||
|
||||
writeWork := writeWork{
|
||||
b: b,
|
||||
n: n,
|
||||
off: readWork.off,
|
||||
err: err,
|
||||
|
||||
|
@ -1281,14 +1370,14 @@ func (f *File) WriteTo(w io.Writer) (written int64, err error) {
|
|||
for {
|
||||
packet, ok := <-cur
|
||||
if !ok {
|
||||
return written, nil
|
||||
return written, errors.New("sftp.File.WriteTo: unexpectedly closed channel")
|
||||
}
|
||||
|
||||
// Because writes are serialized, this will always be the last successfully read byte.
|
||||
f.offset = packet.off + int64(packet.n)
|
||||
f.offset = packet.off + int64(len(packet.b))
|
||||
|
||||
if packet.n > 0 {
|
||||
n, err := w.Write(packet.b[:packet.n])
|
||||
if len(packet.b) > 0 {
|
||||
n, err := w.Write(packet.b)
|
||||
written += int64(n)
|
||||
if err != nil {
|
||||
return written, err
|
||||
|
@ -1407,7 +1496,7 @@ func (f *File) writeAtConcurrent(b []byte, off int64) (int, error) {
|
|||
errCh := make(chan wErr)
|
||||
|
||||
concurrency := len(b)/f.c.maxPacket + 1
|
||||
if concurrency > f.c.maxConcurrentRequests {
|
||||
if concurrency > f.c.maxConcurrentRequests || concurrency < 1 {
|
||||
concurrency = f.c.maxConcurrentRequests
|
||||
}
|
||||
|
||||
|
@ -1459,7 +1548,7 @@ func (f *File) writeAtConcurrent(b []byte, off int64) (int, error) {
|
|||
return len(b), nil
|
||||
}
|
||||
|
||||
// WriteAt writess up to len(b) byte to the File at a given offset `off`. It returns
|
||||
// WriteAt writes up to len(b) byte to the File at a given offset `off`. It returns
|
||||
// the number of bytes written and an error, if any. WriteAt follows io.WriterAt semantics,
|
||||
// so the file offset is not altered during the write.
|
||||
func (f *File) WriteAt(b []byte, off int64) (written int, err error) {
|
||||
|
@ -1495,8 +1584,13 @@ func (f *File) WriteAt(b []byte, off int64) (written int, err error) {
|
|||
return len(b), nil
|
||||
}
|
||||
|
||||
// readFromConcurrent implements ReaderFrom, but works concurrently rather than sequentially.
|
||||
func (f *File) readFromConcurrent(r io.Reader, remain int64) (read int64, err error) {
|
||||
// ReadFromWithConcurrency implements ReaderFrom,
|
||||
// but uses the given concurrency to issue multiple requests at the same time.
|
||||
//
|
||||
// Giving a concurrency of less than one will default to the Client’s max concurrency.
|
||||
//
|
||||
// Otherwise, the given concurrency will be capped by the Client's max concurrency.
|
||||
func (f *File) ReadFromWithConcurrency(r io.Reader, concurrency int) (read int64, err error) {
|
||||
// Split the write into multiple maxPacket sized concurrent writes.
|
||||
// This allows writes with a suitably large reader
|
||||
// to transfer data at a much faster rate due to overlapping round trip times.
|
||||
|
@ -1516,12 +1610,12 @@ func (f *File) readFromConcurrent(r io.Reader, remain int64) (read int64, err er
|
|||
}
|
||||
errCh := make(chan rwErr)
|
||||
|
||||
pool := sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, f.c.maxPacket)
|
||||
},
|
||||
if concurrency > f.c.maxConcurrentRequests || concurrency < 1 {
|
||||
concurrency = f.c.maxConcurrentRequests
|
||||
}
|
||||
|
||||
pool := newBufPool(concurrency, f.c.maxPacket)
|
||||
|
||||
// Slice: cut up the Read into any number of buffers of length <= f.c.maxPacket, and at appropriate offsets.
|
||||
go func() {
|
||||
defer close(workCh)
|
||||
|
@ -1529,7 +1623,7 @@ func (f *File) readFromConcurrent(r io.Reader, remain int64) (read int64, err er
|
|||
off := f.offset
|
||||
|
||||
for {
|
||||
b := pool.Get().([]byte)
|
||||
b := pool.Get()
|
||||
|
||||
n, err := r.Read(b)
|
||||
if n > 0 {
|
||||
|
@ -1554,11 +1648,6 @@ func (f *File) readFromConcurrent(r io.Reader, remain int64) (read int64, err er
|
|||
}
|
||||
}()
|
||||
|
||||
concurrency := int(remain/int64(f.c.maxPacket) + 1) // a bad guess, but better than no guess
|
||||
if concurrency > f.c.maxConcurrentRequests {
|
||||
concurrency = f.c.maxConcurrentRequests
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(concurrency)
|
||||
for i := 0; i < concurrency; i++ {
|
||||
|
@ -1607,7 +1696,7 @@ func (f *File) readFromConcurrent(r io.Reader, remain int64) (read int64, err er
|
|||
// * the offset of the first error from writing,
|
||||
// * the last successfully read offset.
|
||||
//
|
||||
// This could be less than the last succesfully written offset,
|
||||
// This could be less than the last successfully written offset,
|
||||
// which is the whole reason for the UseConcurrentWrites() ClientOption.
|
||||
//
|
||||
// Callers are responsible for truncating any SFTP files to a safe length.
|
||||
|
@ -1638,17 +1727,37 @@ func (f *File) ReadFrom(r io.Reader) (int64, error) {
|
|||
case interface{ Len() int }:
|
||||
remain = int64(r.Len())
|
||||
|
||||
case interface{ Size() int64 }:
|
||||
remain = r.Size()
|
||||
|
||||
case *io.LimitedReader:
|
||||
remain = r.N
|
||||
|
||||
case *os.File:
|
||||
// For files, always presume max concurrency.
|
||||
remain = math.MaxInt64
|
||||
case interface{ Stat() (os.FileInfo, error) }:
|
||||
info, err := r.Stat()
|
||||
if err == nil {
|
||||
remain = info.Size()
|
||||
}
|
||||
}
|
||||
|
||||
if remain < 0 {
|
||||
// We can strongly assert that we want default max concurrency here.
|
||||
return f.ReadFromWithConcurrency(r, f.c.maxConcurrentRequests)
|
||||
}
|
||||
|
||||
if remain > int64(f.c.maxPacket) {
|
||||
// Only use concurrency, if it would be at least two read/writes.
|
||||
return f.readFromConcurrent(r, remain)
|
||||
// Otherwise, only use concurrency, if it would be at least two packets.
|
||||
|
||||
// This is the best reasonable guess we can make.
|
||||
concurrency64 := remain/int64(f.c.maxPacket) + 1
|
||||
|
||||
// We need to cap this value to an `int` size value to avoid overflow on 32-bit machines.
|
||||
// So, we may as well pre-cap it to `f.c.maxConcurrentRequests`.
|
||||
if concurrency64 > int64(f.c.maxConcurrentRequests) {
|
||||
concurrency64 = int64(f.c.maxConcurrentRequests)
|
||||
}
|
||||
|
||||
return f.ReadFromWithConcurrency(r, int(concurrency64))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1719,8 +1828,10 @@ func (f *File) Chown(uid, gid int) error {
|
|||
}
|
||||
|
||||
// Chmod changes the permissions of the current file.
|
||||
//
|
||||
// See Client.Chmod for details.
|
||||
func (f *File) Chmod(mode os.FileMode) error {
|
||||
return f.c.Chmod(f.path, mode)
|
||||
return f.c.setfstat(f.handle, sshFileXferAttrPermissions, toChmodPerm(mode))
|
||||
}
|
||||
|
||||
// Sync requests a flush of the contents of a File to stable storage.
|
||||
|
@ -1752,13 +1863,6 @@ func (f *File) Truncate(size int64) error {
|
|||
return f.c.setfstat(f.handle, sshFileXferAttrSize, uint64(size))
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a > b {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// normaliseError normalises an error into a more standard form that can be
|
||||
// checked against stdlib errors like io.EOF or os.ErrNotExist.
|
||||
func normaliseError(err error) error {
|
||||
|
@ -1781,28 +1885,6 @@ func normaliseError(err error) error {
|
|||
}
|
||||
}
|
||||
|
||||
func unmarshalStatus(id uint32, data []byte) error {
|
||||
sid, data := unmarshalUint32(data)
|
||||
if sid != id {
|
||||
return &unexpectedIDErr{id, sid}
|
||||
}
|
||||
code, data := unmarshalUint32(data)
|
||||
msg, data, _ := unmarshalStringSafe(data)
|
||||
lang, _, _ := unmarshalStringSafe(data)
|
||||
return &StatusError{
|
||||
Code: code,
|
||||
msg: msg,
|
||||
lang: lang,
|
||||
}
|
||||
}
|
||||
|
||||
func marshalStatus(b []byte, err StatusError) []byte {
|
||||
b = marshalUint32(b, err.Code)
|
||||
b = marshalString(b, err.msg)
|
||||
b = marshalString(b, err.lang)
|
||||
return b
|
||||
}
|
||||
|
||||
// flags converts the flags passed to OpenFile into ssh flags.
|
||||
// Unsupported flags are ignored.
|
||||
func flags(f int) uint32 {
|
||||
|
@ -1830,3 +1912,25 @@ func flags(f int) uint32 {
|
|||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// toChmodPerm converts Go permission bits to POSIX permission bits.
|
||||
//
|
||||
// This differs from fromFileMode in that we preserve the POSIX versions of
|
||||
// setuid, setgid and sticky in m, because we've historically supported those
|
||||
// bits, and we mask off any non-permission bits.
|
||||
func toChmodPerm(m os.FileMode) (perm uint32) {
|
||||
const mask = os.ModePerm | s_ISUID | s_ISGID | s_ISVTX
|
||||
perm = uint32(m & mask)
|
||||
|
||||
if m&os.ModeSetuid != 0 {
|
||||
perm |= s_ISUID
|
||||
}
|
||||
if m&os.ModeSetgid != 0 {
|
||||
perm |= s_ISGID
|
||||
}
|
||||
if m&os.ModeSticky != 0 {
|
||||
perm |= s_ISVTX
|
||||
}
|
||||
|
||||
return perm
|
||||
}
|
||||
|
|
|
@ -2,10 +2,9 @@ package sftp
|
|||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// conn implements a bidirectional channel on which client and server
|
||||
|
@ -16,8 +15,6 @@ type conn struct {
|
|||
// this is the same allocator used in packet manager
|
||||
alloc *allocator
|
||||
sync.Mutex // used to serialise writes to sendPacket
|
||||
// sendPacketTest is needed to replicate packet issues in testing
|
||||
sendPacketTest func(w io.Writer, m encoding.BinaryMarshaler) error
|
||||
}
|
||||
|
||||
// the orderID is used in server mode if the allocator is enabled.
|
||||
|
@ -29,9 +26,7 @@ func (c *conn) recvPacket(orderID uint32) (uint8, []byte, error) {
|
|||
func (c *conn) sendPacket(m encoding.BinaryMarshaler) error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if c.sendPacketTest != nil {
|
||||
return c.sendPacketTest(c, m)
|
||||
}
|
||||
|
||||
return sendPacket(c, m)
|
||||
}
|
||||
|
||||
|
@ -84,14 +79,17 @@ func (c *clientConn) recv() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sid, _ := unmarshalUint32(data)
|
||||
sid, _, err := unmarshalUint32Safe(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ch, ok := c.getChannel(sid)
|
||||
if !ok {
|
||||
// This is an unexpected occurrence. Send the error
|
||||
// back to all listeners so that they terminate
|
||||
// gracefully.
|
||||
return errors.Errorf("sid not found: %v", sid)
|
||||
return fmt.Errorf("sid not found: %d", sid)
|
||||
}
|
||||
|
||||
ch <- result{typ: typ, data: data}
|
||||
|
|
|
@ -4,12 +4,12 @@ package sftp
|
|||
|
||||
import "bytes"
|
||||
|
||||
type sink struct{}
|
||||
type sinkfuzz struct{}
|
||||
|
||||
func (*sink) Close() error { return nil }
|
||||
func (*sink) Write(p []byte) (int, error) { return len(p), nil }
|
||||
func (*sinkfuzz) Close() error { return nil }
|
||||
func (*sinkfuzz) Write(p []byte) (int, error) { return len(p), nil }
|
||||
|
||||
var devnull = &sink{}
|
||||
var devnull = &sinkfuzz{}
|
||||
|
||||
// To run: go-fuzz-build && go-fuzz
|
||||
func Fuzz(data []byte) int {
|
||||
|
|
24
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/BUILD.bazel
generated
vendored
Normal file
24
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/BUILD.bazel
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"attrs.go",
|
||||
"buffer.go",
|
||||
"extended_packets.go",
|
||||
"extensions.go",
|
||||
"filexfer.go",
|
||||
"fx.go",
|
||||
"fxp.go",
|
||||
"handle_packets.go",
|
||||
"init_packets.go",
|
||||
"open_packets.go",
|
||||
"packets.go",
|
||||
"path_packets.go",
|
||||
"permissions.go",
|
||||
"response_packets.go",
|
||||
],
|
||||
importmap = "k8s.io/kops/vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer",
|
||||
importpath = "github.com/pkg/sftp/internal/encoding/ssh/filexfer",
|
||||
visibility = ["//vendor/github.com/pkg/sftp:__subpackages__"],
|
||||
)
|
326
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/attrs.go
generated
vendored
Normal file
326
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/attrs.go
generated
vendored
Normal file
|
@ -0,0 +1,326 @@
|
|||
package filexfer
|
||||
|
||||
// Attributes related flags.
|
||||
const (
|
||||
AttrSize = 1 << iota // SSH_FILEXFER_ATTR_SIZE
|
||||
AttrUIDGID // SSH_FILEXFER_ATTR_UIDGID
|
||||
AttrPermissions // SSH_FILEXFER_ATTR_PERMISSIONS
|
||||
AttrACModTime // SSH_FILEXFER_ACMODTIME
|
||||
|
||||
AttrExtended = 1 << 31 // SSH_FILEXFER_ATTR_EXTENDED
|
||||
)
|
||||
|
||||
// Attributes defines the file attributes type defined in draft-ietf-secsh-filexfer-02
|
||||
//
|
||||
// Defined in: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5
|
||||
type Attributes struct {
|
||||
Flags uint32
|
||||
|
||||
// AttrSize
|
||||
Size uint64
|
||||
|
||||
// AttrUIDGID
|
||||
UID uint32
|
||||
GID uint32
|
||||
|
||||
// AttrPermissions
|
||||
Permissions FileMode
|
||||
|
||||
// AttrACmodTime
|
||||
ATime uint32
|
||||
MTime uint32
|
||||
|
||||
// AttrExtended
|
||||
ExtendedAttributes []ExtendedAttribute
|
||||
}
|
||||
|
||||
// GetSize returns the Size field and a bool that is true if and only if the value is valid/defined.
|
||||
func (a *Attributes) GetSize() (size uint64, ok bool) {
|
||||
return a.Size, a.Flags&AttrSize != 0
|
||||
}
|
||||
|
||||
// SetSize is a convenience function that sets the Size field,
|
||||
// and marks the field as valid/defined in Flags.
|
||||
func (a *Attributes) SetSize(size uint64) {
|
||||
a.Flags |= AttrSize
|
||||
a.Size = size
|
||||
}
|
||||
|
||||
// GetUIDGID returns the UID and GID fields and a bool that is true if and only if the values are valid/defined.
|
||||
func (a *Attributes) GetUIDGID() (uid, gid uint32, ok bool) {
|
||||
return a.UID, a.GID, a.Flags&AttrUIDGID != 0
|
||||
}
|
||||
|
||||
// SetUIDGID is a convenience function that sets the UID and GID fields,
|
||||
// and marks the fields as valid/defined in Flags.
|
||||
func (a *Attributes) SetUIDGID(uid, gid uint32) {
|
||||
a.Flags |= AttrUIDGID
|
||||
a.UID = uid
|
||||
a.GID = gid
|
||||
}
|
||||
|
||||
// GetPermissions returns the Permissions field and a bool that is true if and only if the value is valid/defined.
|
||||
func (a *Attributes) GetPermissions() (perms FileMode, ok bool) {
|
||||
return a.Permissions, a.Flags&AttrPermissions != 0
|
||||
}
|
||||
|
||||
// SetPermissions is a convenience function that sets the Permissions field,
|
||||
// and marks the field as valid/defined in Flags.
|
||||
func (a *Attributes) SetPermissions(perms FileMode) {
|
||||
a.Flags |= AttrPermissions
|
||||
a.Permissions = perms
|
||||
}
|
||||
|
||||
// GetACModTime returns the ATime and MTime fields and a bool that is true if and only if the values are valid/defined.
|
||||
func (a *Attributes) GetACModTime() (atime, mtime uint32, ok bool) {
|
||||
return a.ATime, a.MTime, a.Flags&AttrACModTime != 0
|
||||
return a.ATime, a.MTime, a.Flags&AttrACModTime != 0
|
||||
}
|
||||
|
||||
// SetACModTime is a convenience function that sets the ATime and MTime fields,
|
||||
// and marks the fields as valid/defined in Flags.
|
||||
func (a *Attributes) SetACModTime(atime, mtime uint32) {
|
||||
a.Flags |= AttrACModTime
|
||||
a.ATime = atime
|
||||
a.MTime = mtime
|
||||
}
|
||||
|
||||
// Len returns the number of bytes a would marshal into.
|
||||
func (a *Attributes) Len() int {
|
||||
length := 4
|
||||
|
||||
if a.Flags&AttrSize != 0 {
|
||||
length += 8
|
||||
}
|
||||
|
||||
if a.Flags&AttrUIDGID != 0 {
|
||||
length += 4 + 4
|
||||
}
|
||||
|
||||
if a.Flags&AttrPermissions != 0 {
|
||||
length += 4
|
||||
}
|
||||
|
||||
if a.Flags&AttrACModTime != 0 {
|
||||
length += 4 + 4
|
||||
}
|
||||
|
||||
if a.Flags&AttrExtended != 0 {
|
||||
length += 4
|
||||
|
||||
for _, ext := range a.ExtendedAttributes {
|
||||
length += ext.Len()
|
||||
}
|
||||
}
|
||||
|
||||
return length
|
||||
}
|
||||
|
||||
// MarshalInto marshals e onto the end of the given Buffer.
|
||||
func (a *Attributes) MarshalInto(b *Buffer) {
|
||||
b.AppendUint32(a.Flags)
|
||||
|
||||
if a.Flags&AttrSize != 0 {
|
||||
b.AppendUint64(a.Size)
|
||||
}
|
||||
|
||||
if a.Flags&AttrUIDGID != 0 {
|
||||
b.AppendUint32(a.UID)
|
||||
b.AppendUint32(a.GID)
|
||||
}
|
||||
|
||||
if a.Flags&AttrPermissions != 0 {
|
||||
b.AppendUint32(uint32(a.Permissions))
|
||||
}
|
||||
|
||||
if a.Flags&AttrACModTime != 0 {
|
||||
b.AppendUint32(a.ATime)
|
||||
b.AppendUint32(a.MTime)
|
||||
}
|
||||
|
||||
if a.Flags&AttrExtended != 0 {
|
||||
b.AppendUint32(uint32(len(a.ExtendedAttributes)))
|
||||
|
||||
for _, ext := range a.ExtendedAttributes {
|
||||
ext.MarshalInto(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalBinary returns a as the binary encoding of a.
|
||||
func (a *Attributes) MarshalBinary() ([]byte, error) {
|
||||
buf := NewBuffer(make([]byte, 0, a.Len()))
|
||||
a.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom unmarshals an Attributes from the given Buffer into e.
|
||||
//
|
||||
// NOTE: The values of fields not covered in the a.Flags are explicitly undefined.
|
||||
func (a *Attributes) UnmarshalFrom(b *Buffer) (err error) {
|
||||
flags, err := b.ConsumeUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.XXX_UnmarshalByFlags(flags, b)
|
||||
}
|
||||
|
||||
// XXX_UnmarshalByFlags uses the pre-existing a.Flags field to determine which fields to decode.
|
||||
// DO NOT USE THIS: it is an anti-corruption function to implement existing internal usage in pkg/sftp.
|
||||
// This function is not a part of any compatibility promise.
|
||||
func (a *Attributes) XXX_UnmarshalByFlags(flags uint32, b *Buffer) (err error) {
|
||||
a.Flags = flags
|
||||
|
||||
// Short-circuit dummy attributes.
|
||||
if a.Flags == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if a.Flags&AttrSize != 0 {
|
||||
if a.Size, err = b.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if a.Flags&AttrUIDGID != 0 {
|
||||
if a.UID, err = b.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if a.GID, err = b.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if a.Flags&AttrPermissions != 0 {
|
||||
m, err := b.ConsumeUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.Permissions = FileMode(m)
|
||||
}
|
||||
|
||||
if a.Flags&AttrACModTime != 0 {
|
||||
if a.ATime, err = b.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if a.MTime, err = b.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if a.Flags&AttrExtended != 0 {
|
||||
count, err := b.ConsumeUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.ExtendedAttributes = make([]ExtendedAttribute, count)
|
||||
for i := range a.ExtendedAttributes {
|
||||
a.ExtendedAttributes[i].UnmarshalFrom(b)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary encoding of Attributes into e.
|
||||
func (a *Attributes) UnmarshalBinary(data []byte) error {
|
||||
return a.UnmarshalFrom(NewBuffer(data))
|
||||
}
|
||||
|
||||
// ExtendedAttribute defines the extended file attribute type defined in draft-ietf-secsh-filexfer-02
|
||||
//
|
||||
// Defined in: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5
|
||||
type ExtendedAttribute struct {
|
||||
Type string
|
||||
Data string
|
||||
}
|
||||
|
||||
// Len returns the number of bytes e would marshal into.
|
||||
func (e *ExtendedAttribute) Len() int {
|
||||
return 4 + len(e.Type) + 4 + len(e.Data)
|
||||
}
|
||||
|
||||
// MarshalInto marshals e onto the end of the given Buffer.
|
||||
func (e *ExtendedAttribute) MarshalInto(b *Buffer) {
|
||||
b.AppendString(e.Type)
|
||||
b.AppendString(e.Data)
|
||||
}
|
||||
|
||||
// MarshalBinary returns e as the binary encoding of e.
|
||||
func (e *ExtendedAttribute) MarshalBinary() ([]byte, error) {
|
||||
buf := NewBuffer(make([]byte, 0, e.Len()))
|
||||
e.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom unmarshals an ExtendedAattribute from the given Buffer into e.
|
||||
func (e *ExtendedAttribute) UnmarshalFrom(b *Buffer) (err error) {
|
||||
if e.Type, err = b.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if e.Data, err = b.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary encoding of ExtendedAttribute into e.
|
||||
func (e *ExtendedAttribute) UnmarshalBinary(data []byte) error {
|
||||
return e.UnmarshalFrom(NewBuffer(data))
|
||||
}
|
||||
|
||||
// NameEntry implements the SSH_FXP_NAME repeated data type from draft-ietf-secsh-filexfer-02
|
||||
//
|
||||
// This type is incompatible with versions 4 or higher.
|
||||
type NameEntry struct {
|
||||
Filename string
|
||||
Longname string
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Len returns the number of bytes e would marshal into.
|
||||
func (e *NameEntry) Len() int {
|
||||
return 4 + len(e.Filename) + 4 + len(e.Longname) + e.Attrs.Len()
|
||||
}
|
||||
|
||||
// MarshalInto marshals e onto the end of the given Buffer.
|
||||
func (e *NameEntry) MarshalInto(b *Buffer) {
|
||||
b.AppendString(e.Filename)
|
||||
b.AppendString(e.Longname)
|
||||
|
||||
e.Attrs.MarshalInto(b)
|
||||
}
|
||||
|
||||
// MarshalBinary returns e as the binary encoding of e.
|
||||
func (e *NameEntry) MarshalBinary() ([]byte, error) {
|
||||
buf := NewBuffer(make([]byte, 0, e.Len()))
|
||||
e.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom unmarshals an NameEntry from the given Buffer into e.
|
||||
//
|
||||
// NOTE: The values of fields not covered in the a.Flags are explicitly undefined.
|
||||
func (e *NameEntry) UnmarshalFrom(b *Buffer) (err error) {
|
||||
if e.Filename, err = b.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if e.Longname, err = b.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.Attrs.UnmarshalFrom(b)
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary encoding of NameEntry into e.
|
||||
func (e *NameEntry) UnmarshalBinary(data []byte) error {
|
||||
return e.UnmarshalFrom(NewBuffer(data))
|
||||
}
|
293
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/buffer.go
generated
vendored
Normal file
293
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/buffer.go
generated
vendored
Normal file
|
@ -0,0 +1,293 @@
|
|||
package filexfer
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Various encoding errors.
|
||||
var (
|
||||
ErrShortPacket = errors.New("packet too short")
|
||||
ErrLongPacket = errors.New("packet too long")
|
||||
)
|
||||
|
||||
// Buffer wraps up the various encoding details of the SSH format.
|
||||
//
|
||||
// Data types are encoded as per section 4 from https://tools.ietf.org/html/draft-ietf-secsh-architecture-09#page-8
|
||||
type Buffer struct {
|
||||
b []byte
|
||||
off int
|
||||
}
|
||||
|
||||
// NewBuffer creates and initializes a new buffer using buf as its initial contents.
|
||||
// The new buffer takes ownership of buf, and the caller should not use buf after this call.
|
||||
//
|
||||
// In most cases, new(Buffer) (or just declaring a Buffer variable) is sufficient to initialize a Buffer.
|
||||
func NewBuffer(buf []byte) *Buffer {
|
||||
return &Buffer{
|
||||
b: buf,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMarshalBuffer creates a new Buffer ready to start marshaling a Packet into.
|
||||
// It preallocates enough space for uint32(length), uint8(type), uint32(request-id) and size more bytes.
|
||||
func NewMarshalBuffer(size int) *Buffer {
|
||||
return NewBuffer(make([]byte, 4+1+4+size))
|
||||
}
|
||||
|
||||
// Bytes returns a slice of length b.Len() holding the unconsumed bytes in the Buffer.
|
||||
// The slice is valid for use only until the next buffer modification
|
||||
// (that is, only until the next call to an Append or Consume method).
|
||||
func (b *Buffer) Bytes() []byte {
|
||||
return b.b[b.off:]
|
||||
}
|
||||
|
||||
// Len returns the number of unconsumed bytes in the buffer.
|
||||
func (b *Buffer) Len() int { return len(b.b) - b.off }
|
||||
|
||||
// Cap returns the capacity of the buffer’s underlying byte slice,
|
||||
// that is, the total space allocated for the buffer’s data.
|
||||
func (b *Buffer) Cap() int { return cap(b.b) }
|
||||
|
||||
// Reset resets the buffer to be empty, but it retains the underlying storage for use by future Appends.
|
||||
func (b *Buffer) Reset() {
|
||||
b.b = b.b[:0]
|
||||
b.off = 0
|
||||
}
|
||||
|
||||
// StartPacket resets and initializes the buffer to be ready to start marshaling a packet into.
|
||||
// It truncates the buffer, reserves space for uint32(length), then appends the given packetType and requestID.
|
||||
func (b *Buffer) StartPacket(packetType PacketType, requestID uint32) {
|
||||
b.b, b.off = append(b.b[:0], make([]byte, 4)...), 0
|
||||
|
||||
b.AppendUint8(uint8(packetType))
|
||||
b.AppendUint32(requestID)
|
||||
}
|
||||
|
||||
// Packet finalizes the packet started from StartPacket.
|
||||
// It is expected that this will end the ownership of the underlying byte-slice,
|
||||
// and so the returned byte-slices may be reused the same as any other byte-slice,
|
||||
// the caller should not use this buffer after this call.
|
||||
//
|
||||
// It writes the packet body length into the first four bytes of the buffer in network byte order (big endian).
|
||||
// The packet body length is the length of this buffer less the 4-byte length itself, plus the length of payload.
|
||||
//
|
||||
// It is assumed that no Consume methods have been called on this buffer,
|
||||
// and so it returns the whole underlying slice.
|
||||
func (b *Buffer) Packet(payload []byte) (header, payloadPassThru []byte, err error) {
|
||||
b.PutLength(len(b.b) - 4 + len(payload))
|
||||
|
||||
return b.b, payload, nil
|
||||
}
|
||||
|
||||
// ConsumeUint8 consumes a single byte from the buffer.
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint8() (uint8, error) {
|
||||
if b.Len() < 1 {
|
||||
return 0, ErrShortPacket
|
||||
}
|
||||
|
||||
var v uint8
|
||||
v, b.off = b.b[b.off], b.off+1
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// AppendUint8 appends a single byte into the buffer.
|
||||
func (b *Buffer) AppendUint8(v uint8) {
|
||||
b.b = append(b.b, v)
|
||||
}
|
||||
|
||||
// ConsumeBool consumes a single byte from the buffer, and returns true if that byte is non-zero.
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeBool() (bool, error) {
|
||||
v, err := b.ConsumeUint8()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return v != 0, nil
|
||||
}
|
||||
|
||||
// AppendBool appends a single bool into the buffer.
|
||||
// It encodes it as a single byte, with false as 0, and true as 1.
|
||||
func (b *Buffer) AppendBool(v bool) {
|
||||
if v {
|
||||
b.AppendUint8(1)
|
||||
} else {
|
||||
b.AppendUint8(0)
|
||||
}
|
||||
}
|
||||
|
||||
// ConsumeUint16 consumes a single uint16 from the buffer, in network byte order (big-endian).
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint16() (uint16, error) {
|
||||
if b.Len() < 2 {
|
||||
return 0, ErrShortPacket
|
||||
}
|
||||
|
||||
v := binary.BigEndian.Uint16(b.b[b.off:])
|
||||
b.off += 2
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// AppendUint16 appends single uint16 into the buffer, in network byte order (big-endian).
|
||||
func (b *Buffer) AppendUint16(v uint16) {
|
||||
b.b = append(b.b,
|
||||
byte(v>>8),
|
||||
byte(v>>0),
|
||||
)
|
||||
}
|
||||
|
||||
// unmarshalUint32 is used internally to read the packet length.
|
||||
// It is unsafe, and so not exported.
|
||||
// Even within this package, its use should be avoided.
|
||||
func unmarshalUint32(b []byte) uint32 {
|
||||
return binary.BigEndian.Uint32(b[:4])
|
||||
}
|
||||
|
||||
// ConsumeUint32 consumes a single uint32 from the buffer, in network byte order (big-endian).
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint32() (uint32, error) {
|
||||
if b.Len() < 4 {
|
||||
return 0, ErrShortPacket
|
||||
}
|
||||
|
||||
v := binary.BigEndian.Uint32(b.b[b.off:])
|
||||
b.off += 4
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// AppendUint32 appends a single uint32 into the buffer, in network byte order (big-endian).
|
||||
func (b *Buffer) AppendUint32(v uint32) {
|
||||
b.b = append(b.b,
|
||||
byte(v>>24),
|
||||
byte(v>>16),
|
||||
byte(v>>8),
|
||||
byte(v>>0),
|
||||
)
|
||||
}
|
||||
|
||||
// ConsumeUint64 consumes a single uint64 from the buffer, in network byte order (big-endian).
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint64() (uint64, error) {
|
||||
if b.Len() < 8 {
|
||||
return 0, ErrShortPacket
|
||||
}
|
||||
|
||||
v := binary.BigEndian.Uint64(b.b[b.off:])
|
||||
b.off += 8
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// AppendUint64 appends a single uint64 into the buffer, in network byte order (big-endian).
|
||||
func (b *Buffer) AppendUint64(v uint64) {
|
||||
b.b = append(b.b,
|
||||
byte(v>>56),
|
||||
byte(v>>48),
|
||||
byte(v>>40),
|
||||
byte(v>>32),
|
||||
byte(v>>24),
|
||||
byte(v>>16),
|
||||
byte(v>>8),
|
||||
byte(v>>0),
|
||||
)
|
||||
}
|
||||
|
||||
// ConsumeInt64 consumes a single int64 from the buffer, in network byte order (big-endian) with two’s complement.
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeInt64() (int64, error) {
|
||||
u, err := b.ConsumeUint64()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int64(u), err
|
||||
}
|
||||
|
||||
// AppendInt64 appends a single int64 into the buffer, in network byte order (big-endian) with two’s complement.
|
||||
func (b *Buffer) AppendInt64(v int64) {
|
||||
b.AppendUint64(uint64(v))
|
||||
}
|
||||
|
||||
// ConsumeByteSlice consumes a single string of raw binary data from the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
// If the buffer does not have enough data, or defines a length larger than available, it will return ErrShortPacket.
|
||||
//
|
||||
// The returned slice aliases the buffer contents, and is valid only as long as the buffer is not reused
|
||||
// (that is, only until the next call to Reset, PutLength, StartPacket, or UnmarshalBinary).
|
||||
//
|
||||
// In no case will any Consume calls return overlapping slice aliases,
|
||||
// and Append calls are guaranteed to not disturb this slice alias.
|
||||
func (b *Buffer) ConsumeByteSlice() ([]byte, error) {
|
||||
length, err := b.ConsumeUint32()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if b.Len() < int(length) {
|
||||
return nil, ErrShortPacket
|
||||
}
|
||||
|
||||
v := b.b[b.off:]
|
||||
if len(v) > int(length) {
|
||||
v = v[:length:length]
|
||||
}
|
||||
b.off += int(length)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// AppendByteSlice appends a single string of raw binary data into the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
func (b *Buffer) AppendByteSlice(v []byte) {
|
||||
b.AppendUint32(uint32(len(v)))
|
||||
b.b = append(b.b, v...)
|
||||
}
|
||||
|
||||
// ConsumeString consumes a single string of binary data from the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
// If the buffer does not have enough data, or defines a length larger than available, it will return ErrShortPacket.
|
||||
//
|
||||
// NOTE: Go implicitly assumes that strings contain UTF-8 encoded data.
|
||||
// All caveats on using arbitrary binary data in Go strings applies.
|
||||
func (b *Buffer) ConsumeString() (string, error) {
|
||||
v, err := b.ConsumeByteSlice()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(v), nil
|
||||
}
|
||||
|
||||
// AppendString appends a single string of binary data into the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
func (b *Buffer) AppendString(v string) {
|
||||
b.AppendByteSlice([]byte(v))
|
||||
}
|
||||
|
||||
// PutLength writes the given size into the first four bytes of the buffer in network byte order (big endian).
|
||||
func (b *Buffer) PutLength(size int) {
|
||||
if len(b.b) < 4 {
|
||||
b.b = append(b.b, make([]byte, 4-len(b.b))...)
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint32(b.b, uint32(size))
|
||||
}
|
||||
|
||||
// MarshalBinary returns a clone of the full internal buffer.
|
||||
func (b *Buffer) MarshalBinary() ([]byte, error) {
|
||||
clone := make([]byte, len(b.b))
|
||||
n := copy(clone, b.b)
|
||||
return clone[:n], nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary sets the internal buffer of b to be a clone of data, and zeros the internal offset.
|
||||
func (b *Buffer) UnmarshalBinary(data []byte) error {
|
||||
if grow := len(data) - len(b.b); grow > 0 {
|
||||
b.b = append(b.b, make([]byte, grow)...)
|
||||
}
|
||||
|
||||
n := copy(b.b, data)
|
||||
b.b = b.b[:n]
|
||||
b.off = 0
|
||||
return nil
|
||||
}
|
142
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extended_packets.go
generated
vendored
Normal file
142
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extended_packets.go
generated
vendored
Normal file
|
@ -0,0 +1,142 @@
|
|||
package filexfer
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ExtendedData aliases the untyped interface composition of encoding.BinaryMarshaler and encoding.BinaryUnmarshaler.
|
||||
type ExtendedData = interface {
|
||||
encoding.BinaryMarshaler
|
||||
encoding.BinaryUnmarshaler
|
||||
}
|
||||
|
||||
// ExtendedDataConstructor defines a function that returns a new(ArbitraryExtendedPacket).
|
||||
type ExtendedDataConstructor func() ExtendedData
|
||||
|
||||
var extendedPacketTypes = struct {
|
||||
mu sync.RWMutex
|
||||
constructors map[string]ExtendedDataConstructor
|
||||
}{
|
||||
constructors: make(map[string]ExtendedDataConstructor),
|
||||
}
|
||||
|
||||
// RegisterExtendedPacketType defines a specific ExtendedDataConstructor for the given extension string.
|
||||
func RegisterExtendedPacketType(extension string, constructor ExtendedDataConstructor) {
|
||||
extendedPacketTypes.mu.Lock()
|
||||
defer extendedPacketTypes.mu.Unlock()
|
||||
|
||||
if _, exist := extendedPacketTypes.constructors[extension]; exist {
|
||||
panic("encoding/ssh/filexfer: multiple registration of extended packet type " + extension)
|
||||
}
|
||||
|
||||
extendedPacketTypes.constructors[extension] = constructor
|
||||
}
|
||||
|
||||
func newExtendedPacket(extension string) ExtendedData {
|
||||
extendedPacketTypes.mu.RLock()
|
||||
defer extendedPacketTypes.mu.RUnlock()
|
||||
|
||||
if f := extendedPacketTypes.constructors[extension]; f != nil {
|
||||
return f()
|
||||
}
|
||||
|
||||
return new(Buffer)
|
||||
}
|
||||
|
||||
// ExtendedPacket defines the SSH_FXP_CLOSE packet.
|
||||
type ExtendedPacket struct {
|
||||
ExtendedRequest string
|
||||
|
||||
Data ExtendedData
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ExtendedPacket) Type() PacketType {
|
||||
return PacketTypeExtended
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
//
|
||||
// The Data is marshaled into binary, and returned as the payload.
|
||||
func (p *ExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.ExtendedRequest) // string(extended-request)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeExtended, reqid)
|
||||
buf.AppendString(p.ExtendedRequest)
|
||||
|
||||
if p.Data != nil {
|
||||
payload, err = p.Data.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
//
|
||||
// If p.Data is nil, and the extension has been registered, a new type will be made from the registration.
|
||||
// If the extension has not been registered, then a new Buffer will be allocated.
|
||||
// Then the request-specific-data will be unmarshaled from the rest of the buffer.
|
||||
func (p *ExtendedPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.ExtendedRequest, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Data == nil {
|
||||
p.Data = newExtendedPacket(p.ExtendedRequest)
|
||||
}
|
||||
|
||||
return p.Data.UnmarshalBinary(buf.Bytes())
|
||||
}
|
||||
|
||||
// ExtendedReplyPacket defines the SSH_FXP_CLOSE packet.
|
||||
type ExtendedReplyPacket struct {
|
||||
Data ExtendedData
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ExtendedReplyPacket) Type() PacketType {
|
||||
return PacketTypeExtendedReply
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
//
|
||||
// The Data is marshaled into binary, and returned as the payload.
|
||||
func (p *ExtendedReplyPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
buf = NewMarshalBuffer(0)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeExtendedReply, reqid)
|
||||
|
||||
if p.Data != nil {
|
||||
payload, err = p.Data.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
//
|
||||
// If p.Data is nil, and there is request-specific-data,
|
||||
// then the request-specific-data will be wrapped in a Buffer and assigned to p.Data.
|
||||
func (p *ExtendedReplyPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Data == nil {
|
||||
p.Data = new(Buffer)
|
||||
}
|
||||
|
||||
return p.Data.UnmarshalBinary(buf.Bytes())
|
||||
}
|
46
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extensions.go
generated
vendored
Normal file
46
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/extensions.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
package filexfer
|
||||
|
||||
// ExtensionPair defines the extension-pair type defined in draft-ietf-secsh-filexfer-13.
|
||||
// This type is backwards-compatible with how draft-ietf-secsh-filexfer-02 defines extensions.
|
||||
//
|
||||
// Defined in: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-4.2
|
||||
type ExtensionPair struct {
|
||||
Name string
|
||||
Data string
|
||||
}
|
||||
|
||||
// Len returns the number of bytes e would marshal into.
|
||||
func (e *ExtensionPair) Len() int {
|
||||
return 4 + len(e.Name) + 4 + len(e.Data)
|
||||
}
|
||||
|
||||
// MarshalInto marshals e onto the end of the given Buffer.
|
||||
func (e *ExtensionPair) MarshalInto(buf *Buffer) {
|
||||
buf.AppendString(e.Name)
|
||||
buf.AppendString(e.Data)
|
||||
}
|
||||
|
||||
// MarshalBinary returns e as the binary encoding of e.
|
||||
func (e *ExtensionPair) MarshalBinary() ([]byte, error) {
|
||||
buf := NewBuffer(make([]byte, 0, e.Len()))
|
||||
e.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom unmarshals an ExtensionPair from the given Buffer into e.
|
||||
func (e *ExtensionPair) UnmarshalFrom(buf *Buffer) (err error) {
|
||||
if e.Name, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if e.Data, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary encoding of ExtensionPair into e.
|
||||
func (e *ExtensionPair) UnmarshalBinary(data []byte) error {
|
||||
return e.UnmarshalFrom(NewBuffer(data))
|
||||
}
|
54
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/filexfer.go
generated
vendored
Normal file
54
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/filexfer.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Package filexfer implements the wire encoding for secsh-filexfer as described in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02
|
||||
package filexfer
|
||||
|
||||
// PacketMarshaller narrowly defines packets that will only be transmitted.
|
||||
//
|
||||
// ExtendedPacket types will often only implement this interface,
|
||||
// since decoding the whole packet body of an ExtendedPacket can only be done dependent on the ExtendedRequest field.
|
||||
type PacketMarshaller interface {
|
||||
// MarshalPacket is the primary intended way to encode a packet.
|
||||
// The request-id for the packet is set from reqid.
|
||||
//
|
||||
// An optional buffer may be given in b.
|
||||
// If the buffer has a minimum capacity, it shall be truncated and used to marshal the header into.
|
||||
// The minimum capacity for the packet must be a constant expression, and should be at least 9.
|
||||
//
|
||||
// It shall return the main body of the encoded packet in header,
|
||||
// and may optionally return an additional payload to be written immediately after the header.
|
||||
//
|
||||
// It shall encode in the first 4-bytes of the header the proper length of the rest of the header+payload.
|
||||
MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error)
|
||||
}
|
||||
|
||||
// Packet defines the behavior of a full generic SFTP packet.
|
||||
//
|
||||
// InitPacket, and VersionPacket are not generic SFTP packets, and instead implement (Un)MarshalBinary.
|
||||
//
|
||||
// ExtendedPacket types should not iplement this interface,
|
||||
// since decoding the whole packet body of an ExtendedPacket can only be done dependent on the ExtendedRequest field.
|
||||
type Packet interface {
|
||||
PacketMarshaller
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with the specific packet.
|
||||
Type() PacketType
|
||||
|
||||
// UnmarshalPacketBody decodes a packet body from the given Buffer.
|
||||
// It is assumed that the common header values of the length, type and request-id have already been consumed.
|
||||
//
|
||||
// Implementations should not alias the given Buffer,
|
||||
// instead they can consider prepopulating an internal buffer as a hint,
|
||||
// and copying into that buffer if it has sufficient length.
|
||||
UnmarshalPacketBody(buf *Buffer) error
|
||||
}
|
||||
|
||||
// ComposePacket converts returns from MarshalPacket into an equivalent call to MarshalBinary.
|
||||
func ComposePacket(header, payload []byte, err error) ([]byte, error) {
|
||||
return append(header, payload...), err
|
||||
}
|
||||
|
||||
// Default length values,
|
||||
// Defined in draft-ietf-secsh-filexfer-02 section 3.
|
||||
const (
|
||||
DefaultMaxPacketLength = 34000
|
||||
DefaultMaxDataLength = 32768
|
||||
)
|
|
@ -0,0 +1,147 @@
|
|||
package filexfer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Status defines the SFTP error codes used in SSH_FXP_STATUS response packets.
|
||||
type Status uint32
|
||||
|
||||
// Defines the various SSH_FX_* values.
|
||||
const (
|
||||
// see draft-ietf-secsh-filexfer-02
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7
|
||||
StatusOK = Status(iota)
|
||||
StatusEOF
|
||||
StatusNoSuchFile
|
||||
StatusPermissionDenied
|
||||
StatusFailure
|
||||
StatusBadMessage
|
||||
StatusNoConnection
|
||||
StatusConnectionLost
|
||||
StatusOPUnsupported
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-03#section-7
|
||||
StatusV4InvalidHandle
|
||||
StatusV4NoSuchPath
|
||||
StatusV4FileAlreadyExists
|
||||
StatusV4WriteProtect
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-7
|
||||
StatusV4NoMedia
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-7
|
||||
StatusV5NoSpaceOnFilesystem
|
||||
StatusV5QuotaExceeded
|
||||
StatusV5UnknownPrincipal
|
||||
StatusV5LockConflict
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-06#section-8
|
||||
StatusV6DirNotEmpty
|
||||
StatusV6NotADirectory
|
||||
StatusV6InvalidFilename
|
||||
StatusV6LinkLoop
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-07#section-8
|
||||
StatusV6CannotDelete
|
||||
StatusV6InvalidParameter
|
||||
StatusV6FileIsADirectory
|
||||
StatusV6ByteRangeLockConflict
|
||||
StatusV6ByteRangeLockRefused
|
||||
StatusV6DeletePending
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-08#section-8.1
|
||||
StatusV6FileCorrupt
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-10#section-9.1
|
||||
StatusV6OwnerInvalid
|
||||
StatusV6GroupInvalid
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.1
|
||||
StatusV6NoMatchingByteRangeLock
|
||||
)
|
||||
|
||||
func (s Status) Error() string {
|
||||
return s.String()
|
||||
}
|
||||
|
||||
// Is returns true if the target is the same Status code,
|
||||
// or target is a StatusPacket with the same Status code.
|
||||
func (s Status) Is(target error) bool {
|
||||
if target, ok := target.(*StatusPacket); ok {
|
||||
return target.StatusCode == s
|
||||
}
|
||||
|
||||
return s == target
|
||||
}
|
||||
|
||||
func (s Status) String() string {
|
||||
switch s {
|
||||
case StatusOK:
|
||||
return "SSH_FX_OK"
|
||||
case StatusEOF:
|
||||
return "SSH_FX_EOF"
|
||||
case StatusNoSuchFile:
|
||||
return "SSH_FX_NO_SUCH_FILE"
|
||||
case StatusPermissionDenied:
|
||||
return "SSH_FX_PERMISSION_DENIED"
|
||||
case StatusFailure:
|
||||
return "SSH_FX_FAILURE"
|
||||
case StatusBadMessage:
|
||||
return "SSH_FX_BAD_MESSAGE"
|
||||
case StatusNoConnection:
|
||||
return "SSH_FX_NO_CONNECTION"
|
||||
case StatusConnectionLost:
|
||||
return "SSH_FX_CONNECTION_LOST"
|
||||
case StatusOPUnsupported:
|
||||
return "SSH_FX_OP_UNSUPPORTED"
|
||||
case StatusV4InvalidHandle:
|
||||
return "SSH_FX_INVALID_HANDLE"
|
||||
case StatusV4NoSuchPath:
|
||||
return "SSH_FX_NO_SUCH_PATH"
|
||||
case StatusV4FileAlreadyExists:
|
||||
return "SSH_FX_FILE_ALREADY_EXISTS"
|
||||
case StatusV4WriteProtect:
|
||||
return "SSH_FX_WRITE_PROTECT"
|
||||
case StatusV4NoMedia:
|
||||
return "SSH_FX_NO_MEDIA"
|
||||
case StatusV5NoSpaceOnFilesystem:
|
||||
return "SSH_FX_NO_SPACE_ON_FILESYSTEM"
|
||||
case StatusV5QuotaExceeded:
|
||||
return "SSH_FX_QUOTA_EXCEEDED"
|
||||
case StatusV5UnknownPrincipal:
|
||||
return "SSH_FX_UNKNOWN_PRINCIPAL"
|
||||
case StatusV5LockConflict:
|
||||
return "SSH_FX_LOCK_CONFLICT"
|
||||
case StatusV6DirNotEmpty:
|
||||
return "SSH_FX_DIR_NOT_EMPTY"
|
||||
case StatusV6NotADirectory:
|
||||
return "SSH_FX_NOT_A_DIRECTORY"
|
||||
case StatusV6InvalidFilename:
|
||||
return "SSH_FX_INVALID_FILENAME"
|
||||
case StatusV6LinkLoop:
|
||||
return "SSH_FX_LINK_LOOP"
|
||||
case StatusV6CannotDelete:
|
||||
return "SSH_FX_CANNOT_DELETE"
|
||||
case StatusV6InvalidParameter:
|
||||
return "SSH_FX_INVALID_PARAMETER"
|
||||
case StatusV6FileIsADirectory:
|
||||
return "SSH_FX_FILE_IS_A_DIRECTORY"
|
||||
case StatusV6ByteRangeLockConflict:
|
||||
return "SSH_FX_BYTE_RANGE_LOCK_CONFLICT"
|
||||
case StatusV6ByteRangeLockRefused:
|
||||
return "SSH_FX_BYTE_RANGE_LOCK_REFUSED"
|
||||
case StatusV6DeletePending:
|
||||
return "SSH_FX_DELETE_PENDING"
|
||||
case StatusV6FileCorrupt:
|
||||
return "SSH_FX_FILE_CORRUPT"
|
||||
case StatusV6OwnerInvalid:
|
||||
return "SSH_FX_OWNER_INVALID"
|
||||
case StatusV6GroupInvalid:
|
||||
return "SSH_FX_GROUP_INVALID"
|
||||
case StatusV6NoMatchingByteRangeLock:
|
||||
return "SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK"
|
||||
default:
|
||||
return fmt.Sprintf("SSH_FX_UNKNOWN(%d)", s)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package filexfer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// PacketType defines the various SFTP packet types.
|
||||
type PacketType uint8
|
||||
|
||||
// Request packet types.
|
||||
const (
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
PacketTypeInit = PacketType(iota + 1)
|
||||
PacketTypeVersion
|
||||
PacketTypeOpen
|
||||
PacketTypeClose
|
||||
PacketTypeRead
|
||||
PacketTypeWrite
|
||||
PacketTypeLStat
|
||||
PacketTypeFStat
|
||||
PacketTypeSetstat
|
||||
PacketTypeFSetstat
|
||||
PacketTypeOpenDir
|
||||
PacketTypeReadDir
|
||||
PacketTypeRemove
|
||||
PacketTypeMkdir
|
||||
PacketTypeRmdir
|
||||
PacketTypeRealPath
|
||||
PacketTypeStat
|
||||
PacketTypeRename
|
||||
PacketTypeReadLink
|
||||
PacketTypeSymlink
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-07#section-3.3
|
||||
PacketTypeV6Link
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-08#section-3.3
|
||||
PacketTypeV6Block
|
||||
PacketTypeV6Unblock
|
||||
)
|
||||
|
||||
// Response packet types.
|
||||
const (
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
PacketTypeStatus = PacketType(iota + 101)
|
||||
PacketTypeHandle
|
||||
PacketTypeData
|
||||
PacketTypeName
|
||||
PacketTypeAttrs
|
||||
)
|
||||
|
||||
// Extended packet types.
|
||||
const (
|
||||
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
PacketTypeExtended = PacketType(iota + 200)
|
||||
PacketTypeExtendedReply
|
||||
)
|
||||
|
||||
func (f PacketType) String() string {
|
||||
switch f {
|
||||
case PacketTypeInit:
|
||||
return "SSH_FXP_INIT"
|
||||
case PacketTypeVersion:
|
||||
return "SSH_FXP_VERSION"
|
||||
case PacketTypeOpen:
|
||||
return "SSH_FXP_OPEN"
|
||||
case PacketTypeClose:
|
||||
return "SSH_FXP_CLOSE"
|
||||
case PacketTypeRead:
|
||||
return "SSH_FXP_READ"
|
||||
case PacketTypeWrite:
|
||||
return "SSH_FXP_WRITE"
|
||||
case PacketTypeLStat:
|
||||
return "SSH_FXP_LSTAT"
|
||||
case PacketTypeFStat:
|
||||
return "SSH_FXP_FSTAT"
|
||||
case PacketTypeSetstat:
|
||||
return "SSH_FXP_SETSTAT"
|
||||
case PacketTypeFSetstat:
|
||||
return "SSH_FXP_FSETSTAT"
|
||||
case PacketTypeOpenDir:
|
||||
return "SSH_FXP_OPENDIR"
|
||||
case PacketTypeReadDir:
|
||||
return "SSH_FXP_READDIR"
|
||||
case PacketTypeRemove:
|
||||
return "SSH_FXP_REMOVE"
|
||||
case PacketTypeMkdir:
|
||||
return "SSH_FXP_MKDIR"
|
||||
case PacketTypeRmdir:
|
||||
return "SSH_FXP_RMDIR"
|
||||
case PacketTypeRealPath:
|
||||
return "SSH_FXP_REALPATH"
|
||||
case PacketTypeStat:
|
||||
return "SSH_FXP_STAT"
|
||||
case PacketTypeRename:
|
||||
return "SSH_FXP_RENAME"
|
||||
case PacketTypeReadLink:
|
||||
return "SSH_FXP_READLINK"
|
||||
case PacketTypeSymlink:
|
||||
return "SSH_FXP_SYMLINK"
|
||||
case PacketTypeV6Link:
|
||||
return "SSH_FXP_LINK"
|
||||
case PacketTypeV6Block:
|
||||
return "SSH_FXP_BLOCK"
|
||||
case PacketTypeV6Unblock:
|
||||
return "SSH_FXP_UNBLOCK"
|
||||
case PacketTypeStatus:
|
||||
return "SSH_FXP_STATUS"
|
||||
case PacketTypeHandle:
|
||||
return "SSH_FXP_HANDLE"
|
||||
case PacketTypeData:
|
||||
return "SSH_FXP_DATA"
|
||||
case PacketTypeName:
|
||||
return "SSH_FXP_NAME"
|
||||
case PacketTypeAttrs:
|
||||
return "SSH_FXP_ATTRS"
|
||||
case PacketTypeExtended:
|
||||
return "SSH_FXP_EXTENDED"
|
||||
case PacketTypeExtendedReply:
|
||||
return "SSH_FXP_EXTENDED_REPLY"
|
||||
default:
|
||||
return fmt.Sprintf("SSH_FXP_UNKNOWN(%d)", f)
|
||||
}
|
||||
}
|
249
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/handle_packets.go
generated
vendored
Normal file
249
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/handle_packets.go
generated
vendored
Normal file
|
@ -0,0 +1,249 @@
|
|||
package filexfer
|
||||
|
||||
// ClosePacket defines the SSH_FXP_CLOSE packet.
|
||||
type ClosePacket struct {
|
||||
Handle string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ClosePacket) Type() PacketType {
|
||||
return PacketTypeClose
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *ClosePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Handle) // string(handle)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeClose, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *ClosePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadPacket defines the SSH_FXP_READ packet.
|
||||
type ReadPacket struct {
|
||||
Handle string
|
||||
Offset uint64
|
||||
Len uint32
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ReadPacket) Type() PacketType {
|
||||
return PacketTypeRead
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *ReadPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// string(handle) + uint64(offset) + uint32(len)
|
||||
size := 4 + len(p.Handle) + 8 + 4
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeRead, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
buf.AppendUint64(p.Offset)
|
||||
buf.AppendUint32(p.Len)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *ReadPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Offset, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Len, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePacket defines the SSH_FXP_WRITE packet.
|
||||
type WritePacket struct {
|
||||
Handle string
|
||||
Offset uint64
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *WritePacket) Type() PacketType {
|
||||
return PacketTypeWrite
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *WritePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// string(handle) + uint64(offset) + uint32(len(data)); data content in payload
|
||||
size := 4 + len(p.Handle) + 8 + 4
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeWrite, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
buf.AppendUint64(p.Offset)
|
||||
buf.AppendUint32(uint32(len(p.Data)))
|
||||
|
||||
return buf.Packet(p.Data)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
//
|
||||
// If p.Data is already populated, and of sufficient length to hold the data,
|
||||
// then this will copy the data into that byte slice.
|
||||
//
|
||||
// If p.Data has a length insufficient to hold the data,
|
||||
// then this will make a new slice of sufficient length, and copy the data into that.
|
||||
//
|
||||
// This means this _does not_ alias any of the data buffer that is passed in.
|
||||
func (p *WritePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Offset, err = buf.ConsumeUint64(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := buf.ConsumeByteSlice()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(p.Data) < len(data) {
|
||||
p.Data = make([]byte, len(data))
|
||||
}
|
||||
|
||||
n := copy(p.Data, data)
|
||||
p.Data = p.Data[:n]
|
||||
return nil
|
||||
}
|
||||
|
||||
// FStatPacket defines the SSH_FXP_FSTAT packet.
|
||||
type FStatPacket struct {
|
||||
Handle string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *FStatPacket) Type() PacketType {
|
||||
return PacketTypeFStat
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *FStatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Handle) // string(handle)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeFStat, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *FStatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FSetstatPacket defines the SSH_FXP_FSETSTAT packet.
|
||||
type FSetstatPacket struct {
|
||||
Handle string
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *FSetstatPacket) Type() PacketType {
|
||||
return PacketTypeFSetstat
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *FSetstatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Handle) + p.Attrs.Len() // string(handle) + ATTRS(attrs)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeFSetstat, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
|
||||
p.Attrs.MarshalInto(buf)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *FSetstatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
}
|
||||
|
||||
// ReadDirPacket defines the SSH_FXP_READDIR packet.
|
||||
type ReadDirPacket struct {
|
||||
Handle string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ReadDirPacket) Type() PacketType {
|
||||
return PacketTypeReadDir
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *ReadDirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Handle) // string(handle)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeReadDir, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *ReadDirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
99
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/init_packets.go
generated
vendored
Normal file
99
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/init_packets.go
generated
vendored
Normal file
|
@ -0,0 +1,99 @@
|
|||
package filexfer
|
||||
|
||||
// InitPacket defines the SSH_FXP_INIT packet.
|
||||
type InitPacket struct {
|
||||
Version uint32
|
||||
Extensions []*ExtensionPair
|
||||
}
|
||||
|
||||
// MarshalBinary returns p as the binary encoding of p.
|
||||
func (p *InitPacket) MarshalBinary() ([]byte, error) {
|
||||
size := 1 + 4 // byte(type) + uint32(version)
|
||||
|
||||
for _, ext := range p.Extensions {
|
||||
size += ext.Len()
|
||||
}
|
||||
|
||||
b := NewBuffer(make([]byte, 4, 4+size))
|
||||
b.AppendUint8(uint8(PacketTypeInit))
|
||||
b.AppendUint32(p.Version)
|
||||
|
||||
for _, ext := range p.Extensions {
|
||||
ext.MarshalInto(b)
|
||||
}
|
||||
|
||||
b.PutLength(size)
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary unmarshals a full raw packet out of the given data.
|
||||
// It is assumed that the uint32(length) has already been consumed to receive the data.
|
||||
// It is also assumed that the uint8(type) has already been consumed to which packet to unmarshal into.
|
||||
func (p *InitPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
buf := NewBuffer(data)
|
||||
|
||||
if p.Version, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for buf.Len() > 0 {
|
||||
var ext ExtensionPair
|
||||
if err := ext.UnmarshalFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Extensions = append(p.Extensions, &ext)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VersionPacket defines the SSH_FXP_VERSION packet.
|
||||
type VersionPacket struct {
|
||||
Version uint32
|
||||
Extensions []*ExtensionPair
|
||||
}
|
||||
|
||||
// MarshalBinary returns p as the binary encoding of p.
|
||||
func (p *VersionPacket) MarshalBinary() ([]byte, error) {
|
||||
size := 1 + 4 // byte(type) + uint32(version)
|
||||
|
||||
for _, ext := range p.Extensions {
|
||||
size += ext.Len()
|
||||
}
|
||||
|
||||
b := NewBuffer(make([]byte, 4, 4+size))
|
||||
b.AppendUint8(uint8(PacketTypeVersion))
|
||||
b.AppendUint32(p.Version)
|
||||
|
||||
for _, ext := range p.Extensions {
|
||||
ext.MarshalInto(b)
|
||||
}
|
||||
|
||||
b.PutLength(size)
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary unmarshals a full raw packet out of the given data.
|
||||
// It is assumed that the uint32(length) has already been consumed to receive the data.
|
||||
// It is also assumed that the uint8(type) has already been consumed to which packet to unmarshal into.
|
||||
func (p *VersionPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
buf := NewBuffer(data)
|
||||
|
||||
if p.Version, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for buf.Len() > 0 {
|
||||
var ext ExtensionPair
|
||||
if err := ext.UnmarshalFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Extensions = append(p.Extensions, &ext)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
89
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/open_packets.go
generated
vendored
Normal file
89
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/open_packets.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
package filexfer
|
||||
|
||||
// SSH_FXF_* flags.
|
||||
const (
|
||||
FlagRead = 1 << iota // SSH_FXF_READ
|
||||
FlagWrite // SSH_FXF_WRITE
|
||||
FlagAppend // SSH_FXF_APPEND
|
||||
FlagCreate // SSH_FXF_CREAT
|
||||
FlagTruncate // SSH_FXF_TRUNC
|
||||
FlagExclusive // SSH_FXF_EXCL
|
||||
)
|
||||
|
||||
// OpenPacket defines the SSH_FXP_OPEN packet.
|
||||
type OpenPacket struct {
|
||||
Filename string
|
||||
PFlags uint32
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *OpenPacket) Type() PacketType {
|
||||
return PacketTypeOpen
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *OpenPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// string(filename) + uint32(pflags) + ATTRS(attrs)
|
||||
size := 4 + len(p.Filename) + 4 + p.Attrs.Len()
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeOpen, reqid)
|
||||
buf.AppendString(p.Filename)
|
||||
buf.AppendUint32(p.PFlags)
|
||||
|
||||
p.Attrs.MarshalInto(buf)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *OpenPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Filename, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.PFlags, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
}
|
||||
|
||||
// OpenDirPacket defines the SSH_FXP_OPENDIR packet.
|
||||
type OpenDirPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *OpenDirPacket) Type() PacketType {
|
||||
return PacketTypeOpenDir
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *OpenDirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeOpenDir, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *OpenDirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
323
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/packets.go
generated
vendored
Normal file
323
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/packets.go
generated
vendored
Normal file
|
@ -0,0 +1,323 @@
|
|||
package filexfer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// smallBufferSize is an initial allocation minimal capacity.
|
||||
const smallBufferSize = 64
|
||||
|
||||
func newPacketFromType(typ PacketType) (Packet, error) {
|
||||
switch typ {
|
||||
case PacketTypeOpen:
|
||||
return new(OpenPacket), nil
|
||||
case PacketTypeClose:
|
||||
return new(ClosePacket), nil
|
||||
case PacketTypeRead:
|
||||
return new(ReadPacket), nil
|
||||
case PacketTypeWrite:
|
||||
return new(WritePacket), nil
|
||||
case PacketTypeLStat:
|
||||
return new(LStatPacket), nil
|
||||
case PacketTypeFStat:
|
||||
return new(FStatPacket), nil
|
||||
case PacketTypeSetstat:
|
||||
return new(SetstatPacket), nil
|
||||
case PacketTypeFSetstat:
|
||||
return new(FSetstatPacket), nil
|
||||
case PacketTypeOpenDir:
|
||||
return new(OpenDirPacket), nil
|
||||
case PacketTypeReadDir:
|
||||
return new(ReadDirPacket), nil
|
||||
case PacketTypeRemove:
|
||||
return new(RemovePacket), nil
|
||||
case PacketTypeMkdir:
|
||||
return new(MkdirPacket), nil
|
||||
case PacketTypeRmdir:
|
||||
return new(RmdirPacket), nil
|
||||
case PacketTypeRealPath:
|
||||
return new(RealPathPacket), nil
|
||||
case PacketTypeStat:
|
||||
return new(StatPacket), nil
|
||||
case PacketTypeRename:
|
||||
return new(RenamePacket), nil
|
||||
case PacketTypeReadLink:
|
||||
return new(ReadLinkPacket), nil
|
||||
case PacketTypeSymlink:
|
||||
return new(SymlinkPacket), nil
|
||||
case PacketTypeExtended:
|
||||
return new(ExtendedPacket), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected request packet type: %v", typ)
|
||||
}
|
||||
}
|
||||
|
||||
// RawPacket implements the general packet format from draft-ietf-secsh-filexfer-02
|
||||
//
|
||||
// RawPacket is intended for use in clients receiving responses,
|
||||
// where a response will be expected to be of a limited number of types,
|
||||
// and unmarshaling unknown/unexpected response packets is unnecessary.
|
||||
//
|
||||
// For servers expecting to receive arbitrary request packet types,
|
||||
// use RequestPacket.
|
||||
//
|
||||
// Defined in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
type RawPacket struct {
|
||||
PacketType PacketType
|
||||
RequestID uint32
|
||||
|
||||
Data Buffer
|
||||
}
|
||||
|
||||
// Type returns the Type field defining the SSH_FXP_xy type for this packet.
|
||||
func (p *RawPacket) Type() PacketType {
|
||||
return p.PacketType
|
||||
}
|
||||
|
||||
// Reset clears the pointers and reference-semantic variables of RawPacket,
|
||||
// releasing underlying resources, and making them and the RawPacket suitable to be reused,
|
||||
// so long as no other references have been kept.
|
||||
func (p *RawPacket) Reset() {
|
||||
p.Data = Buffer{}
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
//
|
||||
// The internal p.RequestID is overridden by the reqid argument.
|
||||
func (p *RawPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
buf = NewMarshalBuffer(0)
|
||||
}
|
||||
|
||||
buf.StartPacket(p.PacketType, reqid)
|
||||
|
||||
return buf.Packet(p.Data.Bytes())
|
||||
}
|
||||
|
||||
// MarshalBinary returns p as the binary encoding of p.
|
||||
//
|
||||
// This is a convenience implementation primarily intended for tests,
|
||||
// because it is inefficient with allocations.
|
||||
func (p *RawPacket) MarshalBinary() ([]byte, error) {
|
||||
return ComposePacket(p.MarshalPacket(p.RequestID, nil))
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes a RawPacket from the given Buffer into p.
|
||||
//
|
||||
// The Data field will alias the passed in Buffer,
|
||||
// so the buffer passed in should not be reused before RawPacket.Reset().
|
||||
func (p *RawPacket) UnmarshalFrom(buf *Buffer) error {
|
||||
typ, err := buf.ConsumeUint8()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.PacketType = PacketType(typ)
|
||||
|
||||
if p.RequestID, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Data = *buf
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes a full raw packet out of the given data.
|
||||
// It is assumed that the uint32(length) has already been consumed to receive the data.
|
||||
//
|
||||
// This is a convenience implementation primarily intended for tests,
|
||||
// because this must clone the given data byte slice,
|
||||
// as Data is not allowed to alias any part of the data byte slice.
|
||||
func (p *RawPacket) UnmarshalBinary(data []byte) error {
|
||||
clone := make([]byte, len(data))
|
||||
n := copy(clone, data)
|
||||
return p.UnmarshalFrom(NewBuffer(clone[:n]))
|
||||
}
|
||||
|
||||
// readPacket reads a uint32 length-prefixed binary data packet from r.
|
||||
// using the given byte slice as a backing array.
|
||||
//
|
||||
// If the packet length read from r is bigger than maxPacketLength,
|
||||
// or greater than math.MaxInt32 on a 32-bit implementation,
|
||||
// then a `ErrLongPacket` error will be returned.
|
||||
//
|
||||
// If the given byte slice is insufficient to hold the packet,
|
||||
// then it will be extended to fill the packet size.
|
||||
func readPacket(r io.Reader, b []byte, maxPacketLength uint32) ([]byte, error) {
|
||||
if cap(b) < 4 {
|
||||
// We will need allocate our own buffer just for reading the packet length.
|
||||
|
||||
// However, we don’t really want to allocate an extremely narrow buffer (4-bytes),
|
||||
// and cause unnecessary allocation churn from both length reads and small packet reads,
|
||||
// so we use smallBufferSize from the bytes package as a reasonable guess.
|
||||
|
||||
// But if callers really do want to force narrow throw-away allocation of every packet body,
|
||||
// they can do so with a buffer of capacity 4.
|
||||
b = make([]byte, smallBufferSize)
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(r, b[:4]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
length := unmarshalUint32(b)
|
||||
if int(length) < 5 {
|
||||
// Must have at least uint8(type) and uint32(request-id)
|
||||
|
||||
if int(length) < 0 {
|
||||
// Only possible when strconv.IntSize == 32,
|
||||
// the packet length is longer than math.MaxInt32,
|
||||
// and thus longer than any possible slice.
|
||||
return nil, ErrLongPacket
|
||||
}
|
||||
|
||||
return nil, ErrShortPacket
|
||||
}
|
||||
if length > maxPacketLength {
|
||||
return nil, ErrLongPacket
|
||||
}
|
||||
|
||||
if int(length) > cap(b) {
|
||||
// We know int(length) must be positive, because of tests above.
|
||||
b = make([]byte, length)
|
||||
}
|
||||
|
||||
n, err := io.ReadFull(r, b[:length])
|
||||
return b[:n], err
|
||||
}
|
||||
|
||||
// ReadFrom provides a simple functional packet reader,
|
||||
// using the given byte slice as a backing array.
|
||||
//
|
||||
// To protect against potential denial of service attacks,
|
||||
// if the read packet length is longer than maxPacketLength,
|
||||
// then no packet data will be read, and ErrLongPacket will be returned.
|
||||
// (On 32-bit int architectures, all packets >= 2^31 in length
|
||||
// will return ErrLongPacket regardless of maxPacketLength.)
|
||||
//
|
||||
// If the read packet length is longer than cap(b),
|
||||
// then a throw-away slice will allocated to meet the exact packet length.
|
||||
// This can be used to limit the length of reused buffers,
|
||||
// while still allowing reception of occasional large packets.
|
||||
//
|
||||
// The Data field may alias the passed in byte slice,
|
||||
// so the byte slice passed in should not be reused before RawPacket.Reset().
|
||||
func (p *RawPacket) ReadFrom(r io.Reader, b []byte, maxPacketLength uint32) error {
|
||||
b, err := readPacket(r, b, maxPacketLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.UnmarshalFrom(NewBuffer(b))
|
||||
}
|
||||
|
||||
// RequestPacket implements the general packet format from draft-ietf-secsh-filexfer-02
|
||||
// but also automatically decode/encodes valid request packets (2 < type < 100 || type == 200).
|
||||
//
|
||||
// RequestPacket is intended for use in servers receiving requests,
|
||||
// where any arbitrary request may be received, and so decoding them automatically
|
||||
// is useful.
|
||||
//
|
||||
// For clients expecting to receive specific response packet types,
|
||||
// where automatic unmarshaling of the packet body does not make sense,
|
||||
// use RawPacket.
|
||||
//
|
||||
// Defined in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
type RequestPacket struct {
|
||||
RequestID uint32
|
||||
|
||||
Request Packet
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with the underlying packet.
|
||||
func (p *RequestPacket) Type() PacketType {
|
||||
return p.Request.Type()
|
||||
}
|
||||
|
||||
// Reset clears the pointers and reference-semantic variables in RequestPacket,
|
||||
// releasing underlying resources, and making them and the RequestPacket suitable to be reused,
|
||||
// so long as no other references have been kept.
|
||||
func (p *RequestPacket) Reset() {
|
||||
p.Request = nil
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
//
|
||||
// The internal p.RequestID is overridden by the reqid argument.
|
||||
func (p *RequestPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
if p.Request == nil {
|
||||
return nil, nil, errors.New("empty request packet")
|
||||
}
|
||||
|
||||
return p.Request.MarshalPacket(reqid, b)
|
||||
}
|
||||
|
||||
// MarshalBinary returns p as the binary encoding of p.
|
||||
//
|
||||
// This is a convenience implementation primarily intended for tests,
|
||||
// because it is inefficient with allocations.
|
||||
func (p *RequestPacket) MarshalBinary() ([]byte, error) {
|
||||
return ComposePacket(p.MarshalPacket(p.RequestID, nil))
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes a RequestPacket from the given Buffer into p.
|
||||
//
|
||||
// The Request field may alias the passed in Buffer, (e.g. SSH_FXP_WRITE),
|
||||
// so the buffer passed in should not be reused before RequestPacket.Reset().
|
||||
func (p *RequestPacket) UnmarshalFrom(buf *Buffer) error {
|
||||
typ, err := buf.ConsumeUint8()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Request, err = newPacketFromType(PacketType(typ))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.RequestID, err = buf.ConsumeUint32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.Request.UnmarshalPacketBody(buf)
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes a full request packet out of the given data.
|
||||
// It is assumed that the uint32(length) has already been consumed to receive the data.
|
||||
//
|
||||
// This is a convenience implementation primarily intended for tests,
|
||||
// because this must clone the given data byte slice,
|
||||
// as Request is not allowed to alias any part of the data byte slice.
|
||||
func (p *RequestPacket) UnmarshalBinary(data []byte) error {
|
||||
clone := make([]byte, len(data))
|
||||
n := copy(clone, data)
|
||||
return p.UnmarshalFrom(NewBuffer(clone[:n]))
|
||||
}
|
||||
|
||||
// ReadFrom provides a simple functional packet reader,
|
||||
// using the given byte slice as a backing array.
|
||||
//
|
||||
// To protect against potential denial of service attacks,
|
||||
// if the read packet length is longer than maxPacketLength,
|
||||
// then no packet data will be read, and ErrLongPacket will be returned.
|
||||
// (On 32-bit int architectures, all packets >= 2^31 in length
|
||||
// will return ErrLongPacket regardless of maxPacketLength.)
|
||||
//
|
||||
// If the read packet length is longer than cap(b),
|
||||
// then a throw-away slice will allocated to meet the exact packet length.
|
||||
// This can be used to limit the length of reused buffers,
|
||||
// while still allowing reception of occasional large packets.
|
||||
//
|
||||
// The Request field may alias the passed in byte slice,
|
||||
// so the byte slice passed in should not be reused before RawPacket.Reset().
|
||||
func (p *RequestPacket) ReadFrom(r io.Reader, b []byte, maxPacketLength uint32) error {
|
||||
b, err := readPacket(r, b, maxPacketLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.UnmarshalFrom(NewBuffer(b))
|
||||
}
|
368
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/path_packets.go
generated
vendored
Normal file
368
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/path_packets.go
generated
vendored
Normal file
|
@ -0,0 +1,368 @@
|
|||
package filexfer
|
||||
|
||||
// LStatPacket defines the SSH_FXP_LSTAT packet.
|
||||
type LStatPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *LStatPacket) Type() PacketType {
|
||||
return PacketTypeLStat
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *LStatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeLStat, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *LStatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetstatPacket defines the SSH_FXP_SETSTAT packet.
|
||||
type SetstatPacket struct {
|
||||
Path string
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *SetstatPacket) Type() PacketType {
|
||||
return PacketTypeSetstat
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *SetstatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) + p.Attrs.Len() // string(path) + ATTRS(attrs)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeSetstat, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
p.Attrs.MarshalInto(buf)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *SetstatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
}
|
||||
|
||||
// RemovePacket defines the SSH_FXP_REMOVE packet.
|
||||
type RemovePacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *RemovePacket) Type() PacketType {
|
||||
return PacketTypeRemove
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *RemovePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeRemove, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *RemovePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MkdirPacket defines the SSH_FXP_MKDIR packet.
|
||||
type MkdirPacket struct {
|
||||
Path string
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *MkdirPacket) Type() PacketType {
|
||||
return PacketTypeMkdir
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *MkdirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) + p.Attrs.Len() // string(path) + ATTRS(attrs)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeMkdir, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
p.Attrs.MarshalInto(buf)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *MkdirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
}
|
||||
|
||||
// RmdirPacket defines the SSH_FXP_RMDIR packet.
|
||||
type RmdirPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *RmdirPacket) Type() PacketType {
|
||||
return PacketTypeRmdir
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *RmdirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeRmdir, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *RmdirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RealPathPacket defines the SSH_FXP_REALPATH packet.
|
||||
type RealPathPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *RealPathPacket) Type() PacketType {
|
||||
return PacketTypeRealPath
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *RealPathPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeRealPath, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *RealPathPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StatPacket defines the SSH_FXP_STAT packet.
|
||||
type StatPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *StatPacket) Type() PacketType {
|
||||
return PacketTypeStat
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *StatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeStat, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *StatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RenamePacket defines the SSH_FXP_RENAME packet.
|
||||
type RenamePacket struct {
|
||||
OldPath string
|
||||
NewPath string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *RenamePacket) Type() PacketType {
|
||||
return PacketTypeRename
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *RenamePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// string(oldpath) + string(newpath)
|
||||
size := 4 + len(p.OldPath) + 4 + len(p.NewPath)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeRename, reqid)
|
||||
buf.AppendString(p.OldPath)
|
||||
buf.AppendString(p.NewPath)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *RenamePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.OldPath, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.NewPath, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadLinkPacket defines the SSH_FXP_READLINK packet.
|
||||
type ReadLinkPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *ReadLinkPacket) Type() PacketType {
|
||||
return PacketTypeReadLink
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *ReadLinkPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Path) // string(path)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeReadLink, reqid)
|
||||
buf.AppendString(p.Path)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *ReadLinkPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Path, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SymlinkPacket defines the SSH_FXP_SYMLINK packet.
|
||||
//
|
||||
// The order of the arguments to the SSH_FXP_SYMLINK method was inadvertently reversed.
|
||||
// Unfortunately, the reversal was not noticed until the server was widely deployed.
|
||||
// Covered in Section 3.1 of https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
|
||||
type SymlinkPacket struct {
|
||||
LinkPath string
|
||||
TargetPath string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *SymlinkPacket) Type() PacketType {
|
||||
return PacketTypeSymlink
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *SymlinkPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// string(targetpath) + string(linkpath)
|
||||
size := 4 + len(p.TargetPath) + 4 + len(p.LinkPath)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeSymlink, reqid)
|
||||
|
||||
// Arguments were inadvertently reversed.
|
||||
buf.AppendString(p.TargetPath)
|
||||
buf.AppendString(p.LinkPath)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *SymlinkPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
// Arguments were inadvertently reversed.
|
||||
if p.TargetPath, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.LinkPath, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
114
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/permissions.go
generated
vendored
Normal file
114
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/permissions.go
generated
vendored
Normal file
|
@ -0,0 +1,114 @@
|
|||
package filexfer
|
||||
|
||||
// FileMode represents a file’s mode and permission bits.
|
||||
// The bits are defined according to POSIX standards,
|
||||
// and may not apply to the OS being built for.
|
||||
type FileMode uint32
|
||||
|
||||
// Permission flags, defined here to avoid potential inconsistencies in individual OS implementations.
|
||||
const (
|
||||
ModePerm FileMode = 0o0777 // S_IRWXU | S_IRWXG | S_IRWXO
|
||||
ModeUserRead FileMode = 0o0400 // S_IRUSR
|
||||
ModeUserWrite FileMode = 0o0200 // S_IWUSR
|
||||
ModeUserExec FileMode = 0o0100 // S_IXUSR
|
||||
ModeGroupRead FileMode = 0o0040 // S_IRGRP
|
||||
ModeGroupWrite FileMode = 0o0020 // S_IWGRP
|
||||
ModeGroupExec FileMode = 0o0010 // S_IXGRP
|
||||
ModeOtherRead FileMode = 0o0004 // S_IROTH
|
||||
ModeOtherWrite FileMode = 0o0002 // S_IWOTH
|
||||
ModeOtherExec FileMode = 0o0001 // S_IXOTH
|
||||
|
||||
ModeSetUID FileMode = 0o4000 // S_ISUID
|
||||
ModeSetGID FileMode = 0o2000 // S_ISGID
|
||||
ModeSticky FileMode = 0o1000 // S_ISVTX
|
||||
|
||||
ModeType FileMode = 0xF000 // S_IFMT
|
||||
ModeNamedPipe FileMode = 0x1000 // S_IFIFO
|
||||
ModeCharDevice FileMode = 0x2000 // S_IFCHR
|
||||
ModeDir FileMode = 0x4000 // S_IFDIR
|
||||
ModeDevice FileMode = 0x6000 // S_IFBLK
|
||||
ModeRegular FileMode = 0x8000 // S_IFREG
|
||||
ModeSymlink FileMode = 0xA000 // S_IFLNK
|
||||
ModeSocket FileMode = 0xC000 // S_IFSOCK
|
||||
)
|
||||
|
||||
// IsDir reports whether m describes a directory.
|
||||
// That is, it tests for m.Type() == ModeDir.
|
||||
func (m FileMode) IsDir() bool {
|
||||
return (m & ModeType) == ModeDir
|
||||
}
|
||||
|
||||
// IsRegular reports whether m describes a regular file.
|
||||
// That is, it tests for m.Type() == ModeRegular
|
||||
func (m FileMode) IsRegular() bool {
|
||||
return (m & ModeType) == ModeRegular
|
||||
}
|
||||
|
||||
// Perm returns the POSIX permission bits in m (m & ModePerm).
|
||||
func (m FileMode) Perm() FileMode {
|
||||
return (m & ModePerm)
|
||||
}
|
||||
|
||||
// Type returns the type bits in m (m & ModeType).
|
||||
func (m FileMode) Type() FileMode {
|
||||
return (m & ModeType)
|
||||
}
|
||||
|
||||
// String returns a `-rwxrwxrwx` style string representing the `ls -l` POSIX permissions string.
|
||||
func (m FileMode) String() string {
|
||||
var buf [10]byte
|
||||
|
||||
switch m.Type() {
|
||||
case ModeRegular:
|
||||
buf[0] = '-'
|
||||
case ModeDir:
|
||||
buf[0] = 'd'
|
||||
case ModeSymlink:
|
||||
buf[0] = 'l'
|
||||
case ModeDevice:
|
||||
buf[0] = 'b'
|
||||
case ModeCharDevice:
|
||||
buf[0] = 'c'
|
||||
case ModeNamedPipe:
|
||||
buf[0] = 'p'
|
||||
case ModeSocket:
|
||||
buf[0] = 's'
|
||||
default:
|
||||
buf[0] = '?'
|
||||
}
|
||||
|
||||
const rwx = "rwxrwxrwx"
|
||||
for i, c := range rwx {
|
||||
if m&(1<<uint(9-1-i)) != 0 {
|
||||
buf[i+1] = byte(c)
|
||||
} else {
|
||||
buf[i+1] = '-'
|
||||
}
|
||||
}
|
||||
|
||||
if m&ModeSetUID != 0 {
|
||||
if buf[3] == 'x' {
|
||||
buf[3] = 's'
|
||||
} else {
|
||||
buf[3] = 'S'
|
||||
}
|
||||
}
|
||||
|
||||
if m&ModeSetGID != 0 {
|
||||
if buf[6] == 'x' {
|
||||
buf[6] = 's'
|
||||
} else {
|
||||
buf[6] = 'S'
|
||||
}
|
||||
}
|
||||
|
||||
if m&ModeSticky != 0 {
|
||||
if buf[9] == 'x' {
|
||||
buf[9] = 't'
|
||||
} else {
|
||||
buf[9] = 'T'
|
||||
}
|
||||
}
|
||||
|
||||
return string(buf[:])
|
||||
}
|
243
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/response_packets.go
generated
vendored
Normal file
243
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/response_packets.go
generated
vendored
Normal file
|
@ -0,0 +1,243 @@
|
|||
package filexfer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// StatusPacket defines the SSH_FXP_STATUS packet.
|
||||
//
|
||||
// Specified in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7
|
||||
type StatusPacket struct {
|
||||
StatusCode Status
|
||||
ErrorMessage string
|
||||
LanguageTag string
|
||||
}
|
||||
|
||||
// Error makes StatusPacket an error type.
|
||||
func (p *StatusPacket) Error() string {
|
||||
if p.ErrorMessage == "" {
|
||||
return "sftp: " + p.StatusCode.String()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("sftp: %q (%s)", p.ErrorMessage, p.StatusCode)
|
||||
}
|
||||
|
||||
// Is returns true if target is a StatusPacket with the same StatusCode,
|
||||
// or target is a Status code which is the same as SatusCode.
|
||||
func (p *StatusPacket) Is(target error) bool {
|
||||
if target, ok := target.(*StatusPacket); ok {
|
||||
return p.StatusCode == target.StatusCode
|
||||
}
|
||||
|
||||
return p.StatusCode == target
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *StatusPacket) Type() PacketType {
|
||||
return PacketTypeStatus
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *StatusPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
// uint32(error/status code) + string(error message) + string(language tag)
|
||||
size := 4 + 4 + len(p.ErrorMessage) + 4 + len(p.LanguageTag)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeStatus, reqid)
|
||||
buf.AppendUint32(uint32(p.StatusCode))
|
||||
buf.AppendString(p.ErrorMessage)
|
||||
buf.AppendString(p.LanguageTag)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *StatusPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
statusCode, err := buf.ConsumeUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.StatusCode = Status(statusCode)
|
||||
|
||||
if p.ErrorMessage, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.LanguageTag, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandlePacket defines the SSH_FXP_HANDLE packet.
|
||||
type HandlePacket struct {
|
||||
Handle string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *HandlePacket) Type() PacketType {
|
||||
return PacketTypeHandle
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *HandlePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 + len(p.Handle) // string(handle)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeHandle, reqid)
|
||||
buf.AppendString(p.Handle)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *HandlePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
if p.Handle, err = buf.ConsumeString(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DataPacket defines the SSH_FXP_DATA packet.
|
||||
type DataPacket struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *DataPacket) Type() PacketType {
|
||||
return PacketTypeData
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *DataPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 // uint32(len(data)); data content in payload
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeData, reqid)
|
||||
buf.AppendUint32(uint32(len(p.Data)))
|
||||
|
||||
return buf.Packet(p.Data)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
//
|
||||
// If p.Data is already populated, and of sufficient length to hold the data,
|
||||
// then this will copy the data into that byte slice.
|
||||
//
|
||||
// If p.Data has a length insufficient to hold the data,
|
||||
// then this will make a new slice of sufficient length, and copy the data into that.
|
||||
//
|
||||
// This means this _does not_ alias any of the data buffer that is passed in.
|
||||
func (p *DataPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
data, err := buf.ConsumeByteSlice()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(p.Data) < len(data) {
|
||||
p.Data = make([]byte, len(data))
|
||||
}
|
||||
|
||||
n := copy(p.Data, data)
|
||||
p.Data = p.Data[:n]
|
||||
return nil
|
||||
}
|
||||
|
||||
// NamePacket defines the SSH_FXP_NAME packet.
|
||||
type NamePacket struct {
|
||||
Entries []*NameEntry
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *NamePacket) Type() PacketType {
|
||||
return PacketTypeName
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *NamePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := 4 // uint32(len(entries))
|
||||
|
||||
for _, e := range p.Entries {
|
||||
size += e.Len()
|
||||
}
|
||||
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeName, reqid)
|
||||
buf.AppendUint32(uint32(len(p.Entries)))
|
||||
|
||||
for _, e := range p.Entries {
|
||||
e.MarshalInto(buf)
|
||||
}
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *NamePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
count, err := buf.ConsumeUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Entries = make([]*NameEntry, 0, count)
|
||||
|
||||
for i := uint32(0); i < count; i++ {
|
||||
var e NameEntry
|
||||
if err := e.UnmarshalFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Entries = append(p.Entries, &e)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AttrsPacket defines the SSH_FXP_ATTRS packet.
|
||||
type AttrsPacket struct {
|
||||
Attrs Attributes
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_xy value associated with this packet type.
|
||||
func (p *AttrsPacket) Type() PacketType {
|
||||
return PacketTypeAttrs
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
func (p *AttrsPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
buf := NewBuffer(b)
|
||||
if buf.Cap() < 9 {
|
||||
size := p.Attrs.Len() // ATTRS(attrs)
|
||||
buf = NewMarshalBuffer(size)
|
||||
}
|
||||
|
||||
buf.StartPacket(PacketTypeAttrs, reqid)
|
||||
p.Attrs.MarshalInto(buf)
|
||||
|
||||
return buf.Packet(payload)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
||||
// It is assumed that the uint32(request-id) has already been consumed.
|
||||
func (p *AttrsPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
||||
return p.Attrs.UnmarshalFrom(buf)
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package sftp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
|
||||
)
|
||||
|
||||
func lsFormatID(id uint32) string {
|
||||
return strconv.FormatUint(uint64(id), 10)
|
||||
}
|
||||
|
||||
type osIDLookup struct{}
|
||||
|
||||
func (osIDLookup) Filelist(*Request) (ListerAt, error) {
|
||||
return nil, errors.New("unimplemented stub")
|
||||
}
|
||||
|
||||
func (osIDLookup) LookupUserName(uid string) string {
|
||||
u, err := user.LookupId(uid)
|
||||
if err != nil {
|
||||
return uid
|
||||
}
|
||||
|
||||
return u.Username
|
||||
}
|
||||
|
||||
func (osIDLookup) LookupGroupName(gid string) string {
|
||||
g, err := user.LookupGroupId(gid)
|
||||
if err != nil {
|
||||
return gid
|
||||
}
|
||||
|
||||
return g.Name
|
||||
}
|
||||
|
||||
// runLs formats the FileInfo as per `ls -l` style, which is in the 'longname' field of a SSH_FXP_NAME entry.
|
||||
// This is a fairly simple implementation, just enough to look close to openssh in simple cases.
|
||||
func runLs(idLookup NameLookupFileLister, dirent os.FileInfo) string {
|
||||
// example from openssh sftp server:
|
||||
// crw-rw-rw- 1 root wheel 0 Jul 31 20:52 ttyvd
|
||||
// format:
|
||||
// {directory / char device / etc}{rwxrwxrwx} {number of links} owner group size month day [time (this year) | year (otherwise)] name
|
||||
|
||||
symPerms := sshfx.FileMode(fromFileMode(dirent.Mode())).String()
|
||||
|
||||
var numLinks uint64 = 1
|
||||
uid, gid := "0", "0"
|
||||
|
||||
switch sys := dirent.Sys().(type) {
|
||||
case *sshfx.Attributes:
|
||||
uid = lsFormatID(sys.UID)
|
||||
gid = lsFormatID(sys.GID)
|
||||
case *FileStat:
|
||||
uid = lsFormatID(sys.UID)
|
||||
gid = lsFormatID(sys.GID)
|
||||
default:
|
||||
numLinks, uid, gid = lsLinksUIDGID(dirent)
|
||||
}
|
||||
|
||||
if idLookup != nil {
|
||||
uid, gid = idLookup.LookupUserName(uid), idLookup.LookupGroupName(gid)
|
||||
}
|
||||
|
||||
mtime := dirent.ModTime()
|
||||
date := mtime.Format("Jan 2")
|
||||
|
||||
var yearOrTime string
|
||||
if mtime.Before(time.Now().AddDate(0, -6, 0)) {
|
||||
yearOrTime = mtime.Format("2006")
|
||||
} else {
|
||||
yearOrTime = mtime.Format("15:04")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %4d %-8s %-8s %8d %s %5s %s", symPerms, numLinks, uid, gid, dirent.Size(), date, yearOrTime, dirent.Name())
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// +build plan9
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func lsLinksUIDGID(fi os.FileInfo) (numLinks uint64, uid, gid string) {
|
||||
numLinks = 1
|
||||
uid, gid = "0", "0"
|
||||
|
||||
switch sys := fi.Sys().(type) {
|
||||
case *syscall.Dir:
|
||||
uid = sys.Uid
|
||||
gid = sys.Gid
|
||||
}
|
||||
|
||||
return numLinks, uid, gid
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// +build windows android
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func lsLinksUIDGID(fi os.FileInfo) (numLinks uint64, uid, gid string) {
|
||||
return 1, "0", "0"
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris js
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func lsLinksUIDGID(fi os.FileInfo) (numLinks uint64, uid, gid string) {
|
||||
numLinks = 1
|
||||
uid, gid = "0", "0"
|
||||
|
||||
switch sys := fi.Sys().(type) {
|
||||
case *syscall.Stat_t:
|
||||
numLinks = uint64(sys.Nlink)
|
||||
uid = lsFormatID(sys.Uid)
|
||||
gid = lsFormatID(sys.Gid)
|
||||
default:
|
||||
}
|
||||
|
||||
return numLinks, uid, gid
|
||||
}
|
|
@ -3,198 +3,39 @@ package sftp
|
|||
import (
|
||||
"path"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// ErrBadPattern indicates a globbing pattern was malformed.
|
||||
var ErrBadPattern = path.ErrBadPattern
|
||||
|
||||
// Unix separator
|
||||
const separator = "/"
|
||||
|
||||
// Match reports whether name matches the shell file name pattern.
|
||||
// The pattern syntax is:
|
||||
//
|
||||
// pattern:
|
||||
// { term }
|
||||
// term:
|
||||
// '*' matches any sequence of non-Separator characters
|
||||
// '?' matches any single non-Separator character
|
||||
// '[' [ '^' ] { character-range } ']'
|
||||
// character class (must be non-empty)
|
||||
// c matches character c (c != '*', '?', '\\', '[')
|
||||
// '\\' c matches character c
|
||||
//
|
||||
// character-range:
|
||||
// c matches character c (c != '\\', '-', ']')
|
||||
// '\\' c matches character c
|
||||
// lo '-' hi matches character c for lo <= c <= hi
|
||||
//
|
||||
// Match requires pattern to match all of name, not just a substring.
|
||||
// The only possible returned error is ErrBadPattern, when pattern
|
||||
// is malformed.
|
||||
//
|
||||
// Match reports whether name matches the shell pattern.
|
||||
//
|
||||
// This is an alias for path.Match from the standard library,
|
||||
// offered so that callers need not import the path package.
|
||||
// For details, see https://golang.org/pkg/path/#Match.
|
||||
func Match(pattern, name string) (matched bool, err error) {
|
||||
return path.Match(pattern, name)
|
||||
}
|
||||
|
||||
// detect if byte(char) is path separator
|
||||
func isPathSeparator(c byte) bool {
|
||||
return string(c) == "/"
|
||||
return c == '/'
|
||||
}
|
||||
|
||||
// scanChunk gets the next segment of pattern, which is a non-star string
|
||||
// possibly preceded by a star.
|
||||
func scanChunk(pattern string) (star bool, chunk, rest string) {
|
||||
for len(pattern) > 0 && pattern[0] == '*' {
|
||||
pattern = pattern[1:]
|
||||
star = true
|
||||
}
|
||||
inrange := false
|
||||
var i int
|
||||
Scan:
|
||||
for i = 0; i < len(pattern); i++ {
|
||||
switch pattern[i] {
|
||||
case '\\':
|
||||
|
||||
// error check handled in matchChunk: bad pattern.
|
||||
if i+1 < len(pattern) {
|
||||
i++
|
||||
}
|
||||
case '[':
|
||||
inrange = true
|
||||
case ']':
|
||||
inrange = false
|
||||
case '*':
|
||||
if !inrange {
|
||||
break Scan
|
||||
}
|
||||
}
|
||||
}
|
||||
return star, pattern[0:i], pattern[i:]
|
||||
}
|
||||
|
||||
// matchChunk checks whether chunk matches the beginning of s.
|
||||
// If so, it returns the remainder of s (after the match).
|
||||
// Chunk is all single-character operators: literals, char classes, and ?.
|
||||
func matchChunk(chunk, s string) (rest string, ok bool, err error) {
|
||||
for len(chunk) > 0 {
|
||||
if len(s) == 0 {
|
||||
return
|
||||
}
|
||||
switch chunk[0] {
|
||||
case '[':
|
||||
// character class
|
||||
r, n := utf8.DecodeRuneInString(s)
|
||||
s = s[n:]
|
||||
chunk = chunk[1:]
|
||||
// We can't end right after '[', we're expecting at least
|
||||
// a closing bracket and possibly a caret.
|
||||
if len(chunk) == 0 {
|
||||
err = ErrBadPattern
|
||||
return
|
||||
}
|
||||
// possibly negated
|
||||
negated := chunk[0] == '^'
|
||||
if negated {
|
||||
chunk = chunk[1:]
|
||||
}
|
||||
// parse all ranges
|
||||
match := false
|
||||
nrange := 0
|
||||
for {
|
||||
if len(chunk) > 0 && chunk[0] == ']' && nrange > 0 {
|
||||
chunk = chunk[1:]
|
||||
break
|
||||
}
|
||||
var lo, hi rune
|
||||
if lo, chunk, err = getEsc(chunk); err != nil {
|
||||
return
|
||||
}
|
||||
hi = lo
|
||||
if chunk[0] == '-' {
|
||||
if hi, chunk, err = getEsc(chunk[1:]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if lo <= r && r <= hi {
|
||||
match = true
|
||||
}
|
||||
nrange++
|
||||
}
|
||||
if match == negated {
|
||||
return
|
||||
}
|
||||
|
||||
case '?':
|
||||
if isPathSeparator(s[0]) {
|
||||
return
|
||||
}
|
||||
_, n := utf8.DecodeRuneInString(s)
|
||||
s = s[n:]
|
||||
chunk = chunk[1:]
|
||||
|
||||
case '\\':
|
||||
chunk = chunk[1:]
|
||||
if len(chunk) == 0 {
|
||||
err = ErrBadPattern
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
|
||||
default:
|
||||
if chunk[0] != s[0] {
|
||||
return
|
||||
}
|
||||
s = s[1:]
|
||||
chunk = chunk[1:]
|
||||
}
|
||||
}
|
||||
return s, true, nil
|
||||
}
|
||||
|
||||
// getEsc gets a possibly-escaped character from chunk, for a character class.
|
||||
func getEsc(chunk string) (r rune, nchunk string, err error) {
|
||||
if len(chunk) == 0 || chunk[0] == '-' || chunk[0] == ']' {
|
||||
err = ErrBadPattern
|
||||
return
|
||||
}
|
||||
if chunk[0] == '\\' {
|
||||
chunk = chunk[1:]
|
||||
if len(chunk) == 0 {
|
||||
err = ErrBadPattern
|
||||
return
|
||||
}
|
||||
}
|
||||
r, n := utf8.DecodeRuneInString(chunk)
|
||||
if r == utf8.RuneError && n == 1 {
|
||||
err = ErrBadPattern
|
||||
}
|
||||
nchunk = chunk[n:]
|
||||
if len(nchunk) == 0 {
|
||||
err = ErrBadPattern
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Split splits path immediately following the final Separator,
|
||||
// Split splits the path p immediately following the final slash,
|
||||
// separating it into a directory and file name component.
|
||||
// If there is no Separator in path, Split returns an empty dir
|
||||
// and file set to path.
|
||||
// The returned values have the property that path = dir+file.
|
||||
func Split(path string) (dir, file string) {
|
||||
i := len(path) - 1
|
||||
for i >= 0 && !isPathSeparator(path[i]) {
|
||||
i--
|
||||
}
|
||||
return path[:i+1], path[i+1:]
|
||||
//
|
||||
// This is an alias for path.Split from the standard library,
|
||||
// offered so that callers need not import the path package.
|
||||
// For details, see https://golang.org/pkg/path/#Split.
|
||||
func Split(p string) (dir, file string) {
|
||||
return path.Split(p)
|
||||
}
|
||||
|
||||
// Glob returns the names of all files matching pattern or nil
|
||||
// if there is no matching file. The syntax of patterns is the same
|
||||
// as in Match. The pattern may describe hierarchical names such as
|
||||
// /usr/*/bin/ed (assuming the Separator is '/').
|
||||
// /usr/*/bin/ed.
|
||||
//
|
||||
// Glob ignores file system errors such as I/O errors reading directories.
|
||||
// The only possible returned error is ErrBadPattern, when pattern
|
||||
|
@ -241,8 +82,7 @@ func cleanGlobPath(path string) string {
|
|||
switch path {
|
||||
case "":
|
||||
return "."
|
||||
case string(separator):
|
||||
// do nothing to the path
|
||||
case "/":
|
||||
return path
|
||||
default:
|
||||
return path[0 : len(path)-1] // chop off trailing separator
|
||||
|
@ -280,9 +120,12 @@ func (c *Client) glob(dir, pattern string, matches []string) (m []string, e erro
|
|||
return
|
||||
}
|
||||
|
||||
// Join joins any number of path elements into a single path, adding
|
||||
// a Separator if necessary.
|
||||
// all empty strings are ignored.
|
||||
// Join joins any number of path elements into a single path, separating
|
||||
// them with slashes.
|
||||
//
|
||||
// This is an alias for path.Join from the standard library,
|
||||
// offered so that callers need not import the path package.
|
||||
// For details, see https://golang.org/pkg/path/#Join.
|
||||
func Join(elem ...string) string {
|
||||
return path.Join(elem...)
|
||||
}
|
||||
|
@ -290,6 +133,5 @@ func Join(elem ...string) string {
|
|||
// hasMeta reports whether path contains any of the magic characters
|
||||
// recognized by Match.
|
||||
func hasMeta(path string) bool {
|
||||
// TODO(niemeyer): Should other magic characters be added here?
|
||||
return strings.ContainsAny(path, "*?[")
|
||||
return strings.ContainsAny(path, "\\*?[")
|
||||
}
|
||||
|
|
|
@ -2,8 +2,7 @@ package sftp
|
|||
|
||||
import (
|
||||
"encoding"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// all incoming packets
|
||||
|
@ -125,7 +124,7 @@ func makePacket(p rxPacket) (requestPacket, error) {
|
|||
case sshFxpExtended:
|
||||
pkt = &sshFxpExtendedPacket{}
|
||||
default:
|
||||
return nil, errors.Errorf("unhandled packet type: %s", p.pktType)
|
||||
return nil, fmt.Errorf("unhandled packet type: %s", p.pktType)
|
||||
}
|
||||
if err := pkt.UnmarshalBinary(p.pktBytes); err != nil {
|
||||
// Return partially unpacked packet to allow callers to return
|
||||
|
|
|
@ -4,12 +4,11 @@ import (
|
|||
"bytes"
|
||||
"encoding"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -38,6 +37,50 @@ func marshalString(b []byte, v string) []byte {
|
|||
return append(marshalUint32(b, uint32(len(v))), v...)
|
||||
}
|
||||
|
||||
func marshalFileInfo(b []byte, fi os.FileInfo) []byte {
|
||||
// attributes variable struct, and also variable per protocol version
|
||||
// spec version 3 attributes:
|
||||
// uint32 flags
|
||||
// uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
|
||||
// uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID
|
||||
// uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID
|
||||
// uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
|
||||
// uint32 atime present only if flag SSH_FILEXFER_ACMODTIME
|
||||
// uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME
|
||||
// uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
|
||||
// string extended_type
|
||||
// string extended_data
|
||||
// ... more extended data (extended_type - extended_data pairs),
|
||||
// so that number of pairs equals extended_count
|
||||
|
||||
flags, fileStat := fileStatFromInfo(fi)
|
||||
|
||||
b = marshalUint32(b, flags)
|
||||
if flags&sshFileXferAttrSize != 0 {
|
||||
b = marshalUint64(b, fileStat.Size)
|
||||
}
|
||||
if flags&sshFileXferAttrUIDGID != 0 {
|
||||
b = marshalUint32(b, fileStat.UID)
|
||||
b = marshalUint32(b, fileStat.GID)
|
||||
}
|
||||
if flags&sshFileXferAttrPermissions != 0 {
|
||||
b = marshalUint32(b, fileStat.Mode)
|
||||
}
|
||||
if flags&sshFileXferAttrACmodTime != 0 {
|
||||
b = marshalUint32(b, fileStat.Atime)
|
||||
b = marshalUint32(b, fileStat.Mtime)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func marshalStatus(b []byte, err StatusError) []byte {
|
||||
b = marshalUint32(b, err.Code)
|
||||
b = marshalString(b, err.msg)
|
||||
b = marshalString(b, err.lang)
|
||||
return b
|
||||
}
|
||||
|
||||
func marshal(b []byte, v interface{}) []byte {
|
||||
if v == nil {
|
||||
return b
|
||||
|
@ -116,6 +159,63 @@ func unmarshalStringSafe(b []byte) (string, []byte, error) {
|
|||
return string(b[:n]), b[n:], nil
|
||||
}
|
||||
|
||||
func unmarshalAttrs(b []byte) (*FileStat, []byte) {
|
||||
flags, b := unmarshalUint32(b)
|
||||
return unmarshalFileStat(flags, b)
|
||||
}
|
||||
|
||||
func unmarshalFileStat(flags uint32, b []byte) (*FileStat, []byte) {
|
||||
var fs FileStat
|
||||
if flags&sshFileXferAttrSize == sshFileXferAttrSize {
|
||||
fs.Size, b, _ = unmarshalUint64Safe(b)
|
||||
}
|
||||
if flags&sshFileXferAttrUIDGID == sshFileXferAttrUIDGID {
|
||||
fs.UID, b, _ = unmarshalUint32Safe(b)
|
||||
}
|
||||
if flags&sshFileXferAttrUIDGID == sshFileXferAttrUIDGID {
|
||||
fs.GID, b, _ = unmarshalUint32Safe(b)
|
||||
}
|
||||
if flags&sshFileXferAttrPermissions == sshFileXferAttrPermissions {
|
||||
fs.Mode, b, _ = unmarshalUint32Safe(b)
|
||||
}
|
||||
if flags&sshFileXferAttrACmodTime == sshFileXferAttrACmodTime {
|
||||
fs.Atime, b, _ = unmarshalUint32Safe(b)
|
||||
fs.Mtime, b, _ = unmarshalUint32Safe(b)
|
||||
}
|
||||
if flags&sshFileXferAttrExtended == sshFileXferAttrExtended {
|
||||
var count uint32
|
||||
count, b, _ = unmarshalUint32Safe(b)
|
||||
ext := make([]StatExtended, count)
|
||||
for i := uint32(0); i < count; i++ {
|
||||
var typ string
|
||||
var data string
|
||||
typ, b, _ = unmarshalStringSafe(b)
|
||||
data, b, _ = unmarshalStringSafe(b)
|
||||
ext[i] = StatExtended{
|
||||
ExtType: typ,
|
||||
ExtData: data,
|
||||
}
|
||||
}
|
||||
fs.Extended = ext
|
||||
}
|
||||
return &fs, b
|
||||
}
|
||||
|
||||
func unmarshalStatus(id uint32, data []byte) error {
|
||||
sid, data := unmarshalUint32(data)
|
||||
if sid != id {
|
||||
return &unexpectedIDErr{id, sid}
|
||||
}
|
||||
code, data := unmarshalUint32(data)
|
||||
msg, data, _ := unmarshalStringSafe(data)
|
||||
lang, _, _ := unmarshalStringSafe(data)
|
||||
return &StatusError{
|
||||
Code: code,
|
||||
msg: msg,
|
||||
lang: lang,
|
||||
}
|
||||
}
|
||||
|
||||
type packetMarshaler interface {
|
||||
marshalPacket() (header, payload []byte, err error)
|
||||
}
|
||||
|
@ -133,7 +233,7 @@ func marshalPacket(m encoding.BinaryMarshaler) (header, payload []byte, err erro
|
|||
func sendPacket(w io.Writer, m encoding.BinaryMarshaler) error {
|
||||
header, payload, err := marshalPacket(m)
|
||||
if err != nil {
|
||||
return errors.Errorf("binary marshaller failed: %v", err)
|
||||
return fmt.Errorf("binary marshaller failed: %w", err)
|
||||
}
|
||||
|
||||
length := len(header) + len(payload) - 4 // subtract the uint32(length) from the start
|
||||
|
@ -146,12 +246,12 @@ func sendPacket(w io.Writer, m encoding.BinaryMarshaler) error {
|
|||
binary.BigEndian.PutUint32(header[:4], uint32(length))
|
||||
|
||||
if _, err := w.Write(header); err != nil {
|
||||
return errors.Errorf("failed to send packet: %v", err)
|
||||
return fmt.Errorf("failed to send packet: %w", err)
|
||||
}
|
||||
|
||||
if len(payload) > 0 {
|
||||
if _, err := w.Write(payload); err != nil {
|
||||
return errors.Errorf("failed to send packet payload: %v", err)
|
||||
return fmt.Errorf("failed to send packet payload: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -639,12 +739,17 @@ func (p *sshFxpReadPacket) UnmarshalBinary(b []byte) error {
|
|||
const dataHeaderLen = 4 + 1 + 4 + 4
|
||||
|
||||
func (p *sshFxpReadPacket) getDataSlice(alloc *allocator, orderID uint32) []byte {
|
||||
dataLen := clamp(p.Len, maxTxPacket)
|
||||
dataLen := p.Len
|
||||
if dataLen > maxTxPacket {
|
||||
dataLen = maxTxPacket
|
||||
}
|
||||
|
||||
if alloc != nil {
|
||||
// GetPage returns a slice with capacity = maxMsgLength this is enough to avoid new allocations in
|
||||
// sshFxpDataPacket.MarshalBinary
|
||||
return alloc.GetPage(orderID)[:dataLen]
|
||||
}
|
||||
|
||||
// allocate with extra space for the header
|
||||
return make([]byte, dataLen, dataLen+dataHeaderLen)
|
||||
}
|
||||
|
@ -1017,6 +1122,7 @@ func (p *StatVFS) marshalPacket() ([]byte, []byte, error) {
|
|||
return header, buf.Bytes(), err
|
||||
}
|
||||
|
||||
// MarshalBinary encodes the StatVFS as an SSH_FXP_EXTENDED_REPLY packet.
|
||||
func (p *StatVFS) MarshalBinary() ([]byte, error) {
|
||||
header, payload, err := p.marshalPacket()
|
||||
return append(header, payload...), err
|
||||
|
@ -1086,7 +1192,7 @@ func (p *sshFxpExtendedPacket) UnmarshalBinary(b []byte) error {
|
|||
case "hardlink@openssh.com":
|
||||
p.SpecificPacket = &sshFxpExtendedPacketHardlink{}
|
||||
default:
|
||||
return errors.Wrapf(errUnknownExtendedPacket, "packet type %v", p.SpecificPacket)
|
||||
return fmt.Errorf("packet type %v: %w", p.SpecificPacket, errUnknownExtendedPacket)
|
||||
}
|
||||
|
||||
return p.SpecificPacket.UnmarshalBinary(bOrig)
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
package sftp
|
||||
|
||||
// bufPool provides a pool of byte-slices to be reused in various parts of the package.
|
||||
// It is safe to use concurrently through a pointer.
|
||||
type bufPool struct {
|
||||
ch chan []byte
|
||||
blen int
|
||||
}
|
||||
|
||||
func newBufPool(depth, bufLen int) *bufPool {
|
||||
return &bufPool{
|
||||
ch: make(chan []byte, depth),
|
||||
blen: bufLen,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *bufPool) Get() []byte {
|
||||
if p.blen <= 0 {
|
||||
panic("bufPool: new buffer creation length must be greater than zero")
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case b := <-p.ch:
|
||||
if cap(b) < p.blen {
|
||||
// just in case: throw away any buffer with insufficient capacity.
|
||||
continue
|
||||
}
|
||||
|
||||
return b[:p.blen]
|
||||
|
||||
default:
|
||||
return make([]byte, p.blen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *bufPool) Put(b []byte) {
|
||||
if p == nil {
|
||||
// functional default: no reuse.
|
||||
return
|
||||
}
|
||||
|
||||
if cap(b) < p.blen || cap(b) > p.blen*2 {
|
||||
// DO NOT reuse buffers with insufficient capacity.
|
||||
// This could cause panics when resizing to p.blen.
|
||||
|
||||
// DO NOT reuse buffers with excessive capacity.
|
||||
// This could cause memory leaks.
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case p.ch <- b:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
type resChanPool chan chan result
|
||||
|
||||
func newResChanPool(depth int) resChanPool {
|
||||
return make(chan chan result, depth)
|
||||
}
|
||||
|
||||
func (p resChanPool) Get() chan result {
|
||||
select {
|
||||
case ch := <-p:
|
||||
return ch
|
||||
default:
|
||||
return make(chan result, 1)
|
||||
}
|
||||
}
|
||||
|
||||
func (p resChanPool) Put(ch chan result) {
|
||||
select {
|
||||
case p <- ch:
|
||||
default:
|
||||
}
|
||||
}
|
|
@ -58,6 +58,6 @@ func (a FileStat) FileMode() os.FileMode {
|
|||
// Attributes parses file attributes byte blob and return them in a
|
||||
// FileStat object.
|
||||
func (r *Request) Attributes() *FileStat {
|
||||
fs, _ := getFileStat(r.Flags, r.Attrs)
|
||||
fs, _ := unmarshalFileStat(r.Flags, r.Attrs)
|
||||
return fs
|
||||
}
|
||||
|
|
|
@ -37,18 +37,18 @@ func (e fxerr) Error() string {
|
|||
case ErrSSHFxEOF:
|
||||
return "EOF"
|
||||
case ErrSSHFxNoSuchFile:
|
||||
return "No Such File"
|
||||
return "no such file"
|
||||
case ErrSSHFxPermissionDenied:
|
||||
return "Permission Denied"
|
||||
return "permission denied"
|
||||
case ErrSSHFxBadMessage:
|
||||
return "Bad Message"
|
||||
return "bad message"
|
||||
case ErrSSHFxNoConnection:
|
||||
return "No Connection"
|
||||
return "no connection"
|
||||
case ErrSSHFxConnectionLost:
|
||||
return "Connection Lost"
|
||||
return "connection lost"
|
||||
case ErrSSHFxOpUnsupported:
|
||||
return "Operation Unsupported"
|
||||
return "operation unsupported"
|
||||
default:
|
||||
return "Failure"
|
||||
return "failure"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -464,10 +464,19 @@ func (fs *root) Lstat(r *Request) (ListerAt, error) {
|
|||
return listerat{file}, nil
|
||||
}
|
||||
|
||||
// implements RealpathFileLister interface
|
||||
func (fs *root) Realpath(p string) string {
|
||||
if fs.startDirectory == "" || fs.startDirectory == "/" {
|
||||
return cleanPath(p)
|
||||
}
|
||||
return cleanPathWithBase(fs.startDirectory, p)
|
||||
}
|
||||
|
||||
// In memory file-system-y thing that the Hanlders live on
|
||||
type root struct {
|
||||
rootFile *memFile
|
||||
mockErr error
|
||||
rootFile *memFile
|
||||
mockErr error
|
||||
startDirectory string
|
||||
|
||||
mu sync.Mutex
|
||||
files map[string]*memFile
|
||||
|
|
|
@ -86,6 +86,23 @@ type LstatFileLister interface {
|
|||
Lstat(*Request) (ListerAt, error)
|
||||
}
|
||||
|
||||
// RealPathFileLister is a FileLister that implements the Realpath method.
|
||||
// We use "/" as start directory for relative paths, implementing this
|
||||
// interface you can customize the start directory.
|
||||
// You have to return an absolute POSIX path.
|
||||
type RealPathFileLister interface {
|
||||
FileLister
|
||||
RealPath(string) string
|
||||
}
|
||||
|
||||
// NameLookupFileLister is a FileLister that implmeents the LookupUsername and LookupGroupName methods.
|
||||
// If this interface is implemented, then longname ls formatting will use these to convert usernames and groupnames.
|
||||
type NameLookupFileLister interface {
|
||||
FileLister
|
||||
LookupUserName(string) string
|
||||
LookupGroupName(string) string
|
||||
}
|
||||
|
||||
// ListerAt does for file lists what io.ReaderAt does for files.
|
||||
// ListAt should return the number of entries copied and an io.EOF
|
||||
// error if at end of list. This is testable by comparing how many you
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
|
||||
package sftp
|
||||
|
||||
import "syscall"
|
||||
import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func fakeFileInfoSys() interface{} {
|
||||
return &syscall.Dir{}
|
||||
|
@ -11,3 +15,20 @@ func fakeFileInfoSys() interface{} {
|
|||
func testOsSys(sys interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func toLocalPath(p string) string {
|
||||
lp := filepath.FromSlash(p)
|
||||
|
||||
if path.IsAbs(p) {
|
||||
tmp := lp[1:]
|
||||
|
||||
if filepath.IsAbs(tmp) {
|
||||
// If the FromSlash without any starting slashes is absolute,
|
||||
// then we have a filepath encoded with a prefix '/'.
|
||||
// e.g. "/#s/boot" to "#s/boot"
|
||||
return tmp
|
||||
}
|
||||
}
|
||||
|
||||
return lp
|
||||
}
|
||||
|
|
|
@ -2,13 +2,12 @@ package sftp
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var maxTxPacket uint32 = 1 << 15
|
||||
|
@ -23,12 +22,14 @@ type Handlers struct {
|
|||
|
||||
// RequestServer abstracts the sftp protocol with an http request-like protocol
|
||||
type RequestServer struct {
|
||||
Handlers Handlers
|
||||
|
||||
*serverConn
|
||||
Handlers Handlers
|
||||
pktMgr *packetManager
|
||||
openRequests map[string]*Request
|
||||
openRequestLock sync.RWMutex
|
||||
handleCount int
|
||||
pktMgr *packetManager
|
||||
|
||||
mu sync.RWMutex
|
||||
handleCount int
|
||||
openRequests map[string]*Request
|
||||
}
|
||||
|
||||
// A RequestServerOption is a function which applies configuration to a RequestServer.
|
||||
|
@ -56,9 +57,11 @@ func NewRequestServer(rwc io.ReadWriteCloser, h Handlers, options ...RequestServ
|
|||
},
|
||||
}
|
||||
rs := &RequestServer{
|
||||
serverConn: svrConn,
|
||||
Handlers: h,
|
||||
pktMgr: newPktMgr(svrConn),
|
||||
Handlers: h,
|
||||
|
||||
serverConn: svrConn,
|
||||
pktMgr: newPktMgr(svrConn),
|
||||
|
||||
openRequests: make(map[string]*Request),
|
||||
}
|
||||
|
||||
|
@ -70,13 +73,15 @@ func NewRequestServer(rwc io.ReadWriteCloser, h Handlers, options ...RequestServ
|
|||
|
||||
// New Open packet/Request
|
||||
func (rs *RequestServer) nextRequest(r *Request) string {
|
||||
rs.openRequestLock.Lock()
|
||||
defer rs.openRequestLock.Unlock()
|
||||
rs.mu.Lock()
|
||||
defer rs.mu.Unlock()
|
||||
|
||||
rs.handleCount++
|
||||
handle := strconv.Itoa(rs.handleCount)
|
||||
r.handle = handle
|
||||
rs.openRequests[handle] = r
|
||||
return handle
|
||||
|
||||
r.handle = strconv.Itoa(rs.handleCount)
|
||||
rs.openRequests[r.handle] = r
|
||||
|
||||
return r.handle
|
||||
}
|
||||
|
||||
// Returns Request from openRequests, bool is false if it is missing.
|
||||
|
@ -85,20 +90,23 @@ func (rs *RequestServer) nextRequest(r *Request) string {
|
|||
// you can do different things with. What you are doing with it are denoted by
|
||||
// the first packet of that type (read/write/etc).
|
||||
func (rs *RequestServer) getRequest(handle string) (*Request, bool) {
|
||||
rs.openRequestLock.RLock()
|
||||
defer rs.openRequestLock.RUnlock()
|
||||
rs.mu.RLock()
|
||||
defer rs.mu.RUnlock()
|
||||
|
||||
r, ok := rs.openRequests[handle]
|
||||
return r, ok
|
||||
}
|
||||
|
||||
// Close the Request and clear from openRequests map
|
||||
func (rs *RequestServer) closeRequest(handle string) error {
|
||||
rs.openRequestLock.Lock()
|
||||
defer rs.openRequestLock.Unlock()
|
||||
rs.mu.Lock()
|
||||
defer rs.mu.Unlock()
|
||||
|
||||
if r, ok := rs.openRequests[handle]; ok {
|
||||
delete(rs.openRequests, handle)
|
||||
return r.close()
|
||||
}
|
||||
|
||||
return EBADF
|
||||
}
|
||||
|
||||
|
@ -122,8 +130,8 @@ func (rs *RequestServer) serveLoop(pktChan chan<- orderedRequest) error {
|
|||
|
||||
pkt, err = makePacket(rxPacket{fxp(pktType), pktBytes})
|
||||
if err != nil {
|
||||
switch errors.Cause(err) {
|
||||
case errUnknownExtendedPacket:
|
||||
switch {
|
||||
case errors.Is(err, errUnknownExtendedPacket):
|
||||
// do nothing
|
||||
default:
|
||||
debug("makePacket err: %v", err)
|
||||
|
@ -143,8 +151,10 @@ func (rs *RequestServer) Serve() error {
|
|||
rs.pktMgr.alloc.Free()
|
||||
}
|
||||
}()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
runWorker := func(ch chan orderedRequest) {
|
||||
wg.Add(1)
|
||||
|
@ -161,8 +171,8 @@ func (rs *RequestServer) Serve() error {
|
|||
|
||||
wg.Wait() // wait for all workers to exit
|
||||
|
||||
rs.openRequestLock.Lock()
|
||||
defer rs.openRequestLock.Unlock()
|
||||
rs.mu.Lock()
|
||||
defer rs.mu.Unlock()
|
||||
|
||||
// make sure all open requests are properly closed
|
||||
// (eg. possible on dropped connections, client crashes, etc.)
|
||||
|
@ -179,9 +189,7 @@ func (rs *RequestServer) Serve() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (rs *RequestServer) packetWorker(
|
||||
ctx context.Context, pktChan chan orderedRequest,
|
||||
) error {
|
||||
func (rs *RequestServer) packetWorker(ctx context.Context, pktChan chan orderedRequest) error {
|
||||
for pkt := range pktChan {
|
||||
orderID := pkt.orderID()
|
||||
if epkt, ok := pkt.requestPacket.(*sshFxpExtendedPacket); ok {
|
||||
|
@ -198,7 +206,13 @@ func (rs *RequestServer) packetWorker(
|
|||
handle := pkt.getHandle()
|
||||
rpkt = statusFromError(pkt.ID, rs.closeRequest(handle))
|
||||
case *sshFxpRealpathPacket:
|
||||
rpkt = cleanPacketPath(pkt)
|
||||
var realPath string
|
||||
if realPather, ok := rs.Handlers.FileList.(RealPathFileLister); ok {
|
||||
realPath = realPather.RealPath(pkt.getPath())
|
||||
} else {
|
||||
realPath = cleanPath(pkt.getPath())
|
||||
}
|
||||
rpkt = cleanPacketPath(pkt, realPath)
|
||||
case *sshFxpOpendirPacket:
|
||||
request := requestFromPacket(ctx, pkt)
|
||||
handle := rs.nextRequest(request)
|
||||
|
@ -263,14 +277,13 @@ func (rs *RequestServer) packetWorker(
|
|||
}
|
||||
|
||||
// clean and return name packet for file
|
||||
func cleanPacketPath(pkt *sshFxpRealpathPacket) responsePacket {
|
||||
path := cleanPath(pkt.getPath())
|
||||
func cleanPacketPath(pkt *sshFxpRealpathPacket, realPath string) responsePacket {
|
||||
return &sshFxpNamePacket{
|
||||
ID: pkt.id(),
|
||||
NameAttrs: []*sshFxpNameAttr{
|
||||
{
|
||||
Name: path,
|
||||
LongName: path,
|
||||
Name: realPath,
|
||||
LongName: realPath,
|
||||
Attrs: emptyFileStat,
|
||||
},
|
||||
},
|
||||
|
@ -279,9 +292,13 @@ func cleanPacketPath(pkt *sshFxpRealpathPacket) responsePacket {
|
|||
|
||||
// Makes sure we have a clean POSIX (/) absolute path to work with
|
||||
func cleanPath(p string) string {
|
||||
p = filepath.ToSlash(p)
|
||||
if !path.IsAbs(p) {
|
||||
p = "/" + p
|
||||
}
|
||||
return path.Clean(p)
|
||||
return cleanPathWithBase("/", p)
|
||||
}
|
||||
|
||||
func cleanPathWithBase(base, p string) string {
|
||||
p = filepath.ToSlash(filepath.Clean(p))
|
||||
if !path.IsAbs(p) {
|
||||
return path.Join(base, p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
|
|
@ -21,3 +21,7 @@ func testOsSys(sys interface{}) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toLocalPath(p string) string {
|
||||
return p
|
||||
}
|
||||
|
|
|
@ -2,20 +2,125 @@ package sftp
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// MaxFilelist is the max number of files to return in a readdir batch.
|
||||
var MaxFilelist int64 = 100
|
||||
|
||||
// state encapsulates the reader/writer/readdir from handlers.
|
||||
type state struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
writerAt io.WriterAt
|
||||
readerAt io.ReaderAt
|
||||
writerAtReaderAt WriterAtReaderAt
|
||||
listerAt ListerAt
|
||||
lsoffset int64
|
||||
}
|
||||
|
||||
// copy returns a shallow copy the state.
|
||||
// This is broken out to specific fields,
|
||||
// because we have to copy around the mutex in state.
|
||||
func (s *state) copy() state {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return state{
|
||||
writerAt: s.writerAt,
|
||||
readerAt: s.readerAt,
|
||||
writerAtReaderAt: s.writerAtReaderAt,
|
||||
listerAt: s.listerAt,
|
||||
lsoffset: s.lsoffset,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *state) setReaderAt(rd io.ReaderAt) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.readerAt = rd
|
||||
}
|
||||
|
||||
func (s *state) getReaderAt() io.ReaderAt {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.readerAt
|
||||
}
|
||||
|
||||
func (s *state) setWriterAt(rd io.WriterAt) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.writerAt = rd
|
||||
}
|
||||
|
||||
func (s *state) getWriterAt() io.WriterAt {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.writerAt
|
||||
}
|
||||
|
||||
func (s *state) setWriterAtReaderAt(rw WriterAtReaderAt) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.writerAtReaderAt = rw
|
||||
}
|
||||
|
||||
func (s *state) getWriterAtReaderAt() WriterAtReaderAt {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.writerAtReaderAt
|
||||
}
|
||||
|
||||
func (s *state) getAllReaderWriters() (io.ReaderAt, io.WriterAt, WriterAtReaderAt) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.readerAt, s.writerAt, s.writerAtReaderAt
|
||||
}
|
||||
|
||||
// Returns current offset for file list
|
||||
func (s *state) lsNext() int64 {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.lsoffset
|
||||
}
|
||||
|
||||
// Increases next offset
|
||||
func (s *state) lsInc(offset int64) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.lsoffset += offset
|
||||
}
|
||||
|
||||
// manage file read/write state
|
||||
func (s *state) setListerAt(la ListerAt) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.listerAt = la
|
||||
}
|
||||
|
||||
func (s *state) getListerAt() ListerAt {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.listerAt
|
||||
}
|
||||
|
||||
// Request contains the data and state for the incoming service request.
|
||||
type Request struct {
|
||||
// Get, Put, Setstat, Stat, Rename, Remove
|
||||
|
@ -26,20 +131,40 @@ type Request struct {
|
|||
Attrs []byte // convert to sub-struct
|
||||
Target string // for renames and sym-links
|
||||
handle string
|
||||
|
||||
// reader/writer/readdir from handlers
|
||||
state state
|
||||
state
|
||||
|
||||
// context lasts duration of request
|
||||
ctx context.Context
|
||||
cancelCtx context.CancelFunc
|
||||
}
|
||||
|
||||
type state struct {
|
||||
*sync.RWMutex
|
||||
writerAt io.WriterAt
|
||||
readerAt io.ReaderAt
|
||||
writerReaderAt WriterAtReaderAt
|
||||
listerAt ListerAt
|
||||
lsoffset int64
|
||||
// NewRequest creates a new Request object.
|
||||
func NewRequest(method, path string) *Request {
|
||||
return &Request{
|
||||
Method: method,
|
||||
Filepath: cleanPath(path),
|
||||
}
|
||||
}
|
||||
|
||||
// copy returns a shallow copy of existing request.
|
||||
// This is broken out to specific fields,
|
||||
// because we have to copy around the mutex in state.
|
||||
func (r *Request) copy() *Request {
|
||||
return &Request{
|
||||
Method: r.Method,
|
||||
Filepath: r.Filepath,
|
||||
Flags: r.Flags,
|
||||
Attrs: r.Attrs,
|
||||
Target: r.Target,
|
||||
handle: r.handle,
|
||||
|
||||
state: r.state.copy(),
|
||||
|
||||
ctx: r.ctx,
|
||||
cancelCtx: r.cancelCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// New Request initialized based on packet data
|
||||
|
@ -66,21 +191,6 @@ func requestFromPacket(ctx context.Context, pkt hasPath) *Request {
|
|||
return request
|
||||
}
|
||||
|
||||
// NewRequest creates a new Request object.
|
||||
func NewRequest(method, path string) *Request {
|
||||
return &Request{Method: method, Filepath: cleanPath(path),
|
||||
state: state{RWMutex: new(sync.RWMutex)}}
|
||||
}
|
||||
|
||||
// shallow copy of existing request
|
||||
func (r *Request) copy() *Request {
|
||||
r.state.Lock()
|
||||
defer r.state.Unlock()
|
||||
r2 := new(Request)
|
||||
*r2 = *r
|
||||
return r2
|
||||
}
|
||||
|
||||
// Context returns the request's context. To change the context,
|
||||
// use WithContext.
|
||||
//
|
||||
|
@ -108,33 +218,6 @@ func (r *Request) WithContext(ctx context.Context) *Request {
|
|||
return r2
|
||||
}
|
||||
|
||||
// Returns current offset for file list
|
||||
func (r *Request) lsNext() int64 {
|
||||
r.state.RLock()
|
||||
defer r.state.RUnlock()
|
||||
return r.state.lsoffset
|
||||
}
|
||||
|
||||
// Increases next offset
|
||||
func (r *Request) lsInc(offset int64) {
|
||||
r.state.Lock()
|
||||
defer r.state.Unlock()
|
||||
r.state.lsoffset = r.state.lsoffset + offset
|
||||
}
|
||||
|
||||
// manage file read/write state
|
||||
func (r *Request) setListerState(la ListerAt) {
|
||||
r.state.Lock()
|
||||
defer r.state.Unlock()
|
||||
r.state.listerAt = la
|
||||
}
|
||||
|
||||
func (r *Request) getLister() ListerAt {
|
||||
r.state.RLock()
|
||||
defer r.state.RUnlock()
|
||||
return r.state.listerAt
|
||||
}
|
||||
|
||||
// Close reader/writer if possible
|
||||
func (r *Request) close() error {
|
||||
defer func() {
|
||||
|
@ -143,11 +226,7 @@ func (r *Request) close() error {
|
|||
}
|
||||
}()
|
||||
|
||||
r.state.RLock()
|
||||
wr := r.state.writerAt
|
||||
rd := r.state.readerAt
|
||||
rw := r.state.writerReaderAt
|
||||
r.state.RUnlock()
|
||||
rd, wr, rw := r.getAllReaderWriters()
|
||||
|
||||
var err error
|
||||
|
||||
|
@ -164,7 +243,8 @@ func (r *Request) close() error {
|
|||
if err2 := c.Close(); err == nil {
|
||||
// update error if it is still nil
|
||||
err = err2
|
||||
r.state.writerReaderAt = nil
|
||||
|
||||
r.setWriterAtReaderAt(nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,11 +264,7 @@ func (r *Request) transferError(err error) {
|
|||
return
|
||||
}
|
||||
|
||||
r.state.RLock()
|
||||
wr := r.state.writerAt
|
||||
rd := r.state.readerAt
|
||||
rw := r.state.writerReaderAt
|
||||
r.state.RUnlock()
|
||||
rd, wr, rw := r.getAllReaderWriters()
|
||||
|
||||
if t, ok := wr.(TransferError); ok {
|
||||
t.TransferError(err)
|
||||
|
@ -219,8 +295,7 @@ func (r *Request) call(handlers Handlers, pkt requestPacket, alloc *allocator, o
|
|||
case "Stat", "Lstat", "Readlink":
|
||||
return filestat(handlers.FileList, r, pkt)
|
||||
default:
|
||||
return statusFromError(pkt.id(),
|
||||
errors.Errorf("unexpected method: %s", r.Method))
|
||||
return statusFromError(pkt.id(), fmt.Errorf("unexpected method: %s", r.Method))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,8 +314,13 @@ func (r *Request) open(h Handlers, pkt requestPacket) responsePacket {
|
|||
if err != nil {
|
||||
return statusFromError(id, err)
|
||||
}
|
||||
r.state.writerReaderAt = rw
|
||||
return &sshFxpHandlePacket{ID: id, Handle: r.handle}
|
||||
|
||||
r.setWriterAtReaderAt(rw)
|
||||
|
||||
return &sshFxpHandlePacket{
|
||||
ID: id,
|
||||
Handle: r.handle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,18 +329,26 @@ func (r *Request) open(h Handlers, pkt requestPacket) responsePacket {
|
|||
if err != nil {
|
||||
return statusFromError(id, err)
|
||||
}
|
||||
r.state.writerAt = wr
|
||||
|
||||
r.setWriterAt(wr)
|
||||
|
||||
case flags.Read:
|
||||
r.Method = "Get"
|
||||
rd, err := h.FileGet.Fileread(r)
|
||||
if err != nil {
|
||||
return statusFromError(id, err)
|
||||
}
|
||||
r.state.readerAt = rd
|
||||
|
||||
r.setReaderAt(rd)
|
||||
|
||||
default:
|
||||
return statusFromError(id, errors.New("bad file flags"))
|
||||
}
|
||||
return &sshFxpHandlePacket{ID: id, Handle: r.handle}
|
||||
|
||||
return &sshFxpHandlePacket{
|
||||
ID: id,
|
||||
Handle: r.handle,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Request) opendir(h Handlers, pkt requestPacket) responsePacket {
|
||||
|
@ -269,25 +357,30 @@ func (r *Request) opendir(h Handlers, pkt requestPacket) responsePacket {
|
|||
if err != nil {
|
||||
return statusFromError(pkt.id(), wrapPathError(r.Filepath, err))
|
||||
}
|
||||
r.state.listerAt = la
|
||||
return &sshFxpHandlePacket{ID: pkt.id(), Handle: r.handle}
|
||||
|
||||
r.setListerAt(la)
|
||||
|
||||
return &sshFxpHandlePacket{
|
||||
ID: pkt.id(),
|
||||
Handle: r.handle,
|
||||
}
|
||||
}
|
||||
|
||||
// wrap FileReader handler
|
||||
func fileget(h FileReader, r *Request, pkt requestPacket, alloc *allocator, orderID uint32) responsePacket {
|
||||
r.state.RLock()
|
||||
reader := r.state.readerAt
|
||||
r.state.RUnlock()
|
||||
if reader == nil {
|
||||
rd := r.getReaderAt()
|
||||
if rd == nil {
|
||||
return statusFromError(pkt.id(), errors.New("unexpected read packet"))
|
||||
}
|
||||
|
||||
data, offset, _ := packetData(pkt, alloc, orderID)
|
||||
n, err := reader.ReadAt(data, offset)
|
||||
|
||||
n, err := rd.ReadAt(data, offset)
|
||||
// only return EOF error if no data left to read
|
||||
if err != nil && (err != io.EOF || n == 0) {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
|
||||
return &sshFxpDataPacket{
|
||||
ID: pkt.id(),
|
||||
Length: uint32(n),
|
||||
|
@ -297,43 +390,46 @@ func fileget(h FileReader, r *Request, pkt requestPacket, alloc *allocator, orde
|
|||
|
||||
// wrap FileWriter handler
|
||||
func fileput(h FileWriter, r *Request, pkt requestPacket, alloc *allocator, orderID uint32) responsePacket {
|
||||
r.state.RLock()
|
||||
writer := r.state.writerAt
|
||||
r.state.RUnlock()
|
||||
if writer == nil {
|
||||
wr := r.getWriterAt()
|
||||
if wr == nil {
|
||||
return statusFromError(pkt.id(), errors.New("unexpected write packet"))
|
||||
}
|
||||
|
||||
data, offset, _ := packetData(pkt, alloc, orderID)
|
||||
_, err := writer.WriteAt(data, offset)
|
||||
|
||||
_, err := wr.WriteAt(data, offset)
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
|
||||
// wrap OpenFileWriter handler
|
||||
func fileputget(h FileWriter, r *Request, pkt requestPacket, alloc *allocator, orderID uint32) responsePacket {
|
||||
r.state.RLock()
|
||||
writerReader := r.state.writerReaderAt
|
||||
r.state.RUnlock()
|
||||
if writerReader == nil {
|
||||
rw := r.getWriterAtReaderAt()
|
||||
if rw == nil {
|
||||
return statusFromError(pkt.id(), errors.New("unexpected write and read packet"))
|
||||
}
|
||||
|
||||
switch p := pkt.(type) {
|
||||
case *sshFxpReadPacket:
|
||||
data, offset := p.getDataSlice(alloc, orderID), int64(p.Offset)
|
||||
n, err := writerReader.ReadAt(data, offset)
|
||||
|
||||
n, err := rw.ReadAt(data, offset)
|
||||
// only return EOF error if no data left to read
|
||||
if err != nil && (err != io.EOF || n == 0) {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
|
||||
return &sshFxpDataPacket{
|
||||
ID: pkt.id(),
|
||||
Length: uint32(n),
|
||||
Data: data[:n],
|
||||
}
|
||||
|
||||
case *sshFxpWritePacket:
|
||||
data, offset := p.Data, int64(p.Offset)
|
||||
_, err := writerReader.WriteAt(data, offset)
|
||||
|
||||
_, err := rw.WriteAt(data, offset)
|
||||
return statusFromError(pkt.id(), err)
|
||||
|
||||
default:
|
||||
return statusFromError(pkt.id(), errors.New("unexpected packet type for read or write"))
|
||||
}
|
||||
|
@ -358,7 +454,8 @@ func filecmd(h FileCmder, r *Request, pkt requestPacket) responsePacket {
|
|||
r.Attrs = p.Attrs.([]byte)
|
||||
}
|
||||
|
||||
if r.Method == "PosixRename" {
|
||||
switch r.Method {
|
||||
case "PosixRename":
|
||||
if posixRenamer, ok := h.(PosixRenameFileCmder); ok {
|
||||
err := posixRenamer.PosixRename(r)
|
||||
return statusFromError(pkt.id(), err)
|
||||
|
@ -368,9 +465,8 @@ func filecmd(h FileCmder, r *Request, pkt requestPacket) responsePacket {
|
|||
r.Method = "Rename"
|
||||
err := h.Filecmd(r)
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
|
||||
if r.Method == "StatVFS" {
|
||||
case "StatVFS":
|
||||
if statVFSCmdr, ok := h.(StatVFSFileCmder); ok {
|
||||
stat, err := statVFSCmdr.StatVFS(r)
|
||||
if err != nil {
|
||||
|
@ -389,8 +485,7 @@ func filecmd(h FileCmder, r *Request, pkt requestPacket) responsePacket {
|
|||
|
||||
// wrap FileLister handler
|
||||
func filelist(h FileLister, r *Request, pkt requestPacket) responsePacket {
|
||||
var err error
|
||||
lister := r.getLister()
|
||||
lister := r.getListerAt()
|
||||
if lister == nil {
|
||||
return statusFromError(pkt.id(), errors.New("unexpected dir packet"))
|
||||
}
|
||||
|
@ -404,25 +499,31 @@ func filelist(h FileLister, r *Request, pkt requestPacket) responsePacket {
|
|||
|
||||
switch r.Method {
|
||||
case "List":
|
||||
if err != nil && err != io.EOF {
|
||||
if err != nil && (err != io.EOF || n == 0) {
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
if err == io.EOF && n == 0 {
|
||||
return statusFromError(pkt.id(), io.EOF)
|
||||
}
|
||||
dirname := filepath.ToSlash(path.Base(r.Filepath))
|
||||
ret := &sshFxpNamePacket{ID: pkt.id()}
|
||||
|
||||
nameAttrs := make([]*sshFxpNameAttr, 0, len(finfo))
|
||||
|
||||
// If the type conversion fails, we get untyped `nil`,
|
||||
// which is handled by not looking up any names.
|
||||
idLookup, _ := h.(NameLookupFileLister)
|
||||
|
||||
for _, fi := range finfo {
|
||||
ret.NameAttrs = append(ret.NameAttrs, &sshFxpNameAttr{
|
||||
nameAttrs = append(nameAttrs, &sshFxpNameAttr{
|
||||
Name: fi.Name(),
|
||||
LongName: runLs(dirname, fi),
|
||||
LongName: runLs(idLookup, fi),
|
||||
Attrs: []interface{}{fi},
|
||||
})
|
||||
}
|
||||
return ret
|
||||
|
||||
return &sshFxpNamePacket{
|
||||
ID: pkt.id(),
|
||||
NameAttrs: nameAttrs,
|
||||
}
|
||||
|
||||
default:
|
||||
err = errors.Errorf("unexpected method: %s", r.Method)
|
||||
err = fmt.Errorf("unexpected method: %s", r.Method)
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
}
|
||||
|
@ -455,8 +556,11 @@ func filestat(h FileLister, r *Request, pkt requestPacket) responsePacket {
|
|||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
if n == 0 {
|
||||
err = &os.PathError{Op: strings.ToLower(r.Method), Path: r.Filepath,
|
||||
Err: syscall.ENOENT}
|
||||
err = &os.PathError{
|
||||
Op: strings.ToLower(r.Method),
|
||||
Path: r.Filepath,
|
||||
Err: syscall.ENOENT,
|
||||
}
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
return &sshFxpStatResponse{
|
||||
|
@ -468,8 +572,11 @@ func filestat(h FileLister, r *Request, pkt requestPacket) responsePacket {
|
|||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
if n == 0 {
|
||||
err = &os.PathError{Op: "readlink", Path: r.Filepath,
|
||||
Err: syscall.ENOENT}
|
||||
err = &os.PathError{
|
||||
Op: "readlink",
|
||||
Path: r.Filepath,
|
||||
Err: syscall.ENOENT,
|
||||
}
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
filename := finfo[0].Name()
|
||||
|
@ -484,7 +591,7 @@ func filestat(h FileLister, r *Request, pkt requestPacket) responsePacket {
|
|||
},
|
||||
}
|
||||
default:
|
||||
err = errors.Errorf("unexpected method: %s", r.Method)
|
||||
err = fmt.Errorf("unexpected method: %s", r.Method)
|
||||
return statusFromError(pkt.id(), err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
package sftp
|
||||
|
||||
import "syscall"
|
||||
import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func fakeFileInfoSys() interface{} {
|
||||
return syscall.Win32FileAttributeData{}
|
||||
|
@ -9,3 +13,32 @@ func fakeFileInfoSys() interface{} {
|
|||
func testOsSys(sys interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func toLocalPath(p string) string {
|
||||
lp := filepath.FromSlash(p)
|
||||
|
||||
if path.IsAbs(p) {
|
||||
tmp := lp
|
||||
for len(tmp) > 0 && tmp[0] == '\\' {
|
||||
tmp = tmp[1:]
|
||||
}
|
||||
|
||||
if filepath.IsAbs(tmp) {
|
||||
// If the FromSlash without any starting slashes is absolute,
|
||||
// then we have a filepath encoded with a prefix '/'.
|
||||
// e.g. "/C:/Windows" to "C:\\Windows"
|
||||
return tmp
|
||||
}
|
||||
|
||||
tmp += "\\"
|
||||
|
||||
if filepath.IsAbs(tmp) {
|
||||
// If the FromSlash without any starting slashes but with extra end slash is absolute,
|
||||
// then we have a filepath encoded with a prefix '/' and a dropped '/' at the end.
|
||||
// e.g. "/C:" to "C:\\"
|
||||
return tmp
|
||||
}
|
||||
}
|
||||
|
||||
return lp
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ package sftp
|
|||
|
||||
import (
|
||||
"encoding"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -13,8 +14,6 @@ import (
|
|||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -175,7 +174,7 @@ func handlePacket(s *Server, p orderedRequest) error {
|
|||
}
|
||||
case *sshFxpStatPacket:
|
||||
// stat the requested file
|
||||
info, err := os.Stat(p.Path)
|
||||
info, err := os.Stat(toLocalPath(p.Path))
|
||||
rpkt = &sshFxpStatResponse{
|
||||
ID: p.ID,
|
||||
info: info,
|
||||
|
@ -185,7 +184,7 @@ func handlePacket(s *Server, p orderedRequest) error {
|
|||
}
|
||||
case *sshFxpLstatPacket:
|
||||
// stat the requested file
|
||||
info, err := os.Lstat(p.Path)
|
||||
info, err := os.Lstat(toLocalPath(p.Path))
|
||||
rpkt = &sshFxpStatResponse{
|
||||
ID: p.ID,
|
||||
info: info,
|
||||
|
@ -209,24 +208,24 @@ func handlePacket(s *Server, p orderedRequest) error {
|
|||
}
|
||||
case *sshFxpMkdirPacket:
|
||||
// TODO FIXME: ignore flags field
|
||||
err := os.Mkdir(p.Path, 0755)
|
||||
err := os.Mkdir(toLocalPath(p.Path), 0755)
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
case *sshFxpRmdirPacket:
|
||||
err := os.Remove(p.Path)
|
||||
err := os.Remove(toLocalPath(p.Path))
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
case *sshFxpRemovePacket:
|
||||
err := os.Remove(p.Filename)
|
||||
err := os.Remove(toLocalPath(p.Filename))
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
case *sshFxpRenamePacket:
|
||||
err := os.Rename(p.Oldpath, p.Newpath)
|
||||
err := os.Rename(toLocalPath(p.Oldpath), toLocalPath(p.Newpath))
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
case *sshFxpSymlinkPacket:
|
||||
err := os.Symlink(p.Targetpath, p.Linkpath)
|
||||
err := os.Symlink(toLocalPath(p.Targetpath), toLocalPath(p.Linkpath))
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
case *sshFxpClosePacket:
|
||||
rpkt = statusFromError(p.ID, s.closeHandle(p.Handle))
|
||||
case *sshFxpReadlinkPacket:
|
||||
f, err := os.Readlink(p.Path)
|
||||
f, err := os.Readlink(toLocalPath(p.Path))
|
||||
rpkt = &sshFxpNamePacket{
|
||||
ID: p.ID,
|
||||
NameAttrs: []*sshFxpNameAttr{
|
||||
|
@ -241,7 +240,7 @@ func handlePacket(s *Server, p orderedRequest) error {
|
|||
rpkt = statusFromError(p.ID, err)
|
||||
}
|
||||
case *sshFxpRealpathPacket:
|
||||
f, err := filepath.Abs(p.Path)
|
||||
f, err := filepath.Abs(toLocalPath(p.Path))
|
||||
f = cleanPath(f)
|
||||
rpkt = &sshFxpNamePacket{
|
||||
ID: p.ID,
|
||||
|
@ -257,6 +256,8 @@ func handlePacket(s *Server, p orderedRequest) error {
|
|||
rpkt = statusFromError(p.ID, err)
|
||||
}
|
||||
case *sshFxpOpendirPacket:
|
||||
p.Path = toLocalPath(p.Path)
|
||||
|
||||
if stat, err := os.Stat(p.Path); err != nil {
|
||||
rpkt = statusFromError(p.ID, err)
|
||||
} else if !stat.IsDir() {
|
||||
|
@ -306,7 +307,7 @@ func handlePacket(s *Server, p orderedRequest) error {
|
|||
case serverRespondablePacket:
|
||||
rpkt = p.respond(s)
|
||||
default:
|
||||
return errors.Errorf("unexpected packet type %T", p)
|
||||
return fmt.Errorf("unexpected packet type %T", p)
|
||||
}
|
||||
|
||||
s.pktMgr.readyPacket(s.pktMgr.newOrderedResponse(rpkt, orderID))
|
||||
|
@ -346,8 +347,8 @@ func (svr *Server) Serve() error {
|
|||
|
||||
pkt, err = makePacket(rxPacket{fxp(pktType), pktBytes})
|
||||
if err != nil {
|
||||
switch errors.Cause(err) {
|
||||
case errUnknownExtendedPacket:
|
||||
switch {
|
||||
case errors.Is(err, errUnknownExtendedPacket):
|
||||
//if err := svr.serverConn.sendError(pkt, ErrSshFxOpUnsupported); err != nil {
|
||||
// debug("failed to send err packet: %v", err)
|
||||
// svr.conn.Close() // shuts down recvPacket
|
||||
|
@ -445,7 +446,7 @@ func (p *sshFxpOpenPacket) respond(svr *Server) responsePacket {
|
|||
osFlags |= os.O_EXCL
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(p.Path, osFlags, 0644)
|
||||
f, err := os.OpenFile(toLocalPath(p.Path), osFlags, 0644)
|
||||
if err != nil {
|
||||
return statusFromError(p.ID, err)
|
||||
}
|
||||
|
@ -460,17 +461,18 @@ func (p *sshFxpReaddirPacket) respond(svr *Server) responsePacket {
|
|||
return statusFromError(p.ID, EBADF)
|
||||
}
|
||||
|
||||
dirname := f.Name()
|
||||
dirents, err := f.Readdir(128)
|
||||
if err != nil {
|
||||
return statusFromError(p.ID, err)
|
||||
}
|
||||
|
||||
idLookup := osIDLookup{}
|
||||
|
||||
ret := &sshFxpNamePacket{ID: p.ID}
|
||||
for _, dirent := range dirents {
|
||||
ret.NameAttrs = append(ret.NameAttrs, &sshFxpNameAttr{
|
||||
Name: dirent.Name(),
|
||||
LongName: runLs(dirname, dirent),
|
||||
LongName: runLs(idLookup, dirent),
|
||||
Attrs: []interface{}{dirent},
|
||||
})
|
||||
}
|
||||
|
@ -482,6 +484,8 @@ func (p *sshFxpSetstatPacket) respond(svr *Server) responsePacket {
|
|||
b := p.Attrs.([]byte)
|
||||
var err error
|
||||
|
||||
p.Path = toLocalPath(p.Path)
|
||||
|
||||
debug("setstat name \"%s\"", p.Path)
|
||||
if (p.Flags & sshFileXferAttrSize) != 0 {
|
||||
var size uint64
|
||||
|
@ -610,99 +614,3 @@ func statusFromError(id uint32, err error) *sshFxpStatusPacket {
|
|||
|
||||
return ret
|
||||
}
|
||||
|
||||
func clamp(v, max uint32) uint32 {
|
||||
if v > max {
|
||||
return max
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func runLsTypeWord(dirent os.FileInfo) string {
|
||||
// find first character, the type char
|
||||
// b Block special file.
|
||||
// c Character special file.
|
||||
// d Directory.
|
||||
// l Symbolic link.
|
||||
// s Socket link.
|
||||
// p FIFO.
|
||||
// - Regular file.
|
||||
tc := '-'
|
||||
mode := dirent.Mode()
|
||||
if (mode & os.ModeDir) != 0 {
|
||||
tc = 'd'
|
||||
} else if (mode & os.ModeDevice) != 0 {
|
||||
tc = 'b'
|
||||
if (mode & os.ModeCharDevice) != 0 {
|
||||
tc = 'c'
|
||||
}
|
||||
} else if (mode & os.ModeSymlink) != 0 {
|
||||
tc = 'l'
|
||||
} else if (mode & os.ModeSocket) != 0 {
|
||||
tc = 's'
|
||||
} else if (mode & os.ModeNamedPipe) != 0 {
|
||||
tc = 'p'
|
||||
}
|
||||
|
||||
// owner
|
||||
orc := '-'
|
||||
if (mode & 0400) != 0 {
|
||||
orc = 'r'
|
||||
}
|
||||
owc := '-'
|
||||
if (mode & 0200) != 0 {
|
||||
owc = 'w'
|
||||
}
|
||||
oxc := '-'
|
||||
ox := (mode & 0100) != 0
|
||||
setuid := (mode & os.ModeSetuid) != 0
|
||||
if ox && setuid {
|
||||
oxc = 's'
|
||||
} else if setuid {
|
||||
oxc = 'S'
|
||||
} else if ox {
|
||||
oxc = 'x'
|
||||
}
|
||||
|
||||
// group
|
||||
grc := '-'
|
||||
if (mode & 040) != 0 {
|
||||
grc = 'r'
|
||||
}
|
||||
gwc := '-'
|
||||
if (mode & 020) != 0 {
|
||||
gwc = 'w'
|
||||
}
|
||||
gxc := '-'
|
||||
gx := (mode & 010) != 0
|
||||
setgid := (mode & os.ModeSetgid) != 0
|
||||
if gx && setgid {
|
||||
gxc = 's'
|
||||
} else if setgid {
|
||||
gxc = 'S'
|
||||
} else if gx {
|
||||
gxc = 'x'
|
||||
}
|
||||
|
||||
// all / others
|
||||
arc := '-'
|
||||
if (mode & 04) != 0 {
|
||||
arc = 'r'
|
||||
}
|
||||
awc := '-'
|
||||
if (mode & 02) != 0 {
|
||||
awc = 'w'
|
||||
}
|
||||
axc := '-'
|
||||
ax := (mode & 01) != 0
|
||||
sticky := (mode & os.ModeSticky) != 0
|
||||
if ax && sticky {
|
||||
axc = 't'
|
||||
} else if sticky {
|
||||
axc = 'T'
|
||||
} else if ax {
|
||||
axc = 'x'
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%c%c%c%c%c%c%c%c%c%c", tc, orc, owc, oxc, grc, gwc, gxc, arc, awc, axc)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ func (p *sshFxpExtendedPacketStatVFS) respond(svr *Server) responsePacket {
|
|||
if err != nil {
|
||||
return statusFromError(p.ID, err)
|
||||
}
|
||||
retPkt.ID = p.ID
|
||||
|
||||
return retPkt
|
||||
}
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
// +build !cgo plan9 windows android
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func runLs(dirname string, dirent os.FileInfo) string {
|
||||
typeword := runLsTypeWord(dirent)
|
||||
numLinks := 1
|
||||
if dirent.IsDir() {
|
||||
numLinks = 0
|
||||
}
|
||||
username := "root"
|
||||
groupname := "root"
|
||||
mtime := dirent.ModTime()
|
||||
monthStr := mtime.Month().String()[0:3]
|
||||
day := mtime.Day()
|
||||
year := mtime.Year()
|
||||
now := time.Now()
|
||||
isOld := mtime.Before(now.Add(-time.Hour * 24 * 365 / 2))
|
||||
|
||||
yearOrTime := fmt.Sprintf("%02d:%02d", mtime.Hour(), mtime.Minute())
|
||||
if isOld {
|
||||
yearOrTime = fmt.Sprintf("%d", year)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %4d %-8s %-8s %8d %s %2d %5s %s", typeword, numLinks, username, groupname, dirent.Size(), monthStr, day, yearOrTime, dirent.Name())
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris aix
|
||||
// +build cgo
|
||||
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func runLsStatt(dirent os.FileInfo, statt *syscall.Stat_t) string {
|
||||
// example from openssh sftp server:
|
||||
// crw-rw-rw- 1 root wheel 0 Jul 31 20:52 ttyvd
|
||||
// format:
|
||||
// {directory / char device / etc}{rwxrwxrwx} {number of links} owner group size month day [time (this year) | year (otherwise)] name
|
||||
|
||||
typeword := runLsTypeWord(dirent)
|
||||
numLinks := statt.Nlink
|
||||
uid := statt.Uid
|
||||
gid := statt.Gid
|
||||
username := fmt.Sprintf("%d", uid)
|
||||
groupname := fmt.Sprintf("%d", gid)
|
||||
// TODO FIXME: uid -> username, gid -> groupname lookup for ls -l format output
|
||||
|
||||
mtime := dirent.ModTime()
|
||||
monthStr := mtime.Month().String()[0:3]
|
||||
day := mtime.Day()
|
||||
year := mtime.Year()
|
||||
now := time.Now()
|
||||
isOld := mtime.Before(now.Add(-time.Hour * 24 * 365 / 2))
|
||||
|
||||
yearOrTime := fmt.Sprintf("%02d:%02d", mtime.Hour(), mtime.Minute())
|
||||
if isOld {
|
||||
yearOrTime = fmt.Sprintf("%d", year)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %4d %-8s %-8s %8d %s %2d %5s %s", typeword, numLinks, username, groupname, dirent.Size(), monthStr, day, yearOrTime, dirent.Name())
|
||||
}
|
||||
|
||||
// ls -l style output for a file, which is in the 'long output' section of a readdir response packet
|
||||
// this is a very simple (lazy) implementation, just enough to look almost like openssh in a few basic cases
|
||||
func runLs(dirname string, dirent os.FileInfo) string {
|
||||
dsys := dirent.Sys()
|
||||
if dsys == nil {
|
||||
} else if statt, ok := dsys.(*syscall.Stat_t); !ok {
|
||||
} else {
|
||||
return runLsStatt(dirent, statt)
|
||||
}
|
||||
|
||||
return path.Join(dirname, dirent.Name())
|
||||
}
|
|
@ -4,8 +4,6 @@ package sftp
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -58,9 +56,9 @@ const (
|
|||
sshFxNoMedia = 13
|
||||
sshFxNoSpaceOnFilesystem = 14
|
||||
sshFxQuotaExceeded = 15
|
||||
sshFxUnlnownPrincipal = 16
|
||||
sshFxUnknownPrincipal = 16
|
||||
sshFxLockConflict = 17
|
||||
sshFxDitNotEmpty = 18
|
||||
sshFxDirNotEmpty = 18
|
||||
sshFxNotADirectory = 19
|
||||
sshFxInvalidFilename = 20
|
||||
sshFxLinkLoop = 21
|
||||
|
@ -194,21 +192,21 @@ func (u *unexpectedPacketErr) Error() string {
|
|||
}
|
||||
|
||||
func unimplementedPacketErr(u uint8) error {
|
||||
return errors.Errorf("sftp: unimplemented packet type: got %v", fxp(u))
|
||||
return fmt.Errorf("sftp: unimplemented packet type: got %v", fxp(u))
|
||||
}
|
||||
|
||||
type unexpectedIDErr struct{ want, got uint32 }
|
||||
|
||||
func (u *unexpectedIDErr) Error() string {
|
||||
return fmt.Sprintf("sftp: unexpected id: want %v, got %v", u.want, u.got)
|
||||
return fmt.Sprintf("sftp: unexpected id: want %d, got %d", u.want, u.got)
|
||||
}
|
||||
|
||||
func unimplementedSeekWhence(whence int) error {
|
||||
return errors.Errorf("sftp: unimplemented seek whence %v", whence)
|
||||
return fmt.Errorf("sftp: unimplemented seek whence %d", whence)
|
||||
}
|
||||
|
||||
func unexpectedCount(want, got uint32) error {
|
||||
return errors.Errorf("sftp: unexpected count: want %v, got %v", want, got)
|
||||
return fmt.Errorf("sftp: unexpected count: want %d, got %d", want, got)
|
||||
}
|
||||
|
||||
type unexpectedVersionErr struct{ want, got uint32 }
|
||||
|
@ -239,7 +237,7 @@ func getSupportedExtensionByName(extensionName string) (sshExtensionPair, error)
|
|||
return supportedExtension, nil
|
||||
}
|
||||
}
|
||||
return sshExtensionPair{}, fmt.Errorf("Unsupported extension: %v", extensionName)
|
||||
return sshExtensionPair{}, fmt.Errorf("unsupported extension: %s", extensionName)
|
||||
}
|
||||
|
||||
// SetSFTPExtensions allows to customize the supported server extensions.
|
||||
|
|
|
@ -41,9 +41,15 @@ func translateSyscallError(err error) (uint32, bool) {
|
|||
return 0, false
|
||||
}
|
||||
|
||||
// isRegular returns true if the mode describes a regular file.
|
||||
func isRegular(mode uint32) bool {
|
||||
return mode&S_IFMT == syscall.S_IFREG
|
||||
}
|
||||
|
||||
// toFileMode converts sftp filemode bits to the os.FileMode specification
|
||||
func toFileMode(mode uint32) os.FileMode {
|
||||
var fm = os.FileMode(mode & 0777)
|
||||
|
||||
switch mode & S_IFMT {
|
||||
case syscall.S_IFBLK:
|
||||
fm |= os.ModeDevice
|
||||
|
@ -60,37 +66,38 @@ func toFileMode(mode uint32) os.FileMode {
|
|||
case syscall.S_IFSOCK:
|
||||
fm |= os.ModeSocket
|
||||
}
|
||||
|
||||
return fm
|
||||
}
|
||||
|
||||
// fromFileMode converts from the os.FileMode specification to sftp filemode bits
|
||||
func fromFileMode(mode os.FileMode) uint32 {
|
||||
ret := uint32(0)
|
||||
ret := uint32(mode & os.ModePerm)
|
||||
|
||||
if mode&os.ModeDevice != 0 {
|
||||
if mode&os.ModeCharDevice != 0 {
|
||||
ret |= syscall.S_IFCHR
|
||||
} else {
|
||||
ret |= syscall.S_IFBLK
|
||||
}
|
||||
}
|
||||
if mode&os.ModeDir != 0 {
|
||||
switch mode & os.ModeType {
|
||||
case os.ModeDevice | os.ModeCharDevice:
|
||||
ret |= syscall.S_IFCHR
|
||||
case os.ModeDevice:
|
||||
ret |= syscall.S_IFBLK
|
||||
case os.ModeDir:
|
||||
ret |= syscall.S_IFDIR
|
||||
}
|
||||
if mode&os.ModeSymlink != 0 {
|
||||
ret |= syscall.S_IFLNK
|
||||
}
|
||||
if mode&os.ModeNamedPipe != 0 {
|
||||
case os.ModeNamedPipe:
|
||||
ret |= syscall.S_IFIFO
|
||||
}
|
||||
if mode&os.ModeSocket != 0 {
|
||||
case os.ModeSymlink:
|
||||
ret |= syscall.S_IFLNK
|
||||
case 0:
|
||||
ret |= syscall.S_IFREG
|
||||
case os.ModeSocket:
|
||||
ret |= syscall.S_IFSOCK
|
||||
}
|
||||
|
||||
if mode&os.ModeType == 0 {
|
||||
ret |= syscall.S_IFREG
|
||||
}
|
||||
ret |= uint32(mode & os.ModePerm)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// Plan 9 doesn't have setuid, setgid or sticky, but a Plan 9 client should
|
||||
// be able to send these bits to a POSIX server.
|
||||
const (
|
||||
s_ISUID = 04000
|
||||
s_ISGID = 02000
|
||||
s_ISVTX = 01000
|
||||
)
|
||||
|
|
|
@ -43,9 +43,15 @@ func translateSyscallError(err error) (uint32, bool) {
|
|||
return 0, false
|
||||
}
|
||||
|
||||
// isRegular returns true if the mode describes a regular file.
|
||||
func isRegular(mode uint32) bool {
|
||||
return mode&S_IFMT == syscall.S_IFREG
|
||||
}
|
||||
|
||||
// toFileMode converts sftp filemode bits to the os.FileMode specification
|
||||
func toFileMode(mode uint32) os.FileMode {
|
||||
var fm = os.FileMode(mode & 0777)
|
||||
|
||||
switch mode & S_IFMT {
|
||||
case syscall.S_IFBLK:
|
||||
fm |= os.ModeDevice
|
||||
|
@ -62,55 +68,56 @@ func toFileMode(mode uint32) os.FileMode {
|
|||
case syscall.S_IFSOCK:
|
||||
fm |= os.ModeSocket
|
||||
}
|
||||
if mode&syscall.S_ISGID != 0 {
|
||||
fm |= os.ModeSetgid
|
||||
}
|
||||
|
||||
if mode&syscall.S_ISUID != 0 {
|
||||
fm |= os.ModeSetuid
|
||||
}
|
||||
if mode&syscall.S_ISGID != 0 {
|
||||
fm |= os.ModeSetgid
|
||||
}
|
||||
if mode&syscall.S_ISVTX != 0 {
|
||||
fm |= os.ModeSticky
|
||||
}
|
||||
|
||||
return fm
|
||||
}
|
||||
|
||||
// fromFileMode converts from the os.FileMode specification to sftp filemode bits
|
||||
func fromFileMode(mode os.FileMode) uint32 {
|
||||
ret := uint32(0)
|
||||
ret := uint32(mode & os.ModePerm)
|
||||
|
||||
if mode&os.ModeDevice != 0 {
|
||||
if mode&os.ModeCharDevice != 0 {
|
||||
ret |= syscall.S_IFCHR
|
||||
} else {
|
||||
ret |= syscall.S_IFBLK
|
||||
}
|
||||
}
|
||||
if mode&os.ModeDir != 0 {
|
||||
switch mode & os.ModeType {
|
||||
case os.ModeDevice | os.ModeCharDevice:
|
||||
ret |= syscall.S_IFCHR
|
||||
case os.ModeDevice:
|
||||
ret |= syscall.S_IFBLK
|
||||
case os.ModeDir:
|
||||
ret |= syscall.S_IFDIR
|
||||
}
|
||||
if mode&os.ModeSymlink != 0 {
|
||||
ret |= syscall.S_IFLNK
|
||||
}
|
||||
if mode&os.ModeNamedPipe != 0 {
|
||||
case os.ModeNamedPipe:
|
||||
ret |= syscall.S_IFIFO
|
||||
case os.ModeSymlink:
|
||||
ret |= syscall.S_IFLNK
|
||||
case 0:
|
||||
ret |= syscall.S_IFREG
|
||||
case os.ModeSocket:
|
||||
ret |= syscall.S_IFSOCK
|
||||
}
|
||||
|
||||
if mode&os.ModeSetuid != 0 {
|
||||
ret |= syscall.S_ISUID
|
||||
}
|
||||
if mode&os.ModeSetgid != 0 {
|
||||
ret |= syscall.S_ISGID
|
||||
}
|
||||
if mode&os.ModeSetuid != 0 {
|
||||
ret |= syscall.S_ISUID
|
||||
}
|
||||
if mode&os.ModeSticky != 0 {
|
||||
ret |= syscall.S_ISVTX
|
||||
}
|
||||
if mode&os.ModeSocket != 0 {
|
||||
ret |= syscall.S_IFSOCK
|
||||
}
|
||||
|
||||
if mode&os.ModeType == 0 {
|
||||
ret |= syscall.S_IFREG
|
||||
}
|
||||
ret |= uint32(mode & os.ModePerm)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
const (
|
||||
s_ISUID = syscall.S_ISUID
|
||||
s_ISGID = syscall.S_ISGID
|
||||
s_ISVTX = syscall.S_ISVTX
|
||||
)
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// These constants from [PROTOCOL.certkeys] represent the algorithm names
|
||||
// These constants from [PROTOCOL.certkeys] represent the key algorithm names
|
||||
// for certificate types supported by this package.
|
||||
const (
|
||||
CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
|
||||
|
@ -27,6 +27,14 @@ const (
|
|||
CertAlgoSKED25519v01 = "sk-ssh-ed25519-cert-v01@openssh.com"
|
||||
)
|
||||
|
||||
// These constants from [PROTOCOL.certkeys] represent additional signature
|
||||
// algorithm names for certificate types supported by this package.
|
||||
const (
|
||||
CertSigAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
|
||||
CertSigAlgoRSASHA2256v01 = "rsa-sha2-256-cert-v01@openssh.com"
|
||||
CertSigAlgoRSASHA2512v01 = "rsa-sha2-512-cert-v01@openssh.com"
|
||||
)
|
||||
|
||||
// Certificate types distinguish between host and user
|
||||
// certificates. The values can be set in the CertType field of
|
||||
// Certificate.
|
||||
|
@ -423,6 +431,12 @@ func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
|
|||
}
|
||||
c.SignatureKey = authority.PublicKey()
|
||||
|
||||
if v, ok := authority.(AlgorithmSigner); ok {
|
||||
if v.PublicKey().Type() == KeyAlgoRSA {
|
||||
authority = &rsaSigner{v, SigAlgoRSASHA2512}
|
||||
}
|
||||
}
|
||||
|
||||
sig, err := authority.Sign(rand, c.bytesForSigning())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -431,8 +445,14 @@ func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// certAlgoNames includes a mapping from signature algorithms to the
|
||||
// corresponding certificate signature algorithm. When a key type (such
|
||||
// as ED25516) is associated with only one algorithm, the KeyAlgo
|
||||
// constant is used instead of the SigAlgo.
|
||||
var certAlgoNames = map[string]string{
|
||||
KeyAlgoRSA: CertAlgoRSAv01,
|
||||
SigAlgoRSA: CertSigAlgoRSAv01,
|
||||
SigAlgoRSASHA2256: CertSigAlgoRSASHA2256v01,
|
||||
SigAlgoRSASHA2512: CertSigAlgoRSASHA2512v01,
|
||||
KeyAlgoDSA: CertAlgoDSAv01,
|
||||
KeyAlgoECDSA256: CertAlgoECDSA256v01,
|
||||
KeyAlgoECDSA384: CertAlgoECDSA384v01,
|
||||
|
|
|
@ -394,6 +394,10 @@ func (c *gcmCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error)
|
|||
}
|
||||
c.incIV()
|
||||
|
||||
if len(plain) == 0 {
|
||||
return nil, errors.New("ssh: empty packet")
|
||||
}
|
||||
|
||||
padding := plain[0]
|
||||
if padding < 4 {
|
||||
// padding is a byte, so it automatically satisfies
|
||||
|
@ -710,6 +714,10 @@ func (c *chacha20Poly1305Cipher) readCipherPacket(seqNum uint32, r io.Reader) ([
|
|||
plain := c.buf[4:contentEnd]
|
||||
s.XORKeyStream(plain, plain)
|
||||
|
||||
if len(plain) == 0 {
|
||||
return nil, errors.New("ssh: empty packet")
|
||||
}
|
||||
|
||||
padding := plain[0]
|
||||
if padding < 4 {
|
||||
// padding is a byte, so it automatically satisfies
|
||||
|
|
|
@ -115,12 +115,25 @@ func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) e
|
|||
|
||||
// verifyHostKeySignature verifies the host key obtained in the key
|
||||
// exchange.
|
||||
func verifyHostKeySignature(hostKey PublicKey, result *kexResult) error {
|
||||
func verifyHostKeySignature(hostKey PublicKey, algo string, result *kexResult) error {
|
||||
sig, rest, ok := parseSignatureBody(result.Signature)
|
||||
if len(rest) > 0 || !ok {
|
||||
return errors.New("ssh: signature parse error")
|
||||
}
|
||||
|
||||
// For keys, underlyingAlgo is exactly algo. For certificates,
|
||||
// we have to look up the underlying key algorithm that SSH
|
||||
// uses to evaluate signatures.
|
||||
underlyingAlgo := algo
|
||||
for sigAlgo, certAlgo := range certAlgoNames {
|
||||
if certAlgo == algo {
|
||||
underlyingAlgo = sigAlgo
|
||||
}
|
||||
}
|
||||
if sig.Format != underlyingAlgo {
|
||||
return fmt.Errorf("ssh: invalid signature algorithm %q, expected %q", sig.Format, underlyingAlgo)
|
||||
}
|
||||
|
||||
return hostKey.Verify(result.H, sig)
|
||||
}
|
||||
|
||||
|
|
|
@ -69,11 +69,13 @@ var preferredKexAlgos = []string{
|
|||
// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
|
||||
// of authenticating servers) in preference order.
|
||||
var supportedHostKeyAlgos = []string{
|
||||
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
|
||||
CertSigAlgoRSASHA2512v01, CertSigAlgoRSASHA2256v01,
|
||||
CertSigAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
|
||||
CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
|
||||
|
||||
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
|
||||
KeyAlgoRSA, KeyAlgoDSA,
|
||||
SigAlgoRSASHA2512, SigAlgoRSASHA2256,
|
||||
SigAlgoRSA, KeyAlgoDSA,
|
||||
|
||||
KeyAlgoED25519,
|
||||
}
|
||||
|
@ -90,16 +92,20 @@ var supportedCompressions = []string{compressionNone}
|
|||
// hashFuncs keeps the mapping of supported algorithms to their respective
|
||||
// hashes needed for signature verification.
|
||||
var hashFuncs = map[string]crypto.Hash{
|
||||
KeyAlgoRSA: crypto.SHA1,
|
||||
KeyAlgoDSA: crypto.SHA1,
|
||||
KeyAlgoECDSA256: crypto.SHA256,
|
||||
KeyAlgoECDSA384: crypto.SHA384,
|
||||
KeyAlgoECDSA521: crypto.SHA512,
|
||||
CertAlgoRSAv01: crypto.SHA1,
|
||||
CertAlgoDSAv01: crypto.SHA1,
|
||||
CertAlgoECDSA256v01: crypto.SHA256,
|
||||
CertAlgoECDSA384v01: crypto.SHA384,
|
||||
CertAlgoECDSA521v01: crypto.SHA512,
|
||||
SigAlgoRSA: crypto.SHA1,
|
||||
SigAlgoRSASHA2256: crypto.SHA256,
|
||||
SigAlgoRSASHA2512: crypto.SHA512,
|
||||
KeyAlgoDSA: crypto.SHA1,
|
||||
KeyAlgoECDSA256: crypto.SHA256,
|
||||
KeyAlgoECDSA384: crypto.SHA384,
|
||||
KeyAlgoECDSA521: crypto.SHA512,
|
||||
CertSigAlgoRSAv01: crypto.SHA1,
|
||||
CertSigAlgoRSASHA2256v01: crypto.SHA256,
|
||||
CertSigAlgoRSASHA2512v01: crypto.SHA512,
|
||||
CertAlgoDSAv01: crypto.SHA1,
|
||||
CertAlgoECDSA256v01: crypto.SHA256,
|
||||
CertAlgoECDSA384v01: crypto.SHA384,
|
||||
CertAlgoECDSA521v01: crypto.SHA512,
|
||||
}
|
||||
|
||||
// unexpectedMessageError results when the SSH message that we received didn't
|
||||
|
|
|
@ -457,8 +457,15 @@ func (t *handshakeTransport) sendKexInit() error {
|
|||
|
||||
if len(t.hostKeys) > 0 {
|
||||
for _, k := range t.hostKeys {
|
||||
msg.ServerHostKeyAlgos = append(
|
||||
msg.ServerHostKeyAlgos, k.PublicKey().Type())
|
||||
algo := k.PublicKey().Type()
|
||||
switch algo {
|
||||
case KeyAlgoRSA:
|
||||
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, []string{SigAlgoRSASHA2512, SigAlgoRSASHA2256, SigAlgoRSA}...)
|
||||
case CertAlgoRSAv01:
|
||||
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, []string{CertSigAlgoRSASHA2512v01, CertSigAlgoRSASHA2256v01, CertSigAlgoRSAv01}...)
|
||||
default:
|
||||
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, algo)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
msg.ServerHostKeyAlgos = t.hostKeyAlgorithms
|
||||
|
@ -614,8 +621,22 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
|||
func (t *handshakeTransport) server(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) {
|
||||
var hostKey Signer
|
||||
for _, k := range t.hostKeys {
|
||||
if algs.hostKey == k.PublicKey().Type() {
|
||||
kt := k.PublicKey().Type()
|
||||
if kt == algs.hostKey {
|
||||
hostKey = k
|
||||
} else if signer, ok := k.(AlgorithmSigner); ok {
|
||||
// Some signature algorithms don't show up as key types
|
||||
// so we have to manually check for a compatible host key.
|
||||
switch kt {
|
||||
case KeyAlgoRSA:
|
||||
if algs.hostKey == SigAlgoRSASHA2256 || algs.hostKey == SigAlgoRSASHA2512 {
|
||||
hostKey = &rsaSigner{signer, algs.hostKey}
|
||||
}
|
||||
case CertAlgoRSAv01:
|
||||
if algs.hostKey == CertSigAlgoRSASHA2256v01 || algs.hostKey == CertSigAlgoRSASHA2512v01 {
|
||||
hostKey = &rsaSigner{signer, certToPrivAlgo(algs.hostKey)}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -634,7 +655,7 @@ func (t *handshakeTransport) client(kex kexAlgorithm, algs *algorithms, magics *
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err := verifyHostKeySignature(hostKey, result); err != nil {
|
||||
if err := verifyHostKeySignature(hostKey, algs.hostKey, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -939,6 +939,15 @@ func newDSAPrivateKey(key *dsa.PrivateKey) (Signer, error) {
|
|||
return &dsaPrivateKey{key}, nil
|
||||
}
|
||||
|
||||
type rsaSigner struct {
|
||||
AlgorithmSigner
|
||||
defaultAlgorithm string
|
||||
}
|
||||
|
||||
func (s *rsaSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
|
||||
return s.AlgorithmSigner.SignWithAlgorithm(rand, data, s.defaultAlgorithm)
|
||||
}
|
||||
|
||||
type wrappedSigner struct {
|
||||
signer crypto.Signer
|
||||
pubKey PublicKey
|
||||
|
|
|
@ -284,7 +284,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
|
|||
|
||||
func isAcceptableAlgo(algo string) bool {
|
||||
switch algo {
|
||||
case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoSKECDSA256, KeyAlgoED25519, KeyAlgoSKED25519,
|
||||
case SigAlgoRSA, SigAlgoRSASHA2256, SigAlgoRSASHA2512, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoSKECDSA256, KeyAlgoED25519, KeyAlgoSKED25519,
|
||||
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01:
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -113,8 +113,8 @@ func getEnvAny(names ...string) string {
|
|||
// environment, or a proxy should not be used for the given request, as
|
||||
// defined by NO_PROXY.
|
||||
//
|
||||
// As a special case, if req.URL.Host is "localhost" (with or without a
|
||||
// port number), then a nil URL and nil error will be returned.
|
||||
// As a special case, if req.URL.Host is "localhost" or a loopback address
|
||||
// (with or without a port number), then a nil URL and nil error will be returned.
|
||||
func (cfg *Config) ProxyFunc() func(reqURL *url.URL) (*url.URL, error) {
|
||||
// Preprocess the Config settings for more efficient evaluation.
|
||||
cfg1 := &config{
|
||||
|
|
|
@ -16,6 +16,12 @@ import (
|
|||
|
||||
// ClientConnPool manages a pool of HTTP/2 client connections.
|
||||
type ClientConnPool interface {
|
||||
// GetClientConn returns a specific HTTP/2 connection (usually
|
||||
// a TLS-TCP connection) to an HTTP/2 server. On success, the
|
||||
// returned ClientConn accounts for the upcoming RoundTrip
|
||||
// call, so the caller should not omit it. If the caller needs
|
||||
// to, ClientConn.RoundTrip can be called with a bogus
|
||||
// new(http.Request) to release the stream reservation.
|
||||
GetClientConn(req *http.Request, addr string) (*ClientConn, error)
|
||||
MarkDead(*ClientConn)
|
||||
}
|
||||
|
@ -42,7 +48,7 @@ type clientConnPool struct {
|
|||
conns map[string][]*ClientConn // key is host:port
|
||||
dialing map[string]*dialCall // currently in-flight dials
|
||||
keys map[*ClientConn][]string
|
||||
addConnCalls map[string]*addConnCall // in-flight addConnIfNeede calls
|
||||
addConnCalls map[string]*addConnCall // in-flight addConnIfNeeded calls
|
||||
}
|
||||
|
||||
func (p *clientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) {
|
||||
|
@ -54,28 +60,8 @@ const (
|
|||
noDialOnMiss = false
|
||||
)
|
||||
|
||||
// shouldTraceGetConn reports whether getClientConn should call any
|
||||
// ClientTrace.GetConn hook associated with the http.Request.
|
||||
//
|
||||
// This complexity is needed to avoid double calls of the GetConn hook
|
||||
// during the back-and-forth between net/http and x/net/http2 (when the
|
||||
// net/http.Transport is upgraded to also speak http2), as well as support
|
||||
// the case where x/net/http2 is being used directly.
|
||||
func (p *clientConnPool) shouldTraceGetConn(st clientConnIdleState) bool {
|
||||
// If our Transport wasn't made via ConfigureTransport, always
|
||||
// trace the GetConn hook if provided, because that means the
|
||||
// http2 package is being used directly and it's the one
|
||||
// dialing, as opposed to net/http.
|
||||
if _, ok := p.t.ConnPool.(noDialClientConnPool); !ok {
|
||||
return true
|
||||
}
|
||||
// Otherwise, only use the GetConn hook if this connection has
|
||||
// been used previously for other requests. For fresh
|
||||
// connections, the net/http package does the dialing.
|
||||
return !st.freshConn
|
||||
}
|
||||
|
||||
func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) {
|
||||
// TODO(dneil): Dial a new connection when t.DisableKeepAlives is set?
|
||||
if isConnectionCloseRequest(req) && dialOnMiss {
|
||||
// It gets its own connection.
|
||||
traceGetConn(req, addr)
|
||||
|
@ -89,10 +75,14 @@ func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMis
|
|||
for {
|
||||
p.mu.Lock()
|
||||
for _, cc := range p.conns[addr] {
|
||||
if st := cc.idleState(); st.canTakeNewRequest {
|
||||
if p.shouldTraceGetConn(st) {
|
||||
if cc.ReserveNewRequest() {
|
||||
// When a connection is presented to us by the net/http package,
|
||||
// the GetConn hook has already been called.
|
||||
// Don't call it a second time here.
|
||||
if !cc.getConnCalled {
|
||||
traceGetConn(req, addr)
|
||||
}
|
||||
cc.getConnCalled = false
|
||||
p.mu.Unlock()
|
||||
return cc, nil
|
||||
}
|
||||
|
@ -108,7 +98,13 @@ func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMis
|
|||
if shouldRetryDial(call, req) {
|
||||
continue
|
||||
}
|
||||
return call.res, call.err
|
||||
cc, err := call.res, call.err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cc.ReserveNewRequest() {
|
||||
return cc, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,6 +201,7 @@ func (c *addConnCall) run(t *Transport, key string, tc *tls.Conn) {
|
|||
if err != nil {
|
||||
c.err = err
|
||||
} else {
|
||||
cc.getConnCalled = true // already called by the net/http package
|
||||
p.addConnLocked(key, cc)
|
||||
}
|
||||
delete(p.addConnCalls, key)
|
||||
|
|
|
@ -53,6 +53,13 @@ func (e ErrCode) String() string {
|
|||
return fmt.Sprintf("unknown error code 0x%x", uint32(e))
|
||||
}
|
||||
|
||||
func (e ErrCode) stringToken() string {
|
||||
if s, ok := errCodeName[e]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("ERR_UNKNOWN_%d", uint32(e))
|
||||
}
|
||||
|
||||
// ConnectionError is an error that results in the termination of the
|
||||
// entire connection.
|
||||
type ConnectionError ErrCode
|
||||
|
@ -67,6 +74,11 @@ type StreamError struct {
|
|||
Cause error // optional additional detail
|
||||
}
|
||||
|
||||
// errFromPeer is a sentinel error value for StreamError.Cause to
|
||||
// indicate that the StreamError was sent from the peer over the wire
|
||||
// and wasn't locally generated in the Transport.
|
||||
var errFromPeer = errors.New("received from peer")
|
||||
|
||||
func streamError(id uint32, code ErrCode) StreamError {
|
||||
return StreamError{StreamID: id, Code: code}
|
||||
}
|
||||
|
|
|
@ -122,7 +122,7 @@ var flagName = map[FrameType]map[Flags]string{
|
|||
// a frameParser parses a frame given its FrameHeader and payload
|
||||
// bytes. The length of payload will always equal fh.Length (which
|
||||
// might be 0).
|
||||
type frameParser func(fc *frameCache, fh FrameHeader, payload []byte) (Frame, error)
|
||||
type frameParser func(fc *frameCache, fh FrameHeader, countError func(string), payload []byte) (Frame, error)
|
||||
|
||||
var frameParsers = map[FrameType]frameParser{
|
||||
FrameData: parseDataFrame,
|
||||
|
@ -267,6 +267,11 @@ type Framer struct {
|
|||
lastFrame Frame
|
||||
errDetail error
|
||||
|
||||
// countError is a non-nil func that's called on a frame parse
|
||||
// error with some unique error path token. It's initialized
|
||||
// from Transport.CountError or Server.CountError.
|
||||
countError func(errToken string)
|
||||
|
||||
// lastHeaderStream is non-zero if the last frame was an
|
||||
// unfinished HEADERS/CONTINUATION.
|
||||
lastHeaderStream uint32
|
||||
|
@ -426,6 +431,7 @@ func NewFramer(w io.Writer, r io.Reader) *Framer {
|
|||
fr := &Framer{
|
||||
w: w,
|
||||
r: r,
|
||||
countError: func(string) {},
|
||||
logReads: logFrameReads,
|
||||
logWrites: logFrameWrites,
|
||||
debugReadLoggerf: log.Printf,
|
||||
|
@ -500,7 +506,7 @@ func (fr *Framer) ReadFrame() (Frame, error) {
|
|||
if _, err := io.ReadFull(fr.r, payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := typeFrameParser(fh.Type)(fr.frameCache, fh, payload)
|
||||
f, err := typeFrameParser(fh.Type)(fr.frameCache, fh, fr.countError, payload)
|
||||
if err != nil {
|
||||
if ce, ok := err.(connError); ok {
|
||||
return nil, fr.connError(ce.Code, ce.Reason)
|
||||
|
@ -588,13 +594,14 @@ func (f *DataFrame) Data() []byte {
|
|||
return f.data
|
||||
}
|
||||
|
||||
func parseDataFrame(fc *frameCache, fh FrameHeader, payload []byte) (Frame, error) {
|
||||
func parseDataFrame(fc *frameCache, fh FrameHeader, countError func(string), payload []byte) (Frame, error) {
|
||||
if fh.StreamID == 0 {
|
||||
// DATA frames MUST be associated with a stream. If a
|
||||
// DATA frame is received whose stream identifier
|
||||
// field is 0x0, the recipient MUST respond with a
|
||||
// connection error (Section 5.4.1) of type
|
||||
// PROTOCOL_ERROR.
|
||||
countError("frame_data_stream_0")
|
||||
return nil, connError{ErrCodeProtocol, "DATA frame with stream ID 0"}
|
||||
}
|
||||
f := fc.getDataFrame()
|
||||
|
@ -605,6 +612,7 @@ func parseDataFrame(fc *frameCache, fh FrameHeader, payload []byte) (Frame, erro
|
|||
var err error
|
||||
payload, padSize, err = readByte(payload)
|
||||
if err != nil {
|
||||
countError("frame_data_pad_byte_short")
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@ -613,6 +621,7 @@ func parseDataFrame(fc *frameCache, fh FrameHeader, payload []byte) (Frame, erro
|
|||
// length of the frame payload, the recipient MUST
|
||||
// treat this as a connection error.
|
||||
// Filed: https://github.com/http2/http2-spec/issues/610
|
||||
countError("frame_data_pad_too_big")
|
||||
return nil, connError{ErrCodeProtocol, "pad size larger than data payload"}
|
||||
}
|
||||
f.data = payload[:len(payload)-int(padSize)]
|
||||
|
@ -695,7 +704,7 @@ type SettingsFrame struct {
|
|||
p []byte
|
||||
}
|
||||
|
||||
func parseSettingsFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
||||
func parseSettingsFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) {
|
||||
if fh.Flags.Has(FlagSettingsAck) && fh.Length > 0 {
|
||||
// When this (ACK 0x1) bit is set, the payload of the
|
||||
// SETTINGS frame MUST be empty. Receipt of a
|
||||
|
@ -703,6 +712,7 @@ func parseSettingsFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error)
|
|||
// field value other than 0 MUST be treated as a
|
||||
// connection error (Section 5.4.1) of type
|
||||
// FRAME_SIZE_ERROR.
|
||||
countError("frame_settings_ack_with_length")
|
||||
return nil, ConnectionError(ErrCodeFrameSize)
|
||||
}
|
||||
if fh.StreamID != 0 {
|
||||
|
@ -713,14 +723,17 @@ func parseSettingsFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error)
|
|||
// field is anything other than 0x0, the endpoint MUST
|
||||
// respond with a connection error (Section 5.4.1) of
|
||||
// type PROTOCOL_ERROR.
|
||||
countError("frame_settings_has_stream")
|
||||
return nil, ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
if len(p)%6 != 0 {
|
||||
countError("frame_settings_mod_6")
|
||||
// Expecting even number of 6 byte settings.
|
||||
return nil, ConnectionError(ErrCodeFrameSize)
|
||||
}
|
||||
f := &SettingsFrame{FrameHeader: fh, p: p}
|
||||
if v, ok := f.Value(SettingInitialWindowSize); ok && v > (1<<31)-1 {
|
||||
countError("frame_settings_window_size_too_big")
|
||||
// Values above the maximum flow control window size of 2^31 - 1 MUST
|
||||
// be treated as a connection error (Section 5.4.1) of type
|
||||
// FLOW_CONTROL_ERROR.
|
||||
|
@ -832,11 +845,13 @@ type PingFrame struct {
|
|||
|
||||
func (f *PingFrame) IsAck() bool { return f.Flags.Has(FlagPingAck) }
|
||||
|
||||
func parsePingFrame(_ *frameCache, fh FrameHeader, payload []byte) (Frame, error) {
|
||||
func parsePingFrame(_ *frameCache, fh FrameHeader, countError func(string), payload []byte) (Frame, error) {
|
||||
if len(payload) != 8 {
|
||||
countError("frame_ping_length")
|
||||
return nil, ConnectionError(ErrCodeFrameSize)
|
||||
}
|
||||
if fh.StreamID != 0 {
|
||||
countError("frame_ping_has_stream")
|
||||
return nil, ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
f := &PingFrame{FrameHeader: fh}
|
||||
|
@ -872,11 +887,13 @@ func (f *GoAwayFrame) DebugData() []byte {
|
|||
return f.debugData
|
||||
}
|
||||
|
||||
func parseGoAwayFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
||||
func parseGoAwayFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) {
|
||||
if fh.StreamID != 0 {
|
||||
countError("frame_goaway_has_stream")
|
||||
return nil, ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
if len(p) < 8 {
|
||||
countError("frame_goaway_short")
|
||||
return nil, ConnectionError(ErrCodeFrameSize)
|
||||
}
|
||||
return &GoAwayFrame{
|
||||
|
@ -912,7 +929,7 @@ func (f *UnknownFrame) Payload() []byte {
|
|||
return f.p
|
||||
}
|
||||
|
||||
func parseUnknownFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
||||
func parseUnknownFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) {
|
||||
return &UnknownFrame{fh, p}, nil
|
||||
}
|
||||
|
||||
|
@ -923,8 +940,9 @@ type WindowUpdateFrame struct {
|
|||
Increment uint32 // never read with high bit set
|
||||
}
|
||||
|
||||
func parseWindowUpdateFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
||||
func parseWindowUpdateFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) {
|
||||
if len(p) != 4 {
|
||||
countError("frame_windowupdate_bad_len")
|
||||
return nil, ConnectionError(ErrCodeFrameSize)
|
||||
}
|
||||
inc := binary.BigEndian.Uint32(p[:4]) & 0x7fffffff // mask off high reserved bit
|
||||
|
@ -936,8 +954,10 @@ func parseWindowUpdateFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, err
|
|||
// control window MUST be treated as a connection
|
||||
// error (Section 5.4.1).
|
||||
if fh.StreamID == 0 {
|
||||
countError("frame_windowupdate_zero_inc_conn")
|
||||
return nil, ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
countError("frame_windowupdate_zero_inc_stream")
|
||||
return nil, streamError(fh.StreamID, ErrCodeProtocol)
|
||||
}
|
||||
return &WindowUpdateFrame{
|
||||
|
@ -988,7 +1008,7 @@ func (f *HeadersFrame) HasPriority() bool {
|
|||
return f.FrameHeader.Flags.Has(FlagHeadersPriority)
|
||||
}
|
||||
|
||||
func parseHeadersFrame(_ *frameCache, fh FrameHeader, p []byte) (_ Frame, err error) {
|
||||
func parseHeadersFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (_ Frame, err error) {
|
||||
hf := &HeadersFrame{
|
||||
FrameHeader: fh,
|
||||
}
|
||||
|
@ -997,11 +1017,13 @@ func parseHeadersFrame(_ *frameCache, fh FrameHeader, p []byte) (_ Frame, err er
|
|||
// is received whose stream identifier field is 0x0, the recipient MUST
|
||||
// respond with a connection error (Section 5.4.1) of type
|
||||
// PROTOCOL_ERROR.
|
||||
countError("frame_headers_zero_stream")
|
||||
return nil, connError{ErrCodeProtocol, "HEADERS frame with stream ID 0"}
|
||||
}
|
||||
var padLength uint8
|
||||
if fh.Flags.Has(FlagHeadersPadded) {
|
||||
if p, padLength, err = readByte(p); err != nil {
|
||||
countError("frame_headers_pad_short")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -1009,16 +1031,19 @@ func parseHeadersFrame(_ *frameCache, fh FrameHeader, p []byte) (_ Frame, err er
|
|||
var v uint32
|
||||
p, v, err = readUint32(p)
|
||||
if err != nil {
|
||||
countError("frame_headers_prio_short")
|
||||
return nil, err
|
||||
}
|
||||
hf.Priority.StreamDep = v & 0x7fffffff
|
||||
hf.Priority.Exclusive = (v != hf.Priority.StreamDep) // high bit was set
|
||||
p, hf.Priority.Weight, err = readByte(p)
|
||||
if err != nil {
|
||||
countError("frame_headers_prio_weight_short")
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(p)-int(padLength) <= 0 {
|
||||
if len(p)-int(padLength) < 0 {
|
||||
countError("frame_headers_pad_too_big")
|
||||
return nil, streamError(fh.StreamID, ErrCodeProtocol)
|
||||
}
|
||||
hf.headerFragBuf = p[:len(p)-int(padLength)]
|
||||
|
@ -1125,11 +1150,13 @@ func (p PriorityParam) IsZero() bool {
|
|||
return p == PriorityParam{}
|
||||
}
|
||||
|
||||
func parsePriorityFrame(_ *frameCache, fh FrameHeader, payload []byte) (Frame, error) {
|
||||
func parsePriorityFrame(_ *frameCache, fh FrameHeader, countError func(string), payload []byte) (Frame, error) {
|
||||
if fh.StreamID == 0 {
|
||||
countError("frame_priority_zero_stream")
|
||||
return nil, connError{ErrCodeProtocol, "PRIORITY frame with stream ID 0"}
|
||||
}
|
||||
if len(payload) != 5 {
|
||||
countError("frame_priority_bad_length")
|
||||
return nil, connError{ErrCodeFrameSize, fmt.Sprintf("PRIORITY frame payload size was %d; want 5", len(payload))}
|
||||
}
|
||||
v := binary.BigEndian.Uint32(payload[:4])
|
||||
|
@ -1172,11 +1199,13 @@ type RSTStreamFrame struct {
|
|||
ErrCode ErrCode
|
||||
}
|
||||
|
||||
func parseRSTStreamFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
||||
func parseRSTStreamFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) {
|
||||
if len(p) != 4 {
|
||||
countError("frame_rststream_bad_len")
|
||||
return nil, ConnectionError(ErrCodeFrameSize)
|
||||
}
|
||||
if fh.StreamID == 0 {
|
||||
countError("frame_rststream_zero_stream")
|
||||
return nil, ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
return &RSTStreamFrame{fh, ErrCode(binary.BigEndian.Uint32(p[:4]))}, nil
|
||||
|
@ -1202,8 +1231,9 @@ type ContinuationFrame struct {
|
|||
headerFragBuf []byte
|
||||
}
|
||||
|
||||
func parseContinuationFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
||||
func parseContinuationFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) {
|
||||
if fh.StreamID == 0 {
|
||||
countError("frame_continuation_zero_stream")
|
||||
return nil, connError{ErrCodeProtocol, "CONTINUATION frame with stream ID 0"}
|
||||
}
|
||||
return &ContinuationFrame{fh, p}, nil
|
||||
|
@ -1252,7 +1282,7 @@ func (f *PushPromiseFrame) HeadersEnded() bool {
|
|||
return f.FrameHeader.Flags.Has(FlagPushPromiseEndHeaders)
|
||||
}
|
||||
|
||||
func parsePushPromise(_ *frameCache, fh FrameHeader, p []byte) (_ Frame, err error) {
|
||||
func parsePushPromise(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (_ Frame, err error) {
|
||||
pp := &PushPromiseFrame{
|
||||
FrameHeader: fh,
|
||||
}
|
||||
|
@ -1263,6 +1293,7 @@ func parsePushPromise(_ *frameCache, fh FrameHeader, p []byte) (_ Frame, err err
|
|||
// with. If the stream identifier field specifies the value
|
||||
// 0x0, a recipient MUST respond with a connection error
|
||||
// (Section 5.4.1) of type PROTOCOL_ERROR.
|
||||
countError("frame_pushpromise_zero_stream")
|
||||
return nil, ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
// The PUSH_PROMISE frame includes optional padding.
|
||||
|
@ -1270,18 +1301,21 @@ func parsePushPromise(_ *frameCache, fh FrameHeader, p []byte) (_ Frame, err err
|
|||
var padLength uint8
|
||||
if fh.Flags.Has(FlagPushPromisePadded) {
|
||||
if p, padLength, err = readByte(p); err != nil {
|
||||
countError("frame_pushpromise_pad_short")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
p, pp.PromiseID, err = readUint32(p)
|
||||
if err != nil {
|
||||
countError("frame_pushpromise_promiseid_short")
|
||||
return
|
||||
}
|
||||
pp.PromiseID = pp.PromiseID & (1<<31 - 1)
|
||||
|
||||
if int(padLength) > len(p) {
|
||||
// like the DATA frame, error out if padding is longer than the body.
|
||||
countError("frame_pushpromise_pad_too_big")
|
||||
return nil, ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
pp.headerFragBuf = p[:len(p)-int(padLength)]
|
||||
|
|
|
@ -140,25 +140,29 @@ func buildRootHuffmanNode() {
|
|||
panic("unexpected size")
|
||||
}
|
||||
lazyRootHuffmanNode = newInternalNode()
|
||||
for i, code := range huffmanCodes {
|
||||
addDecoderNode(byte(i), code, huffmanCodeLen[i])
|
||||
}
|
||||
}
|
||||
// allocate a leaf node for each of the 256 symbols
|
||||
leaves := new([256]node)
|
||||
|
||||
func addDecoderNode(sym byte, code uint32, codeLen uint8) {
|
||||
cur := lazyRootHuffmanNode
|
||||
for codeLen > 8 {
|
||||
codeLen -= 8
|
||||
i := uint8(code >> codeLen)
|
||||
if cur.children[i] == nil {
|
||||
cur.children[i] = newInternalNode()
|
||||
for sym, code := range huffmanCodes {
|
||||
codeLen := huffmanCodeLen[sym]
|
||||
|
||||
cur := lazyRootHuffmanNode
|
||||
for codeLen > 8 {
|
||||
codeLen -= 8
|
||||
i := uint8(code >> codeLen)
|
||||
if cur.children[i] == nil {
|
||||
cur.children[i] = newInternalNode()
|
||||
}
|
||||
cur = cur.children[i]
|
||||
}
|
||||
shift := 8 - codeLen
|
||||
start, end := int(uint8(code<<shift)), int(1<<shift)
|
||||
|
||||
leaves[sym].sym = byte(sym)
|
||||
leaves[sym].codeLen = codeLen
|
||||
for i := start; i < start+end; i++ {
|
||||
cur.children[i] = &leaves[sym]
|
||||
}
|
||||
cur = cur.children[i]
|
||||
}
|
||||
shift := 8 - codeLen
|
||||
start, end := int(uint8(code<<shift)), int(1<<shift)
|
||||
for i := start; i < start+end; i++ {
|
||||
cur.children[i] = &node{sym: sym, codeLen: codeLen}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,17 @@ type pipeBuffer interface {
|
|||
io.Reader
|
||||
}
|
||||
|
||||
// setBuffer initializes the pipe buffer.
|
||||
// It has no effect if the pipe is already closed.
|
||||
func (p *pipe) setBuffer(b pipeBuffer) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
if p.err != nil || p.breakErr != nil {
|
||||
return
|
||||
}
|
||||
p.b = b
|
||||
}
|
||||
|
||||
func (p *pipe) Len() int {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
|
|
@ -130,6 +130,12 @@ type Server struct {
|
|||
// If nil, a default scheduler is chosen.
|
||||
NewWriteScheduler func() WriteScheduler
|
||||
|
||||
// CountError, if non-nil, is called on HTTP/2 server errors.
|
||||
// It's intended to increment a metric for monitoring, such
|
||||
// as an expvar or Prometheus metric.
|
||||
// The errType consists of only ASCII word characters.
|
||||
CountError func(errType string)
|
||||
|
||||
// Internal state. This is a pointer (rather than embedded directly)
|
||||
// so that we don't embed a Mutex in this struct, which will make the
|
||||
// struct non-copyable, which might break some callers.
|
||||
|
@ -405,6 +411,9 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
|||
sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf)
|
||||
|
||||
fr := NewFramer(sc.bw, c)
|
||||
if s.CountError != nil {
|
||||
fr.countError = s.CountError
|
||||
}
|
||||
fr.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil)
|
||||
fr.MaxHeaderListSize = sc.maxHeaderListSize()
|
||||
fr.SetMaxReadFrameSize(s.maxReadFrameSize())
|
||||
|
@ -1399,7 +1408,7 @@ func (sc *serverConn) processFrame(f Frame) error {
|
|||
// First frame received must be SETTINGS.
|
||||
if !sc.sawFirstSettings {
|
||||
if _, ok := f.(*SettingsFrame); !ok {
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
return sc.countError("first_settings", ConnectionError(ErrCodeProtocol))
|
||||
}
|
||||
sc.sawFirstSettings = true
|
||||
}
|
||||
|
@ -1424,7 +1433,7 @@ func (sc *serverConn) processFrame(f Frame) error {
|
|||
case *PushPromiseFrame:
|
||||
// A client cannot push. Thus, servers MUST treat the receipt of a PUSH_PROMISE
|
||||
// frame as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
return sc.countError("push_promise", ConnectionError(ErrCodeProtocol))
|
||||
default:
|
||||
sc.vlogf("http2: server ignoring frame: %v", f.Header())
|
||||
return nil
|
||||
|
@ -1444,7 +1453,7 @@ func (sc *serverConn) processPing(f *PingFrame) error {
|
|||
// identifier field value other than 0x0, the recipient MUST
|
||||
// respond with a connection error (Section 5.4.1) of type
|
||||
// PROTOCOL_ERROR."
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
return sc.countError("ping_on_stream", ConnectionError(ErrCodeProtocol))
|
||||
}
|
||||
if sc.inGoAway && sc.goAwayCode != ErrCodeNo {
|
||||
return nil
|
||||
|
@ -1463,7 +1472,7 @@ func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
|
|||
// or PRIORITY on a stream in this state MUST be
|
||||
// treated as a connection error (Section 5.4.1) of
|
||||
// type PROTOCOL_ERROR."
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
return sc.countError("stream_idle", ConnectionError(ErrCodeProtocol))
|
||||
}
|
||||
if st == nil {
|
||||
// "WINDOW_UPDATE can be sent by a peer that has sent a
|
||||
|
@ -1474,7 +1483,7 @@ func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
|
|||
return nil
|
||||
}
|
||||
if !st.flow.add(int32(f.Increment)) {
|
||||
return streamError(f.StreamID, ErrCodeFlowControl)
|
||||
return sc.countError("bad_flow", streamError(f.StreamID, ErrCodeFlowControl))
|
||||
}
|
||||
default: // connection-level flow control
|
||||
if !sc.flow.add(int32(f.Increment)) {
|
||||
|
@ -1495,7 +1504,7 @@ func (sc *serverConn) processResetStream(f *RSTStreamFrame) error {
|
|||
// identifying an idle stream is received, the
|
||||
// recipient MUST treat this as a connection error
|
||||
// (Section 5.4.1) of type PROTOCOL_ERROR.
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
return sc.countError("reset_idle_stream", ConnectionError(ErrCodeProtocol))
|
||||
}
|
||||
if st != nil {
|
||||
st.cancelCtx()
|
||||
|
@ -1547,7 +1556,7 @@ func (sc *serverConn) processSettings(f *SettingsFrame) error {
|
|||
// Why is the peer ACKing settings we never sent?
|
||||
// The spec doesn't mention this case, but
|
||||
// hang up on them anyway.
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
return sc.countError("ack_mystery", ConnectionError(ErrCodeProtocol))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1555,7 +1564,7 @@ func (sc *serverConn) processSettings(f *SettingsFrame) error {
|
|||
// This isn't actually in the spec, but hang up on
|
||||
// suspiciously large settings frames or those with
|
||||
// duplicate entries.
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
return sc.countError("settings_big_or_dups", ConnectionError(ErrCodeProtocol))
|
||||
}
|
||||
if err := f.ForeachSetting(sc.processSetting); err != nil {
|
||||
return err
|
||||
|
@ -1622,7 +1631,7 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
|
|||
// control window to exceed the maximum size as a
|
||||
// connection error (Section 5.4.1) of type
|
||||
// FLOW_CONTROL_ERROR."
|
||||
return ConnectionError(ErrCodeFlowControl)
|
||||
return sc.countError("setting_win_size", ConnectionError(ErrCodeFlowControl))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -1655,7 +1664,7 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
|||
// or PRIORITY on a stream in this state MUST be
|
||||
// treated as a connection error (Section 5.4.1) of
|
||||
// type PROTOCOL_ERROR."
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
return sc.countError("data_on_idle", ConnectionError(ErrCodeProtocol))
|
||||
}
|
||||
|
||||
// "If a DATA frame is received whose stream is not in "open"
|
||||
|
@ -1672,7 +1681,7 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
|||
// and return any flow control bytes since we're not going
|
||||
// to consume them.
|
||||
if sc.inflow.available() < int32(f.Length) {
|
||||
return streamError(id, ErrCodeFlowControl)
|
||||
return sc.countError("data_flow", streamError(id, ErrCodeFlowControl))
|
||||
}
|
||||
// Deduct the flow control from inflow, since we're
|
||||
// going to immediately add it back in
|
||||
|
@ -1685,7 +1694,7 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
|||
// Already have a stream error in flight. Don't send another.
|
||||
return nil
|
||||
}
|
||||
return streamError(id, ErrCodeStreamClosed)
|
||||
return sc.countError("closed", streamError(id, ErrCodeStreamClosed))
|
||||
}
|
||||
if st.body == nil {
|
||||
panic("internal error: should have a body in this state")
|
||||
|
@ -1697,12 +1706,12 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
|||
// RFC 7540, sec 8.1.2.6: A request or response is also malformed if the
|
||||
// value of a content-length header field does not equal the sum of the
|
||||
// DATA frame payload lengths that form the body.
|
||||
return streamError(id, ErrCodeProtocol)
|
||||
return sc.countError("send_too_much", streamError(id, ErrCodeProtocol))
|
||||
}
|
||||
if f.Length > 0 {
|
||||
// Check whether the client has flow control quota.
|
||||
if st.inflow.available() < int32(f.Length) {
|
||||
return streamError(id, ErrCodeFlowControl)
|
||||
return sc.countError("flow_on_data_length", streamError(id, ErrCodeFlowControl))
|
||||
}
|
||||
st.inflow.take(int32(f.Length))
|
||||
|
||||
|
@ -1710,7 +1719,7 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
|||
wrote, err := st.body.Write(data)
|
||||
if err != nil {
|
||||
sc.sendWindowUpdate(nil, int(f.Length)-wrote)
|
||||
return streamError(id, ErrCodeStreamClosed)
|
||||
return sc.countError("body_write_err", streamError(id, ErrCodeStreamClosed))
|
||||
}
|
||||
if wrote != len(data) {
|
||||
panic("internal error: bad Writer")
|
||||
|
@ -1796,7 +1805,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
|||
// stream identifier MUST respond with a connection error
|
||||
// (Section 5.4.1) of type PROTOCOL_ERROR.
|
||||
if id%2 != 1 {
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
return sc.countError("headers_even", ConnectionError(ErrCodeProtocol))
|
||||
}
|
||||
// A HEADERS frame can be used to create a new stream or
|
||||
// send a trailer for an open one. If we already have a stream
|
||||
|
@ -1813,7 +1822,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
|||
// this state, it MUST respond with a stream error (Section 5.4.2) of
|
||||
// type STREAM_CLOSED.
|
||||
if st.state == stateHalfClosedRemote {
|
||||
return streamError(id, ErrCodeStreamClosed)
|
||||
return sc.countError("headers_half_closed", streamError(id, ErrCodeStreamClosed))
|
||||
}
|
||||
return st.processTrailerHeaders(f)
|
||||
}
|
||||
|
@ -1824,7 +1833,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
|||
// receives an unexpected stream identifier MUST respond with
|
||||
// a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
||||
if id <= sc.maxClientStreamID {
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
return sc.countError("stream_went_down", ConnectionError(ErrCodeProtocol))
|
||||
}
|
||||
sc.maxClientStreamID = id
|
||||
|
||||
|
@ -1841,14 +1850,14 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
|||
if sc.curClientStreams+1 > sc.advMaxStreams {
|
||||
if sc.unackedSettings == 0 {
|
||||
// They should know better.
|
||||
return streamError(id, ErrCodeProtocol)
|
||||
return sc.countError("over_max_streams", streamError(id, ErrCodeProtocol))
|
||||
}
|
||||
// Assume it's a network race, where they just haven't
|
||||
// received our last SETTINGS update. But actually
|
||||
// this can't happen yet, because we don't yet provide
|
||||
// a way for users to adjust server parameters at
|
||||
// runtime.
|
||||
return streamError(id, ErrCodeRefusedStream)
|
||||
return sc.countError("over_max_streams_race", streamError(id, ErrCodeRefusedStream))
|
||||
}
|
||||
|
||||
initialState := stateOpen
|
||||
|
@ -1858,7 +1867,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
|||
st := sc.newStream(id, 0, initialState)
|
||||
|
||||
if f.HasPriority() {
|
||||
if err := checkPriority(f.StreamID, f.Priority); err != nil {
|
||||
if err := sc.checkPriority(f.StreamID, f.Priority); err != nil {
|
||||
return err
|
||||
}
|
||||
sc.writeSched.AdjustStream(st.id, f.Priority)
|
||||
|
@ -1902,15 +1911,15 @@ func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
|
|||
sc := st.sc
|
||||
sc.serveG.check()
|
||||
if st.gotTrailerHeader {
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
return sc.countError("dup_trailers", ConnectionError(ErrCodeProtocol))
|
||||
}
|
||||
st.gotTrailerHeader = true
|
||||
if !f.StreamEnded() {
|
||||
return streamError(st.id, ErrCodeProtocol)
|
||||
return sc.countError("trailers_not_ended", streamError(st.id, ErrCodeProtocol))
|
||||
}
|
||||
|
||||
if len(f.PseudoFields()) > 0 {
|
||||
return streamError(st.id, ErrCodeProtocol)
|
||||
return sc.countError("trailers_pseudo", streamError(st.id, ErrCodeProtocol))
|
||||
}
|
||||
if st.trailer != nil {
|
||||
for _, hf := range f.RegularFields() {
|
||||
|
@ -1919,7 +1928,7 @@ func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
|
|||
// TODO: send more details to the peer somehow. But http2 has
|
||||
// no way to send debug data at a stream level. Discuss with
|
||||
// HTTP folk.
|
||||
return streamError(st.id, ErrCodeProtocol)
|
||||
return sc.countError("trailers_bogus", streamError(st.id, ErrCodeProtocol))
|
||||
}
|
||||
st.trailer[key] = append(st.trailer[key], hf.Value)
|
||||
}
|
||||
|
@ -1928,13 +1937,13 @@ func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func checkPriority(streamID uint32, p PriorityParam) error {
|
||||
func (sc *serverConn) checkPriority(streamID uint32, p PriorityParam) error {
|
||||
if streamID == p.StreamDep {
|
||||
// Section 5.3.1: "A stream cannot depend on itself. An endpoint MUST treat
|
||||
// this as a stream error (Section 5.4.2) of type PROTOCOL_ERROR."
|
||||
// Section 5.3.3 says that a stream can depend on one of its dependencies,
|
||||
// so it's only self-dependencies that are forbidden.
|
||||
return streamError(streamID, ErrCodeProtocol)
|
||||
return sc.countError("priority", streamError(streamID, ErrCodeProtocol))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1943,7 +1952,7 @@ func (sc *serverConn) processPriority(f *PriorityFrame) error {
|
|||
if sc.inGoAway {
|
||||
return nil
|
||||
}
|
||||
if err := checkPriority(f.StreamID, f.PriorityParam); err != nil {
|
||||
if err := sc.checkPriority(f.StreamID, f.PriorityParam); err != nil {
|
||||
return err
|
||||
}
|
||||
sc.writeSched.AdjustStream(f.StreamID, f.PriorityParam)
|
||||
|
@ -2000,7 +2009,7 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res
|
|||
isConnect := rp.method == "CONNECT"
|
||||
if isConnect {
|
||||
if rp.path != "" || rp.scheme != "" || rp.authority == "" {
|
||||
return nil, nil, streamError(f.StreamID, ErrCodeProtocol)
|
||||
return nil, nil, sc.countError("bad_connect", streamError(f.StreamID, ErrCodeProtocol))
|
||||
}
|
||||
} else if rp.method == "" || rp.path == "" || (rp.scheme != "https" && rp.scheme != "http") {
|
||||
// See 8.1.2.6 Malformed Requests and Responses:
|
||||
|
@ -2013,13 +2022,13 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res
|
|||
// "All HTTP/2 requests MUST include exactly one valid
|
||||
// value for the :method, :scheme, and :path
|
||||
// pseudo-header fields"
|
||||
return nil, nil, streamError(f.StreamID, ErrCodeProtocol)
|
||||
return nil, nil, sc.countError("bad_path_method", streamError(f.StreamID, ErrCodeProtocol))
|
||||
}
|
||||
|
||||
bodyOpen := !f.StreamEnded()
|
||||
if rp.method == "HEAD" && bodyOpen {
|
||||
// HEAD requests can't have bodies
|
||||
return nil, nil, streamError(f.StreamID, ErrCodeProtocol)
|
||||
return nil, nil, sc.countError("head_body", streamError(f.StreamID, ErrCodeProtocol))
|
||||
}
|
||||
|
||||
rp.header = make(http.Header)
|
||||
|
@ -2102,7 +2111,7 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
|
|||
var err error
|
||||
url_, err = url.ParseRequestURI(rp.path)
|
||||
if err != nil {
|
||||
return nil, nil, streamError(st.id, ErrCodeProtocol)
|
||||
return nil, nil, sc.countError("bad_path", streamError(st.id, ErrCodeProtocol))
|
||||
}
|
||||
requestURI = rp.path
|
||||
}
|
||||
|
@ -2985,3 +2994,31 @@ func h1ServerKeepAlivesDisabled(hs *http.Server) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (sc *serverConn) countError(name string, err error) error {
|
||||
if sc == nil || sc.srv == nil {
|
||||
return err
|
||||
}
|
||||
f := sc.srv.CountError
|
||||
if f == nil {
|
||||
return err
|
||||
}
|
||||
var typ string
|
||||
var code ErrCode
|
||||
switch e := err.(type) {
|
||||
case ConnectionError:
|
||||
typ = "conn"
|
||||
code = ErrCode(e)
|
||||
case StreamError:
|
||||
typ = "stream"
|
||||
code = ErrCode(e.Code)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
codeStr := errCodeName[code]
|
||||
if codeStr == "" {
|
||||
codeStr = strconv.Itoa(int(code))
|
||||
}
|
||||
f(fmt.Sprintf("%s_%s_%s", typ, codeStr, name))
|
||||
return err
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,8 +3,10 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
|||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"go118.go",
|
||||
"idna10.0.0.go",
|
||||
"idna9.0.0.go",
|
||||
"pre_go118.go",
|
||||
"punycode.go",
|
||||
"tables10.0.0.go",
|
||||
"tables11.0.0.go",
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
package idna
|
||||
|
||||
// Transitional processing is disabled by default in Go 1.18.
|
||||
// https://golang.org/issue/47510
|
||||
const transitionalLookup = false
|
|
@ -59,10 +59,10 @@ type Option func(*options)
|
|||
// Transitional sets a Profile to use the Transitional mapping as defined in UTS
|
||||
// #46. This will cause, for example, "ß" to be mapped to "ss". Using the
|
||||
// transitional mapping provides a compromise between IDNA2003 and IDNA2008
|
||||
// compatibility. It is used by most browsers when resolving domain names. This
|
||||
// compatibility. It is used by some browsers when resolving domain names. This
|
||||
// option is only meaningful if combined with MapForLookup.
|
||||
func Transitional(transitional bool) Option {
|
||||
return func(o *options) { o.transitional = true }
|
||||
return func(o *options) { o.transitional = transitional }
|
||||
}
|
||||
|
||||
// VerifyDNSLength sets whether a Profile should fail if any of the IDN parts
|
||||
|
@ -284,7 +284,7 @@ var (
|
|||
|
||||
punycode = &Profile{}
|
||||
lookup = &Profile{options{
|
||||
transitional: true,
|
||||
transitional: transitionalLookup,
|
||||
useSTD3Rules: true,
|
||||
checkHyphens: true,
|
||||
checkJoiners: true,
|
||||
|
|
|
@ -58,10 +58,10 @@ type Option func(*options)
|
|||
// Transitional sets a Profile to use the Transitional mapping as defined in UTS
|
||||
// #46. This will cause, for example, "ß" to be mapped to "ss". Using the
|
||||
// transitional mapping provides a compromise between IDNA2003 and IDNA2008
|
||||
// compatibility. It is used by most browsers when resolving domain names. This
|
||||
// compatibility. It is used by some browsers when resolving domain names. This
|
||||
// option is only meaningful if combined with MapForLookup.
|
||||
func Transitional(transitional bool) Option {
|
||||
return func(o *options) { o.transitional = true }
|
||||
return func(o *options) { o.transitional = transitional }
|
||||
}
|
||||
|
||||
// VerifyDNSLength sets whether a Profile should fail if any of the IDN parts
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.18
|
||||
// +build !go1.18
|
||||
|
||||
package idna
|
||||
|
||||
const transitionalLookup = true
|
|
@ -49,6 +49,7 @@ func decode(encoded string) (string, error) {
|
|||
}
|
||||
}
|
||||
i, n, bias := int32(0), initialN, initialBias
|
||||
overflow := false
|
||||
for pos < len(encoded) {
|
||||
oldI, w := i, int32(1)
|
||||
for k := base; ; k += base {
|
||||
|
@ -60,29 +61,32 @@ func decode(encoded string) (string, error) {
|
|||
return "", punyError(encoded)
|
||||
}
|
||||
pos++
|
||||
i += digit * w
|
||||
if i < 0 {
|
||||
i, overflow = madd(i, digit, w)
|
||||
if overflow {
|
||||
return "", punyError(encoded)
|
||||
}
|
||||
t := k - bias
|
||||
if t < tmin {
|
||||
if k <= bias {
|
||||
t = tmin
|
||||
} else if t > tmax {
|
||||
} else if k >= bias+tmax {
|
||||
t = tmax
|
||||
}
|
||||
if digit < t {
|
||||
break
|
||||
}
|
||||
w *= base - t
|
||||
if w >= math.MaxInt32/base {
|
||||
w, overflow = madd(0, w, base-t)
|
||||
if overflow {
|
||||
return "", punyError(encoded)
|
||||
}
|
||||
}
|
||||
if len(output) >= 1024 {
|
||||
return "", punyError(encoded)
|
||||
}
|
||||
x := int32(len(output) + 1)
|
||||
bias = adapt(i-oldI, x, oldI == 0)
|
||||
n += i / x
|
||||
i %= x
|
||||
if n > utf8.MaxRune || len(output) >= 1024 {
|
||||
if n < 0 || n > utf8.MaxRune {
|
||||
return "", punyError(encoded)
|
||||
}
|
||||
output = append(output, 0)
|
||||
|
@ -115,6 +119,7 @@ func encode(prefix, s string) (string, error) {
|
|||
if b > 0 {
|
||||
output = append(output, '-')
|
||||
}
|
||||
overflow := false
|
||||
for remaining != 0 {
|
||||
m := int32(0x7fffffff)
|
||||
for _, r := range s {
|
||||
|
@ -122,8 +127,8 @@ func encode(prefix, s string) (string, error) {
|
|||
m = r
|
||||
}
|
||||
}
|
||||
delta += (m - n) * (h + 1)
|
||||
if delta < 0 {
|
||||
delta, overflow = madd(delta, m-n, h+1)
|
||||
if overflow {
|
||||
return "", punyError(s)
|
||||
}
|
||||
n = m
|
||||
|
@ -141,9 +146,9 @@ func encode(prefix, s string) (string, error) {
|
|||
q := delta
|
||||
for k := base; ; k += base {
|
||||
t := k - bias
|
||||
if t < tmin {
|
||||
if k <= bias {
|
||||
t = tmin
|
||||
} else if t > tmax {
|
||||
} else if k >= bias+tmax {
|
||||
t = tmax
|
||||
}
|
||||
if q < t {
|
||||
|
@ -164,6 +169,15 @@ func encode(prefix, s string) (string, error) {
|
|||
return string(output), nil
|
||||
}
|
||||
|
||||
// madd computes a + (b * c), detecting overflow.
|
||||
func madd(a, b, c int32) (next int32, overflow bool) {
|
||||
p := int64(b) * int64(c)
|
||||
if p > math.MaxInt32-int64(a) {
|
||||
return 0, true
|
||||
}
|
||||
return a + int32(p), false
|
||||
}
|
||||
|
||||
func decodeDigit(x byte) (digit int32, ok bool) {
|
||||
switch {
|
||||
case '0' <= x && x <= '9':
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build darwin && go1.12
|
||||
// +build darwin,go1.12
|
||||
|
||||
// This exists solely so we can linkname in symbols from syscall.
|
||||
|
|
|
@ -15,7 +15,3 @@ func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
|
|||
// xgetbv with ecx = 0 is implemented in cpu_x86.s for gc compiler
|
||||
// and in cpu_gccgo.c for gccgo.
|
||||
func xgetbv() (eax, edx uint32)
|
||||
|
||||
// darwinSupportsAVX512 is implemented in cpu_x86.s for gc compiler
|
||||
// and in cpu_gccgo_x86.go for gccgo.
|
||||
func darwinSupportsAVX512() bool
|
||||
|
|
|
@ -90,9 +90,10 @@ func archInit() {
|
|||
osSupportsAVX = isSet(1, eax) && isSet(2, eax)
|
||||
|
||||
if runtime.GOOS == "darwin" {
|
||||
// Check darwin commpage for AVX512 support. Necessary because:
|
||||
// https://github.com/apple/darwin-xnu/blob/0a798f6738bc1db01281fc08ae024145e84df927/osfmk/i386/fpu.c#L175-L201
|
||||
osSupportsAVX512 = osSupportsAVX && darwinSupportsAVX512()
|
||||
// Darwin doesn't save/restore AVX-512 mask registers correctly across signal handlers.
|
||||
// Since users can't rely on mask register contents, let's not advertise AVX-512 support.
|
||||
// See issue 49233.
|
||||
osSupportsAVX512 = false
|
||||
} else {
|
||||
// Check if OPMASK and ZMM registers have OS support.
|
||||
osSupportsAVX512 = osSupportsAVX && isSet(5, eax) && isSet(6, eax) && isSet(7, eax)
|
||||
|
|
|
@ -26,27 +26,3 @@ TEXT ·xgetbv(SB),NOSPLIT,$0-8
|
|||
MOVL AX, eax+0(FP)
|
||||
MOVL DX, edx+4(FP)
|
||||
RET
|
||||
|
||||
// func darwinSupportsAVX512() bool
|
||||
TEXT ·darwinSupportsAVX512(SB), NOSPLIT, $0-1
|
||||
MOVB $0, ret+0(FP) // default to false
|
||||
#ifdef GOOS_darwin // return if not darwin
|
||||
#ifdef GOARCH_amd64 // return if not amd64
|
||||
// These values from:
|
||||
// https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/osfmk/i386/cpu_capabilities.h
|
||||
#define commpage64_base_address 0x00007fffffe00000
|
||||
#define commpage64_cpu_capabilities64 (commpage64_base_address+0x010)
|
||||
#define commpage64_version (commpage64_base_address+0x01E)
|
||||
#define hasAVX512F 0x0000004000000000
|
||||
MOVQ $commpage64_version, BX
|
||||
CMPW (BX), $13 // cpu_capabilities64 undefined in versions < 13
|
||||
JL no_avx512
|
||||
MOVQ $commpage64_cpu_capabilities64, BX
|
||||
MOVQ $hasAVX512F, CX
|
||||
TESTQ (BX), CX
|
||||
JZ no_avx512
|
||||
MOVB $1, ret+0(FP)
|
||||
no_avx512:
|
||||
#endif
|
||||
#endif
|
||||
RET
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.5
|
||||
// +build go1.5
|
||||
|
||||
package plan9
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.5
|
||||
// +build !go1.5
|
||||
|
||||
package plan9
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build plan9 && race
|
||||
// +build plan9,race
|
||||
|
||||
package plan9
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build plan9 && !race
|
||||
// +build plan9,!race
|
||||
|
||||
package plan9
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build plan9
|
||||
// +build plan9
|
||||
|
||||
package plan9
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build plan9
|
||||
// +build plan9
|
||||
|
||||
// Package plan9 contains an interface to the low-level operating system
|
||||
|
|
|
@ -132,8 +132,10 @@ func Pipe(p []int) (err error) {
|
|||
}
|
||||
var pp [2]int32
|
||||
err = pipe(&pp)
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
if err == nil {
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// go run mksyscall.go -l32 -plan9 -tags plan9,386 syscall_plan9.go
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build plan9 && 386
|
||||
// +build plan9,386
|
||||
|
||||
package plan9
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// go run mksyscall.go -l32 -plan9 -tags plan9,amd64 syscall_plan9.go
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build plan9 && amd64
|
||||
// +build plan9,amd64
|
||||
|
||||
package plan9
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// go run mksyscall.go -l32 -plan9 -tags plan9,arm syscall_plan9.go
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build plan9 && arm
|
||||
// +build plan9,arm
|
||||
|
||||
package plan9
|
||||
|
|
|
@ -149,7 +149,7 @@ To add a constant, add the header that includes it to the appropriate variable.
|
|||
Then, edit the regex (if necessary) to match the desired constant. Avoid making
|
||||
the regex too broad to avoid matching unintended constants.
|
||||
|
||||
### mkmerge.go
|
||||
### internal/mkmerge
|
||||
|
||||
This program is used to extract duplicate const, func, and type declarations
|
||||
from the generated architecture-specific files listed below, and merge these
|
||||
|
|
|
@ -50,7 +50,7 @@ if [[ "$GOOS" = "linux" ]]; then
|
|||
# Use the Docker-based build system
|
||||
# Files generated through docker (use $cmd so you can Ctl-C the build or run)
|
||||
$cmd docker build --tag generate:$GOOS $GOOS
|
||||
$cmd docker run --interactive --tty --volume $(cd -- "$(dirname -- "$0")" && /bin/pwd):/build generate:$GOOS
|
||||
$cmd docker run --interactive --tty --volume $(cd -- "$(dirname -- "$0")/.." && /bin/pwd):/build generate:$GOOS
|
||||
exit
|
||||
fi
|
||||
|
||||
|
|
|
@ -239,6 +239,7 @@ struct ltchars {
|
|||
#include <linux/magic.h>
|
||||
#include <linux/memfd.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/netfilter/nfnetlink.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/net_namespace.h>
|
||||
|
@ -260,6 +261,7 @@ struct ltchars {
|
|||
#include <linux/vm_sockets.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/wireguard.h>
|
||||
|
||||
#include <mtd/ubi-user.h>
|
||||
#include <mtd/mtd-user.h>
|
||||
|
@ -520,7 +522,7 @@ ccflags="$@"
|
|||
$2 ~ /^HW_MACHINE$/ ||
|
||||
$2 ~ /^SYSCTL_VERS/ ||
|
||||
$2 !~ "MNT_BITS" &&
|
||||
$2 ~ /^(MS|MNT|UMOUNT)_/ ||
|
||||
$2 ~ /^(MS|MNT|MOUNT|UMOUNT)_/ ||
|
||||
$2 ~ /^NS_GET_/ ||
|
||||
$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
|
||||
$2 ~ /^(O|F|[ES]?FD|NAME|S|PTRACE|PT|TFD)_/ ||
|
||||
|
@ -605,6 +607,7 @@ ccflags="$@"
|
|||
$2 ~ /^MTD/ ||
|
||||
$2 ~ /^OTP/ ||
|
||||
$2 ~ /^MEM/ ||
|
||||
$2 ~ /^WG/ ||
|
||||
$2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE)/ {printf("\t%s = C.%s\n", $2, $2)}
|
||||
$2 ~ /^__WCOREFLAG$/ {next}
|
||||
$2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)}
|
||||
|
|
|
@ -67,9 +67,7 @@ func ParseOrigDstAddr(m *SocketControlMessage) (Sockaddr, error) {
|
|||
sa := new(SockaddrInet4)
|
||||
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
|
||||
sa.Port = int(p[0])<<8 + int(p[1])
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.Addr[i] = pp.Addr[i]
|
||||
}
|
||||
sa.Addr = pp.Addr
|
||||
return sa, nil
|
||||
|
||||
case m.Header.Level == SOL_IPV6 && m.Header.Type == IPV6_ORIGDSTADDR:
|
||||
|
@ -78,9 +76,7 @@ func ParseOrigDstAddr(m *SocketControlMessage) (Sockaddr, error) {
|
|||
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
|
||||
sa.Port = int(p[0])<<8 + int(p[1])
|
||||
sa.ZoneId = pp.Scope_id
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.Addr[i] = pp.Addr[i]
|
||||
}
|
||||
sa.Addr = pp.Addr
|
||||
return sa, nil
|
||||
|
||||
default:
|
||||
|
|
|
@ -70,9 +70,7 @@ func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, _Socklen, error) {
|
|||
p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port))
|
||||
p[0] = byte(sa.Port >> 8)
|
||||
p[1] = byte(sa.Port)
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.raw.Addr[i] = sa.Addr[i]
|
||||
}
|
||||
sa.raw.Addr = sa.Addr
|
||||
return unsafe.Pointer(&sa.raw), SizeofSockaddrInet4, nil
|
||||
}
|
||||
|
||||
|
@ -85,9 +83,7 @@ func (sa *SockaddrInet6) sockaddr() (unsafe.Pointer, _Socklen, error) {
|
|||
p[0] = byte(sa.Port >> 8)
|
||||
p[1] = byte(sa.Port)
|
||||
sa.raw.Scope_id = sa.ZoneId
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.raw.Addr[i] = sa.Addr[i]
|
||||
}
|
||||
sa.raw.Addr = sa.Addr
|
||||
return unsafe.Pointer(&sa.raw), SizeofSockaddrInet6, nil
|
||||
}
|
||||
|
||||
|
@ -261,9 +257,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
|
|||
sa := new(SockaddrInet4)
|
||||
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
|
||||
sa.Port = int(p[0])<<8 + int(p[1])
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.Addr[i] = pp.Addr[i]
|
||||
}
|
||||
sa.Addr = pp.Addr
|
||||
return sa, nil
|
||||
|
||||
case AF_INET6:
|
||||
|
@ -272,9 +266,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
|
|||
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
|
||||
sa.Port = int(p[0])<<8 + int(p[1])
|
||||
sa.ZoneId = pp.Scope_id
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.Addr[i] = pp.Addr[i]
|
||||
}
|
||||
sa.Addr = pp.Addr
|
||||
return sa, nil
|
||||
}
|
||||
return nil, EAFNOSUPPORT
|
||||
|
@ -385,6 +377,11 @@ func (w WaitStatus) TrapCause() int { return -1 }
|
|||
|
||||
//sys fcntl(fd int, cmd int, arg int) (val int, err error)
|
||||
|
||||
//sys fsyncRange(fd int, how int, start int64, length int64) (err error) = fsync_range
|
||||
func Fsync(fd int) error {
|
||||
return fsyncRange(fd, O_SYNC, 0, 0)
|
||||
}
|
||||
|
||||
/*
|
||||
* Direct access
|
||||
*/
|
||||
|
@ -401,7 +398,6 @@ func (w WaitStatus) TrapCause() int { return -1 }
|
|||
//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error)
|
||||
//sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error)
|
||||
//sys Fdatasync(fd int) (err error)
|
||||
//sys Fsync(fd int) (err error)
|
||||
// readdir_r
|
||||
//sysnb Getpgid(pid int) (pgid int, err error)
|
||||
|
||||
|
@ -523,8 +519,10 @@ func Pipe(p []int) (err error) {
|
|||
}
|
||||
var pp [2]_C_int
|
||||
err = pipe(&pp)
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
if err == nil {
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -163,9 +163,7 @@ func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, _Socklen, error) {
|
|||
p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port))
|
||||
p[0] = byte(sa.Port >> 8)
|
||||
p[1] = byte(sa.Port)
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.raw.Addr[i] = sa.Addr[i]
|
||||
}
|
||||
sa.raw.Addr = sa.Addr
|
||||
return unsafe.Pointer(&sa.raw), _Socklen(sa.raw.Len), nil
|
||||
}
|
||||
|
||||
|
@ -179,9 +177,7 @@ func (sa *SockaddrInet6) sockaddr() (unsafe.Pointer, _Socklen, error) {
|
|||
p[0] = byte(sa.Port >> 8)
|
||||
p[1] = byte(sa.Port)
|
||||
sa.raw.Scope_id = sa.ZoneId
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.raw.Addr[i] = sa.Addr[i]
|
||||
}
|
||||
sa.raw.Addr = sa.Addr
|
||||
return unsafe.Pointer(&sa.raw), _Socklen(sa.raw.Len), nil
|
||||
}
|
||||
|
||||
|
@ -210,9 +206,7 @@ func (sa *SockaddrDatalink) sockaddr() (unsafe.Pointer, _Socklen, error) {
|
|||
sa.raw.Nlen = sa.Nlen
|
||||
sa.raw.Alen = sa.Alen
|
||||
sa.raw.Slen = sa.Slen
|
||||
for i := 0; i < len(sa.raw.Data); i++ {
|
||||
sa.raw.Data[i] = sa.Data[i]
|
||||
}
|
||||
sa.raw.Data = sa.Data
|
||||
return unsafe.Pointer(&sa.raw), SizeofSockaddrDatalink, nil
|
||||
}
|
||||
|
||||
|
@ -228,9 +222,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
|
|||
sa.Nlen = pp.Nlen
|
||||
sa.Alen = pp.Alen
|
||||
sa.Slen = pp.Slen
|
||||
for i := 0; i < len(sa.Data); i++ {
|
||||
sa.Data[i] = pp.Data[i]
|
||||
}
|
||||
sa.Data = pp.Data
|
||||
return sa, nil
|
||||
|
||||
case AF_UNIX:
|
||||
|
@ -262,9 +254,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
|
|||
sa := new(SockaddrInet4)
|
||||
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
|
||||
sa.Port = int(p[0])<<8 + int(p[1])
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.Addr[i] = pp.Addr[i]
|
||||
}
|
||||
sa.Addr = pp.Addr
|
||||
return sa, nil
|
||||
|
||||
case AF_INET6:
|
||||
|
@ -273,9 +263,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
|
|||
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
|
||||
sa.Port = int(p[0])<<8 + int(p[1])
|
||||
sa.ZoneId = pp.Scope_id
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.Addr[i] = pp.Addr[i]
|
||||
}
|
||||
sa.Addr = pp.Addr
|
||||
return sa, nil
|
||||
}
|
||||
return anyToSockaddrGOOS(fd, rsa)
|
||||
|
|
|
@ -159,8 +159,10 @@ func Pipe(p []int) (err error) {
|
|||
}
|
||||
var x [2]int32
|
||||
err = pipe(&x)
|
||||
p[0] = int(x[0])
|
||||
p[1] = int(x[1])
|
||||
if err == nil {
|
||||
p[0] = int(x[0])
|
||||
p[1] = int(x[1])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -430,8 +432,25 @@ func GetsockoptXucred(fd, level, opt int) (*Xucred, error) {
|
|||
return x, err
|
||||
}
|
||||
|
||||
func SysctlKinfoProcSlice(name string) ([]KinfoProc, error) {
|
||||
mib, err := sysctlmib(name)
|
||||
func SysctlKinfoProc(name string, args ...int) (*KinfoProc, error) {
|
||||
mib, err := sysctlmib(name, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var kinfo KinfoProc
|
||||
n := uintptr(SizeofKinfoProc)
|
||||
if err := sysctl(mib, (*byte)(unsafe.Pointer(&kinfo)), &n, nil, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != SizeofKinfoProc {
|
||||
return nil, EIO
|
||||
}
|
||||
return &kinfo, nil
|
||||
}
|
||||
|
||||
func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) {
|
||||
mib, err := sysctlmib(name, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -101,7 +101,10 @@ func Pipe(p []int) (err error) {
|
|||
if len(p) != 2 {
|
||||
return EINVAL
|
||||
}
|
||||
p[0], p[1], err = pipe()
|
||||
r, w, err := pipe()
|
||||
if err == nil {
|
||||
p[0], p[1] = r, w
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -114,7 +117,10 @@ func Pipe2(p []int, flags int) (err error) {
|
|||
var pp [2]_C_int
|
||||
// pipe2 on dragonfly takes an fds array as an argument, but still
|
||||
// returns the file descriptors.
|
||||
p[0], p[1], err = pipe2(&pp, flags)
|
||||
r, w, err := pipe2(&pp, flags)
|
||||
if err == nil {
|
||||
p[0], p[1] = r, w
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -110,8 +110,10 @@ func Pipe2(p []int, flags int) error {
|
|||
}
|
||||
var pp [2]_C_int
|
||||
err := pipe2(&pp, flags)
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
if err == nil {
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -131,8 +131,10 @@ func Pipe2(p []int, flags int) error {
|
|||
}
|
||||
var pp [2]_C_int
|
||||
err := pipe2(&pp, flags)
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
if err == nil {
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -372,9 +374,7 @@ func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, _Socklen, error) {
|
|||
p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port))
|
||||
p[0] = byte(sa.Port >> 8)
|
||||
p[1] = byte(sa.Port)
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.raw.Addr[i] = sa.Addr[i]
|
||||
}
|
||||
sa.raw.Addr = sa.Addr
|
||||
return unsafe.Pointer(&sa.raw), SizeofSockaddrInet4, nil
|
||||
}
|
||||
|
||||
|
@ -387,9 +387,7 @@ func (sa *SockaddrInet6) sockaddr() (unsafe.Pointer, _Socklen, error) {
|
|||
p[0] = byte(sa.Port >> 8)
|
||||
p[1] = byte(sa.Port)
|
||||
sa.raw.Scope_id = sa.ZoneId
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.raw.Addr[i] = sa.Addr[i]
|
||||
}
|
||||
sa.raw.Addr = sa.Addr
|
||||
return unsafe.Pointer(&sa.raw), SizeofSockaddrInet6, nil
|
||||
}
|
||||
|
||||
|
@ -438,9 +436,7 @@ func (sa *SockaddrLinklayer) sockaddr() (unsafe.Pointer, _Socklen, error) {
|
|||
sa.raw.Hatype = sa.Hatype
|
||||
sa.raw.Pkttype = sa.Pkttype
|
||||
sa.raw.Halen = sa.Halen
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.raw.Addr[i] = sa.Addr[i]
|
||||
}
|
||||
sa.raw.Addr = sa.Addr
|
||||
return unsafe.Pointer(&sa.raw), SizeofSockaddrLinklayer, nil
|
||||
}
|
||||
|
||||
|
@ -855,12 +851,10 @@ func (sa *SockaddrTIPC) sockaddr() (unsafe.Pointer, _Socklen, error) {
|
|||
if sa.Addr == nil {
|
||||
return nil, 0, EINVAL
|
||||
}
|
||||
|
||||
sa.raw.Family = AF_TIPC
|
||||
sa.raw.Scope = int8(sa.Scope)
|
||||
sa.raw.Addrtype = sa.Addr.tipcAddrtype()
|
||||
sa.raw.Addr = sa.Addr.tipcAddr()
|
||||
|
||||
return unsafe.Pointer(&sa.raw), SizeofSockaddrTIPC, nil
|
||||
}
|
||||
|
||||
|
@ -874,9 +868,7 @@ type SockaddrL2TPIP struct {
|
|||
func (sa *SockaddrL2TPIP) sockaddr() (unsafe.Pointer, _Socklen, error) {
|
||||
sa.raw.Family = AF_INET
|
||||
sa.raw.Conn_id = sa.ConnId
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.raw.Addr[i] = sa.Addr[i]
|
||||
}
|
||||
sa.raw.Addr = sa.Addr
|
||||
return unsafe.Pointer(&sa.raw), SizeofSockaddrL2TPIP, nil
|
||||
}
|
||||
|
||||
|
@ -892,9 +884,7 @@ func (sa *SockaddrL2TPIP6) sockaddr() (unsafe.Pointer, _Socklen, error) {
|
|||
sa.raw.Family = AF_INET6
|
||||
sa.raw.Conn_id = sa.ConnId
|
||||
sa.raw.Scope_id = sa.ZoneId
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.raw.Addr[i] = sa.Addr[i]
|
||||
}
|
||||
sa.raw.Addr = sa.Addr
|
||||
return unsafe.Pointer(&sa.raw), SizeofSockaddrL2TPIP6, nil
|
||||
}
|
||||
|
||||
|
@ -990,9 +980,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
|
|||
sa.Hatype = pp.Hatype
|
||||
sa.Pkttype = pp.Pkttype
|
||||
sa.Halen = pp.Halen
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.Addr[i] = pp.Addr[i]
|
||||
}
|
||||
sa.Addr = pp.Addr
|
||||
return sa, nil
|
||||
|
||||
case AF_UNIX:
|
||||
|
@ -1031,18 +1019,14 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
|
|||
pp := (*RawSockaddrL2TPIP)(unsafe.Pointer(rsa))
|
||||
sa := new(SockaddrL2TPIP)
|
||||
sa.ConnId = pp.Conn_id
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.Addr[i] = pp.Addr[i]
|
||||
}
|
||||
sa.Addr = pp.Addr
|
||||
return sa, nil
|
||||
default:
|
||||
pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa))
|
||||
sa := new(SockaddrInet4)
|
||||
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
|
||||
sa.Port = int(p[0])<<8 + int(p[1])
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.Addr[i] = pp.Addr[i]
|
||||
}
|
||||
sa.Addr = pp.Addr
|
||||
return sa, nil
|
||||
}
|
||||
|
||||
|
@ -1058,9 +1042,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
|
|||
sa := new(SockaddrL2TPIP6)
|
||||
sa.ConnId = pp.Conn_id
|
||||
sa.ZoneId = pp.Scope_id
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.Addr[i] = pp.Addr[i]
|
||||
}
|
||||
sa.Addr = pp.Addr
|
||||
return sa, nil
|
||||
default:
|
||||
pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa))
|
||||
|
@ -1068,9 +1050,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
|
|||
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
|
||||
sa.Port = int(p[0])<<8 + int(p[1])
|
||||
sa.ZoneId = pp.Scope_id
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.Addr[i] = pp.Addr[i]
|
||||
}
|
||||
sa.Addr = pp.Addr
|
||||
return sa, nil
|
||||
}
|
||||
|
||||
|
@ -1797,6 +1777,16 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
|
|||
return mount(source, target, fstype, flags, datap)
|
||||
}
|
||||
|
||||
//sys mountSetattr(dirfd int, pathname string, flags uint, attr *MountAttr, size uintptr) (err error) = SYS_MOUNT_SETATTR
|
||||
|
||||
// MountSetattr is a wrapper for mount_setattr(2).
|
||||
// https://man7.org/linux/man-pages/man2/mount_setattr.2.html
|
||||
//
|
||||
// Requires kernel >= 5.12.
|
||||
func MountSetattr(dirfd int, pathname string, flags uint, attr *MountAttr) error {
|
||||
return mountSetattr(dirfd, pathname, flags, attr, unsafe.Sizeof(*attr))
|
||||
}
|
||||
|
||||
func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
|
||||
if raceenabled {
|
||||
raceReleaseMerge(unsafe.Pointer(&ioSync))
|
||||
|
|
|
@ -110,14 +110,8 @@ func direntNamlen(buf []byte) (uint64, bool) {
|
|||
return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
|
||||
}
|
||||
|
||||
//sysnb pipe() (fd1 int, fd2 int, err error)
|
||||
|
||||
func Pipe(p []int) (err error) {
|
||||
if len(p) != 2 {
|
||||
return EINVAL
|
||||
}
|
||||
p[0], p[1], err = pipe()
|
||||
return
|
||||
return Pipe2(p, 0)
|
||||
}
|
||||
|
||||
//sysnb pipe2(p *[2]_C_int, flags int) (err error)
|
||||
|
@ -128,8 +122,10 @@ func Pipe2(p []int, flags int) error {
|
|||
}
|
||||
var pp [2]_C_int
|
||||
err := pipe2(&pp, flags)
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
if err == nil {
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -87,8 +87,10 @@ func Pipe2(p []int, flags int) error {
|
|||
}
|
||||
var pp [2]_C_int
|
||||
err := pipe2(&pp, flags)
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
if err == nil {
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -66,8 +66,10 @@ func Pipe(p []int) (err error) {
|
|||
if n != 0 {
|
||||
return err
|
||||
}
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
if err == nil {
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -79,8 +81,10 @@ func Pipe2(p []int, flags int) error {
|
|||
}
|
||||
var pp [2]_C_int
|
||||
err := pipe2(&pp, flags)
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
if err == nil {
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -92,9 +96,7 @@ func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, _Socklen, error) {
|
|||
p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port))
|
||||
p[0] = byte(sa.Port >> 8)
|
||||
p[1] = byte(sa.Port)
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.raw.Addr[i] = sa.Addr[i]
|
||||
}
|
||||
sa.raw.Addr = sa.Addr
|
||||
return unsafe.Pointer(&sa.raw), SizeofSockaddrInet4, nil
|
||||
}
|
||||
|
||||
|
@ -107,9 +109,7 @@ func (sa *SockaddrInet6) sockaddr() (unsafe.Pointer, _Socklen, error) {
|
|||
p[0] = byte(sa.Port >> 8)
|
||||
p[1] = byte(sa.Port)
|
||||
sa.raw.Scope_id = sa.ZoneId
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.raw.Addr[i] = sa.Addr[i]
|
||||
}
|
||||
sa.raw.Addr = sa.Addr
|
||||
return unsafe.Pointer(&sa.raw), SizeofSockaddrInet6, nil
|
||||
}
|
||||
|
||||
|
@ -417,9 +417,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
|
|||
sa := new(SockaddrInet4)
|
||||
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
|
||||
sa.Port = int(p[0])<<8 + int(p[1])
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.Addr[i] = pp.Addr[i]
|
||||
}
|
||||
sa.Addr = pp.Addr
|
||||
return sa, nil
|
||||
|
||||
case AF_INET6:
|
||||
|
@ -428,9 +426,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
|
|||
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
|
||||
sa.Port = int(p[0])<<8 + int(p[1])
|
||||
sa.ZoneId = pp.Scope_id
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.Addr[i] = pp.Addr[i]
|
||||
}
|
||||
sa.Addr = pp.Addr
|
||||
return sa, nil
|
||||
}
|
||||
return nil, EAFNOSUPPORT
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue