Bump golang.org/x/net from 0.0.0-20180921000356-2f5d2388922f to 0.7.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.0.0-20180921000356-2f5d2388922f to 0.7.0. - [Release notes](https://github.com/golang/net/releases) - [Commits](https://github.com/golang/net/commits/v0.7.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
parent
f3ab5471dd
commit
882d662e11
4
go.mod
4
go.mod
|
@ -6,6 +6,6 @@ require (
|
||||||
git.rootprojects.org/root/go-gitver/v2 v2.0.2
|
git.rootprojects.org/root/go-gitver/v2 v2.0.2
|
||||||
github.com/UnnoTed/fileb0x v1.1.3
|
github.com/UnnoTed/fileb0x v1.1.3
|
||||||
github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936
|
github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936
|
||||||
golang.org/x/net v0.0.0-20180921000356-2f5d2388922f
|
golang.org/x/net v0.7.0
|
||||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb
|
golang.org/x/sys v0.5.0
|
||||||
)
|
)
|
||||||
|
|
35
go.sum
35
go.sum
|
@ -42,13 +42,40 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8=
|
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8=
|
||||||
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
|
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
|
||||||
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b h1:2b9XGzhjiYsYPnKXoEfL7klWZQIt8IfyRCz62gCqqlQ=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/net v0.0.0-20180921000356-2f5d2388922f h1:QM2QVxvDoW9PFSPp/zy9FgxJLfaWTZlS61KEPtBwacM=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/net v0.0.0-20180921000356-2f5d2388922f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180921000356-2f5d2388922f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||||
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20181019160139-8e24a49d80f8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181019160139-8e24a49d80f8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||||
|
|
|
@ -4,7 +4,10 @@
|
||||||
|
|
||||||
// Package acme provides an implementation of the
|
// Package acme provides an implementation of the
|
||||||
// Automatic Certificate Management Environment (ACME) spec.
|
// Automatic Certificate Management Environment (ACME) spec.
|
||||||
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02 for details.
|
// The initial implementation was based on ACME draft-02 and
|
||||||
|
// is now being extended to comply with RFC 8555.
|
||||||
|
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02
|
||||||
|
// and https://tools.ietf.org/html/rfc8555 for details.
|
||||||
//
|
//
|
||||||
// Most common scenarios will want to use autocert subdirectory instead,
|
// Most common scenarios will want to use autocert subdirectory instead,
|
||||||
// which provides automatic access to certificates from Let's Encrypt
|
// which provides automatic access to certificates from Let's Encrypt
|
||||||
|
@ -41,7 +44,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
|
// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
|
||||||
LetsEncryptURL = "https://acme-v01.api.letsencrypt.org/directory"
|
LetsEncryptURL = "https://acme-v02.api.letsencrypt.org/directory"
|
||||||
|
|
||||||
// ALPNProto is the ALPN protocol name used by a CA server when validating
|
// ALPNProto is the ALPN protocol name used by a CA server when validating
|
||||||
// tls-alpn-01 challenges.
|
// tls-alpn-01 challenges.
|
||||||
|
@ -52,12 +55,16 @@ const (
|
||||||
ALPNProto = "acme-tls/1"
|
ALPNProto = "acme-tls/1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// idPeACMEIdentifierV1 is the OID for the ACME extension for the TLS-ALPN challenge.
|
// idPeACMEIdentifier is the OID for the ACME extension for the TLS-ALPN challenge.
|
||||||
var idPeACMEIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 30, 1}
|
// https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05#section-5.1
|
||||||
|
var idPeACMEIdentifier = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxChainLen = 5 // max depth and breadth of a certificate chain
|
maxChainLen = 5 // max depth and breadth of a certificate chain
|
||||||
maxCertSize = 1 << 20 // max size of a certificate, in bytes
|
maxCertSize = 1 << 20 // max size of a certificate, in DER bytes
|
||||||
|
// Used for decoding certs from application/pem-certificate-chain response,
|
||||||
|
// the default when in RFC mode.
|
||||||
|
maxCertChainSize = maxCertSize * maxChainLen
|
||||||
|
|
||||||
// Max number of collected nonces kept in memory.
|
// Max number of collected nonces kept in memory.
|
||||||
// Expect usual peak of 1 or 2.
|
// Expect usual peak of 1 or 2.
|
||||||
|
@ -77,6 +84,10 @@ const (
|
||||||
type Client struct {
|
type Client struct {
|
||||||
// Key is the account key used to register with a CA and sign requests.
|
// Key is the account key used to register with a CA and sign requests.
|
||||||
// Key.Public() must return a *rsa.PublicKey or *ecdsa.PublicKey.
|
// Key.Public() must return a *rsa.PublicKey or *ecdsa.PublicKey.
|
||||||
|
//
|
||||||
|
// The following algorithms are supported:
|
||||||
|
// RS256, ES256, ES384 and ES512.
|
||||||
|
// See RFC7518 for more details about the algorithms.
|
||||||
Key crypto.Signer
|
Key crypto.Signer
|
||||||
|
|
||||||
// HTTPClient optionally specifies an HTTP client to use
|
// HTTPClient optionally specifies an HTTP client to use
|
||||||
|
@ -105,30 +116,60 @@ type Client struct {
|
||||||
// The jitter is a random value up to 1 second.
|
// The jitter is a random value up to 1 second.
|
||||||
RetryBackoff func(n int, r *http.Request, resp *http.Response) time.Duration
|
RetryBackoff func(n int, r *http.Request, resp *http.Response) time.Duration
|
||||||
|
|
||||||
dirMu sync.Mutex // guards writes to dir
|
// UserAgent is prepended to the User-Agent header sent to the ACME server,
|
||||||
dir *Directory // cached result of Client's Discover method
|
// which by default is this package's name and version.
|
||||||
|
//
|
||||||
|
// Reusable libraries and tools in particular should set this value to be
|
||||||
|
// identifiable by the server, in case they are causing issues.
|
||||||
|
UserAgent string
|
||||||
|
|
||||||
|
cacheMu sync.Mutex
|
||||||
|
dir *Directory // cached result of Client's Discover method
|
||||||
|
kid keyID // cached Account.URI obtained from registerRFC or getAccountRFC
|
||||||
|
|
||||||
noncesMu sync.Mutex
|
noncesMu sync.Mutex
|
||||||
nonces map[string]struct{} // nonces collected from previous responses
|
nonces map[string]struct{} // nonces collected from previous responses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// accountKID returns a key ID associated with c.Key, the account identity
|
||||||
|
// provided by the CA during RFC based registration.
|
||||||
|
// It assumes c.Discover has already been called.
|
||||||
|
//
|
||||||
|
// accountKID requires at most one network roundtrip.
|
||||||
|
// It caches only successful result.
|
||||||
|
//
|
||||||
|
// When in pre-RFC mode or when c.getRegRFC responds with an error, accountKID
|
||||||
|
// returns noKeyID.
|
||||||
|
func (c *Client) accountKID(ctx context.Context) keyID {
|
||||||
|
c.cacheMu.Lock()
|
||||||
|
defer c.cacheMu.Unlock()
|
||||||
|
if !c.dir.rfcCompliant() {
|
||||||
|
return noKeyID
|
||||||
|
}
|
||||||
|
if c.kid != noKeyID {
|
||||||
|
return c.kid
|
||||||
|
}
|
||||||
|
a, err := c.getRegRFC(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return noKeyID
|
||||||
|
}
|
||||||
|
c.kid = keyID(a.URI)
|
||||||
|
return c.kid
|
||||||
|
}
|
||||||
|
|
||||||
// Discover performs ACME server discovery using c.DirectoryURL.
|
// Discover performs ACME server discovery using c.DirectoryURL.
|
||||||
//
|
//
|
||||||
// It caches successful result. So, subsequent calls will not result in
|
// It caches successful result. So, subsequent calls will not result in
|
||||||
// a network round-trip. This also means mutating c.DirectoryURL after successful call
|
// a network round-trip. This also means mutating c.DirectoryURL after successful call
|
||||||
// of this method will have no effect.
|
// of this method will have no effect.
|
||||||
func (c *Client) Discover(ctx context.Context) (Directory, error) {
|
func (c *Client) Discover(ctx context.Context) (Directory, error) {
|
||||||
c.dirMu.Lock()
|
c.cacheMu.Lock()
|
||||||
defer c.dirMu.Unlock()
|
defer c.cacheMu.Unlock()
|
||||||
if c.dir != nil {
|
if c.dir != nil {
|
||||||
return *c.dir, nil
|
return *c.dir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
dirURL := c.DirectoryURL
|
res, err := c.get(ctx, c.directoryURL(), wantStatus(http.StatusOK))
|
||||||
if dirURL == "" {
|
|
||||||
dirURL = LetsEncryptURL
|
|
||||||
}
|
|
||||||
res, err := c.get(ctx, dirURL, wantStatus(http.StatusOK))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Directory{}, err
|
return Directory{}, err
|
||||||
}
|
}
|
||||||
|
@ -136,32 +177,68 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) {
|
||||||
c.addNonce(res.Header)
|
c.addNonce(res.Header)
|
||||||
|
|
||||||
var v struct {
|
var v struct {
|
||||||
Reg string `json:"new-reg"`
|
Reg string `json:"new-reg"`
|
||||||
Authz string `json:"new-authz"`
|
RegRFC string `json:"newAccount"`
|
||||||
Cert string `json:"new-cert"`
|
Authz string `json:"new-authz"`
|
||||||
Revoke string `json:"revoke-cert"`
|
AuthzRFC string `json:"newAuthz"`
|
||||||
Meta struct {
|
OrderRFC string `json:"newOrder"`
|
||||||
Terms string `json:"terms-of-service"`
|
Cert string `json:"new-cert"`
|
||||||
Website string `json:"website"`
|
Revoke string `json:"revoke-cert"`
|
||||||
CAA []string `json:"caa-identities"`
|
RevokeRFC string `json:"revokeCert"`
|
||||||
|
NonceRFC string `json:"newNonce"`
|
||||||
|
KeyChangeRFC string `json:"keyChange"`
|
||||||
|
Meta struct {
|
||||||
|
Terms string `json:"terms-of-service"`
|
||||||
|
TermsRFC string `json:"termsOfService"`
|
||||||
|
WebsiteRFC string `json:"website"`
|
||||||
|
CAA []string `json:"caa-identities"`
|
||||||
|
CAARFC []string `json:"caaIdentities"`
|
||||||
|
ExternalAcctRFC bool `json:"externalAccountRequired"`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
return Directory{}, err
|
return Directory{}, err
|
||||||
}
|
}
|
||||||
|
if v.OrderRFC == "" {
|
||||||
|
// Non-RFC compliant ACME CA.
|
||||||
|
c.dir = &Directory{
|
||||||
|
RegURL: v.Reg,
|
||||||
|
AuthzURL: v.Authz,
|
||||||
|
CertURL: v.Cert,
|
||||||
|
RevokeURL: v.Revoke,
|
||||||
|
Terms: v.Meta.Terms,
|
||||||
|
Website: v.Meta.WebsiteRFC,
|
||||||
|
CAA: v.Meta.CAA,
|
||||||
|
}
|
||||||
|
return *c.dir, nil
|
||||||
|
}
|
||||||
|
// RFC compliant ACME CA.
|
||||||
c.dir = &Directory{
|
c.dir = &Directory{
|
||||||
RegURL: v.Reg,
|
RegURL: v.RegRFC,
|
||||||
AuthzURL: v.Authz,
|
AuthzURL: v.AuthzRFC,
|
||||||
CertURL: v.Cert,
|
OrderURL: v.OrderRFC,
|
||||||
RevokeURL: v.Revoke,
|
RevokeURL: v.RevokeRFC,
|
||||||
Terms: v.Meta.Terms,
|
NonceURL: v.NonceRFC,
|
||||||
Website: v.Meta.Website,
|
KeyChangeURL: v.KeyChangeRFC,
|
||||||
CAA: v.Meta.CAA,
|
Terms: v.Meta.TermsRFC,
|
||||||
|
Website: v.Meta.WebsiteRFC,
|
||||||
|
CAA: v.Meta.CAARFC,
|
||||||
|
ExternalAccountRequired: v.Meta.ExternalAcctRFC,
|
||||||
}
|
}
|
||||||
return *c.dir, nil
|
return *c.dir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) directoryURL() string {
|
||||||
|
if c.DirectoryURL != "" {
|
||||||
|
return c.DirectoryURL
|
||||||
|
}
|
||||||
|
return LetsEncryptURL
|
||||||
|
}
|
||||||
|
|
||||||
// CreateCert requests a new certificate using the Certificate Signing Request csr encoded in DER format.
|
// CreateCert requests a new certificate using the Certificate Signing Request csr encoded in DER format.
|
||||||
|
// It is incompatible with RFC 8555. Callers should use CreateOrderCert when interfacing
|
||||||
|
// with an RFC-compliant CA.
|
||||||
|
//
|
||||||
// The exp argument indicates the desired certificate validity duration. CA may issue a certificate
|
// The exp argument indicates the desired certificate validity duration. CA may issue a certificate
|
||||||
// with a different duration.
|
// with a different duration.
|
||||||
// If the bundle argument is true, the returned value will also contain the CA (issuer) certificate chain.
|
// If the bundle argument is true, the returned value will also contain the CA (issuer) certificate chain.
|
||||||
|
@ -192,7 +269,7 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
|
||||||
req.NotAfter = now.Add(exp).Format(time.RFC3339)
|
req.NotAfter = now.Add(exp).Format(time.RFC3339)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := c.post(ctx, c.Key, c.dir.CertURL, req, wantStatus(http.StatusCreated))
|
res, err := c.post(ctx, nil, c.dir.CertURL, req, wantStatus(http.StatusCreated))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
@ -213,12 +290,22 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
|
||||||
// It retries the request until the certificate is successfully retrieved,
|
// It retries the request until the certificate is successfully retrieved,
|
||||||
// context is cancelled by the caller or an error response is received.
|
// context is cancelled by the caller or an error response is received.
|
||||||
//
|
//
|
||||||
// The returned value will also contain the CA (issuer) certificate if the bundle argument is true.
|
// If the bundle argument is true, the returned value also contains the CA (issuer)
|
||||||
|
// certificate chain.
|
||||||
//
|
//
|
||||||
// FetchCert returns an error if the CA's response or chain was unreasonably large.
|
// FetchCert returns an error if the CA's response or chain was unreasonably large.
|
||||||
// Callers are encouraged to parse the returned value to ensure the certificate is valid
|
// Callers are encouraged to parse the returned value to ensure the certificate is valid
|
||||||
// and has expected features.
|
// and has expected features.
|
||||||
func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
|
func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
|
||||||
|
dir, err := c.Discover(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if dir.rfcCompliant() {
|
||||||
|
return c.fetchCertRFC(ctx, url, bundle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy non-authenticated GET request.
|
||||||
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -233,10 +320,15 @@ func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]by
|
||||||
// For instance, the key pair of the certificate may be authorized.
|
// For instance, the key pair of the certificate may be authorized.
|
||||||
// If the key is nil, c.Key is used instead.
|
// If the key is nil, c.Key is used instead.
|
||||||
func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
|
func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
|
||||||
if _, err := c.Discover(ctx); err != nil {
|
dir, err := c.Discover(ctx)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if dir.rfcCompliant() {
|
||||||
|
return c.revokeCertRFC(ctx, key, cert, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy CA.
|
||||||
body := &struct {
|
body := &struct {
|
||||||
Resource string `json:"resource"`
|
Resource string `json:"resource"`
|
||||||
Cert string `json:"certificate"`
|
Cert string `json:"certificate"`
|
||||||
|
@ -246,10 +338,7 @@ func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte,
|
||||||
Cert: base64.RawURLEncoding.EncodeToString(cert),
|
Cert: base64.RawURLEncoding.EncodeToString(cert),
|
||||||
Reason: int(reason),
|
Reason: int(reason),
|
||||||
}
|
}
|
||||||
if key == nil {
|
res, err := c.post(ctx, key, dir.RevokeURL, body, wantStatus(http.StatusOK))
|
||||||
key = c.Key
|
|
||||||
}
|
|
||||||
res, err := c.post(ctx, key, c.dir.RevokeURL, body, wantStatus(http.StatusOK))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -261,20 +350,34 @@ func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte,
|
||||||
// during account registration. See Register method of Client for more details.
|
// during account registration. See Register method of Client for more details.
|
||||||
func AcceptTOS(tosURL string) bool { return true }
|
func AcceptTOS(tosURL string) bool { return true }
|
||||||
|
|
||||||
// Register creates a new account registration by following the "new-reg" flow.
|
// Register creates a new account with the CA using c.Key.
|
||||||
// It returns the registered account. The account is not modified.
|
// It returns the registered account. The account acct is not modified.
|
||||||
//
|
//
|
||||||
// The registration may require the caller to agree to the CA's Terms of Service (TOS).
|
// The registration may require the caller to agree to the CA's Terms of Service (TOS).
|
||||||
// If so, and the account has not indicated the acceptance of the terms (see Account for details),
|
// If so, and the account has not indicated the acceptance of the terms (see Account for details),
|
||||||
// Register calls prompt with a TOS URL provided by the CA. Prompt should report
|
// Register calls prompt with a TOS URL provided by the CA. Prompt should report
|
||||||
// whether the caller agrees to the terms. To always accept the terms, the caller can use AcceptTOS.
|
// whether the caller agrees to the terms. To always accept the terms, the caller can use AcceptTOS.
|
||||||
func (c *Client) Register(ctx context.Context, a *Account, prompt func(tosURL string) bool) (*Account, error) {
|
//
|
||||||
if _, err := c.Discover(ctx); err != nil {
|
// When interfacing with an RFC-compliant CA, non-RFC 8555 fields of acct are ignored
|
||||||
return nil, err
|
// and prompt is called if Directory's Terms field is non-zero.
|
||||||
|
// Also see Error's Instance field for when a CA requires already registered accounts to agree
|
||||||
|
// to an updated Terms of Service.
|
||||||
|
func (c *Client) Register(ctx context.Context, acct *Account, prompt func(tosURL string) bool) (*Account, error) {
|
||||||
|
if c.Key == nil {
|
||||||
|
return nil, errors.New("acme: client.Key must be set to Register")
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
dir, err := c.Discover(ctx)
|
||||||
if a, err = c.doReg(ctx, c.dir.RegURL, "new-reg", a); err != nil {
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if dir.rfcCompliant() {
|
||||||
|
return c.registerRFC(ctx, acct, prompt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy ACME draft registration flow.
|
||||||
|
a, err := c.doReg(ctx, dir.RegURL, "new-reg", acct)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var accept bool
|
var accept bool
|
||||||
|
@ -288,9 +391,20 @@ func (c *Client) Register(ctx context.Context, a *Account, prompt func(tosURL st
|
||||||
return a, err
|
return a, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetReg retrieves an existing registration.
|
// GetReg retrieves an existing account associated with c.Key.
|
||||||
// The url argument is an Account URI.
|
//
|
||||||
|
// The url argument is an Account URI used with pre-RFC 8555 CAs.
|
||||||
|
// It is ignored when interfacing with an RFC-compliant CA.
|
||||||
func (c *Client) GetReg(ctx context.Context, url string) (*Account, error) {
|
func (c *Client) GetReg(ctx context.Context, url string) (*Account, error) {
|
||||||
|
dir, err := c.Discover(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if dir.rfcCompliant() {
|
||||||
|
return c.getRegRFC(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy CA.
|
||||||
a, err := c.doReg(ctx, url, "reg", nil)
|
a, err := c.doReg(ctx, url, "reg", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -301,9 +415,21 @@ func (c *Client) GetReg(ctx context.Context, url string) (*Account, error) {
|
||||||
|
|
||||||
// UpdateReg updates an existing registration.
|
// UpdateReg updates an existing registration.
|
||||||
// It returns an updated account copy. The provided account is not modified.
|
// It returns an updated account copy. The provided account is not modified.
|
||||||
func (c *Client) UpdateReg(ctx context.Context, a *Account) (*Account, error) {
|
//
|
||||||
uri := a.URI
|
// When interfacing with RFC-compliant CAs, a.URI is ignored and the account URL
|
||||||
a, err := c.doReg(ctx, uri, "reg", a)
|
// associated with c.Key is used instead.
|
||||||
|
func (c *Client) UpdateReg(ctx context.Context, acct *Account) (*Account, error) {
|
||||||
|
dir, err := c.Discover(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if dir.rfcCompliant() {
|
||||||
|
return c.updateRegRFC(ctx, acct)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy CA.
|
||||||
|
uri := acct.URI
|
||||||
|
a, err := c.doReg(ctx, uri, "reg", acct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -311,14 +437,36 @@ func (c *Client) UpdateReg(ctx context.Context, a *Account) (*Account, error) {
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authorize performs the initial step in an authorization flow.
|
// Authorize performs the initial step in the pre-authorization flow,
|
||||||
|
// as opposed to order-based flow.
|
||||||
// The caller will then need to choose from and perform a set of returned
|
// The caller will then need to choose from and perform a set of returned
|
||||||
// challenges using c.Accept in order to successfully complete authorization.
|
// challenges using c.Accept in order to successfully complete authorization.
|
||||||
//
|
//
|
||||||
|
// Once complete, the caller can use AuthorizeOrder which the CA
|
||||||
|
// should provision with the already satisfied authorization.
|
||||||
|
// For pre-RFC CAs, the caller can proceed directly to requesting a certificate
|
||||||
|
// using CreateCert method.
|
||||||
|
//
|
||||||
// If an authorization has been previously granted, the CA may return
|
// If an authorization has been previously granted, the CA may return
|
||||||
// a valid authorization (Authorization.Status is StatusValid). If so, the caller
|
// a valid authorization which has its Status field set to StatusValid.
|
||||||
// need not fulfill any challenge and can proceed to requesting a certificate.
|
//
|
||||||
|
// More about pre-authorization can be found at
|
||||||
|
// https://tools.ietf.org/html/rfc8555#section-7.4.1.
|
||||||
func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, error) {
|
func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, error) {
|
||||||
|
return c.authorize(ctx, "dns", domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthorizeIP is the same as Authorize but requests IP address authorization.
|
||||||
|
// Clients which successfully obtain such authorization may request to issue
|
||||||
|
// a certificate for IP addresses.
|
||||||
|
//
|
||||||
|
// See the ACME spec extension for more details about IP address identifiers:
|
||||||
|
// https://tools.ietf.org/html/draft-ietf-acme-ip.
|
||||||
|
func (c *Client) AuthorizeIP(ctx context.Context, ipaddr string) (*Authorization, error) {
|
||||||
|
return c.authorize(ctx, "ip", ipaddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) authorize(ctx context.Context, typ, val string) (*Authorization, error) {
|
||||||
if _, err := c.Discover(ctx); err != nil {
|
if _, err := c.Discover(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -332,9 +480,9 @@ func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization,
|
||||||
Identifier authzID `json:"identifier"`
|
Identifier authzID `json:"identifier"`
|
||||||
}{
|
}{
|
||||||
Resource: "new-authz",
|
Resource: "new-authz",
|
||||||
Identifier: authzID{Type: "dns", Value: domain},
|
Identifier: authzID{Type: typ, Value: val},
|
||||||
}
|
}
|
||||||
res, err := c.post(ctx, c.Key, c.dir.AuthzURL, req, wantStatus(http.StatusCreated))
|
res, err := c.post(ctx, nil, c.dir.AuthzURL, req, wantStatus(http.StatusCreated))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -355,7 +503,17 @@ func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization,
|
||||||
// If a caller needs to poll an authorization until its status is final,
|
// If a caller needs to poll an authorization until its status is final,
|
||||||
// see the WaitAuthorization method.
|
// see the WaitAuthorization method.
|
||||||
func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
||||||
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
dir, err := c.Discover(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var res *http.Response
|
||||||
|
if dir.rfcCompliant() {
|
||||||
|
res, err = c.postAsGet(ctx, url, wantStatus(http.StatusOK))
|
||||||
|
} else {
|
||||||
|
res, err = c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -372,11 +530,16 @@ func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorizati
|
||||||
// The url argument is an Authorization.URI value.
|
// The url argument is an Authorization.URI value.
|
||||||
//
|
//
|
||||||
// If successful, the caller will be required to obtain a new authorization
|
// If successful, the caller will be required to obtain a new authorization
|
||||||
// using the Authorize method before being able to request a new certificate
|
// using the Authorize or AuthorizeOrder methods before being able to request
|
||||||
// for the domain associated with the authorization.
|
// a new certificate for the domain associated with the authorization.
|
||||||
//
|
//
|
||||||
// It does not revoke existing certificates.
|
// It does not revoke existing certificates.
|
||||||
func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
|
func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
|
||||||
|
// Required for c.accountKID() when in RFC mode.
|
||||||
|
if _, err := c.Discover(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
req := struct {
|
req := struct {
|
||||||
Resource string `json:"resource"`
|
Resource string `json:"resource"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
|
@ -386,7 +549,7 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
|
||||||
Status: "deactivated",
|
Status: "deactivated",
|
||||||
Delete: true,
|
Delete: true,
|
||||||
}
|
}
|
||||||
res, err := c.post(ctx, c.Key, url, req, wantStatus(http.StatusOK))
|
res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -402,8 +565,18 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
|
||||||
// In all other cases WaitAuthorization returns an error.
|
// In all other cases WaitAuthorization returns an error.
|
||||||
// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
|
// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
|
||||||
func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
||||||
|
// Required for c.accountKID() when in RFC mode.
|
||||||
|
dir, err := c.Discover(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
getfn := c.postAsGet
|
||||||
|
if !dir.rfcCompliant() {
|
||||||
|
getfn = c.get
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
res, err := getfn(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -446,10 +619,21 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat
|
||||||
//
|
//
|
||||||
// A client typically polls a challenge status using this method.
|
// A client typically polls a challenge status using this method.
|
||||||
func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
|
func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
|
||||||
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
// Required for c.accountKID() when in RFC mode.
|
||||||
|
dir, err := c.Discover(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getfn := c.postAsGet
|
||||||
|
if !dir.rfcCompliant() {
|
||||||
|
getfn = c.get
|
||||||
|
}
|
||||||
|
res, err := getfn(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
v := wireChallenge{URI: url}
|
v := wireChallenge{URI: url}
|
||||||
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
|
@ -463,21 +647,29 @@ func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, erro
|
||||||
//
|
//
|
||||||
// The server will then perform the validation asynchronously.
|
// The server will then perform the validation asynchronously.
|
||||||
func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error) {
|
func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error) {
|
||||||
auth, err := keyAuth(c.Key.Public(), chal.Token)
|
// Required for c.accountKID() when in RFC mode.
|
||||||
|
dir, err := c.Discover(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req := struct {
|
var req interface{} = json.RawMessage("{}") // RFC-compliant CA
|
||||||
Resource string `json:"resource"`
|
if !dir.rfcCompliant() {
|
||||||
Type string `json:"type"`
|
auth, err := keyAuth(c.Key.Public(), chal.Token)
|
||||||
Auth string `json:"keyAuthorization"`
|
if err != nil {
|
||||||
}{
|
return nil, err
|
||||||
Resource: "challenge",
|
}
|
||||||
Type: chal.Type,
|
req = struct {
|
||||||
Auth: auth,
|
Resource string `json:"resource"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Auth string `json:"keyAuthorization"`
|
||||||
|
}{
|
||||||
|
Resource: "challenge",
|
||||||
|
Type: chal.Type,
|
||||||
|
Auth: auth,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
res, err := c.post(ctx, c.Key, chal.URI, req, wantStatus(
|
res, err := c.post(ctx, nil, chal.URI, req, wantStatus(
|
||||||
http.StatusOK, // according to the spec
|
http.StatusOK, // according to the spec
|
||||||
http.StatusAccepted, // Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md)
|
http.StatusAccepted, // Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md)
|
||||||
))
|
))
|
||||||
|
@ -527,21 +719,8 @@ func (c *Client) HTTP01ChallengePath(token string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response.
|
// TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response.
|
||||||
// Servers can present the certificate to validate the challenge and prove control
|
|
||||||
// over a domain name.
|
|
||||||
//
|
//
|
||||||
// The implementation is incomplete in that the returned value is a single certificate,
|
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of ACME spec.
|
||||||
// computed only for Z0 of the key authorization. ACME CAs are expected to update
|
|
||||||
// their implementations to use the newer version, TLS-SNI-02.
|
|
||||||
// For more details on TLS-SNI-01 see https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-7.3.
|
|
||||||
//
|
|
||||||
// The token argument is a Challenge.Token value.
|
|
||||||
// If a WithKey option is provided, its private part signs the returned cert,
|
|
||||||
// and the public part is used to specify the signee.
|
|
||||||
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
|
||||||
//
|
|
||||||
// The returned certificate is valid for the next 24 hours and must be presented only when
|
|
||||||
// the server name of the TLS ClientHello matches exactly the returned name value.
|
|
||||||
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
||||||
ka, err := keyAuth(c.Key.Public(), token)
|
ka, err := keyAuth(c.Key.Public(), token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -558,17 +737,8 @@ func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tl
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response.
|
// TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response.
|
||||||
// Servers can present the certificate to validate the challenge and prove control
|
|
||||||
// over a domain name. For more details on TLS-SNI-02 see
|
|
||||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-7.3.
|
|
||||||
//
|
//
|
||||||
// The token argument is a Challenge.Token value.
|
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of ACME spec.
|
||||||
// If a WithKey option is provided, its private part signs the returned cert,
|
|
||||||
// and the public part is used to specify the signee.
|
|
||||||
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
|
||||||
//
|
|
||||||
// The returned certificate is valid for the next 24 hours and must be presented only when
|
|
||||||
// the server name in the TLS ClientHello matches exactly the returned name value.
|
|
||||||
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
||||||
b := sha256.Sum256([]byte(token))
|
b := sha256.Sum256([]byte(token))
|
||||||
h := hex.EncodeToString(b[:])
|
h := hex.EncodeToString(b[:])
|
||||||
|
@ -613,7 +783,7 @@ func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption)
|
||||||
return tls.Certificate{}, err
|
return tls.Certificate{}, err
|
||||||
}
|
}
|
||||||
acmeExtension := pkix.Extension{
|
acmeExtension := pkix.Extension{
|
||||||
Id: idPeACMEIdentifierV1,
|
Id: idPeACMEIdentifier,
|
||||||
Critical: true,
|
Critical: true,
|
||||||
Value: extValue,
|
Value: extValue,
|
||||||
}
|
}
|
||||||
|
@ -635,7 +805,7 @@ func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption)
|
||||||
return tlsChallengeCert([]string{domain}, newOpt)
|
return tlsChallengeCert([]string{domain}, newOpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// doReg sends all types of registration requests.
|
// doReg sends all types of registration requests the old way (pre-RFC world).
|
||||||
// The type of request is identified by typ argument, which is a "resource"
|
// The type of request is identified by typ argument, which is a "resource"
|
||||||
// in the ACME spec terms.
|
// in the ACME spec terms.
|
||||||
//
|
//
|
||||||
|
@ -654,7 +824,7 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun
|
||||||
req.Contact = acct.Contact
|
req.Contact = acct.Contact
|
||||||
req.Agreement = acct.AgreedTerms
|
req.Agreement = acct.AgreedTerms
|
||||||
}
|
}
|
||||||
res, err := c.post(ctx, c.Key, url, req, wantStatus(
|
res, err := c.post(ctx, nil, url, req, wantStatus(
|
||||||
http.StatusOK, // updates and deletes
|
http.StatusOK, // updates and deletes
|
||||||
http.StatusCreated, // new account creation
|
http.StatusCreated, // new account creation
|
||||||
http.StatusAccepted, // Let's Encrypt divergent implementation
|
http.StatusAccepted, // Let's Encrypt divergent implementation
|
||||||
|
@ -693,12 +863,22 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun
|
||||||
}
|
}
|
||||||
|
|
||||||
// popNonce returns a nonce value previously stored with c.addNonce
|
// popNonce returns a nonce value previously stored with c.addNonce
|
||||||
// or fetches a fresh one from the given URL.
|
// or fetches a fresh one from c.dir.NonceURL.
|
||||||
|
// If NonceURL is empty, it first tries c.directoryURL() and, failing that,
|
||||||
|
// the provided url.
|
||||||
func (c *Client) popNonce(ctx context.Context, url string) (string, error) {
|
func (c *Client) popNonce(ctx context.Context, url string) (string, error) {
|
||||||
c.noncesMu.Lock()
|
c.noncesMu.Lock()
|
||||||
defer c.noncesMu.Unlock()
|
defer c.noncesMu.Unlock()
|
||||||
if len(c.nonces) == 0 {
|
if len(c.nonces) == 0 {
|
||||||
return c.fetchNonce(ctx, url)
|
if c.dir != nil && c.dir.NonceURL != "" {
|
||||||
|
return c.fetchNonce(ctx, c.dir.NonceURL)
|
||||||
|
}
|
||||||
|
dirURL := c.directoryURL()
|
||||||
|
v, err := c.fetchNonce(ctx, dirURL)
|
||||||
|
if err != nil && url != dirURL {
|
||||||
|
v, err = c.fetchNonce(ctx, url)
|
||||||
|
}
|
||||||
|
return v, err
|
||||||
}
|
}
|
||||||
var nonce string
|
var nonce string
|
||||||
for nonce = range c.nonces {
|
for nonce = range c.nonces {
|
||||||
|
|
|
@ -32,8 +32,12 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/acme"
|
"golang.org/x/crypto/acme"
|
||||||
|
"golang.org/x/net/idna"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultACMEDirectory is the default ACME Directory URL used when the Manager's Client is nil.
|
||||||
|
const DefaultACMEDirectory = "https://acme-v02.api.letsencrypt.org/directory"
|
||||||
|
|
||||||
// createCertRetryAfter is how much time to wait before removing a failed state
|
// createCertRetryAfter is how much time to wait before removing a failed state
|
||||||
// entry due to an unsuccessful createCert call.
|
// entry due to an unsuccessful createCert call.
|
||||||
// This is a variable instead of a const for testing.
|
// This is a variable instead of a const for testing.
|
||||||
|
@ -62,14 +66,20 @@ type HostPolicy func(ctx context.Context, host string) error
|
||||||
// HostWhitelist returns a policy where only the specified host names are allowed.
|
// HostWhitelist returns a policy where only the specified host names are allowed.
|
||||||
// Only exact matches are currently supported. Subdomains, regexp or wildcard
|
// Only exact matches are currently supported. Subdomains, regexp or wildcard
|
||||||
// will not match.
|
// will not match.
|
||||||
|
//
|
||||||
|
// Note that all hosts will be converted to Punycode via idna.Lookup.ToASCII so that
|
||||||
|
// Manager.GetCertificate can handle the Unicode IDN and mixedcase hosts correctly.
|
||||||
|
// Invalid hosts will be silently ignored.
|
||||||
func HostWhitelist(hosts ...string) HostPolicy {
|
func HostWhitelist(hosts ...string) HostPolicy {
|
||||||
whitelist := make(map[string]bool, len(hosts))
|
whitelist := make(map[string]bool, len(hosts))
|
||||||
for _, h := range hosts {
|
for _, h := range hosts {
|
||||||
whitelist[h] = true
|
if h, err := idna.Lookup.ToASCII(h); err == nil {
|
||||||
|
whitelist[h] = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return func(_ context.Context, host string) error {
|
return func(_ context.Context, host string) error {
|
||||||
if !whitelist[host] {
|
if !whitelist[host] {
|
||||||
return errors.New("acme/autocert: host not configured")
|
return fmt.Errorf("acme/autocert: host %q not configured in HostWhitelist", host)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -81,9 +91,9 @@ func defaultHostPolicy(context.Context, string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manager is a stateful certificate manager built on top of acme.Client.
|
// Manager is a stateful certificate manager built on top of acme.Client.
|
||||||
// It obtains and refreshes certificates automatically using "tls-alpn-01",
|
// It obtains and refreshes certificates automatically using "tls-alpn-01"
|
||||||
// "tls-sni-01", "tls-sni-02" and "http-01" challenge types,
|
// or "http-01" challenge types, as well as providing them to a TLS server
|
||||||
// as well as providing them to a TLS server via tls.Config.
|
// via tls.Config.
|
||||||
//
|
//
|
||||||
// You must specify a cache implementation, such as DirCache,
|
// You must specify a cache implementation, such as DirCache,
|
||||||
// to reuse obtained certificates across program restarts.
|
// to reuse obtained certificates across program restarts.
|
||||||
|
@ -128,9 +138,10 @@ type Manager struct {
|
||||||
// Client is used to perform low-level operations, such as account registration
|
// Client is used to perform low-level operations, such as account registration
|
||||||
// and requesting new certificates.
|
// and requesting new certificates.
|
||||||
//
|
//
|
||||||
// If Client is nil, a zero-value acme.Client is used with acme.LetsEncryptURL
|
// If Client is nil, a zero-value acme.Client is used with DefaultACMEDirectory
|
||||||
// as directory endpoint. If the Client.Key is nil, a new ECDSA P-256 key is
|
// as the directory endpoint.
|
||||||
// generated and, if Cache is not nil, stored in cache.
|
// If the Client.Key is nil, a new ECDSA P-256 key is generated and,
|
||||||
|
// if Cache is not nil, stored in cache.
|
||||||
//
|
//
|
||||||
// Mutating the field after the first call of GetCertificate method will have no effect.
|
// Mutating the field after the first call of GetCertificate method will have no effect.
|
||||||
Client *acme.Client
|
Client *acme.Client
|
||||||
|
@ -167,8 +178,8 @@ type Manager struct {
|
||||||
renewalMu sync.Mutex
|
renewalMu sync.Mutex
|
||||||
renewal map[certKey]*domainRenewal
|
renewal map[certKey]*domainRenewal
|
||||||
|
|
||||||
// tokensMu guards the rest of the fields: tryHTTP01, certTokens and httpTokens.
|
// challengeMu guards tryHTTP01, certTokens and httpTokens.
|
||||||
tokensMu sync.RWMutex
|
challengeMu sync.RWMutex
|
||||||
// tryHTTP01 indicates whether the Manager should try "http-01" challenge type
|
// tryHTTP01 indicates whether the Manager should try "http-01" challenge type
|
||||||
// during the authorization flow.
|
// during the authorization flow.
|
||||||
tryHTTP01 bool
|
tryHTTP01 bool
|
||||||
|
@ -177,12 +188,11 @@ type Manager struct {
|
||||||
// to be provisioned.
|
// to be provisioned.
|
||||||
// The entries are stored for the duration of the authorization flow.
|
// The entries are stored for the duration of the authorization flow.
|
||||||
httpTokens map[string][]byte
|
httpTokens map[string][]byte
|
||||||
// certTokens contains temporary certificates for tls-sni and tls-alpn challenges
|
// certTokens contains temporary certificates for tls-alpn-01 challenges
|
||||||
// and is keyed by token domain name, which matches server name of ClientHello.
|
// and is keyed by the domain name which matches the ClientHello server name.
|
||||||
// Keys always have ".acme.invalid" suffix for tls-sni. Otherwise, they are domain names
|
|
||||||
// for tls-alpn.
|
|
||||||
// The entries are stored for the duration of the authorization flow.
|
// The entries are stored for the duration of the authorization flow.
|
||||||
certTokens map[string]*tls.Certificate
|
certTokens map[string]*tls.Certificate
|
||||||
|
|
||||||
// nowFunc, if not nil, returns the current time. This may be set for
|
// nowFunc, if not nil, returns the current time. This may be set for
|
||||||
// testing purposes.
|
// testing purposes.
|
||||||
nowFunc func() time.Time
|
nowFunc func() time.Time
|
||||||
|
@ -219,7 +229,7 @@ func (m *Manager) TLSConfig() *tls.Config {
|
||||||
|
|
||||||
// GetCertificate implements the tls.Config.GetCertificate hook.
|
// GetCertificate implements the tls.Config.GetCertificate hook.
|
||||||
// It provides a TLS certificate for hello.ServerName host, including answering
|
// It provides a TLS certificate for hello.ServerName host, including answering
|
||||||
// tls-alpn-01 and *.acme.invalid (tls-sni-01 and tls-sni-02) challenges.
|
// tls-alpn-01 challenges.
|
||||||
// All other fields of hello are ignored.
|
// All other fields of hello are ignored.
|
||||||
//
|
//
|
||||||
// If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting
|
// If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting
|
||||||
|
@ -228,9 +238,7 @@ func (m *Manager) TLSConfig() *tls.Config {
|
||||||
// This does not affect cached certs. See HostPolicy field description for more details.
|
// This does not affect cached certs. See HostPolicy field description for more details.
|
||||||
//
|
//
|
||||||
// If GetCertificate is used directly, instead of via Manager.TLSConfig, package users will
|
// If GetCertificate is used directly, instead of via Manager.TLSConfig, package users will
|
||||||
// also have to add acme.ALPNProto to NextProtos for tls-alpn-01, or use HTTPHandler
|
// also have to add acme.ALPNProto to NextProtos for tls-alpn-01, or use HTTPHandler for http-01.
|
||||||
// for http-01. (The tls-sni-* challenges have been deprecated by popular ACME providers
|
|
||||||
// due to security issues in the ecosystem.)
|
|
||||||
func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
if m.Prompt == nil {
|
if m.Prompt == nil {
|
||||||
return nil, errors.New("acme/autocert: Manager.Prompt not set")
|
return nil, errors.New("acme/autocert: Manager.Prompt not set")
|
||||||
|
@ -243,7 +251,17 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
|
||||||
if !strings.Contains(strings.Trim(name, "."), ".") {
|
if !strings.Contains(strings.Trim(name, "."), ".") {
|
||||||
return nil, errors.New("acme/autocert: server name component count invalid")
|
return nil, errors.New("acme/autocert: server name component count invalid")
|
||||||
}
|
}
|
||||||
if strings.ContainsAny(name, `+/\`) {
|
|
||||||
|
// Note that this conversion is necessary because some server names in the handshakes
|
||||||
|
// started by some clients (such as cURL) are not converted to Punycode, which will
|
||||||
|
// prevent us from obtaining certificates for them. In addition, we should also treat
|
||||||
|
// example.com and EXAMPLE.COM as equivalent and return the same certificate for them.
|
||||||
|
// Fortunately, this conversion also helped us deal with this kind of mixedcase problems.
|
||||||
|
//
|
||||||
|
// Due to the "σςΣ" problem (see https://unicode.org/faq/idn.html#22), we can't use
|
||||||
|
// idna.Punycode.ToASCII (or just idna.ToASCII) here.
|
||||||
|
name, err := idna.Lookup.ToASCII(name)
|
||||||
|
if err != nil {
|
||||||
return nil, errors.New("acme/autocert: server name contains invalid character")
|
return nil, errors.New("acme/autocert: server name contains invalid character")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,13 +270,10 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// Check whether this is a token cert requested for TLS-SNI or TLS-ALPN challenge.
|
// Check whether this is a token cert requested for TLS-ALPN challenge.
|
||||||
if wantsTokenCert(hello) {
|
if wantsTokenCert(hello) {
|
||||||
m.tokensMu.RLock()
|
m.challengeMu.RLock()
|
||||||
defer m.tokensMu.RUnlock()
|
defer m.challengeMu.RUnlock()
|
||||||
// It's ok to use the same token cert key for both tls-sni and tls-alpn
|
|
||||||
// because there's always at most 1 token cert per on-going domain authorization.
|
|
||||||
// See m.verify for details.
|
|
||||||
if cert := m.certTokens[name]; cert != nil {
|
if cert := m.certTokens[name]; cert != nil {
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
@ -301,8 +316,7 @@ func wantsTokenCert(hello *tls.ClientHelloInfo) bool {
|
||||||
if len(hello.SupportedProtos) == 1 && hello.SupportedProtos[0] == acme.ALPNProto {
|
if len(hello.SupportedProtos) == 1 && hello.SupportedProtos[0] == acme.ALPNProto {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// tls-sni-xx
|
return false
|
||||||
return strings.HasSuffix(hello.ServerName, ".acme.invalid")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func supportsECDSA(hello *tls.ClientHelloInfo) bool {
|
func supportsECDSA(hello *tls.ClientHelloInfo) bool {
|
||||||
|
@ -367,8 +381,8 @@ func supportsECDSA(hello *tls.ClientHelloInfo) bool {
|
||||||
// If HTTPHandler is never called, the Manager will only use the "tls-alpn-01"
|
// If HTTPHandler is never called, the Manager will only use the "tls-alpn-01"
|
||||||
// challenge for domain verification.
|
// challenge for domain verification.
|
||||||
func (m *Manager) HTTPHandler(fallback http.Handler) http.Handler {
|
func (m *Manager) HTTPHandler(fallback http.Handler) http.Handler {
|
||||||
m.tokensMu.Lock()
|
m.challengeMu.Lock()
|
||||||
defer m.tokensMu.Unlock()
|
defer m.challengeMu.Unlock()
|
||||||
m.tryHTTP01 = true
|
m.tryHTTP01 = true
|
||||||
|
|
||||||
if fallback == nil {
|
if fallback == nil {
|
||||||
|
@ -631,71 +645,64 @@ func (m *Manager) certState(ck certKey) (*certState, error) {
|
||||||
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
|
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
|
||||||
// The key argument is the certificate private key.
|
// The key argument is the certificate private key.
|
||||||
func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck certKey) (der [][]byte, leaf *x509.Certificate, err error) {
|
func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck certKey) (der [][]byte, leaf *x509.Certificate, err error) {
|
||||||
client, err := m.acmeClient(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := m.verify(ctx, client, ck.domain); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
csr, err := certRequest(key, ck.domain, m.ExtraExtensions)
|
csr, err := certRequest(key, ck.domain, m.ExtraExtensions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
der, _, err = client.CreateCert(ctx, csr, 0, true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
leaf, err = validCert(ck, der, key, m.now())
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return der, leaf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// revokePendingAuthz revokes all authorizations idenfied by the elements of uri slice.
|
|
||||||
// It ignores revocation errors.
|
|
||||||
func (m *Manager) revokePendingAuthz(ctx context.Context, uri []string) {
|
|
||||||
client, err := m.acmeClient(ctx)
|
client, err := m.acmeClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
for _, u := range uri {
|
dir, err := client.Discover(ctx)
|
||||||
client.RevokeAuthorization(ctx, u)
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var chain [][]byte
|
||||||
|
switch {
|
||||||
|
// Pre-RFC legacy CA.
|
||||||
|
case dir.OrderURL == "":
|
||||||
|
if err := m.verify(ctx, client, ck.domain); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
der, _, err := client.CreateCert(ctx, csr, 0, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
chain = der
|
||||||
|
// RFC 8555 compliant CA.
|
||||||
|
default:
|
||||||
|
o, err := m.verifyRFC(ctx, client, ck.domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
der, _, err := client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
chain = der
|
||||||
|
}
|
||||||
|
leaf, err = validCert(ck, chain, key, m.now())
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return chain, leaf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify runs the identifier (domain) authorization flow
|
// verify runs the identifier (domain) pre-authorization flow for legacy CAs
|
||||||
// using each applicable ACME challenge type.
|
// using each applicable ACME challenge type.
|
||||||
func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string) error {
|
func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string) error {
|
||||||
// The list of challenge types we'll try to fulfill
|
// Remove all hanging authorizations to reduce rate limit quotas
|
||||||
// in this specific order.
|
// after we're done.
|
||||||
challengeTypes := []string{"tls-alpn-01", "tls-sni-02", "tls-sni-01"}
|
var authzURLs []string
|
||||||
m.tokensMu.RLock()
|
|
||||||
if m.tryHTTP01 {
|
|
||||||
challengeTypes = append(challengeTypes, "http-01")
|
|
||||||
}
|
|
||||||
m.tokensMu.RUnlock()
|
|
||||||
|
|
||||||
// Keep track of pending authzs and revoke the ones that did not validate.
|
|
||||||
pendingAuthzs := make(map[string]bool)
|
|
||||||
defer func() {
|
defer func() {
|
||||||
var uri []string
|
go m.deactivatePendingAuthz(authzURLs)
|
||||||
for k, pending := range pendingAuthzs {
|
|
||||||
if pending {
|
|
||||||
uri = append(uri, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(uri) > 0 {
|
|
||||||
// Use "detached" background context.
|
|
||||||
// The revocations need not happen in the current verification flow.
|
|
||||||
go m.revokePendingAuthz(context.Background(), uri)
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// errs accumulates challenge failure errors, printed if all fail
|
// errs accumulates challenge failure errors, printed if all fail
|
||||||
errs := make(map[*acme.Challenge]error)
|
errs := make(map[*acme.Challenge]error)
|
||||||
|
challengeTypes := m.supportedChallengeTypes()
|
||||||
var nextTyp int // challengeType index of the next challenge type to try
|
var nextTyp int // challengeType index of the next challenge type to try
|
||||||
for {
|
for {
|
||||||
// Start domain authorization and get the challenge.
|
// Start domain authorization and get the challenge.
|
||||||
|
@ -703,6 +710,7 @@ func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
authzURLs = append(authzURLs, authz.URI)
|
||||||
// No point in accepting challenges if the authorization status
|
// No point in accepting challenges if the authorization status
|
||||||
// is in a final state.
|
// is in a final state.
|
||||||
switch authz.Status {
|
switch authz.Status {
|
||||||
|
@ -712,8 +720,6 @@ func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string
|
||||||
return fmt.Errorf("acme/autocert: invalid authorization %q", authz.URI)
|
return fmt.Errorf("acme/autocert: invalid authorization %q", authz.URI)
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingAuthzs[authz.URI] = true
|
|
||||||
|
|
||||||
// Pick the next preferred challenge.
|
// Pick the next preferred challenge.
|
||||||
var chal *acme.Challenge
|
var chal *acme.Challenge
|
||||||
for chal == nil && nextTyp < len(challengeTypes) {
|
for chal == nil && nextTyp < len(challengeTypes) {
|
||||||
|
@ -743,11 +749,126 @@ func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string
|
||||||
errs[chal] = err
|
errs[chal] = err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
delete(pendingAuthzs, authz.URI)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// verifyRFC runs the identifier (domain) order-based authorization flow for RFC compliant CAs
|
||||||
|
// using each applicable ACME challenge type.
|
||||||
|
func (m *Manager) verifyRFC(ctx context.Context, client *acme.Client, domain string) (*acme.Order, error) {
|
||||||
|
// Try each supported challenge type starting with a new order each time.
|
||||||
|
// The nextTyp index of the next challenge type to try is shared across
|
||||||
|
// all order authorizations: if we've tried a challenge type once and it didn't work,
|
||||||
|
// it will most likely not work on another order's authorization either.
|
||||||
|
challengeTypes := m.supportedChallengeTypes()
|
||||||
|
nextTyp := 0 // challengeTypes index
|
||||||
|
AuthorizeOrderLoop:
|
||||||
|
for {
|
||||||
|
o, err := client.AuthorizeOrder(ctx, acme.DomainIDs(domain))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Remove all hanging authorizations to reduce rate limit quotas
|
||||||
|
// after we're done.
|
||||||
|
defer func(urls []string) {
|
||||||
|
go m.deactivatePendingAuthz(urls)
|
||||||
|
}(o.AuthzURLs)
|
||||||
|
|
||||||
|
// Check if there's actually anything we need to do.
|
||||||
|
switch o.Status {
|
||||||
|
case acme.StatusReady:
|
||||||
|
// Already authorized.
|
||||||
|
return o, nil
|
||||||
|
case acme.StatusPending:
|
||||||
|
// Continue normal Order-based flow.
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("acme/autocert: invalid new order status %q; order URL: %q", o.Status, o.URI)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Satisfy all pending authorizations.
|
||||||
|
for _, zurl := range o.AuthzURLs {
|
||||||
|
z, err := client.GetAuthorization(ctx, zurl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if z.Status != acme.StatusPending {
|
||||||
|
// We are interested only in pending authorizations.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Pick the next preferred challenge.
|
||||||
|
var chal *acme.Challenge
|
||||||
|
for chal == nil && nextTyp < len(challengeTypes) {
|
||||||
|
chal = pickChallenge(challengeTypes[nextTyp], z.Challenges)
|
||||||
|
nextTyp++
|
||||||
|
}
|
||||||
|
if chal == nil {
|
||||||
|
return nil, fmt.Errorf("acme/autocert: unable to satisfy %q for domain %q: no viable challenge type found", z.URI, domain)
|
||||||
|
}
|
||||||
|
// Respond to the challenge and wait for validation result.
|
||||||
|
cleanup, err := m.fulfill(ctx, client, chal, domain)
|
||||||
|
if err != nil {
|
||||||
|
continue AuthorizeOrderLoop
|
||||||
|
}
|
||||||
|
defer cleanup()
|
||||||
|
if _, err := client.Accept(ctx, chal); err != nil {
|
||||||
|
continue AuthorizeOrderLoop
|
||||||
|
}
|
||||||
|
if _, err := client.WaitAuthorization(ctx, z.URI); err != nil {
|
||||||
|
continue AuthorizeOrderLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All authorizations are satisfied.
|
||||||
|
// Wait for the CA to update the order status.
|
||||||
|
o, err = client.WaitOrder(ctx, o.URI)
|
||||||
|
if err != nil {
|
||||||
|
continue AuthorizeOrderLoop
|
||||||
|
}
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pickChallenge(typ string, chal []*acme.Challenge) *acme.Challenge {
|
||||||
|
for _, c := range chal {
|
||||||
|
if c.Type == typ {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) supportedChallengeTypes() []string {
|
||||||
|
m.challengeMu.RLock()
|
||||||
|
defer m.challengeMu.RUnlock()
|
||||||
|
typ := []string{"tls-alpn-01"}
|
||||||
|
if m.tryHTTP01 {
|
||||||
|
typ = append(typ, "http-01")
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// deactivatePendingAuthz relinquishes all authorizations identified by the elements
|
||||||
|
// of the provided uri slice which are in "pending" state.
|
||||||
|
// It ignores revocation errors.
|
||||||
|
//
|
||||||
|
// deactivatePendingAuthz takes no context argument and instead runs with its own
|
||||||
|
// "detached" context because deactivations are done in a goroutine separate from
|
||||||
|
// that of the main issuance or renewal flow.
|
||||||
|
func (m *Manager) deactivatePendingAuthz(uri []string) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
client, err := m.acmeClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, u := range uri {
|
||||||
|
z, err := client.GetAuthorization(ctx, u)
|
||||||
|
if err == nil && z.Status == acme.StatusPending {
|
||||||
|
client.RevokeAuthorization(ctx, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// fulfill provisions a response to the challenge chal.
|
// fulfill provisions a response to the challenge chal.
|
||||||
// The cleanup is non-nil only if provisioning succeeded.
|
// The cleanup is non-nil only if provisioning succeeded.
|
||||||
func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.Challenge, domain string) (cleanup func(), err error) {
|
func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.Challenge, domain string) (cleanup func(), err error) {
|
||||||
|
@ -759,20 +880,6 @@ func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.C
|
||||||
}
|
}
|
||||||
m.putCertToken(ctx, domain, &cert)
|
m.putCertToken(ctx, domain, &cert)
|
||||||
return func() { go m.deleteCertToken(domain) }, nil
|
return func() { go m.deleteCertToken(domain) }, nil
|
||||||
case "tls-sni-01":
|
|
||||||
cert, name, err := client.TLSSNI01ChallengeCert(chal.Token)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
m.putCertToken(ctx, name, &cert)
|
|
||||||
return func() { go m.deleteCertToken(name) }, nil
|
|
||||||
case "tls-sni-02":
|
|
||||||
cert, name, err := client.TLSSNI02ChallengeCert(chal.Token)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
m.putCertToken(ctx, name, &cert)
|
|
||||||
return func() { go m.deleteCertToken(name) }, nil
|
|
||||||
case "http-01":
|
case "http-01":
|
||||||
resp, err := client.HTTP01ChallengeResponse(chal.Token)
|
resp, err := client.HTTP01ChallengeResponse(chal.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -785,20 +892,11 @@ func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.C
|
||||||
return nil, fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type)
|
return nil, fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
func pickChallenge(typ string, chal []*acme.Challenge) *acme.Challenge {
|
|
||||||
for _, c := range chal {
|
|
||||||
if c.Type == typ {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// putCertToken stores the token certificate with the specified name
|
// putCertToken stores the token certificate with the specified name
|
||||||
// in both m.certTokens map and m.Cache.
|
// in both m.certTokens map and m.Cache.
|
||||||
func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certificate) {
|
func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certificate) {
|
||||||
m.tokensMu.Lock()
|
m.challengeMu.Lock()
|
||||||
defer m.tokensMu.Unlock()
|
defer m.challengeMu.Unlock()
|
||||||
if m.certTokens == nil {
|
if m.certTokens == nil {
|
||||||
m.certTokens = make(map[string]*tls.Certificate)
|
m.certTokens = make(map[string]*tls.Certificate)
|
||||||
}
|
}
|
||||||
|
@ -809,8 +907,8 @@ func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certi
|
||||||
// deleteCertToken removes the token certificate with the specified name
|
// deleteCertToken removes the token certificate with the specified name
|
||||||
// from both m.certTokens map and m.Cache.
|
// from both m.certTokens map and m.Cache.
|
||||||
func (m *Manager) deleteCertToken(name string) {
|
func (m *Manager) deleteCertToken(name string) {
|
||||||
m.tokensMu.Lock()
|
m.challengeMu.Lock()
|
||||||
defer m.tokensMu.Unlock()
|
defer m.challengeMu.Unlock()
|
||||||
delete(m.certTokens, name)
|
delete(m.certTokens, name)
|
||||||
if m.Cache != nil {
|
if m.Cache != nil {
|
||||||
ck := certKey{domain: name, isToken: true}
|
ck := certKey{domain: name, isToken: true}
|
||||||
|
@ -821,8 +919,8 @@ func (m *Manager) deleteCertToken(name string) {
|
||||||
// httpToken retrieves an existing http-01 token value from an in-memory map
|
// httpToken retrieves an existing http-01 token value from an in-memory map
|
||||||
// or the optional cache.
|
// or the optional cache.
|
||||||
func (m *Manager) httpToken(ctx context.Context, tokenPath string) ([]byte, error) {
|
func (m *Manager) httpToken(ctx context.Context, tokenPath string) ([]byte, error) {
|
||||||
m.tokensMu.RLock()
|
m.challengeMu.RLock()
|
||||||
defer m.tokensMu.RUnlock()
|
defer m.challengeMu.RUnlock()
|
||||||
if v, ok := m.httpTokens[tokenPath]; ok {
|
if v, ok := m.httpTokens[tokenPath]; ok {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
@ -837,8 +935,8 @@ func (m *Manager) httpToken(ctx context.Context, tokenPath string) ([]byte, erro
|
||||||
//
|
//
|
||||||
// It ignores any error returned from Cache.Put.
|
// It ignores any error returned from Cache.Put.
|
||||||
func (m *Manager) putHTTPToken(ctx context.Context, tokenPath, val string) {
|
func (m *Manager) putHTTPToken(ctx context.Context, tokenPath, val string) {
|
||||||
m.tokensMu.Lock()
|
m.challengeMu.Lock()
|
||||||
defer m.tokensMu.Unlock()
|
defer m.challengeMu.Unlock()
|
||||||
if m.httpTokens == nil {
|
if m.httpTokens == nil {
|
||||||
m.httpTokens = make(map[string][]byte)
|
m.httpTokens = make(map[string][]byte)
|
||||||
}
|
}
|
||||||
|
@ -854,8 +952,8 @@ func (m *Manager) putHTTPToken(ctx context.Context, tokenPath, val string) {
|
||||||
//
|
//
|
||||||
// If m.Cache is non-nil, it blocks until Cache.Delete returns without a timeout.
|
// If m.Cache is non-nil, it blocks until Cache.Delete returns without a timeout.
|
||||||
func (m *Manager) deleteHTTPToken(tokenPath string) {
|
func (m *Manager) deleteHTTPToken(tokenPath string) {
|
||||||
m.tokensMu.Lock()
|
m.challengeMu.Lock()
|
||||||
defer m.tokensMu.Unlock()
|
defer m.challengeMu.Unlock()
|
||||||
delete(m.httpTokens, tokenPath)
|
delete(m.httpTokens, tokenPath)
|
||||||
if m.Cache != nil {
|
if m.Cache != nil {
|
||||||
m.Cache.Delete(context.Background(), httpTokenCacheKey(tokenPath))
|
m.Cache.Delete(context.Background(), httpTokenCacheKey(tokenPath))
|
||||||
|
@ -954,7 +1052,7 @@ func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) {
|
||||||
|
|
||||||
client := m.Client
|
client := m.Client
|
||||||
if client == nil {
|
if client == nil {
|
||||||
client = &acme.Client{DirectoryURL: acme.LetsEncryptURL}
|
client = &acme.Client{DirectoryURL: DefaultACMEDirectory}
|
||||||
}
|
}
|
||||||
if client.Key == nil {
|
if client.Key == nil {
|
||||||
var err error
|
var err error
|
||||||
|
@ -963,20 +1061,32 @@ func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if client.UserAgent == "" {
|
||||||
|
client.UserAgent = "autocert"
|
||||||
|
}
|
||||||
var contact []string
|
var contact []string
|
||||||
if m.Email != "" {
|
if m.Email != "" {
|
||||||
contact = []string{"mailto:" + m.Email}
|
contact = []string{"mailto:" + m.Email}
|
||||||
}
|
}
|
||||||
a := &acme.Account{Contact: contact}
|
a := &acme.Account{Contact: contact}
|
||||||
_, err := client.Register(ctx, a, m.Prompt)
|
_, err := client.Register(ctx, a, m.Prompt)
|
||||||
if ae, ok := err.(*acme.Error); err == nil || ok && ae.StatusCode == http.StatusConflict {
|
if err == nil || isAccountAlreadyExist(err) {
|
||||||
// conflict indicates the key is already registered
|
|
||||||
m.client = client
|
m.client = client
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
return m.client, err
|
return m.client, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isAccountAlreadyExist reports whether the err, as returned from acme.Client.Register,
|
||||||
|
// indicates the account has already been registered.
|
||||||
|
func isAccountAlreadyExist(err error) bool {
|
||||||
|
if err == acme.ErrAccountAlreadyExists {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
ae, ok := err.(*acme.Error)
|
||||||
|
return ok && ae.StatusCode == http.StatusConflict
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Manager) hostPolicy() HostPolicy {
|
func (m *Manager) hostPolicy() HostPolicy {
|
||||||
if m.HostPolicy != nil {
|
if m.HostPolicy != nil {
|
||||||
return m.HostPolicy
|
return m.HostPolicy
|
||||||
|
@ -1023,11 +1133,11 @@ func (s *certState) tlscert() (*tls.Certificate, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// certRequest generates a CSR for the given common name cn and optional SANs.
|
// certRequest generates a CSR for the given common name.
|
||||||
func certRequest(key crypto.Signer, cn string, ext []pkix.Extension, san ...string) ([]byte, error) {
|
func certRequest(key crypto.Signer, name string, ext []pkix.Extension) ([]byte, error) {
|
||||||
req := &x509.CertificateRequest{
|
req := &x509.CertificateRequest{
|
||||||
Subject: pkix.Name{CommonName: cn},
|
Subject: pkix.Name{CommonName: name},
|
||||||
DNSNames: san,
|
DNSNames: []string{name},
|
||||||
ExtraExtensions: ext,
|
ExtraExtensions: ext,
|
||||||
}
|
}
|
||||||
return x509.CreateCertificateRequest(rand.Reader, req, key)
|
return x509.CreateCertificateRequest(rand.Reader, req, key)
|
||||||
|
|
|
@ -77,6 +77,7 @@ func (d DirCache) Put(ctx context.Context, name string, data []byte) error {
|
||||||
if tmp, err = d.writeTempFile(name, data); err != nil {
|
if tmp, err = d.writeTempFile(name, data); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer os.Remove(tmp)
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
// Don't overwrite the file if the context was canceled.
|
// Don't overwrite the file if the context was canceled.
|
||||||
|
@ -116,12 +117,17 @@ func (d DirCache) Delete(ctx context.Context, name string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeTempFile writes b to a temporary file, closes the file and returns its path.
|
// writeTempFile writes b to a temporary file, closes the file and returns its path.
|
||||||
func (d DirCache) writeTempFile(prefix string, b []byte) (string, error) {
|
func (d DirCache) writeTempFile(prefix string, b []byte) (name string, reterr error) {
|
||||||
// TempFile uses 0600 permissions
|
// TempFile uses 0600 permissions
|
||||||
f, err := ioutil.TempFile(string(d), prefix)
|
f, err := ioutil.TempFile(string(d), prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
if reterr != nil {
|
||||||
|
os.Remove(f.Name())
|
||||||
|
}
|
||||||
|
}()
|
||||||
if _, err := f.Write(b); err != nil {
|
if _, err := f.Write(b); err != nil {
|
||||||
f.Close()
|
f.Close()
|
||||||
return "", err
|
return "", err
|
||||||
|
|
|
@ -72,7 +72,6 @@ func NewListener(domains ...string) net.Listener {
|
||||||
// the Manager m's Prompt, Cache, HostPolicy, and other desired options.
|
// the Manager m's Prompt, Cache, HostPolicy, and other desired options.
|
||||||
func (m *Manager) Listener() net.Listener {
|
func (m *Manager) Listener() net.Listener {
|
||||||
ln := &listener{
|
ln := &listener{
|
||||||
m: m,
|
|
||||||
conf: m.TLSConfig(),
|
conf: m.TLSConfig(),
|
||||||
}
|
}
|
||||||
ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443")
|
ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443")
|
||||||
|
@ -80,7 +79,6 @@ func (m *Manager) Listener() net.Listener {
|
||||||
}
|
}
|
||||||
|
|
||||||
type listener struct {
|
type listener struct {
|
||||||
m *Manager
|
|
||||||
conf *tls.Config
|
conf *tls.Config
|
||||||
|
|
||||||
tcpListener net.Listener
|
tcpListener net.Listener
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
@ -155,8 +156,16 @@ func (c *Client) get(ctx context.Context, url string, ok resOkay) (*http.Respons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// postAsGet is POST-as-GET, a replacement for GET in RFC8555
|
||||||
|
// as described in https://tools.ietf.org/html/rfc8555#section-6.3.
|
||||||
|
// It makes a POST request in KID form with zero JWS payload.
|
||||||
|
// See nopayload doc comments in jws.go.
|
||||||
|
func (c *Client) postAsGet(ctx context.Context, url string, ok resOkay) (*http.Response, error) {
|
||||||
|
return c.post(ctx, nil, url, noPayload, ok)
|
||||||
|
}
|
||||||
|
|
||||||
// post issues a signed POST request in JWS format using the provided key
|
// post issues a signed POST request in JWS format using the provided key
|
||||||
// to the specified URL.
|
// to the specified URL. If key is nil, c.Key is used instead.
|
||||||
// It returns a non-error value only when ok reports true.
|
// It returns a non-error value only when ok reports true.
|
||||||
//
|
//
|
||||||
// post retries unsuccessful attempts according to c.RetryBackoff
|
// post retries unsuccessful attempts according to c.RetryBackoff
|
||||||
|
@ -193,14 +202,31 @@ func (c *Client) post(ctx context.Context, key crypto.Signer, url string, body i
|
||||||
}
|
}
|
||||||
|
|
||||||
// postNoRetry signs the body with the given key and POSTs it to the provided url.
|
// postNoRetry signs the body with the given key and POSTs it to the provided url.
|
||||||
// The body argument must be JSON-serializable.
|
|
||||||
// It is used by c.post to retry unsuccessful attempts.
|
// It is used by c.post to retry unsuccessful attempts.
|
||||||
|
// The body argument must be JSON-serializable.
|
||||||
|
//
|
||||||
|
// If key argument is nil, c.Key is used to sign the request.
|
||||||
|
// If key argument is nil and c.accountKID returns a non-zero keyID,
|
||||||
|
// the request is sent in KID form. Otherwise, JWK form is used.
|
||||||
|
//
|
||||||
|
// In practice, when interfacing with RFC-compliant CAs most requests are sent in KID form
|
||||||
|
// and JWK is used only when KID is unavailable: new account endpoint and certificate
|
||||||
|
// revocation requests authenticated by a cert key.
|
||||||
|
// See jwsEncodeJSON for other details.
|
||||||
func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, *http.Request, error) {
|
func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, *http.Request, error) {
|
||||||
|
kid := noKeyID
|
||||||
|
if key == nil {
|
||||||
|
if c.Key == nil {
|
||||||
|
return nil, nil, errors.New("acme: Client.Key must be populated to make POST requests")
|
||||||
|
}
|
||||||
|
key = c.Key
|
||||||
|
kid = c.accountKID(ctx)
|
||||||
|
}
|
||||||
nonce, err := c.popNonce(ctx, url)
|
nonce, err := c.popNonce(ctx, url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
b, err := jwsEncodeJSON(body, key, nonce)
|
b, err := jwsEncodeJSON(body, key, kid, nonce, url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -219,6 +245,7 @@ func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string,
|
||||||
|
|
||||||
// doNoRetry issues a request req, replacing its context (if any) with ctx.
|
// doNoRetry issues a request req, replacing its context (if any) with ctx.
|
||||||
func (c *Client) doNoRetry(ctx context.Context, req *http.Request) (*http.Response, error) {
|
func (c *Client) doNoRetry(ctx context.Context, req *http.Request) (*http.Response, error) {
|
||||||
|
req.Header.Set("User-Agent", c.userAgent())
|
||||||
res, err := c.httpClient().Do(req.WithContext(ctx))
|
res, err := c.httpClient().Do(req.WithContext(ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
select {
|
select {
|
||||||
|
@ -243,6 +270,23 @@ func (c *Client) httpClient() *http.Client {
|
||||||
return http.DefaultClient
|
return http.DefaultClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// packageVersion is the version of the module that contains this package, for
|
||||||
|
// sending as part of the User-Agent header. It's set in version_go112.go.
|
||||||
|
var packageVersion string
|
||||||
|
|
||||||
|
// userAgent returns the User-Agent header value. It includes the package name,
|
||||||
|
// the module version (if available), and the c.UserAgent value (if set).
|
||||||
|
func (c *Client) userAgent() string {
|
||||||
|
ua := "golang.org/x/crypto/acme"
|
||||||
|
if packageVersion != "" {
|
||||||
|
ua += "@" + packageVersion
|
||||||
|
}
|
||||||
|
if c.UserAgent != "" {
|
||||||
|
ua = c.UserAgent + " " + ua
|
||||||
|
}
|
||||||
|
return ua
|
||||||
|
}
|
||||||
|
|
||||||
// isBadNonce reports whether err is an ACME "badnonce" error.
|
// isBadNonce reports whether err is an ACME "badnonce" error.
|
||||||
func isBadNonce(err error) bool {
|
func isBadNonce(err error) bool {
|
||||||
// According to the spec badNonce is urn:ietf:params:acme:error:badNonce.
|
// According to the spec badNonce is urn:ietf:params:acme:error:badNonce.
|
||||||
|
|
|
@ -7,47 +7,81 @@ package acme
|
||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/hmac"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
_ "crypto/sha512" // need for EC keys
|
_ "crypto/sha512" // need for EC keys
|
||||||
|
"encoding/asn1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// keyID is the account identity provided by a CA during registration.
|
||||||
|
type keyID string
|
||||||
|
|
||||||
|
// noKeyID indicates that jwsEncodeJSON should compute and use JWK instead of a KID.
|
||||||
|
// See jwsEncodeJSON for details.
|
||||||
|
const noKeyID = keyID("")
|
||||||
|
|
||||||
|
// noPayload indicates jwsEncodeJSON will encode zero-length octet string
|
||||||
|
// in a JWS request. This is called POST-as-GET in RFC 8555 and is used to make
|
||||||
|
// authenticated GET requests via POSTing with an empty payload.
|
||||||
|
// See https://tools.ietf.org/html/rfc8555#section-6.3 for more details.
|
||||||
|
const noPayload = ""
|
||||||
|
|
||||||
|
// jsonWebSignature can be easily serialized into a JWS following
|
||||||
|
// https://tools.ietf.org/html/rfc7515#section-3.2.
|
||||||
|
type jsonWebSignature struct {
|
||||||
|
Protected string `json:"protected"`
|
||||||
|
Payload string `json:"payload"`
|
||||||
|
Sig string `json:"signature"`
|
||||||
|
}
|
||||||
|
|
||||||
// jwsEncodeJSON signs claimset using provided key and a nonce.
|
// jwsEncodeJSON signs claimset using provided key and a nonce.
|
||||||
// The result is serialized in JSON format.
|
// The result is serialized in JSON format containing either kid or jwk
|
||||||
|
// fields based on the provided keyID value.
|
||||||
|
//
|
||||||
|
// If kid is non-empty, its quoted value is inserted in the protected head
|
||||||
|
// as "kid" field value. Otherwise, JWK is computed using jwkEncode and inserted
|
||||||
|
// as "jwk" field value. The "jwk" and "kid" fields are mutually exclusive.
|
||||||
|
//
|
||||||
// See https://tools.ietf.org/html/rfc7515#section-7.
|
// See https://tools.ietf.org/html/rfc7515#section-7.
|
||||||
func jwsEncodeJSON(claimset interface{}, key crypto.Signer, nonce string) ([]byte, error) {
|
func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid keyID, nonce, url string) ([]byte, error) {
|
||||||
jwk, err := jwkEncode(key.Public())
|
alg, sha := jwsHasher(key.Public())
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
alg, sha := jwsHasher(key)
|
|
||||||
if alg == "" || !sha.Available() {
|
if alg == "" || !sha.Available() {
|
||||||
return nil, ErrUnsupportedKey
|
return nil, ErrUnsupportedKey
|
||||||
}
|
}
|
||||||
phead := fmt.Sprintf(`{"alg":%q,"jwk":%s,"nonce":%q}`, alg, jwk, nonce)
|
var phead string
|
||||||
phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
|
switch kid {
|
||||||
cs, err := json.Marshal(claimset)
|
case noKeyID:
|
||||||
if err != nil {
|
jwk, err := jwkEncode(key.Public())
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
phead = fmt.Sprintf(`{"alg":%q,"jwk":%s,"nonce":%q,"url":%q}`, alg, jwk, nonce, url)
|
||||||
|
default:
|
||||||
|
phead = fmt.Sprintf(`{"alg":%q,"kid":%q,"nonce":%q,"url":%q}`, alg, kid, nonce, url)
|
||||||
|
}
|
||||||
|
phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
|
||||||
|
var payload string
|
||||||
|
if claimset != noPayload {
|
||||||
|
cs, err := json.Marshal(claimset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payload = base64.RawURLEncoding.EncodeToString(cs)
|
||||||
}
|
}
|
||||||
payload := base64.RawURLEncoding.EncodeToString(cs)
|
|
||||||
hash := sha.New()
|
hash := sha.New()
|
||||||
hash.Write([]byte(phead + "." + payload))
|
hash.Write([]byte(phead + "." + payload))
|
||||||
sig, err := jwsSign(key, sha, hash.Sum(nil))
|
sig, err := jwsSign(key, sha, hash.Sum(nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
enc := jsonWebSignature{
|
||||||
enc := struct {
|
|
||||||
Protected string `json:"protected"`
|
|
||||||
Payload string `json:"payload"`
|
|
||||||
Sig string `json:"signature"`
|
|
||||||
}{
|
|
||||||
Protected: phead,
|
Protected: phead,
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
Sig: base64.RawURLEncoding.EncodeToString(sig),
|
Sig: base64.RawURLEncoding.EncodeToString(sig),
|
||||||
|
@ -55,6 +89,43 @@ func jwsEncodeJSON(claimset interface{}, key crypto.Signer, nonce string) ([]byt
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// jwsWithMAC creates and signs a JWS using the given key and the HS256
|
||||||
|
// algorithm. kid and url are included in the protected header. rawPayload
|
||||||
|
// should not be base64-URL-encoded.
|
||||||
|
func jwsWithMAC(key []byte, kid, url string, rawPayload []byte) (*jsonWebSignature, error) {
|
||||||
|
if len(key) == 0 {
|
||||||
|
return nil, errors.New("acme: cannot sign JWS with an empty MAC key")
|
||||||
|
}
|
||||||
|
header := struct {
|
||||||
|
Algorithm string `json:"alg"`
|
||||||
|
KID string `json:"kid"`
|
||||||
|
URL string `json:"url,omitempty"`
|
||||||
|
}{
|
||||||
|
// Only HMAC-SHA256 is supported.
|
||||||
|
Algorithm: "HS256",
|
||||||
|
KID: kid,
|
||||||
|
URL: url,
|
||||||
|
}
|
||||||
|
rawProtected, err := json.Marshal(header)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
protected := base64.RawURLEncoding.EncodeToString(rawProtected)
|
||||||
|
payload := base64.RawURLEncoding.EncodeToString(rawPayload)
|
||||||
|
|
||||||
|
h := hmac.New(sha256.New, key)
|
||||||
|
if _, err := h.Write([]byte(protected + "." + payload)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mac := h.Sum(nil)
|
||||||
|
|
||||||
|
return &jsonWebSignature{
|
||||||
|
Protected: protected,
|
||||||
|
Payload: payload,
|
||||||
|
Sig: base64.RawURLEncoding.EncodeToString(mac),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// jwkEncode encodes public part of an RSA or ECDSA key into a JWK.
|
// jwkEncode encodes public part of an RSA or ECDSA key into a JWK.
|
||||||
// The result is also suitable for creating a JWK thumbprint.
|
// The result is also suitable for creating a JWK thumbprint.
|
||||||
// https://tools.ietf.org/html/rfc7517
|
// https://tools.ietf.org/html/rfc7517
|
||||||
|
@ -97,19 +168,24 @@ func jwkEncode(pub crypto.PublicKey) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// jwsSign signs the digest using the given key.
|
// jwsSign signs the digest using the given key.
|
||||||
// It returns ErrUnsupportedKey if the key type is unknown.
|
// The hash is unused for ECDSA keys.
|
||||||
// The hash is used only for RSA keys.
|
|
||||||
func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
|
func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
|
||||||
switch key := key.(type) {
|
switch pub := key.Public().(type) {
|
||||||
case *rsa.PrivateKey:
|
case *rsa.PublicKey:
|
||||||
return key.Sign(rand.Reader, digest, hash)
|
return key.Sign(rand.Reader, digest, hash)
|
||||||
case *ecdsa.PrivateKey:
|
case *ecdsa.PublicKey:
|
||||||
r, s, err := ecdsa.Sign(rand.Reader, key, digest)
|
sigASN1, err := key.Sign(rand.Reader, digest, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rb, sb := r.Bytes(), s.Bytes()
|
|
||||||
size := key.Params().BitSize / 8
|
var rs struct{ R, S *big.Int }
|
||||||
|
if _, err := asn1.Unmarshal(sigASN1, &rs); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rb, sb := rs.R.Bytes(), rs.S.Bytes()
|
||||||
|
size := pub.Params().BitSize / 8
|
||||||
if size%8 > 0 {
|
if size%8 > 0 {
|
||||||
size++
|
size++
|
||||||
}
|
}
|
||||||
|
@ -124,12 +200,12 @@ func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error)
|
||||||
// jwsHasher indicates suitable JWS algorithm name and a hash function
|
// jwsHasher indicates suitable JWS algorithm name and a hash function
|
||||||
// to use for signing a digest with the provided key.
|
// to use for signing a digest with the provided key.
|
||||||
// It returns ("", 0) if the key is not supported.
|
// It returns ("", 0) if the key is not supported.
|
||||||
func jwsHasher(key crypto.Signer) (string, crypto.Hash) {
|
func jwsHasher(pub crypto.PublicKey) (string, crypto.Hash) {
|
||||||
switch key := key.(type) {
|
switch pub := pub.(type) {
|
||||||
case *rsa.PrivateKey:
|
case *rsa.PublicKey:
|
||||||
return "RS256", crypto.SHA256
|
return "RS256", crypto.SHA256
|
||||||
case *ecdsa.PrivateKey:
|
case *ecdsa.PublicKey:
|
||||||
switch key.Params().Name {
|
switch pub.Params().Name {
|
||||||
case "P-256":
|
case "P-256":
|
||||||
return "ES256", crypto.SHA256
|
return "ES256", crypto.SHA256
|
||||||
case "P-384":
|
case "P-384":
|
||||||
|
|
|
@ -0,0 +1,438 @@
|
||||||
|
// Copyright 2019 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.
|
||||||
|
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeactivateReg permanently disables an existing account associated with c.Key.
|
||||||
|
// A deactivated account can no longer request certificate issuance or access
|
||||||
|
// resources related to the account, such as orders or authorizations.
|
||||||
|
//
|
||||||
|
// It only works with CAs implementing RFC 8555.
|
||||||
|
func (c *Client) DeactivateReg(ctx context.Context) error {
|
||||||
|
url := string(c.accountKID(ctx))
|
||||||
|
if url == "" {
|
||||||
|
return ErrNoAccount
|
||||||
|
}
|
||||||
|
req := json.RawMessage(`{"status": "deactivated"}`)
|
||||||
|
res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerRFC is equivalent to c.Register but for CAs implementing RFC 8555.
|
||||||
|
// It expects c.Discover to have already been called.
|
||||||
|
func (c *Client) registerRFC(ctx context.Context, acct *Account, prompt func(tosURL string) bool) (*Account, error) {
|
||||||
|
c.cacheMu.Lock() // guard c.kid access
|
||||||
|
defer c.cacheMu.Unlock()
|
||||||
|
|
||||||
|
req := struct {
|
||||||
|
TermsAgreed bool `json:"termsOfServiceAgreed,omitempty"`
|
||||||
|
Contact []string `json:"contact,omitempty"`
|
||||||
|
ExternalAccountBinding *jsonWebSignature `json:"externalAccountBinding,omitempty"`
|
||||||
|
}{
|
||||||
|
Contact: acct.Contact,
|
||||||
|
}
|
||||||
|
if c.dir.Terms != "" {
|
||||||
|
req.TermsAgreed = prompt(c.dir.Terms)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set 'externalAccountBinding' field if requested
|
||||||
|
if acct.ExternalAccountBinding != nil {
|
||||||
|
eabJWS, err := c.encodeExternalAccountBinding(acct.ExternalAccountBinding)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("acme: failed to encode external account binding: %v", err)
|
||||||
|
}
|
||||||
|
req.ExternalAccountBinding = eabJWS
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.post(ctx, c.Key, c.dir.RegURL, req, wantStatus(
|
||||||
|
http.StatusOK, // account with this key already registered
|
||||||
|
http.StatusCreated, // new account created
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer res.Body.Close()
|
||||||
|
a, err := responseAccount(res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Cache Account URL even if we return an error to the caller.
|
||||||
|
// It is by all means a valid and usable "kid" value for future requests.
|
||||||
|
c.kid = keyID(a.URI)
|
||||||
|
if res.StatusCode == http.StatusOK {
|
||||||
|
return nil, ErrAccountAlreadyExists
|
||||||
|
}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodeExternalAccountBinding will encode an external account binding stanza
|
||||||
|
// as described in https://tools.ietf.org/html/rfc8555#section-7.3.4.
|
||||||
|
func (c *Client) encodeExternalAccountBinding(eab *ExternalAccountBinding) (*jsonWebSignature, error) {
|
||||||
|
jwk, err := jwkEncode(c.Key.Public())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return jwsWithMAC(eab.Key, eab.KID, c.dir.RegURL, []byte(jwk))
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateRegRFC is equivalent to c.UpdateReg but for CAs implementing RFC 8555.
|
||||||
|
// It expects c.Discover to have already been called.
|
||||||
|
func (c *Client) updateRegRFC(ctx context.Context, a *Account) (*Account, error) {
|
||||||
|
url := string(c.accountKID(ctx))
|
||||||
|
if url == "" {
|
||||||
|
return nil, ErrNoAccount
|
||||||
|
}
|
||||||
|
req := struct {
|
||||||
|
Contact []string `json:"contact,omitempty"`
|
||||||
|
}{
|
||||||
|
Contact: a.Contact,
|
||||||
|
}
|
||||||
|
res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
return responseAccount(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGegRFC is equivalent to c.GetReg but for CAs implementing RFC 8555.
|
||||||
|
// It expects c.Discover to have already been called.
|
||||||
|
func (c *Client) getRegRFC(ctx context.Context) (*Account, error) {
|
||||||
|
req := json.RawMessage(`{"onlyReturnExisting": true}`)
|
||||||
|
res, err := c.post(ctx, c.Key, c.dir.RegURL, req, wantStatus(http.StatusOK))
|
||||||
|
if e, ok := err.(*Error); ok && e.ProblemType == "urn:ietf:params:acme:error:accountDoesNotExist" {
|
||||||
|
return nil, ErrNoAccount
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer res.Body.Close()
|
||||||
|
return responseAccount(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func responseAccount(res *http.Response) (*Account, error) {
|
||||||
|
var v struct {
|
||||||
|
Status string
|
||||||
|
Contact []string
|
||||||
|
Orders string
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
|
return nil, fmt.Errorf("acme: invalid account response: %v", err)
|
||||||
|
}
|
||||||
|
return &Account{
|
||||||
|
URI: res.Header.Get("Location"),
|
||||||
|
Status: v.Status,
|
||||||
|
Contact: v.Contact,
|
||||||
|
OrdersURL: v.Orders,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthorizeOrder initiates the order-based application for certificate issuance,
|
||||||
|
// as opposed to pre-authorization in Authorize.
|
||||||
|
// It is only supported by CAs implementing RFC 8555.
|
||||||
|
//
|
||||||
|
// The caller then needs to fetch each authorization with GetAuthorization,
|
||||||
|
// identify those with StatusPending status and fulfill a challenge using Accept.
|
||||||
|
// Once all authorizations are satisfied, the caller will typically want to poll
|
||||||
|
// order status using WaitOrder until it's in StatusReady state.
|
||||||
|
// To finalize the order and obtain a certificate, the caller submits a CSR with CreateOrderCert.
|
||||||
|
func (c *Client) AuthorizeOrder(ctx context.Context, id []AuthzID, opt ...OrderOption) (*Order, error) {
|
||||||
|
dir, err := c.Discover(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := struct {
|
||||||
|
Identifiers []wireAuthzID `json:"identifiers"`
|
||||||
|
NotBefore string `json:"notBefore,omitempty"`
|
||||||
|
NotAfter string `json:"notAfter,omitempty"`
|
||||||
|
}{}
|
||||||
|
for _, v := range id {
|
||||||
|
req.Identifiers = append(req.Identifiers, wireAuthzID{
|
||||||
|
Type: v.Type,
|
||||||
|
Value: v.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, o := range opt {
|
||||||
|
switch o := o.(type) {
|
||||||
|
case orderNotBeforeOpt:
|
||||||
|
req.NotBefore = time.Time(o).Format(time.RFC3339)
|
||||||
|
case orderNotAfterOpt:
|
||||||
|
req.NotAfter = time.Time(o).Format(time.RFC3339)
|
||||||
|
default:
|
||||||
|
// Package's fault if we let this happen.
|
||||||
|
panic(fmt.Sprintf("unsupported order option type %T", o))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.post(ctx, nil, dir.OrderURL, req, wantStatus(http.StatusCreated))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
return responseOrder(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrder retrives an order identified by the given URL.
|
||||||
|
// For orders created with AuthorizeOrder, the url value is Order.URI.
|
||||||
|
//
|
||||||
|
// If a caller needs to poll an order until its status is final,
|
||||||
|
// see the WaitOrder method.
|
||||||
|
func (c *Client) GetOrder(ctx context.Context, url string) (*Order, error) {
|
||||||
|
if _, err := c.Discover(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
return responseOrder(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitOrder polls an order from the given URL until it is in one of the final states,
|
||||||
|
// StatusReady, StatusValid or StatusInvalid, the CA responded with a non-retryable error
|
||||||
|
// or the context is done.
|
||||||
|
//
|
||||||
|
// It returns a non-nil Order only if its Status is StatusReady or StatusValid.
|
||||||
|
// In all other cases WaitOrder returns an error.
|
||||||
|
// If the Status is StatusInvalid, the returned error is of type *OrderError.
|
||||||
|
func (c *Client) WaitOrder(ctx context.Context, url string) (*Order, error) {
|
||||||
|
if _, err := c.Discover(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
o, err := responseOrder(res)
|
||||||
|
res.Body.Close()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
// Skip and retry.
|
||||||
|
case o.Status == StatusInvalid:
|
||||||
|
return nil, &OrderError{OrderURL: o.URI, Status: o.Status}
|
||||||
|
case o.Status == StatusReady || o.Status == StatusValid:
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
d := retryAfter(res.Header.Get("Retry-After"))
|
||||||
|
if d == 0 {
|
||||||
|
// Default retry-after.
|
||||||
|
// Same reasoning as in WaitAuthorization.
|
||||||
|
d = time.Second
|
||||||
|
}
|
||||||
|
t := time.NewTimer(d)
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Stop()
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case <-t.C:
|
||||||
|
// Retry.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func responseOrder(res *http.Response) (*Order, error) {
|
||||||
|
var v struct {
|
||||||
|
Status string
|
||||||
|
Expires time.Time
|
||||||
|
Identifiers []wireAuthzID
|
||||||
|
NotBefore time.Time
|
||||||
|
NotAfter time.Time
|
||||||
|
Error *wireError
|
||||||
|
Authorizations []string
|
||||||
|
Finalize string
|
||||||
|
Certificate string
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
|
return nil, fmt.Errorf("acme: error reading order: %v", err)
|
||||||
|
}
|
||||||
|
o := &Order{
|
||||||
|
URI: res.Header.Get("Location"),
|
||||||
|
Status: v.Status,
|
||||||
|
Expires: v.Expires,
|
||||||
|
NotBefore: v.NotBefore,
|
||||||
|
NotAfter: v.NotAfter,
|
||||||
|
AuthzURLs: v.Authorizations,
|
||||||
|
FinalizeURL: v.Finalize,
|
||||||
|
CertURL: v.Certificate,
|
||||||
|
}
|
||||||
|
for _, id := range v.Identifiers {
|
||||||
|
o.Identifiers = append(o.Identifiers, AuthzID{Type: id.Type, Value: id.Value})
|
||||||
|
}
|
||||||
|
if v.Error != nil {
|
||||||
|
o.Error = v.Error.error(nil /* headers */)
|
||||||
|
}
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOrderCert submits the CSR (Certificate Signing Request) to a CA at the specified URL.
|
||||||
|
// The URL is the FinalizeURL field of an Order created with AuthorizeOrder.
|
||||||
|
//
|
||||||
|
// If the bundle argument is true, the returned value also contain the CA (issuer)
|
||||||
|
// certificate chain. Otherwise, only a leaf certificate is returned.
|
||||||
|
// The returned URL can be used to re-fetch the certificate using FetchCert.
|
||||||
|
//
|
||||||
|
// This method is only supported by CAs implementing RFC 8555. See CreateCert for pre-RFC CAs.
|
||||||
|
//
|
||||||
|
// CreateOrderCert returns an error if the CA's response is unreasonably large.
|
||||||
|
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
|
||||||
|
func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bundle bool) (der [][]byte, certURL string, err error) {
|
||||||
|
if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC describes this as "finalize order" request.
|
||||||
|
req := struct {
|
||||||
|
CSR string `json:"csr"`
|
||||||
|
}{
|
||||||
|
CSR: base64.RawURLEncoding.EncodeToString(csr),
|
||||||
|
}
|
||||||
|
res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
o, err := responseOrder(res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for CA to issue the cert if they haven't.
|
||||||
|
if o.Status != StatusValid {
|
||||||
|
o, err = c.WaitOrder(ctx, o.URI)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
// The only acceptable status post finalize and WaitOrder is "valid".
|
||||||
|
if o.Status != StatusValid {
|
||||||
|
return nil, "", &OrderError{OrderURL: o.URI, Status: o.Status}
|
||||||
|
}
|
||||||
|
crt, err := c.fetchCertRFC(ctx, o.CertURL, bundle)
|
||||||
|
return crt, o.CertURL, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchCertRFC downloads issued certificate from the given URL.
|
||||||
|
// It expects the CA to respond with PEM-encoded certificate chain.
|
||||||
|
//
|
||||||
|
// The URL argument is the CertURL field of Order.
|
||||||
|
func (c *Client) fetchCertRFC(ctx context.Context, url string, bundle bool) ([][]byte, error) {
|
||||||
|
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
// Get all the bytes up to a sane maximum.
|
||||||
|
// Account very roughly for base64 overhead.
|
||||||
|
const max = maxCertChainSize + maxCertChainSize/33
|
||||||
|
b, err := ioutil.ReadAll(io.LimitReader(res.Body, max+1))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("acme: fetch cert response stream: %v", err)
|
||||||
|
}
|
||||||
|
if len(b) > max {
|
||||||
|
return nil, errors.New("acme: certificate chain is too big")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode PEM chain.
|
||||||
|
var chain [][]byte
|
||||||
|
for {
|
||||||
|
var p *pem.Block
|
||||||
|
p, b = pem.Decode(b)
|
||||||
|
if p == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if p.Type != "CERTIFICATE" {
|
||||||
|
return nil, fmt.Errorf("acme: invalid PEM cert type %q", p.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
chain = append(chain, p.Bytes)
|
||||||
|
if !bundle {
|
||||||
|
return chain, nil
|
||||||
|
}
|
||||||
|
if len(chain) > maxChainLen {
|
||||||
|
return nil, errors.New("acme: certificate chain is too long")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(chain) == 0 {
|
||||||
|
return nil, errors.New("acme: certificate chain is empty")
|
||||||
|
}
|
||||||
|
return chain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sends a cert revocation request in either JWK form when key is non-nil or KID form otherwise.
|
||||||
|
func (c *Client) revokeCertRFC(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
|
||||||
|
req := &struct {
|
||||||
|
Cert string `json:"certificate"`
|
||||||
|
Reason int `json:"reason"`
|
||||||
|
}{
|
||||||
|
Cert: base64.RawURLEncoding.EncodeToString(cert),
|
||||||
|
Reason: int(reason),
|
||||||
|
}
|
||||||
|
res, err := c.post(ctx, key, c.dir.RevokeURL, req, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
if isAlreadyRevoked(err) {
|
||||||
|
// Assume it is not an error to revoke an already revoked cert.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isAlreadyRevoked(err error) bool {
|
||||||
|
e, ok := err.(*Error)
|
||||||
|
return ok && e.ProblemType == "urn:ietf:params:acme:error:alreadyRevoked"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListCertAlternates retrieves any alternate certificate chain URLs for the
|
||||||
|
// given certificate chain URL. These alternate URLs can be passed to FetchCert
|
||||||
|
// in order to retrieve the alternate certificate chains.
|
||||||
|
//
|
||||||
|
// If there are no alternate issuer certificate chains, a nil slice will be
|
||||||
|
// returned.
|
||||||
|
func (c *Client) ListCertAlternates(ctx context.Context, url string) ([]string, error) {
|
||||||
|
if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
// We don't need the body but we need to discard it so we don't end up
|
||||||
|
// preventing keep-alive
|
||||||
|
if _, err := io.Copy(ioutil.Discard, res.Body); err != nil {
|
||||||
|
return nil, fmt.Errorf("acme: cert alternates response stream: %v", err)
|
||||||
|
}
|
||||||
|
alts := linkHeader(res.Header, "alternate")
|
||||||
|
return alts, nil
|
||||||
|
}
|
|
@ -14,14 +14,18 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ACME server response statuses used to describe Authorization and Challenge states.
|
// ACME status values of Account, Order, Authorization and Challenge objects.
|
||||||
|
// See https://tools.ietf.org/html/rfc8555#section-7.1.6 for details.
|
||||||
const (
|
const (
|
||||||
StatusUnknown = "unknown"
|
StatusDeactivated = "deactivated"
|
||||||
StatusPending = "pending"
|
StatusExpired = "expired"
|
||||||
StatusProcessing = "processing"
|
StatusInvalid = "invalid"
|
||||||
StatusValid = "valid"
|
StatusPending = "pending"
|
||||||
StatusInvalid = "invalid"
|
StatusProcessing = "processing"
|
||||||
StatusRevoked = "revoked"
|
StatusReady = "ready"
|
||||||
|
StatusRevoked = "revoked"
|
||||||
|
StatusUnknown = "unknown"
|
||||||
|
StatusValid = "valid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CRLReasonCode identifies the reason for a certificate revocation.
|
// CRLReasonCode identifies the reason for a certificate revocation.
|
||||||
|
@ -41,8 +45,43 @@ const (
|
||||||
CRLReasonAACompromise CRLReasonCode = 10
|
CRLReasonAACompromise CRLReasonCode = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrUnsupportedKey is returned when an unsupported key type is encountered.
|
var (
|
||||||
var ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported")
|
// ErrUnsupportedKey is returned when an unsupported key type is encountered.
|
||||||
|
ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported")
|
||||||
|
|
||||||
|
// ErrAccountAlreadyExists indicates that the Client's key has already been registered
|
||||||
|
// with the CA. It is returned by Register method.
|
||||||
|
ErrAccountAlreadyExists = errors.New("acme: account already exists")
|
||||||
|
|
||||||
|
// ErrNoAccount indicates that the Client's key has not been registered with the CA.
|
||||||
|
ErrNoAccount = errors.New("acme: account does not exist")
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Subproblem describes an ACME subproblem as reported in an Error.
|
||||||
|
type Subproblem struct {
|
||||||
|
// Type is a URI reference that identifies the problem type,
|
||||||
|
// typically in a "urn:acme:error:xxx" form.
|
||||||
|
Type string
|
||||||
|
// Detail is a human-readable explanation specific to this occurrence of the problem.
|
||||||
|
Detail string
|
||||||
|
// Instance indicates a URL that the client should direct a human user to visit
|
||||||
|
// in order for instructions on how to agree to the updated Terms of Service.
|
||||||
|
// In such an event CA sets StatusCode to 403, Type to
|
||||||
|
// "urn:ietf:params:acme:error:userActionRequired", and adds a Link header with relation
|
||||||
|
// "terms-of-service" containing the latest TOS URL.
|
||||||
|
Instance string
|
||||||
|
// Identifier may contain the ACME identifier that the error is for.
|
||||||
|
Identifier *AuthzID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp Subproblem) String() string {
|
||||||
|
str := fmt.Sprintf("%s: ", sp.Type)
|
||||||
|
if sp.Identifier != nil {
|
||||||
|
str += fmt.Sprintf("[%s: %s] ", sp.Identifier.Type, sp.Identifier.Value)
|
||||||
|
}
|
||||||
|
str += sp.Detail
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
// Error is an ACME error, defined in Problem Details for HTTP APIs doc
|
// Error is an ACME error, defined in Problem Details for HTTP APIs doc
|
||||||
// http://tools.ietf.org/html/draft-ietf-appsawg-http-problem.
|
// http://tools.ietf.org/html/draft-ietf-appsawg-http-problem.
|
||||||
|
@ -54,13 +93,30 @@ type Error struct {
|
||||||
ProblemType string
|
ProblemType string
|
||||||
// Detail is a human-readable explanation specific to this occurrence of the problem.
|
// Detail is a human-readable explanation specific to this occurrence of the problem.
|
||||||
Detail string
|
Detail string
|
||||||
|
// Instance indicates a URL that the client should direct a human user to visit
|
||||||
|
// in order for instructions on how to agree to the updated Terms of Service.
|
||||||
|
// In such an event CA sets StatusCode to 403, ProblemType to
|
||||||
|
// "urn:ietf:params:acme:error:userActionRequired" and a Link header with relation
|
||||||
|
// "terms-of-service" containing the latest TOS URL.
|
||||||
|
Instance string
|
||||||
// Header is the original server error response headers.
|
// Header is the original server error response headers.
|
||||||
// It may be nil.
|
// It may be nil.
|
||||||
Header http.Header
|
Header http.Header
|
||||||
|
// Subproblems may contain more detailed information about the individual problems
|
||||||
|
// that caused the error. This field is only sent by RFC 8555 compatible ACME
|
||||||
|
// servers. Defined in RFC 8555 Section 6.7.1.
|
||||||
|
Subproblems []Subproblem
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Error) Error() string {
|
func (e *Error) Error() string {
|
||||||
return fmt.Sprintf("%d %s: %s", e.StatusCode, e.ProblemType, e.Detail)
|
str := fmt.Sprintf("%d %s: %s", e.StatusCode, e.ProblemType, e.Detail)
|
||||||
|
if len(e.Subproblems) > 0 {
|
||||||
|
str += fmt.Sprintf("; subproblems:")
|
||||||
|
for _, sp := range e.Subproblems {
|
||||||
|
str += fmt.Sprintf("\n\t%s", sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthorizationError indicates that an authorization for an identifier
|
// AuthorizationError indicates that an authorization for an identifier
|
||||||
|
@ -83,7 +139,27 @@ func (a *AuthorizationError) Error() string {
|
||||||
for i, err := range a.Errors {
|
for i, err := range a.Errors {
|
||||||
e[i] = err.Error()
|
e[i] = err.Error()
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("acme: authorization error for %s: %s", a.Identifier, strings.Join(e, "; "))
|
|
||||||
|
if a.Identifier != "" {
|
||||||
|
return fmt.Sprintf("acme: authorization error for %s: %s", a.Identifier, strings.Join(e, "; "))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("acme: authorization error: %s", strings.Join(e, "; "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrderError is returned from Client's order related methods.
|
||||||
|
// It indicates the order is unusable and the clients should start over with
|
||||||
|
// AuthorizeOrder.
|
||||||
|
//
|
||||||
|
// The clients can still fetch the order object from CA using GetOrder
|
||||||
|
// to inspect its state.
|
||||||
|
type OrderError struct {
|
||||||
|
OrderURL string
|
||||||
|
Status string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (oe *OrderError) Error() string {
|
||||||
|
return fmt.Sprintf("acme: order %s status: %s", oe.OrderURL, oe.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RateLimit reports whether err represents a rate limit error and
|
// RateLimit reports whether err represents a rate limit error and
|
||||||
|
@ -108,49 +184,110 @@ func RateLimit(err error) (time.Duration, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account is a user account. It is associated with a private key.
|
// Account is a user account. It is associated with a private key.
|
||||||
|
// Non-RFC 8555 fields are empty when interfacing with a compliant CA.
|
||||||
type Account struct {
|
type Account struct {
|
||||||
// URI is the account unique ID, which is also a URL used to retrieve
|
// URI is the account unique ID, which is also a URL used to retrieve
|
||||||
// account data from the CA.
|
// account data from the CA.
|
||||||
|
// When interfacing with RFC 8555-compliant CAs, URI is the "kid" field
|
||||||
|
// value in JWS signed requests.
|
||||||
URI string
|
URI string
|
||||||
|
|
||||||
// Contact is a slice of contact info used during registration.
|
// Contact is a slice of contact info used during registration.
|
||||||
|
// See https://tools.ietf.org/html/rfc8555#section-7.3 for supported
|
||||||
|
// formats.
|
||||||
Contact []string
|
Contact []string
|
||||||
|
|
||||||
|
// Status indicates current account status as returned by the CA.
|
||||||
|
// Possible values are StatusValid, StatusDeactivated, and StatusRevoked.
|
||||||
|
Status string
|
||||||
|
|
||||||
|
// OrdersURL is a URL from which a list of orders submitted by this account
|
||||||
|
// can be fetched.
|
||||||
|
OrdersURL string
|
||||||
|
|
||||||
// The terms user has agreed to.
|
// The terms user has agreed to.
|
||||||
// A value not matching CurrentTerms indicates that the user hasn't agreed
|
// A value not matching CurrentTerms indicates that the user hasn't agreed
|
||||||
// to the actual Terms of Service of the CA.
|
// to the actual Terms of Service of the CA.
|
||||||
|
//
|
||||||
|
// It is non-RFC 8555 compliant. Package users can store the ToS they agree to
|
||||||
|
// during Client's Register call in the prompt callback function.
|
||||||
AgreedTerms string
|
AgreedTerms string
|
||||||
|
|
||||||
// Actual terms of a CA.
|
// Actual terms of a CA.
|
||||||
|
//
|
||||||
|
// It is non-RFC 8555 compliant. Use Directory's Terms field.
|
||||||
|
// When a CA updates their terms and requires an account agreement,
|
||||||
|
// a URL at which instructions to do so is available in Error's Instance field.
|
||||||
CurrentTerms string
|
CurrentTerms string
|
||||||
|
|
||||||
// Authz is the authorization URL used to initiate a new authz flow.
|
// Authz is the authorization URL used to initiate a new authz flow.
|
||||||
|
//
|
||||||
|
// It is non-RFC 8555 compliant. Use Directory's AuthzURL or OrderURL.
|
||||||
Authz string
|
Authz string
|
||||||
|
|
||||||
// Authorizations is a URI from which a list of authorizations
|
// Authorizations is a URI from which a list of authorizations
|
||||||
// granted to this account can be fetched via a GET request.
|
// granted to this account can be fetched via a GET request.
|
||||||
|
//
|
||||||
|
// It is non-RFC 8555 compliant and is obsoleted by OrdersURL.
|
||||||
Authorizations string
|
Authorizations string
|
||||||
|
|
||||||
// Certificates is a URI from which a list of certificates
|
// Certificates is a URI from which a list of certificates
|
||||||
// issued for this account can be fetched via a GET request.
|
// issued for this account can be fetched via a GET request.
|
||||||
|
//
|
||||||
|
// It is non-RFC 8555 compliant and is obsoleted by OrdersURL.
|
||||||
Certificates string
|
Certificates string
|
||||||
|
|
||||||
|
// ExternalAccountBinding represents an arbitrary binding to an account of
|
||||||
|
// the CA which the ACME server is tied to.
|
||||||
|
// See https://tools.ietf.org/html/rfc8555#section-7.3.4 for more details.
|
||||||
|
ExternalAccountBinding *ExternalAccountBinding
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalAccountBinding contains the data needed to form a request with
|
||||||
|
// an external account binding.
|
||||||
|
// See https://tools.ietf.org/html/rfc8555#section-7.3.4 for more details.
|
||||||
|
type ExternalAccountBinding struct {
|
||||||
|
// KID is the Key ID of the symmetric MAC key that the CA provides to
|
||||||
|
// identify an external account from ACME.
|
||||||
|
KID string
|
||||||
|
|
||||||
|
// Key is the bytes of the symmetric key that the CA provides to identify
|
||||||
|
// the account. Key must correspond to the KID.
|
||||||
|
Key []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ExternalAccountBinding) String() string {
|
||||||
|
return fmt.Sprintf("&{KID: %q, Key: redacted}", e.KID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Directory is ACME server discovery data.
|
// Directory is ACME server discovery data.
|
||||||
|
// See https://tools.ietf.org/html/rfc8555#section-7.1.1 for more details.
|
||||||
type Directory struct {
|
type Directory struct {
|
||||||
// RegURL is an account endpoint URL, allowing for creating new
|
// NonceURL indicates an endpoint where to fetch fresh nonce values from.
|
||||||
// and modifying existing accounts.
|
NonceURL string
|
||||||
|
|
||||||
|
// RegURL is an account endpoint URL, allowing for creating new accounts.
|
||||||
|
// Pre-RFC 8555 CAs also allow modifying existing accounts at this URL.
|
||||||
RegURL string
|
RegURL string
|
||||||
|
|
||||||
// AuthzURL is used to initiate Identifier Authorization flow.
|
// OrderURL is used to initiate the certificate issuance flow
|
||||||
|
// as described in RFC 8555.
|
||||||
|
OrderURL string
|
||||||
|
|
||||||
|
// AuthzURL is used to initiate identifier pre-authorization flow.
|
||||||
|
// Empty string indicates the flow is unsupported by the CA.
|
||||||
AuthzURL string
|
AuthzURL string
|
||||||
|
|
||||||
// CertURL is a new certificate issuance endpoint URL.
|
// CertURL is a new certificate issuance endpoint URL.
|
||||||
|
// It is non-RFC 8555 compliant and is obsoleted by OrderURL.
|
||||||
CertURL string
|
CertURL string
|
||||||
|
|
||||||
// RevokeURL is used to initiate a certificate revocation flow.
|
// RevokeURL is used to initiate a certificate revocation flow.
|
||||||
RevokeURL string
|
RevokeURL string
|
||||||
|
|
||||||
|
// KeyChangeURL allows to perform account key rollover flow.
|
||||||
|
KeyChangeURL string
|
||||||
|
|
||||||
// Term is a URI identifying the current terms of service.
|
// Term is a URI identifying the current terms of service.
|
||||||
Terms string
|
Terms string
|
||||||
|
|
||||||
|
@ -162,44 +299,126 @@ type Directory struct {
|
||||||
// recognises as referring to itself for the purposes of CAA record validation
|
// recognises as referring to itself for the purposes of CAA record validation
|
||||||
// as defined in RFC6844.
|
// as defined in RFC6844.
|
||||||
CAA []string
|
CAA []string
|
||||||
|
|
||||||
|
// ExternalAccountRequired indicates that the CA requires for all account-related
|
||||||
|
// requests to include external account binding information.
|
||||||
|
ExternalAccountRequired bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Challenge encodes a returned CA challenge.
|
// rfcCompliant reports whether the ACME server implements RFC 8555.
|
||||||
// Its Error field may be non-nil if the challenge is part of an Authorization
|
// Note that some servers may have incomplete RFC implementation
|
||||||
// with StatusInvalid.
|
// even if the returned value is true.
|
||||||
type Challenge struct {
|
// If rfcCompliant reports false, the server most likely implements draft-02.
|
||||||
// Type is the challenge type, e.g. "http-01", "tls-sni-02", "dns-01".
|
func (d *Directory) rfcCompliant() bool {
|
||||||
Type string
|
return d.OrderURL != ""
|
||||||
|
}
|
||||||
|
|
||||||
// URI is where a challenge response can be posted to.
|
// Order represents a client's request for a certificate.
|
||||||
|
// It tracks the request flow progress through to issuance.
|
||||||
|
type Order struct {
|
||||||
|
// URI uniquely identifies an order.
|
||||||
URI string
|
URI string
|
||||||
|
|
||||||
// Token is a random value that uniquely identifies the challenge.
|
// Status represents the current status of the order.
|
||||||
Token string
|
// It indicates which action the client should take.
|
||||||
|
//
|
||||||
// Status identifies the status of this challenge.
|
// Possible values are StatusPending, StatusReady, StatusProcessing, StatusValid and StatusInvalid.
|
||||||
|
// Pending means the CA does not believe that the client has fulfilled the requirements.
|
||||||
|
// Ready indicates that the client has fulfilled all the requirements and can submit a CSR
|
||||||
|
// to obtain a certificate. This is done with Client's CreateOrderCert.
|
||||||
|
// Processing means the certificate is being issued.
|
||||||
|
// Valid indicates the CA has issued the certificate. It can be downloaded
|
||||||
|
// from the Order's CertURL. This is done with Client's FetchCert.
|
||||||
|
// Invalid means the certificate will not be issued. Users should consider this order
|
||||||
|
// abandoned.
|
||||||
Status string
|
Status string
|
||||||
|
|
||||||
// Error indicates the reason for an authorization failure
|
// Expires is the timestamp after which CA considers this order invalid.
|
||||||
// when this challenge was used.
|
Expires time.Time
|
||||||
// The type of a non-nil value is *Error.
|
|
||||||
Error error
|
// Identifiers contains all identifier objects which the order pertains to.
|
||||||
|
Identifiers []AuthzID
|
||||||
|
|
||||||
|
// NotBefore is the requested value of the notBefore field in the certificate.
|
||||||
|
NotBefore time.Time
|
||||||
|
|
||||||
|
// NotAfter is the requested value of the notAfter field in the certificate.
|
||||||
|
NotAfter time.Time
|
||||||
|
|
||||||
|
// AuthzURLs represents authorizations to complete before a certificate
|
||||||
|
// for identifiers specified in the order can be issued.
|
||||||
|
// It also contains unexpired authorizations that the client has completed
|
||||||
|
// in the past.
|
||||||
|
//
|
||||||
|
// Authorization objects can be fetched using Client's GetAuthorization method.
|
||||||
|
//
|
||||||
|
// The required authorizations are dictated by CA policies.
|
||||||
|
// There may not be a 1:1 relationship between the identifiers and required authorizations.
|
||||||
|
// Required authorizations can be identified by their StatusPending status.
|
||||||
|
//
|
||||||
|
// For orders in the StatusValid or StatusInvalid state these are the authorizations
|
||||||
|
// which were completed.
|
||||||
|
AuthzURLs []string
|
||||||
|
|
||||||
|
// FinalizeURL is the endpoint at which a CSR is submitted to obtain a certificate
|
||||||
|
// once all the authorizations are satisfied.
|
||||||
|
FinalizeURL string
|
||||||
|
|
||||||
|
// CertURL points to the certificate that has been issued in response to this order.
|
||||||
|
CertURL string
|
||||||
|
|
||||||
|
// The error that occurred while processing the order as received from a CA, if any.
|
||||||
|
Error *Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OrderOption allows customizing Client.AuthorizeOrder call.
|
||||||
|
type OrderOption interface {
|
||||||
|
privateOrderOpt()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithOrderNotBefore sets order's NotBefore field.
|
||||||
|
func WithOrderNotBefore(t time.Time) OrderOption {
|
||||||
|
return orderNotBeforeOpt(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithOrderNotAfter sets order's NotAfter field.
|
||||||
|
func WithOrderNotAfter(t time.Time) OrderOption {
|
||||||
|
return orderNotAfterOpt(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
type orderNotBeforeOpt time.Time
|
||||||
|
|
||||||
|
func (orderNotBeforeOpt) privateOrderOpt() {}
|
||||||
|
|
||||||
|
type orderNotAfterOpt time.Time
|
||||||
|
|
||||||
|
func (orderNotAfterOpt) privateOrderOpt() {}
|
||||||
|
|
||||||
// Authorization encodes an authorization response.
|
// Authorization encodes an authorization response.
|
||||||
type Authorization struct {
|
type Authorization struct {
|
||||||
// URI uniquely identifies a authorization.
|
// URI uniquely identifies a authorization.
|
||||||
URI string
|
URI string
|
||||||
|
|
||||||
// Status identifies the status of an authorization.
|
// Status is the current status of an authorization.
|
||||||
|
// Possible values are StatusPending, StatusValid, StatusInvalid, StatusDeactivated,
|
||||||
|
// StatusExpired and StatusRevoked.
|
||||||
Status string
|
Status string
|
||||||
|
|
||||||
// Identifier is what the account is authorized to represent.
|
// Identifier is what the account is authorized to represent.
|
||||||
Identifier AuthzID
|
Identifier AuthzID
|
||||||
|
|
||||||
|
// The timestamp after which the CA considers the authorization invalid.
|
||||||
|
Expires time.Time
|
||||||
|
|
||||||
|
// Wildcard is true for authorizations of a wildcard domain name.
|
||||||
|
Wildcard bool
|
||||||
|
|
||||||
// Challenges that the client needs to fulfill in order to prove possession
|
// Challenges that the client needs to fulfill in order to prove possession
|
||||||
// of the identifier (for pending authorizations).
|
// of the identifier (for pending authorizations).
|
||||||
// For final authorizations, the challenges that were used.
|
// For valid authorizations, the challenge that was validated.
|
||||||
|
// For invalid authorizations, the challenge that was attempted and failed.
|
||||||
|
//
|
||||||
|
// RFC 8555 compatible CAs require users to fuflfill only one of the challenges.
|
||||||
Challenges []*Challenge
|
Challenges []*Challenge
|
||||||
|
|
||||||
// A collection of sets of challenges, each of which would be sufficient
|
// A collection of sets of challenges, each of which would be sufficient
|
||||||
|
@ -207,24 +426,52 @@ type Authorization struct {
|
||||||
// Clients must complete a set of challenges that covers at least one set.
|
// Clients must complete a set of challenges that covers at least one set.
|
||||||
// Challenges are identified by their indices in the challenges array.
|
// Challenges are identified by their indices in the challenges array.
|
||||||
// If this field is empty, the client needs to complete all challenges.
|
// If this field is empty, the client needs to complete all challenges.
|
||||||
|
//
|
||||||
|
// This field is unused in RFC 8555.
|
||||||
Combinations [][]int
|
Combinations [][]int
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthzID is an identifier that an account is authorized to represent.
|
// AuthzID is an identifier that an account is authorized to represent.
|
||||||
type AuthzID struct {
|
type AuthzID struct {
|
||||||
Type string // The type of identifier, e.g. "dns".
|
Type string // The type of identifier, "dns" or "ip".
|
||||||
Value string // The identifier itself, e.g. "example.org".
|
Value string // The identifier itself, e.g. "example.org".
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DomainIDs creates a slice of AuthzID with "dns" identifier type.
|
||||||
|
func DomainIDs(names ...string) []AuthzID {
|
||||||
|
a := make([]AuthzID, len(names))
|
||||||
|
for i, v := range names {
|
||||||
|
a[i] = AuthzID{Type: "dns", Value: v}
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPIDs creates a slice of AuthzID with "ip" identifier type.
|
||||||
|
// Each element of addr is textual form of an address as defined
|
||||||
|
// in RFC1123 Section 2.1 for IPv4 and in RFC5952 Section 4 for IPv6.
|
||||||
|
func IPIDs(addr ...string) []AuthzID {
|
||||||
|
a := make([]AuthzID, len(addr))
|
||||||
|
for i, v := range addr {
|
||||||
|
a[i] = AuthzID{Type: "ip", Value: v}
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// wireAuthzID is ACME JSON representation of authorization identifier objects.
|
||||||
|
type wireAuthzID struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
// wireAuthz is ACME JSON representation of Authorization objects.
|
// wireAuthz is ACME JSON representation of Authorization objects.
|
||||||
type wireAuthz struct {
|
type wireAuthz struct {
|
||||||
|
Identifier wireAuthzID
|
||||||
Status string
|
Status string
|
||||||
|
Expires time.Time
|
||||||
|
Wildcard bool
|
||||||
Challenges []wireChallenge
|
Challenges []wireChallenge
|
||||||
Combinations [][]int
|
Combinations [][]int
|
||||||
Identifier struct {
|
Error *wireError
|
||||||
Type string
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (z *wireAuthz) authorization(uri string) *Authorization {
|
func (z *wireAuthz) authorization(uri string) *Authorization {
|
||||||
|
@ -232,8 +479,10 @@ func (z *wireAuthz) authorization(uri string) *Authorization {
|
||||||
URI: uri,
|
URI: uri,
|
||||||
Status: z.Status,
|
Status: z.Status,
|
||||||
Identifier: AuthzID{Type: z.Identifier.Type, Value: z.Identifier.Value},
|
Identifier: AuthzID{Type: z.Identifier.Type, Value: z.Identifier.Value},
|
||||||
Combinations: z.Combinations, // shallow copy
|
Expires: z.Expires,
|
||||||
|
Wildcard: z.Wildcard,
|
||||||
Challenges: make([]*Challenge, len(z.Challenges)),
|
Challenges: make([]*Challenge, len(z.Challenges)),
|
||||||
|
Combinations: z.Combinations, // shallow copy
|
||||||
}
|
}
|
||||||
for i, v := range z.Challenges {
|
for i, v := range z.Challenges {
|
||||||
a.Challenges[i] = v.challenge()
|
a.Challenges[i] = v.challenge()
|
||||||
|
@ -246,30 +495,69 @@ func (z *wireAuthz) error(uri string) *AuthorizationError {
|
||||||
URI: uri,
|
URI: uri,
|
||||||
Identifier: z.Identifier.Value,
|
Identifier: z.Identifier.Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if z.Error != nil {
|
||||||
|
err.Errors = append(err.Errors, z.Error.error(nil))
|
||||||
|
}
|
||||||
|
|
||||||
for _, raw := range z.Challenges {
|
for _, raw := range z.Challenges {
|
||||||
if raw.Error != nil {
|
if raw.Error != nil {
|
||||||
err.Errors = append(err.Errors, raw.Error.error(nil))
|
err.Errors = append(err.Errors, raw.Error.error(nil))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Challenge encodes a returned CA challenge.
|
||||||
|
// Its Error field may be non-nil if the challenge is part of an Authorization
|
||||||
|
// with StatusInvalid.
|
||||||
|
type Challenge struct {
|
||||||
|
// Type is the challenge type, e.g. "http-01", "tls-alpn-01", "dns-01".
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// URI is where a challenge response can be posted to.
|
||||||
|
URI string
|
||||||
|
|
||||||
|
// Token is a random value that uniquely identifies the challenge.
|
||||||
|
Token string
|
||||||
|
|
||||||
|
// Status identifies the status of this challenge.
|
||||||
|
// In RFC 8555, possible values are StatusPending, StatusProcessing, StatusValid,
|
||||||
|
// and StatusInvalid.
|
||||||
|
Status string
|
||||||
|
|
||||||
|
// Validated is the time at which the CA validated this challenge.
|
||||||
|
// Always zero value in pre-RFC 8555.
|
||||||
|
Validated time.Time
|
||||||
|
|
||||||
|
// Error indicates the reason for an authorization failure
|
||||||
|
// when this challenge was used.
|
||||||
|
// The type of a non-nil value is *Error.
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
// wireChallenge is ACME JSON challenge representation.
|
// wireChallenge is ACME JSON challenge representation.
|
||||||
type wireChallenge struct {
|
type wireChallenge struct {
|
||||||
URI string `json:"uri"`
|
URL string `json:"url"` // RFC
|
||||||
Type string
|
URI string `json:"uri"` // pre-RFC
|
||||||
Token string
|
Type string
|
||||||
Status string
|
Token string
|
||||||
Error *wireError
|
Status string
|
||||||
|
Validated time.Time
|
||||||
|
Error *wireError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *wireChallenge) challenge() *Challenge {
|
func (c *wireChallenge) challenge() *Challenge {
|
||||||
v := &Challenge{
|
v := &Challenge{
|
||||||
URI: c.URI,
|
URI: c.URL,
|
||||||
Type: c.Type,
|
Type: c.Type,
|
||||||
Token: c.Token,
|
Token: c.Token,
|
||||||
Status: c.Status,
|
Status: c.Status,
|
||||||
}
|
}
|
||||||
|
if v.URI == "" {
|
||||||
|
v.URI = c.URI // c.URL was empty; use legacy
|
||||||
|
}
|
||||||
if v.Status == "" {
|
if v.Status == "" {
|
||||||
v.Status = StatusPending
|
v.Status = StatusPending
|
||||||
}
|
}
|
||||||
|
@ -282,18 +570,23 @@ func (c *wireChallenge) challenge() *Challenge {
|
||||||
// wireError is a subset of fields of the Problem Details object
|
// wireError is a subset of fields of the Problem Details object
|
||||||
// as described in https://tools.ietf.org/html/rfc7807#section-3.1.
|
// as described in https://tools.ietf.org/html/rfc7807#section-3.1.
|
||||||
type wireError struct {
|
type wireError struct {
|
||||||
Status int
|
Status int
|
||||||
Type string
|
Type string
|
||||||
Detail string
|
Detail string
|
||||||
|
Instance string
|
||||||
|
Subproblems []Subproblem
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *wireError) error(h http.Header) *Error {
|
func (e *wireError) error(h http.Header) *Error {
|
||||||
return &Error{
|
err := &Error{
|
||||||
StatusCode: e.Status,
|
StatusCode: e.Status,
|
||||||
ProblemType: e.Type,
|
ProblemType: e.Type,
|
||||||
Detail: e.Detail,
|
Detail: e.Detail,
|
||||||
|
Instance: e.Instance,
|
||||||
Header: h,
|
Header: h,
|
||||||
|
Subproblems: e.Subproblems,
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CertOption is an optional argument type for the TLS ChallengeCert methods for
|
// CertOption is an optional argument type for the TLS ChallengeCert methods for
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright 2019 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.12
|
||||||
|
// +build go1.12
|
||||||
|
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import "runtime/debug"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Set packageVersion if the binary was built in modules mode and x/crypto
|
||||||
|
// was not replaced with a different module.
|
||||||
|
info, ok := debug.ReadBuildInfo()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, m := range info.Deps {
|
||||||
|
if m.Path != "golang.org/x/crypto" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if m.Replace == nil {
|
||||||
|
packageVersion = m.Version
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +0,0 @@
|
||||||
# This source code refers to The Go Authors for copyright purposes.
|
|
||||||
# The master list of authors is in the main Go distribution,
|
|
||||||
# visible at http://tip.golang.org/AUTHORS.
|
|
|
@ -1,3 +0,0 @@
|
||||||
# This source code was written by the Go contributors.
|
|
||||||
# The master list of contributors is in the main Go distribution,
|
|
||||||
# visible at http://tip.golang.org/CONTRIBUTORS.
|
|
|
@ -1,56 +0,0 @@
|
||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
// Package context defines the Context type, which carries deadlines,
|
|
||||||
// cancelation signals, and other request-scoped values across API boundaries
|
|
||||||
// and between processes.
|
|
||||||
// As of Go 1.7 this package is available in the standard library under the
|
|
||||||
// name context. https://golang.org/pkg/context.
|
|
||||||
//
|
|
||||||
// Incoming requests to a server should create a Context, and outgoing calls to
|
|
||||||
// servers should accept a Context. The chain of function calls between must
|
|
||||||
// propagate the Context, optionally replacing it with a modified copy created
|
|
||||||
// using WithDeadline, WithTimeout, WithCancel, or WithValue.
|
|
||||||
//
|
|
||||||
// Programs that use Contexts should follow these rules to keep interfaces
|
|
||||||
// consistent across packages and enable static analysis tools to check context
|
|
||||||
// propagation:
|
|
||||||
//
|
|
||||||
// Do not store Contexts inside a struct type; instead, pass a Context
|
|
||||||
// explicitly to each function that needs it. The Context should be the first
|
|
||||||
// parameter, typically named ctx:
|
|
||||||
//
|
|
||||||
// func DoSomething(ctx context.Context, arg Arg) error {
|
|
||||||
// // ... use ctx ...
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Do not pass a nil Context, even if a function permits it. Pass context.TODO
|
|
||||||
// if you are unsure about which Context to use.
|
|
||||||
//
|
|
||||||
// Use context Values only for request-scoped data that transits processes and
|
|
||||||
// APIs, not for passing optional parameters to functions.
|
|
||||||
//
|
|
||||||
// The same Context may be passed to functions running in different goroutines;
|
|
||||||
// Contexts are safe for simultaneous use by multiple goroutines.
|
|
||||||
//
|
|
||||||
// See http://blog.golang.org/context for example code for a server that uses
|
|
||||||
// Contexts.
|
|
||||||
package context // import "golang.org/x/net/context"
|
|
||||||
|
|
||||||
// Background returns a non-nil, empty Context. It is never canceled, has no
|
|
||||||
// values, and has no deadline. It is typically used by the main function,
|
|
||||||
// initialization, and tests, and as the top-level Context for incoming
|
|
||||||
// requests.
|
|
||||||
func Background() Context {
|
|
||||||
return background
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO returns a non-nil, empty Context. Code should use context.TODO when
|
|
||||||
// it's unclear which Context to use or it is not yet available (because the
|
|
||||||
// surrounding function has not yet been extended to accept a Context
|
|
||||||
// parameter). TODO is recognized by static analysis tools that determine
|
|
||||||
// whether Contexts are propagated correctly in a program.
|
|
||||||
func TODO() Context {
|
|
||||||
return todo
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
// Copyright 2016 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.
|
|
||||||
|
|
||||||
// +build go1.7
|
|
||||||
|
|
||||||
package context
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context" // standard library's context, as of Go 1.7
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
todo = context.TODO()
|
|
||||||
background = context.Background()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Canceled is the error returned by Context.Err when the context is canceled.
|
|
||||||
var Canceled = context.Canceled
|
|
||||||
|
|
||||||
// DeadlineExceeded is the error returned by Context.Err when the context's
|
|
||||||
// deadline passes.
|
|
||||||
var DeadlineExceeded = context.DeadlineExceeded
|
|
||||||
|
|
||||||
// WithCancel returns a copy of parent with a new Done channel. The returned
|
|
||||||
// context's Done channel is closed when the returned cancel function is called
|
|
||||||
// or when the parent context's Done channel is closed, whichever happens first.
|
|
||||||
//
|
|
||||||
// Canceling this context releases resources associated with it, so code should
|
|
||||||
// call cancel as soon as the operations running in this Context complete.
|
|
||||||
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
|
|
||||||
ctx, f := context.WithCancel(parent)
|
|
||||||
return ctx, CancelFunc(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithDeadline returns a copy of the parent context with the deadline adjusted
|
|
||||||
// to be no later than d. If the parent's deadline is already earlier than d,
|
|
||||||
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
|
|
||||||
// context's Done channel is closed when the deadline expires, when the returned
|
|
||||||
// cancel function is called, or when the parent context's Done channel is
|
|
||||||
// closed, whichever happens first.
|
|
||||||
//
|
|
||||||
// Canceling this context releases resources associated with it, so code should
|
|
||||||
// call cancel as soon as the operations running in this Context complete.
|
|
||||||
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
|
|
||||||
ctx, f := context.WithDeadline(parent, deadline)
|
|
||||||
return ctx, CancelFunc(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
|
|
||||||
//
|
|
||||||
// Canceling this context releases resources associated with it, so code should
|
|
||||||
// call cancel as soon as the operations running in this Context complete:
|
|
||||||
//
|
|
||||||
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
|
|
||||||
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
|
|
||||||
// defer cancel() // releases resources if slowOperation completes before timeout elapses
|
|
||||||
// return slowOperation(ctx)
|
|
||||||
// }
|
|
||||||
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
|
|
||||||
return WithDeadline(parent, time.Now().Add(timeout))
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithValue returns a copy of parent in which the value associated with key is
|
|
||||||
// val.
|
|
||||||
//
|
|
||||||
// Use context Values only for request-scoped data that transits processes and
|
|
||||||
// APIs, not for passing optional parameters to functions.
|
|
||||||
func WithValue(parent Context, key interface{}, val interface{}) Context {
|
|
||||||
return context.WithValue(parent, key, val)
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
// Copyright 2017 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.
|
|
||||||
|
|
||||||
// +build go1.9
|
|
||||||
|
|
||||||
package context
|
|
||||||
|
|
||||||
import "context" // standard library's context, as of Go 1.7
|
|
||||||
|
|
||||||
// A Context carries a deadline, a cancelation signal, and other values across
|
|
||||||
// API boundaries.
|
|
||||||
//
|
|
||||||
// Context's methods may be called by multiple goroutines simultaneously.
|
|
||||||
type Context = context.Context
|
|
||||||
|
|
||||||
// A CancelFunc tells an operation to abandon its work.
|
|
||||||
// A CancelFunc does not wait for the work to stop.
|
|
||||||
// After the first call, subsequent calls to a CancelFunc do nothing.
|
|
||||||
type CancelFunc = context.CancelFunc
|
|
|
@ -1,300 +0,0 @@
|
||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
// +build !go1.7
|
|
||||||
|
|
||||||
package context
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
|
|
||||||
// struct{}, since vars of this type must have distinct addresses.
|
|
||||||
type emptyCtx int
|
|
||||||
|
|
||||||
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*emptyCtx) Done() <-chan struct{} {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*emptyCtx) Err() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*emptyCtx) Value(key interface{}) interface{} {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *emptyCtx) String() string {
|
|
||||||
switch e {
|
|
||||||
case background:
|
|
||||||
return "context.Background"
|
|
||||||
case todo:
|
|
||||||
return "context.TODO"
|
|
||||||
}
|
|
||||||
return "unknown empty Context"
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
background = new(emptyCtx)
|
|
||||||
todo = new(emptyCtx)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Canceled is the error returned by Context.Err when the context is canceled.
|
|
||||||
var Canceled = errors.New("context canceled")
|
|
||||||
|
|
||||||
// DeadlineExceeded is the error returned by Context.Err when the context's
|
|
||||||
// deadline passes.
|
|
||||||
var DeadlineExceeded = errors.New("context deadline exceeded")
|
|
||||||
|
|
||||||
// WithCancel returns a copy of parent with a new Done channel. The returned
|
|
||||||
// context's Done channel is closed when the returned cancel function is called
|
|
||||||
// or when the parent context's Done channel is closed, whichever happens first.
|
|
||||||
//
|
|
||||||
// Canceling this context releases resources associated with it, so code should
|
|
||||||
// call cancel as soon as the operations running in this Context complete.
|
|
||||||
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
|
|
||||||
c := newCancelCtx(parent)
|
|
||||||
propagateCancel(parent, c)
|
|
||||||
return c, func() { c.cancel(true, Canceled) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// newCancelCtx returns an initialized cancelCtx.
|
|
||||||
func newCancelCtx(parent Context) *cancelCtx {
|
|
||||||
return &cancelCtx{
|
|
||||||
Context: parent,
|
|
||||||
done: make(chan struct{}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// propagateCancel arranges for child to be canceled when parent is.
|
|
||||||
func propagateCancel(parent Context, child canceler) {
|
|
||||||
if parent.Done() == nil {
|
|
||||||
return // parent is never canceled
|
|
||||||
}
|
|
||||||
if p, ok := parentCancelCtx(parent); ok {
|
|
||||||
p.mu.Lock()
|
|
||||||
if p.err != nil {
|
|
||||||
// parent has already been canceled
|
|
||||||
child.cancel(false, p.err)
|
|
||||||
} else {
|
|
||||||
if p.children == nil {
|
|
||||||
p.children = make(map[canceler]bool)
|
|
||||||
}
|
|
||||||
p.children[child] = true
|
|
||||||
}
|
|
||||||
p.mu.Unlock()
|
|
||||||
} else {
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case <-parent.Done():
|
|
||||||
child.cancel(false, parent.Err())
|
|
||||||
case <-child.Done():
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parentCancelCtx follows a chain of parent references until it finds a
|
|
||||||
// *cancelCtx. This function understands how each of the concrete types in this
|
|
||||||
// package represents its parent.
|
|
||||||
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
|
|
||||||
for {
|
|
||||||
switch c := parent.(type) {
|
|
||||||
case *cancelCtx:
|
|
||||||
return c, true
|
|
||||||
case *timerCtx:
|
|
||||||
return c.cancelCtx, true
|
|
||||||
case *valueCtx:
|
|
||||||
parent = c.Context
|
|
||||||
default:
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// removeChild removes a context from its parent.
|
|
||||||
func removeChild(parent Context, child canceler) {
|
|
||||||
p, ok := parentCancelCtx(parent)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.mu.Lock()
|
|
||||||
if p.children != nil {
|
|
||||||
delete(p.children, child)
|
|
||||||
}
|
|
||||||
p.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// A canceler is a context type that can be canceled directly. The
|
|
||||||
// implementations are *cancelCtx and *timerCtx.
|
|
||||||
type canceler interface {
|
|
||||||
cancel(removeFromParent bool, err error)
|
|
||||||
Done() <-chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A cancelCtx can be canceled. When canceled, it also cancels any children
|
|
||||||
// that implement canceler.
|
|
||||||
type cancelCtx struct {
|
|
||||||
Context
|
|
||||||
|
|
||||||
done chan struct{} // closed by the first cancel call.
|
|
||||||
|
|
||||||
mu sync.Mutex
|
|
||||||
children map[canceler]bool // set to nil by the first cancel call
|
|
||||||
err error // set to non-nil by the first cancel call
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cancelCtx) Done() <-chan struct{} {
|
|
||||||
return c.done
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cancelCtx) Err() error {
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
return c.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cancelCtx) String() string {
|
|
||||||
return fmt.Sprintf("%v.WithCancel", c.Context)
|
|
||||||
}
|
|
||||||
|
|
||||||
// cancel closes c.done, cancels each of c's children, and, if
|
|
||||||
// removeFromParent is true, removes c from its parent's children.
|
|
||||||
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
|
|
||||||
if err == nil {
|
|
||||||
panic("context: internal error: missing cancel error")
|
|
||||||
}
|
|
||||||
c.mu.Lock()
|
|
||||||
if c.err != nil {
|
|
||||||
c.mu.Unlock()
|
|
||||||
return // already canceled
|
|
||||||
}
|
|
||||||
c.err = err
|
|
||||||
close(c.done)
|
|
||||||
for child := range c.children {
|
|
||||||
// NOTE: acquiring the child's lock while holding parent's lock.
|
|
||||||
child.cancel(false, err)
|
|
||||||
}
|
|
||||||
c.children = nil
|
|
||||||
c.mu.Unlock()
|
|
||||||
|
|
||||||
if removeFromParent {
|
|
||||||
removeChild(c.Context, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithDeadline returns a copy of the parent context with the deadline adjusted
|
|
||||||
// to be no later than d. If the parent's deadline is already earlier than d,
|
|
||||||
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
|
|
||||||
// context's Done channel is closed when the deadline expires, when the returned
|
|
||||||
// cancel function is called, or when the parent context's Done channel is
|
|
||||||
// closed, whichever happens first.
|
|
||||||
//
|
|
||||||
// Canceling this context releases resources associated with it, so code should
|
|
||||||
// call cancel as soon as the operations running in this Context complete.
|
|
||||||
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
|
|
||||||
if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
|
|
||||||
// The current deadline is already sooner than the new one.
|
|
||||||
return WithCancel(parent)
|
|
||||||
}
|
|
||||||
c := &timerCtx{
|
|
||||||
cancelCtx: newCancelCtx(parent),
|
|
||||||
deadline: deadline,
|
|
||||||
}
|
|
||||||
propagateCancel(parent, c)
|
|
||||||
d := deadline.Sub(time.Now())
|
|
||||||
if d <= 0 {
|
|
||||||
c.cancel(true, DeadlineExceeded) // deadline has already passed
|
|
||||||
return c, func() { c.cancel(true, Canceled) }
|
|
||||||
}
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
if c.err == nil {
|
|
||||||
c.timer = time.AfterFunc(d, func() {
|
|
||||||
c.cancel(true, DeadlineExceeded)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return c, func() { c.cancel(true, Canceled) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
|
|
||||||
// implement Done and Err. It implements cancel by stopping its timer then
|
|
||||||
// delegating to cancelCtx.cancel.
|
|
||||||
type timerCtx struct {
|
|
||||||
*cancelCtx
|
|
||||||
timer *time.Timer // Under cancelCtx.mu.
|
|
||||||
|
|
||||||
deadline time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
|
|
||||||
return c.deadline, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *timerCtx) String() string {
|
|
||||||
return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *timerCtx) cancel(removeFromParent bool, err error) {
|
|
||||||
c.cancelCtx.cancel(false, err)
|
|
||||||
if removeFromParent {
|
|
||||||
// Remove this timerCtx from its parent cancelCtx's children.
|
|
||||||
removeChild(c.cancelCtx.Context, c)
|
|
||||||
}
|
|
||||||
c.mu.Lock()
|
|
||||||
if c.timer != nil {
|
|
||||||
c.timer.Stop()
|
|
||||||
c.timer = nil
|
|
||||||
}
|
|
||||||
c.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
|
|
||||||
//
|
|
||||||
// Canceling this context releases resources associated with it, so code should
|
|
||||||
// call cancel as soon as the operations running in this Context complete:
|
|
||||||
//
|
|
||||||
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
|
|
||||||
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
|
|
||||||
// defer cancel() // releases resources if slowOperation completes before timeout elapses
|
|
||||||
// return slowOperation(ctx)
|
|
||||||
// }
|
|
||||||
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
|
|
||||||
return WithDeadline(parent, time.Now().Add(timeout))
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithValue returns a copy of parent in which the value associated with key is
|
|
||||||
// val.
|
|
||||||
//
|
|
||||||
// Use context Values only for request-scoped data that transits processes and
|
|
||||||
// APIs, not for passing optional parameters to functions.
|
|
||||||
func WithValue(parent Context, key interface{}, val interface{}) Context {
|
|
||||||
return &valueCtx{parent, key, val}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A valueCtx carries a key-value pair. It implements Value for that key and
|
|
||||||
// delegates all other calls to the embedded Context.
|
|
||||||
type valueCtx struct {
|
|
||||||
Context
|
|
||||||
key, val interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *valueCtx) String() string {
|
|
||||||
return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *valueCtx) Value(key interface{}) interface{} {
|
|
||||||
if c.key == key {
|
|
||||||
return c.val
|
|
||||||
}
|
|
||||||
return c.Context.Value(key)
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
// +build !go1.9
|
|
||||||
|
|
||||||
package context
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// A Context carries a deadline, a cancelation signal, and other values across
|
|
||||||
// API boundaries.
|
|
||||||
//
|
|
||||||
// Context's methods may be called by multiple goroutines simultaneously.
|
|
||||||
type Context interface {
|
|
||||||
// Deadline returns the time when work done on behalf of this context
|
|
||||||
// should be canceled. Deadline returns ok==false when no deadline is
|
|
||||||
// set. Successive calls to Deadline return the same results.
|
|
||||||
Deadline() (deadline time.Time, ok bool)
|
|
||||||
|
|
||||||
// Done returns a channel that's closed when work done on behalf of this
|
|
||||||
// context should be canceled. Done may return nil if this context can
|
|
||||||
// never be canceled. Successive calls to Done return the same value.
|
|
||||||
//
|
|
||||||
// WithCancel arranges for Done to be closed when cancel is called;
|
|
||||||
// WithDeadline arranges for Done to be closed when the deadline
|
|
||||||
// expires; WithTimeout arranges for Done to be closed when the timeout
|
|
||||||
// elapses.
|
|
||||||
//
|
|
||||||
// Done is provided for use in select statements:
|
|
||||||
//
|
|
||||||
// // Stream generates values with DoSomething and sends them to out
|
|
||||||
// // until DoSomething returns an error or ctx.Done is closed.
|
|
||||||
// func Stream(ctx context.Context, out chan<- Value) error {
|
|
||||||
// for {
|
|
||||||
// v, err := DoSomething(ctx)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// select {
|
|
||||||
// case <-ctx.Done():
|
|
||||||
// return ctx.Err()
|
|
||||||
// case out <- v:
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// See http://blog.golang.org/pipelines for more examples of how to use
|
|
||||||
// a Done channel for cancelation.
|
|
||||||
Done() <-chan struct{}
|
|
||||||
|
|
||||||
// Err returns a non-nil error value after Done is closed. Err returns
|
|
||||||
// Canceled if the context was canceled or DeadlineExceeded if the
|
|
||||||
// context's deadline passed. No other values for Err are defined.
|
|
||||||
// After Done is closed, successive calls to Err return the same value.
|
|
||||||
Err() error
|
|
||||||
|
|
||||||
// Value returns the value associated with this context for key, or nil
|
|
||||||
// if no value is associated with key. Successive calls to Value with
|
|
||||||
// the same key returns the same result.
|
|
||||||
//
|
|
||||||
// Use context values only for request-scoped data that transits
|
|
||||||
// processes and API boundaries, not for passing optional parameters to
|
|
||||||
// functions.
|
|
||||||
//
|
|
||||||
// A key identifies a specific value in a Context. Functions that wish
|
|
||||||
// to store values in Context typically allocate a key in a global
|
|
||||||
// variable then use that key as the argument to context.WithValue and
|
|
||||||
// Context.Value. A key can be any type that supports equality;
|
|
||||||
// packages should define keys as an unexported type to avoid
|
|
||||||
// collisions.
|
|
||||||
//
|
|
||||||
// Packages that define a Context key should provide type-safe accessors
|
|
||||||
// for the values stores using that key:
|
|
||||||
//
|
|
||||||
// // Package user defines a User type that's stored in Contexts.
|
|
||||||
// package user
|
|
||||||
//
|
|
||||||
// import "golang.org/x/net/context"
|
|
||||||
//
|
|
||||||
// // User is the type of value stored in the Contexts.
|
|
||||||
// type User struct {...}
|
|
||||||
//
|
|
||||||
// // key is an unexported type for keys defined in this package.
|
|
||||||
// // This prevents collisions with keys defined in other packages.
|
|
||||||
// type key int
|
|
||||||
//
|
|
||||||
// // userKey is the key for user.User values in Contexts. It is
|
|
||||||
// // unexported; clients use user.NewContext and user.FromContext
|
|
||||||
// // instead of using this key directly.
|
|
||||||
// var userKey key = 0
|
|
||||||
//
|
|
||||||
// // NewContext returns a new Context that carries value u.
|
|
||||||
// func NewContext(ctx context.Context, u *User) context.Context {
|
|
||||||
// return context.WithValue(ctx, userKey, u)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // FromContext returns the User value stored in ctx, if any.
|
|
||||||
// func FromContext(ctx context.Context) (*User, bool) {
|
|
||||||
// u, ok := ctx.Value(userKey).(*User)
|
|
||||||
// return u, ok
|
|
||||||
// }
|
|
||||||
Value(key interface{}) interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A CancelFunc tells an operation to abandon its work.
|
|
||||||
// A CancelFunc does not wait for the work to stop.
|
|
||||||
// After the first call, subsequent calls to a CancelFunc do nothing.
|
|
||||||
type CancelFunc func()
|
|
|
@ -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
|
|
@ -0,0 +1,770 @@
|
||||||
|
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||||
|
|
||||||
|
// Copyright 2016 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.10
|
||||||
|
// +build go1.10
|
||||||
|
|
||||||
|
// Package idna implements IDNA2008 using the compatibility processing
|
||||||
|
// defined by UTS (Unicode Technical Standard) #46, which defines a standard to
|
||||||
|
// deal with the transition from IDNA2003.
|
||||||
|
//
|
||||||
|
// IDNA2008 (Internationalized Domain Names for Applications), is defined in RFC
|
||||||
|
// 5890, RFC 5891, RFC 5892, RFC 5893 and RFC 5894.
|
||||||
|
// UTS #46 is defined in https://www.unicode.org/reports/tr46.
|
||||||
|
// See https://unicode.org/cldr/utility/idna.jsp for a visualization of the
|
||||||
|
// differences between these two standards.
|
||||||
|
package idna // import "golang.org/x/net/idna"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"golang.org/x/text/secure/bidirule"
|
||||||
|
"golang.org/x/text/unicode/bidi"
|
||||||
|
"golang.org/x/text/unicode/norm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NOTE: Unlike common practice in Go APIs, the functions will return a
|
||||||
|
// sanitized domain name in case of errors. Browsers sometimes use a partially
|
||||||
|
// evaluated string as lookup.
|
||||||
|
// TODO: the current error handling is, in my opinion, the least opinionated.
|
||||||
|
// Other strategies are also viable, though:
|
||||||
|
// Option 1) Return an empty string in case of error, but allow the user to
|
||||||
|
// specify explicitly which errors to ignore.
|
||||||
|
// Option 2) Return the partially evaluated string if it is itself a valid
|
||||||
|
// string, otherwise return the empty string in case of error.
|
||||||
|
// Option 3) Option 1 and 2.
|
||||||
|
// Option 4) Always return an empty string for now and implement Option 1 as
|
||||||
|
// needed, and document that the return string may not be empty in case of
|
||||||
|
// error in the future.
|
||||||
|
// I think Option 1 is best, but it is quite opinionated.
|
||||||
|
|
||||||
|
// ToASCII is a wrapper for Punycode.ToASCII.
|
||||||
|
func ToASCII(s string) (string, error) {
|
||||||
|
return Punycode.process(s, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUnicode is a wrapper for Punycode.ToUnicode.
|
||||||
|
func ToUnicode(s string) (string, error) {
|
||||||
|
return Punycode.process(s, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Option configures a Profile at creation time.
|
||||||
|
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 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 = transitional }
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyDNSLength sets whether a Profile should fail if any of the IDN parts
|
||||||
|
// are longer than allowed by the RFC.
|
||||||
|
//
|
||||||
|
// This option corresponds to the VerifyDnsLength flag in UTS #46.
|
||||||
|
func VerifyDNSLength(verify bool) Option {
|
||||||
|
return func(o *options) { o.verifyDNSLength = verify }
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveLeadingDots removes leading label separators. Leading runes that map to
|
||||||
|
// dots, such as U+3002 IDEOGRAPHIC FULL STOP, are removed as well.
|
||||||
|
func RemoveLeadingDots(remove bool) Option {
|
||||||
|
return func(o *options) { o.removeLeadingDots = remove }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateLabels sets whether to check the mandatory label validation criteria
|
||||||
|
// as defined in Section 5.4 of RFC 5891. This includes testing for correct use
|
||||||
|
// of hyphens ('-'), normalization, validity of runes, and the context rules.
|
||||||
|
// In particular, ValidateLabels also sets the CheckHyphens and CheckJoiners flags
|
||||||
|
// in UTS #46.
|
||||||
|
func ValidateLabels(enable bool) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
// Don't override existing mappings, but set one that at least checks
|
||||||
|
// normalization if it is not set.
|
||||||
|
if o.mapping == nil && enable {
|
||||||
|
o.mapping = normalize
|
||||||
|
}
|
||||||
|
o.trie = trie
|
||||||
|
o.checkJoiners = enable
|
||||||
|
o.checkHyphens = enable
|
||||||
|
if enable {
|
||||||
|
o.fromPuny = validateFromPunycode
|
||||||
|
} else {
|
||||||
|
o.fromPuny = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckHyphens sets whether to check for correct use of hyphens ('-') in
|
||||||
|
// labels. Most web browsers do not have this option set, since labels such as
|
||||||
|
// "r3---sn-apo3qvuoxuxbt-j5pe" are in common use.
|
||||||
|
//
|
||||||
|
// This option corresponds to the CheckHyphens flag in UTS #46.
|
||||||
|
func CheckHyphens(enable bool) Option {
|
||||||
|
return func(o *options) { o.checkHyphens = enable }
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckJoiners sets whether to check the ContextJ rules as defined in Appendix
|
||||||
|
// A of RFC 5892, concerning the use of joiner runes.
|
||||||
|
//
|
||||||
|
// This option corresponds to the CheckJoiners flag in UTS #46.
|
||||||
|
func CheckJoiners(enable bool) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.trie = trie
|
||||||
|
o.checkJoiners = enable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrictDomainName limits the set of permissible ASCII characters to those
|
||||||
|
// allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the
|
||||||
|
// hyphen). This is set by default for MapForLookup and ValidateForRegistration,
|
||||||
|
// but is only useful if ValidateLabels is set.
|
||||||
|
//
|
||||||
|
// This option is useful, for instance, for browsers that allow characters
|
||||||
|
// outside this range, for example a '_' (U+005F LOW LINE). See
|
||||||
|
// http://www.rfc-editor.org/std/std3.txt for more details.
|
||||||
|
//
|
||||||
|
// This option corresponds to the UseSTD3ASCIIRules flag in UTS #46.
|
||||||
|
func StrictDomainName(use bool) Option {
|
||||||
|
return func(o *options) { o.useSTD3Rules = use }
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: the following options pull in tables. The tables should not be linked
|
||||||
|
// in as long as the options are not used.
|
||||||
|
|
||||||
|
// BidiRule enables the Bidi rule as defined in RFC 5893. Any application
|
||||||
|
// that relies on proper validation of labels should include this rule.
|
||||||
|
//
|
||||||
|
// This option corresponds to the CheckBidi flag in UTS #46.
|
||||||
|
func BidiRule() Option {
|
||||||
|
return func(o *options) { o.bidirule = bidirule.ValidString }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateForRegistration sets validation options to verify that a given IDN is
|
||||||
|
// properly formatted for registration as defined by Section 4 of RFC 5891.
|
||||||
|
func ValidateForRegistration() Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.mapping = validateRegistration
|
||||||
|
StrictDomainName(true)(o)
|
||||||
|
ValidateLabels(true)(o)
|
||||||
|
VerifyDNSLength(true)(o)
|
||||||
|
BidiRule()(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapForLookup sets validation and mapping options such that a given IDN is
|
||||||
|
// transformed for domain name lookup according to the requirements set out in
|
||||||
|
// Section 5 of RFC 5891. The mappings follow the recommendations of RFC 5894,
|
||||||
|
// RFC 5895 and UTS 46. It does not add the Bidi Rule. Use the BidiRule option
|
||||||
|
// to add this check.
|
||||||
|
//
|
||||||
|
// The mappings include normalization and mapping case, width and other
|
||||||
|
// compatibility mappings.
|
||||||
|
func MapForLookup() Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.mapping = validateAndMap
|
||||||
|
StrictDomainName(true)(o)
|
||||||
|
ValidateLabels(true)(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type options struct {
|
||||||
|
transitional bool
|
||||||
|
useSTD3Rules bool
|
||||||
|
checkHyphens bool
|
||||||
|
checkJoiners bool
|
||||||
|
verifyDNSLength bool
|
||||||
|
removeLeadingDots bool
|
||||||
|
|
||||||
|
trie *idnaTrie
|
||||||
|
|
||||||
|
// fromPuny calls validation rules when converting A-labels to U-labels.
|
||||||
|
fromPuny func(p *Profile, s string) error
|
||||||
|
|
||||||
|
// mapping implements a validation and mapping step as defined in RFC 5895
|
||||||
|
// or UTS 46, tailored to, for example, domain registration or lookup.
|
||||||
|
mapping func(p *Profile, s string) (mapped string, isBidi bool, err error)
|
||||||
|
|
||||||
|
// bidirule, if specified, checks whether s conforms to the Bidi Rule
|
||||||
|
// defined in RFC 5893.
|
||||||
|
bidirule func(s string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Profile defines the configuration of an IDNA mapper.
|
||||||
|
type Profile struct {
|
||||||
|
options
|
||||||
|
}
|
||||||
|
|
||||||
|
func apply(o *options, opts []Option) {
|
||||||
|
for _, f := range opts {
|
||||||
|
f(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Profile.
|
||||||
|
//
|
||||||
|
// With no options, the returned Profile is the most permissive and equals the
|
||||||
|
// Punycode Profile. Options can be passed to further restrict the Profile. The
|
||||||
|
// MapForLookup and ValidateForRegistration options set a collection of options,
|
||||||
|
// for lookup and registration purposes respectively, which can be tailored by
|
||||||
|
// adding more fine-grained options, where later options override earlier
|
||||||
|
// options.
|
||||||
|
func New(o ...Option) *Profile {
|
||||||
|
p := &Profile{}
|
||||||
|
apply(&p.options, o)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToASCII converts a domain or domain label to its ASCII form. For example,
|
||||||
|
// ToASCII("bücher.example.com") is "xn--bcher-kva.example.com", and
|
||||||
|
// ToASCII("golang") is "golang". If an error is encountered it will return
|
||||||
|
// an error and a (partially) processed result.
|
||||||
|
func (p *Profile) ToASCII(s string) (string, error) {
|
||||||
|
return p.process(s, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUnicode converts a domain or domain label to its Unicode form. For example,
|
||||||
|
// ToUnicode("xn--bcher-kva.example.com") is "bücher.example.com", and
|
||||||
|
// ToUnicode("golang") is "golang". If an error is encountered it will return
|
||||||
|
// an error and a (partially) processed result.
|
||||||
|
func (p *Profile) ToUnicode(s string) (string, error) {
|
||||||
|
pp := *p
|
||||||
|
pp.transitional = false
|
||||||
|
return pp.process(s, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String reports a string with a description of the profile for debugging
|
||||||
|
// purposes. The string format may change with different versions.
|
||||||
|
func (p *Profile) String() string {
|
||||||
|
s := ""
|
||||||
|
if p.transitional {
|
||||||
|
s = "Transitional"
|
||||||
|
} else {
|
||||||
|
s = "NonTransitional"
|
||||||
|
}
|
||||||
|
if p.useSTD3Rules {
|
||||||
|
s += ":UseSTD3Rules"
|
||||||
|
}
|
||||||
|
if p.checkHyphens {
|
||||||
|
s += ":CheckHyphens"
|
||||||
|
}
|
||||||
|
if p.checkJoiners {
|
||||||
|
s += ":CheckJoiners"
|
||||||
|
}
|
||||||
|
if p.verifyDNSLength {
|
||||||
|
s += ":VerifyDNSLength"
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Punycode is a Profile that does raw punycode processing with a minimum
|
||||||
|
// of validation.
|
||||||
|
Punycode *Profile = punycode
|
||||||
|
|
||||||
|
// Lookup is the recommended profile for looking up domain names, according
|
||||||
|
// to Section 5 of RFC 5891. The exact configuration of this profile may
|
||||||
|
// change over time.
|
||||||
|
Lookup *Profile = lookup
|
||||||
|
|
||||||
|
// Display is the recommended profile for displaying domain names.
|
||||||
|
// The configuration of this profile may change over time.
|
||||||
|
Display *Profile = display
|
||||||
|
|
||||||
|
// Registration is the recommended profile for checking whether a given
|
||||||
|
// IDN is valid for registration, according to Section 4 of RFC 5891.
|
||||||
|
Registration *Profile = registration
|
||||||
|
|
||||||
|
punycode = &Profile{}
|
||||||
|
lookup = &Profile{options{
|
||||||
|
transitional: transitionalLookup,
|
||||||
|
useSTD3Rules: true,
|
||||||
|
checkHyphens: true,
|
||||||
|
checkJoiners: true,
|
||||||
|
trie: trie,
|
||||||
|
fromPuny: validateFromPunycode,
|
||||||
|
mapping: validateAndMap,
|
||||||
|
bidirule: bidirule.ValidString,
|
||||||
|
}}
|
||||||
|
display = &Profile{options{
|
||||||
|
useSTD3Rules: true,
|
||||||
|
checkHyphens: true,
|
||||||
|
checkJoiners: true,
|
||||||
|
trie: trie,
|
||||||
|
fromPuny: validateFromPunycode,
|
||||||
|
mapping: validateAndMap,
|
||||||
|
bidirule: bidirule.ValidString,
|
||||||
|
}}
|
||||||
|
registration = &Profile{options{
|
||||||
|
useSTD3Rules: true,
|
||||||
|
verifyDNSLength: true,
|
||||||
|
checkHyphens: true,
|
||||||
|
checkJoiners: true,
|
||||||
|
trie: trie,
|
||||||
|
fromPuny: validateFromPunycode,
|
||||||
|
mapping: validateRegistration,
|
||||||
|
bidirule: bidirule.ValidString,
|
||||||
|
}}
|
||||||
|
|
||||||
|
// TODO: profiles
|
||||||
|
// Register: recommended for approving domain names: don't do any mappings
|
||||||
|
// but rather reject on invalid input. Bundle or block deviation characters.
|
||||||
|
)
|
||||||
|
|
||||||
|
type labelError struct{ label, code_ string }
|
||||||
|
|
||||||
|
func (e labelError) code() string { return e.code_ }
|
||||||
|
func (e labelError) Error() string {
|
||||||
|
return fmt.Sprintf("idna: invalid label %q", e.label)
|
||||||
|
}
|
||||||
|
|
||||||
|
type runeError rune
|
||||||
|
|
||||||
|
func (e runeError) code() string { return "P1" }
|
||||||
|
func (e runeError) Error() string {
|
||||||
|
return fmt.Sprintf("idna: disallowed rune %U", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// process implements the algorithm described in section 4 of UTS #46,
|
||||||
|
// see https://www.unicode.org/reports/tr46.
|
||||||
|
func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||||
|
var err error
|
||||||
|
var isBidi bool
|
||||||
|
if p.mapping != nil {
|
||||||
|
s, isBidi, err = p.mapping(p, s)
|
||||||
|
}
|
||||||
|
// Remove leading empty labels.
|
||||||
|
if p.removeLeadingDots {
|
||||||
|
for ; len(s) > 0 && s[0] == '.'; s = s[1:] {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: allow for a quick check of the tables data.
|
||||||
|
// It seems like we should only create this error on ToASCII, but the
|
||||||
|
// UTS 46 conformance tests suggests we should always check this.
|
||||||
|
if err == nil && p.verifyDNSLength && s == "" {
|
||||||
|
err = &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
labels := labelIter{orig: s}
|
||||||
|
for ; !labels.done(); labels.next() {
|
||||||
|
label := labels.label()
|
||||||
|
if label == "" {
|
||||||
|
// Empty labels are not okay. The label iterator skips the last
|
||||||
|
// label if it is empty.
|
||||||
|
if err == nil && p.verifyDNSLength {
|
||||||
|
err = &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(label, acePrefix) {
|
||||||
|
u, err2 := decode(label[len(acePrefix):])
|
||||||
|
if err2 != nil {
|
||||||
|
if err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
// Spec says keep the old label.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
isBidi = isBidi || bidirule.DirectionString(u) != bidi.LeftToRight
|
||||||
|
labels.set(u)
|
||||||
|
if err == nil && p.fromPuny != nil {
|
||||||
|
err = p.fromPuny(p, u)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
// This should be called on NonTransitional, according to the
|
||||||
|
// spec, but that currently does not have any effect. Use the
|
||||||
|
// original profile to preserve options.
|
||||||
|
err = p.validateLabel(u)
|
||||||
|
}
|
||||||
|
} else if err == nil {
|
||||||
|
err = p.validateLabel(label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isBidi && p.bidirule != nil && err == nil {
|
||||||
|
for labels.reset(); !labels.done(); labels.next() {
|
||||||
|
if !p.bidirule(labels.label()) {
|
||||||
|
err = &labelError{s, "B"}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if toASCII {
|
||||||
|
for labels.reset(); !labels.done(); labels.next() {
|
||||||
|
label := labels.label()
|
||||||
|
if !ascii(label) {
|
||||||
|
a, err2 := encode(acePrefix, label)
|
||||||
|
if err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
label = a
|
||||||
|
labels.set(a)
|
||||||
|
}
|
||||||
|
n := len(label)
|
||||||
|
if p.verifyDNSLength && err == nil && (n == 0 || n > 63) {
|
||||||
|
err = &labelError{label, "A4"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = labels.result()
|
||||||
|
if toASCII && p.verifyDNSLength && err == nil {
|
||||||
|
// Compute the length of the domain name minus the root label and its dot.
|
||||||
|
n := len(s)
|
||||||
|
if n > 0 && s[n-1] == '.' {
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
if len(s) < 1 || n > 253 {
|
||||||
|
err = &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalize(p *Profile, s string) (mapped string, isBidi bool, err error) {
|
||||||
|
// TODO: consider first doing a quick check to see if any of these checks
|
||||||
|
// need to be done. This will make it slower in the general case, but
|
||||||
|
// faster in the common case.
|
||||||
|
mapped = norm.NFC.String(s)
|
||||||
|
isBidi = bidirule.DirectionString(mapped) == bidi.RightToLeft
|
||||||
|
return mapped, isBidi, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateRegistration(p *Profile, s string) (idem string, bidi bool, err error) {
|
||||||
|
// TODO: filter need for normalization in loop below.
|
||||||
|
if !norm.NFC.IsNormalString(s) {
|
||||||
|
return s, false, &labelError{s, "V1"}
|
||||||
|
}
|
||||||
|
for i := 0; i < len(s); {
|
||||||
|
v, sz := trie.lookupString(s[i:])
|
||||||
|
if sz == 0 {
|
||||||
|
return s, bidi, runeError(utf8.RuneError)
|
||||||
|
}
|
||||||
|
bidi = bidi || info(v).isBidi(s[i:])
|
||||||
|
// Copy bytes not copied so far.
|
||||||
|
switch p.simplify(info(v).category()) {
|
||||||
|
// TODO: handle the NV8 defined in the Unicode idna data set to allow
|
||||||
|
// for strict conformance to IDNA2008.
|
||||||
|
case valid, deviation:
|
||||||
|
case disallowed, mapped, unknown, ignored:
|
||||||
|
r, _ := utf8.DecodeRuneInString(s[i:])
|
||||||
|
return s, bidi, runeError(r)
|
||||||
|
}
|
||||||
|
i += sz
|
||||||
|
}
|
||||||
|
return s, bidi, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c info) isBidi(s string) bool {
|
||||||
|
if !c.isMapped() {
|
||||||
|
return c&attributesMask == rtl
|
||||||
|
}
|
||||||
|
// TODO: also store bidi info for mapped data. This is possible, but a bit
|
||||||
|
// cumbersome and not for the common case.
|
||||||
|
p, _ := bidi.LookupString(s)
|
||||||
|
switch p.Class() {
|
||||||
|
case bidi.R, bidi.AL, bidi.AN:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateAndMap(p *Profile, s string) (vm string, bidi bool, err error) {
|
||||||
|
var (
|
||||||
|
b []byte
|
||||||
|
k int
|
||||||
|
)
|
||||||
|
// combinedInfoBits contains the or-ed bits of all runes. We use this
|
||||||
|
// to derive the mayNeedNorm bit later. This may trigger normalization
|
||||||
|
// overeagerly, but it will not do so in the common case. The end result
|
||||||
|
// is another 10% saving on BenchmarkProfile for the common case.
|
||||||
|
var combinedInfoBits info
|
||||||
|
for i := 0; i < len(s); {
|
||||||
|
v, sz := trie.lookupString(s[i:])
|
||||||
|
if sz == 0 {
|
||||||
|
b = append(b, s[k:i]...)
|
||||||
|
b = append(b, "\ufffd"...)
|
||||||
|
k = len(s)
|
||||||
|
if err == nil {
|
||||||
|
err = runeError(utf8.RuneError)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
combinedInfoBits |= info(v)
|
||||||
|
bidi = bidi || info(v).isBidi(s[i:])
|
||||||
|
start := i
|
||||||
|
i += sz
|
||||||
|
// Copy bytes not copied so far.
|
||||||
|
switch p.simplify(info(v).category()) {
|
||||||
|
case valid:
|
||||||
|
continue
|
||||||
|
case disallowed:
|
||||||
|
if err == nil {
|
||||||
|
r, _ := utf8.DecodeRuneInString(s[start:])
|
||||||
|
err = runeError(r)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case mapped, deviation:
|
||||||
|
b = append(b, s[k:start]...)
|
||||||
|
b = info(v).appendMapping(b, s[start:i])
|
||||||
|
case ignored:
|
||||||
|
b = append(b, s[k:start]...)
|
||||||
|
// drop the rune
|
||||||
|
case unknown:
|
||||||
|
b = append(b, s[k:start]...)
|
||||||
|
b = append(b, "\ufffd"...)
|
||||||
|
}
|
||||||
|
k = i
|
||||||
|
}
|
||||||
|
if k == 0 {
|
||||||
|
// No changes so far.
|
||||||
|
if combinedInfoBits&mayNeedNorm != 0 {
|
||||||
|
s = norm.NFC.String(s)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
b = append(b, s[k:]...)
|
||||||
|
if norm.NFC.QuickSpan(b) != len(b) {
|
||||||
|
b = norm.NFC.Bytes(b)
|
||||||
|
}
|
||||||
|
// TODO: the punycode converters require strings as input.
|
||||||
|
s = string(b)
|
||||||
|
}
|
||||||
|
return s, bidi, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// A labelIter allows iterating over domain name labels.
|
||||||
|
type labelIter struct {
|
||||||
|
orig string
|
||||||
|
slice []string
|
||||||
|
curStart int
|
||||||
|
curEnd int
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) reset() {
|
||||||
|
l.curStart = 0
|
||||||
|
l.curEnd = 0
|
||||||
|
l.i = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) done() bool {
|
||||||
|
return l.curStart >= len(l.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) result() string {
|
||||||
|
if l.slice != nil {
|
||||||
|
return strings.Join(l.slice, ".")
|
||||||
|
}
|
||||||
|
return l.orig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) label() string {
|
||||||
|
if l.slice != nil {
|
||||||
|
return l.slice[l.i]
|
||||||
|
}
|
||||||
|
p := strings.IndexByte(l.orig[l.curStart:], '.')
|
||||||
|
l.curEnd = l.curStart + p
|
||||||
|
if p == -1 {
|
||||||
|
l.curEnd = len(l.orig)
|
||||||
|
}
|
||||||
|
return l.orig[l.curStart:l.curEnd]
|
||||||
|
}
|
||||||
|
|
||||||
|
// next sets the value to the next label. It skips the last label if it is empty.
|
||||||
|
func (l *labelIter) next() {
|
||||||
|
l.i++
|
||||||
|
if l.slice != nil {
|
||||||
|
if l.i >= len(l.slice) || l.i == len(l.slice)-1 && l.slice[l.i] == "" {
|
||||||
|
l.curStart = len(l.orig)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l.curStart = l.curEnd + 1
|
||||||
|
if l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
|
||||||
|
l.curStart = len(l.orig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) set(s string) {
|
||||||
|
if l.slice == nil {
|
||||||
|
l.slice = strings.Split(l.orig, ".")
|
||||||
|
}
|
||||||
|
l.slice[l.i] = s
|
||||||
|
}
|
||||||
|
|
||||||
|
// acePrefix is the ASCII Compatible Encoding prefix.
|
||||||
|
const acePrefix = "xn--"
|
||||||
|
|
||||||
|
func (p *Profile) simplify(cat category) category {
|
||||||
|
switch cat {
|
||||||
|
case disallowedSTD3Mapped:
|
||||||
|
if p.useSTD3Rules {
|
||||||
|
cat = disallowed
|
||||||
|
} else {
|
||||||
|
cat = mapped
|
||||||
|
}
|
||||||
|
case disallowedSTD3Valid:
|
||||||
|
if p.useSTD3Rules {
|
||||||
|
cat = disallowed
|
||||||
|
} else {
|
||||||
|
cat = valid
|
||||||
|
}
|
||||||
|
case deviation:
|
||||||
|
if !p.transitional {
|
||||||
|
cat = valid
|
||||||
|
}
|
||||||
|
case validNV8, validXV8:
|
||||||
|
// TODO: handle V2008
|
||||||
|
cat = valid
|
||||||
|
}
|
||||||
|
return cat
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateFromPunycode(p *Profile, s string) error {
|
||||||
|
if !norm.NFC.IsNormalString(s) {
|
||||||
|
return &labelError{s, "V1"}
|
||||||
|
}
|
||||||
|
// TODO: detect whether string may have to be normalized in the following
|
||||||
|
// loop.
|
||||||
|
for i := 0; i < len(s); {
|
||||||
|
v, sz := trie.lookupString(s[i:])
|
||||||
|
if sz == 0 {
|
||||||
|
return runeError(utf8.RuneError)
|
||||||
|
}
|
||||||
|
if c := p.simplify(info(v).category()); c != valid && c != deviation {
|
||||||
|
return &labelError{s, "V6"}
|
||||||
|
}
|
||||||
|
i += sz
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
zwnj = "\u200c"
|
||||||
|
zwj = "\u200d"
|
||||||
|
)
|
||||||
|
|
||||||
|
type joinState int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
stateStart joinState = iota
|
||||||
|
stateVirama
|
||||||
|
stateBefore
|
||||||
|
stateBeforeVirama
|
||||||
|
stateAfter
|
||||||
|
stateFAIL
|
||||||
|
)
|
||||||
|
|
||||||
|
var joinStates = [][numJoinTypes]joinState{
|
||||||
|
stateStart: {
|
||||||
|
joiningL: stateBefore,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
joinZWNJ: stateFAIL,
|
||||||
|
joinZWJ: stateFAIL,
|
||||||
|
joinVirama: stateVirama,
|
||||||
|
},
|
||||||
|
stateVirama: {
|
||||||
|
joiningL: stateBefore,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
},
|
||||||
|
stateBefore: {
|
||||||
|
joiningL: stateBefore,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
joiningT: stateBefore,
|
||||||
|
joinZWNJ: stateAfter,
|
||||||
|
joinZWJ: stateFAIL,
|
||||||
|
joinVirama: stateBeforeVirama,
|
||||||
|
},
|
||||||
|
stateBeforeVirama: {
|
||||||
|
joiningL: stateBefore,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
joiningT: stateBefore,
|
||||||
|
},
|
||||||
|
stateAfter: {
|
||||||
|
joiningL: stateFAIL,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
joiningT: stateAfter,
|
||||||
|
joiningR: stateStart,
|
||||||
|
joinZWNJ: stateFAIL,
|
||||||
|
joinZWJ: stateFAIL,
|
||||||
|
joinVirama: stateAfter, // no-op as we can't accept joiners here
|
||||||
|
},
|
||||||
|
stateFAIL: {
|
||||||
|
0: stateFAIL,
|
||||||
|
joiningL: stateFAIL,
|
||||||
|
joiningD: stateFAIL,
|
||||||
|
joiningT: stateFAIL,
|
||||||
|
joiningR: stateFAIL,
|
||||||
|
joinZWNJ: stateFAIL,
|
||||||
|
joinZWJ: stateFAIL,
|
||||||
|
joinVirama: stateFAIL,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateLabel validates the criteria from Section 4.1. Item 1, 4, and 6 are
|
||||||
|
// already implicitly satisfied by the overall implementation.
|
||||||
|
func (p *Profile) validateLabel(s string) (err error) {
|
||||||
|
if s == "" {
|
||||||
|
if p.verifyDNSLength {
|
||||||
|
return &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if p.checkHyphens {
|
||||||
|
if len(s) > 4 && s[2] == '-' && s[3] == '-' {
|
||||||
|
return &labelError{s, "V2"}
|
||||||
|
}
|
||||||
|
if s[0] == '-' || s[len(s)-1] == '-' {
|
||||||
|
return &labelError{s, "V3"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !p.checkJoiners {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
trie := p.trie // p.checkJoiners is only set if trie is set.
|
||||||
|
// TODO: merge the use of this in the trie.
|
||||||
|
v, sz := trie.lookupString(s)
|
||||||
|
x := info(v)
|
||||||
|
if x.isModifier() {
|
||||||
|
return &labelError{s, "V5"}
|
||||||
|
}
|
||||||
|
// Quickly return in the absence of zero-width (non) joiners.
|
||||||
|
if strings.Index(s, zwj) == -1 && strings.Index(s, zwnj) == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
st := stateStart
|
||||||
|
for i := 0; ; {
|
||||||
|
jt := x.joinType()
|
||||||
|
if s[i:i+sz] == zwj {
|
||||||
|
jt = joinZWJ
|
||||||
|
} else if s[i:i+sz] == zwnj {
|
||||||
|
jt = joinZWNJ
|
||||||
|
}
|
||||||
|
st = joinStates[st][jt]
|
||||||
|
if x.isViramaModifier() {
|
||||||
|
st = joinStates[st][joinVirama]
|
||||||
|
}
|
||||||
|
if i += sz; i == len(s) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
v, sz = trie.lookupString(s[i:])
|
||||||
|
x = info(v)
|
||||||
|
}
|
||||||
|
if st == stateFAIL || st == stateAfter {
|
||||||
|
return &labelError{s, "C"}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ascii(s string) bool {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] >= utf8.RuneSelf {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,718 @@
|
||||||
|
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||||
|
|
||||||
|
// Copyright 2016 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.10
|
||||||
|
// +build !go1.10
|
||||||
|
|
||||||
|
// Package idna implements IDNA2008 using the compatibility processing
|
||||||
|
// defined by UTS (Unicode Technical Standard) #46, which defines a standard to
|
||||||
|
// deal with the transition from IDNA2003.
|
||||||
|
//
|
||||||
|
// IDNA2008 (Internationalized Domain Names for Applications), is defined in RFC
|
||||||
|
// 5890, RFC 5891, RFC 5892, RFC 5893 and RFC 5894.
|
||||||
|
// UTS #46 is defined in https://www.unicode.org/reports/tr46.
|
||||||
|
// See https://unicode.org/cldr/utility/idna.jsp for a visualization of the
|
||||||
|
// differences between these two standards.
|
||||||
|
package idna // import "golang.org/x/net/idna"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"golang.org/x/text/secure/bidirule"
|
||||||
|
"golang.org/x/text/unicode/norm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NOTE: Unlike common practice in Go APIs, the functions will return a
|
||||||
|
// sanitized domain name in case of errors. Browsers sometimes use a partially
|
||||||
|
// evaluated string as lookup.
|
||||||
|
// TODO: the current error handling is, in my opinion, the least opinionated.
|
||||||
|
// Other strategies are also viable, though:
|
||||||
|
// Option 1) Return an empty string in case of error, but allow the user to
|
||||||
|
// specify explicitly which errors to ignore.
|
||||||
|
// Option 2) Return the partially evaluated string if it is itself a valid
|
||||||
|
// string, otherwise return the empty string in case of error.
|
||||||
|
// Option 3) Option 1 and 2.
|
||||||
|
// Option 4) Always return an empty string for now and implement Option 1 as
|
||||||
|
// needed, and document that the return string may not be empty in case of
|
||||||
|
// error in the future.
|
||||||
|
// I think Option 1 is best, but it is quite opinionated.
|
||||||
|
|
||||||
|
// ToASCII is a wrapper for Punycode.ToASCII.
|
||||||
|
func ToASCII(s string) (string, error) {
|
||||||
|
return Punycode.process(s, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUnicode is a wrapper for Punycode.ToUnicode.
|
||||||
|
func ToUnicode(s string) (string, error) {
|
||||||
|
return Punycode.process(s, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Option configures a Profile at creation time.
|
||||||
|
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 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 = transitional }
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyDNSLength sets whether a Profile should fail if any of the IDN parts
|
||||||
|
// are longer than allowed by the RFC.
|
||||||
|
//
|
||||||
|
// This option corresponds to the VerifyDnsLength flag in UTS #46.
|
||||||
|
func VerifyDNSLength(verify bool) Option {
|
||||||
|
return func(o *options) { o.verifyDNSLength = verify }
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveLeadingDots removes leading label separators. Leading runes that map to
|
||||||
|
// dots, such as U+3002 IDEOGRAPHIC FULL STOP, are removed as well.
|
||||||
|
func RemoveLeadingDots(remove bool) Option {
|
||||||
|
return func(o *options) { o.removeLeadingDots = remove }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateLabels sets whether to check the mandatory label validation criteria
|
||||||
|
// as defined in Section 5.4 of RFC 5891. This includes testing for correct use
|
||||||
|
// of hyphens ('-'), normalization, validity of runes, and the context rules.
|
||||||
|
// In particular, ValidateLabels also sets the CheckHyphens and CheckJoiners flags
|
||||||
|
// in UTS #46.
|
||||||
|
func ValidateLabels(enable bool) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
// Don't override existing mappings, but set one that at least checks
|
||||||
|
// normalization if it is not set.
|
||||||
|
if o.mapping == nil && enable {
|
||||||
|
o.mapping = normalize
|
||||||
|
}
|
||||||
|
o.trie = trie
|
||||||
|
o.checkJoiners = enable
|
||||||
|
o.checkHyphens = enable
|
||||||
|
if enable {
|
||||||
|
o.fromPuny = validateFromPunycode
|
||||||
|
} else {
|
||||||
|
o.fromPuny = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckHyphens sets whether to check for correct use of hyphens ('-') in
|
||||||
|
// labels. Most web browsers do not have this option set, since labels such as
|
||||||
|
// "r3---sn-apo3qvuoxuxbt-j5pe" are in common use.
|
||||||
|
//
|
||||||
|
// This option corresponds to the CheckHyphens flag in UTS #46.
|
||||||
|
func CheckHyphens(enable bool) Option {
|
||||||
|
return func(o *options) { o.checkHyphens = enable }
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckJoiners sets whether to check the ContextJ rules as defined in Appendix
|
||||||
|
// A of RFC 5892, concerning the use of joiner runes.
|
||||||
|
//
|
||||||
|
// This option corresponds to the CheckJoiners flag in UTS #46.
|
||||||
|
func CheckJoiners(enable bool) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.trie = trie
|
||||||
|
o.checkJoiners = enable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrictDomainName limits the set of permissable ASCII characters to those
|
||||||
|
// allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the
|
||||||
|
// hyphen). This is set by default for MapForLookup and ValidateForRegistration,
|
||||||
|
// but is only useful if ValidateLabels is set.
|
||||||
|
//
|
||||||
|
// This option is useful, for instance, for browsers that allow characters
|
||||||
|
// outside this range, for example a '_' (U+005F LOW LINE). See
|
||||||
|
// http://www.rfc-editor.org/std/std3.txt for more details.
|
||||||
|
//
|
||||||
|
// This option corresponds to the UseSTD3ASCIIRules flag in UTS #46.
|
||||||
|
func StrictDomainName(use bool) Option {
|
||||||
|
return func(o *options) { o.useSTD3Rules = use }
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: the following options pull in tables. The tables should not be linked
|
||||||
|
// in as long as the options are not used.
|
||||||
|
|
||||||
|
// BidiRule enables the Bidi rule as defined in RFC 5893. Any application
|
||||||
|
// that relies on proper validation of labels should include this rule.
|
||||||
|
//
|
||||||
|
// This option corresponds to the CheckBidi flag in UTS #46.
|
||||||
|
func BidiRule() Option {
|
||||||
|
return func(o *options) { o.bidirule = bidirule.ValidString }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateForRegistration sets validation options to verify that a given IDN is
|
||||||
|
// properly formatted for registration as defined by Section 4 of RFC 5891.
|
||||||
|
func ValidateForRegistration() Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.mapping = validateRegistration
|
||||||
|
StrictDomainName(true)(o)
|
||||||
|
ValidateLabels(true)(o)
|
||||||
|
VerifyDNSLength(true)(o)
|
||||||
|
BidiRule()(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapForLookup sets validation and mapping options such that a given IDN is
|
||||||
|
// transformed for domain name lookup according to the requirements set out in
|
||||||
|
// Section 5 of RFC 5891. The mappings follow the recommendations of RFC 5894,
|
||||||
|
// RFC 5895 and UTS 46. It does not add the Bidi Rule. Use the BidiRule option
|
||||||
|
// to add this check.
|
||||||
|
//
|
||||||
|
// The mappings include normalization and mapping case, width and other
|
||||||
|
// compatibility mappings.
|
||||||
|
func MapForLookup() Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.mapping = validateAndMap
|
||||||
|
StrictDomainName(true)(o)
|
||||||
|
ValidateLabels(true)(o)
|
||||||
|
RemoveLeadingDots(true)(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type options struct {
|
||||||
|
transitional bool
|
||||||
|
useSTD3Rules bool
|
||||||
|
checkHyphens bool
|
||||||
|
checkJoiners bool
|
||||||
|
verifyDNSLength bool
|
||||||
|
removeLeadingDots bool
|
||||||
|
|
||||||
|
trie *idnaTrie
|
||||||
|
|
||||||
|
// fromPuny calls validation rules when converting A-labels to U-labels.
|
||||||
|
fromPuny func(p *Profile, s string) error
|
||||||
|
|
||||||
|
// mapping implements a validation and mapping step as defined in RFC 5895
|
||||||
|
// or UTS 46, tailored to, for example, domain registration or lookup.
|
||||||
|
mapping func(p *Profile, s string) (string, error)
|
||||||
|
|
||||||
|
// bidirule, if specified, checks whether s conforms to the Bidi Rule
|
||||||
|
// defined in RFC 5893.
|
||||||
|
bidirule func(s string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Profile defines the configuration of a IDNA mapper.
|
||||||
|
type Profile struct {
|
||||||
|
options
|
||||||
|
}
|
||||||
|
|
||||||
|
func apply(o *options, opts []Option) {
|
||||||
|
for _, f := range opts {
|
||||||
|
f(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Profile.
|
||||||
|
//
|
||||||
|
// With no options, the returned Profile is the most permissive and equals the
|
||||||
|
// Punycode Profile. Options can be passed to further restrict the Profile. The
|
||||||
|
// MapForLookup and ValidateForRegistration options set a collection of options,
|
||||||
|
// for lookup and registration purposes respectively, which can be tailored by
|
||||||
|
// adding more fine-grained options, where later options override earlier
|
||||||
|
// options.
|
||||||
|
func New(o ...Option) *Profile {
|
||||||
|
p := &Profile{}
|
||||||
|
apply(&p.options, o)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToASCII converts a domain or domain label to its ASCII form. For example,
|
||||||
|
// ToASCII("bücher.example.com") is "xn--bcher-kva.example.com", and
|
||||||
|
// ToASCII("golang") is "golang". If an error is encountered it will return
|
||||||
|
// an error and a (partially) processed result.
|
||||||
|
func (p *Profile) ToASCII(s string) (string, error) {
|
||||||
|
return p.process(s, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUnicode converts a domain or domain label to its Unicode form. For example,
|
||||||
|
// ToUnicode("xn--bcher-kva.example.com") is "bücher.example.com", and
|
||||||
|
// ToUnicode("golang") is "golang". If an error is encountered it will return
|
||||||
|
// an error and a (partially) processed result.
|
||||||
|
func (p *Profile) ToUnicode(s string) (string, error) {
|
||||||
|
pp := *p
|
||||||
|
pp.transitional = false
|
||||||
|
return pp.process(s, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String reports a string with a description of the profile for debugging
|
||||||
|
// purposes. The string format may change with different versions.
|
||||||
|
func (p *Profile) String() string {
|
||||||
|
s := ""
|
||||||
|
if p.transitional {
|
||||||
|
s = "Transitional"
|
||||||
|
} else {
|
||||||
|
s = "NonTransitional"
|
||||||
|
}
|
||||||
|
if p.useSTD3Rules {
|
||||||
|
s += ":UseSTD3Rules"
|
||||||
|
}
|
||||||
|
if p.checkHyphens {
|
||||||
|
s += ":CheckHyphens"
|
||||||
|
}
|
||||||
|
if p.checkJoiners {
|
||||||
|
s += ":CheckJoiners"
|
||||||
|
}
|
||||||
|
if p.verifyDNSLength {
|
||||||
|
s += ":VerifyDNSLength"
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Punycode is a Profile that does raw punycode processing with a minimum
|
||||||
|
// of validation.
|
||||||
|
Punycode *Profile = punycode
|
||||||
|
|
||||||
|
// Lookup is the recommended profile for looking up domain names, according
|
||||||
|
// to Section 5 of RFC 5891. The exact configuration of this profile may
|
||||||
|
// change over time.
|
||||||
|
Lookup *Profile = lookup
|
||||||
|
|
||||||
|
// Display is the recommended profile for displaying domain names.
|
||||||
|
// The configuration of this profile may change over time.
|
||||||
|
Display *Profile = display
|
||||||
|
|
||||||
|
// Registration is the recommended profile for checking whether a given
|
||||||
|
// IDN is valid for registration, according to Section 4 of RFC 5891.
|
||||||
|
Registration *Profile = registration
|
||||||
|
|
||||||
|
punycode = &Profile{}
|
||||||
|
lookup = &Profile{options{
|
||||||
|
transitional: true,
|
||||||
|
removeLeadingDots: true,
|
||||||
|
useSTD3Rules: true,
|
||||||
|
checkHyphens: true,
|
||||||
|
checkJoiners: true,
|
||||||
|
trie: trie,
|
||||||
|
fromPuny: validateFromPunycode,
|
||||||
|
mapping: validateAndMap,
|
||||||
|
bidirule: bidirule.ValidString,
|
||||||
|
}}
|
||||||
|
display = &Profile{options{
|
||||||
|
useSTD3Rules: true,
|
||||||
|
removeLeadingDots: true,
|
||||||
|
checkHyphens: true,
|
||||||
|
checkJoiners: true,
|
||||||
|
trie: trie,
|
||||||
|
fromPuny: validateFromPunycode,
|
||||||
|
mapping: validateAndMap,
|
||||||
|
bidirule: bidirule.ValidString,
|
||||||
|
}}
|
||||||
|
registration = &Profile{options{
|
||||||
|
useSTD3Rules: true,
|
||||||
|
verifyDNSLength: true,
|
||||||
|
checkHyphens: true,
|
||||||
|
checkJoiners: true,
|
||||||
|
trie: trie,
|
||||||
|
fromPuny: validateFromPunycode,
|
||||||
|
mapping: validateRegistration,
|
||||||
|
bidirule: bidirule.ValidString,
|
||||||
|
}}
|
||||||
|
|
||||||
|
// TODO: profiles
|
||||||
|
// Register: recommended for approving domain names: don't do any mappings
|
||||||
|
// but rather reject on invalid input. Bundle or block deviation characters.
|
||||||
|
)
|
||||||
|
|
||||||
|
type labelError struct{ label, code_ string }
|
||||||
|
|
||||||
|
func (e labelError) code() string { return e.code_ }
|
||||||
|
func (e labelError) Error() string {
|
||||||
|
return fmt.Sprintf("idna: invalid label %q", e.label)
|
||||||
|
}
|
||||||
|
|
||||||
|
type runeError rune
|
||||||
|
|
||||||
|
func (e runeError) code() string { return "P1" }
|
||||||
|
func (e runeError) Error() string {
|
||||||
|
return fmt.Sprintf("idna: disallowed rune %U", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// process implements the algorithm described in section 4 of UTS #46,
|
||||||
|
// see https://www.unicode.org/reports/tr46.
|
||||||
|
func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||||
|
var err error
|
||||||
|
if p.mapping != nil {
|
||||||
|
s, err = p.mapping(p, s)
|
||||||
|
}
|
||||||
|
// Remove leading empty labels.
|
||||||
|
if p.removeLeadingDots {
|
||||||
|
for ; len(s) > 0 && s[0] == '.'; s = s[1:] {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// It seems like we should only create this error on ToASCII, but the
|
||||||
|
// UTS 46 conformance tests suggests we should always check this.
|
||||||
|
if err == nil && p.verifyDNSLength && s == "" {
|
||||||
|
err = &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
labels := labelIter{orig: s}
|
||||||
|
for ; !labels.done(); labels.next() {
|
||||||
|
label := labels.label()
|
||||||
|
if label == "" {
|
||||||
|
// Empty labels are not okay. The label iterator skips the last
|
||||||
|
// label if it is empty.
|
||||||
|
if err == nil && p.verifyDNSLength {
|
||||||
|
err = &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(label, acePrefix) {
|
||||||
|
u, err2 := decode(label[len(acePrefix):])
|
||||||
|
if err2 != nil {
|
||||||
|
if err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
// Spec says keep the old label.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
labels.set(u)
|
||||||
|
if err == nil && p.fromPuny != nil {
|
||||||
|
err = p.fromPuny(p, u)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
// This should be called on NonTransitional, according to the
|
||||||
|
// spec, but that currently does not have any effect. Use the
|
||||||
|
// original profile to preserve options.
|
||||||
|
err = p.validateLabel(u)
|
||||||
|
}
|
||||||
|
} else if err == nil {
|
||||||
|
err = p.validateLabel(label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if toASCII {
|
||||||
|
for labels.reset(); !labels.done(); labels.next() {
|
||||||
|
label := labels.label()
|
||||||
|
if !ascii(label) {
|
||||||
|
a, err2 := encode(acePrefix, label)
|
||||||
|
if err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
label = a
|
||||||
|
labels.set(a)
|
||||||
|
}
|
||||||
|
n := len(label)
|
||||||
|
if p.verifyDNSLength && err == nil && (n == 0 || n > 63) {
|
||||||
|
err = &labelError{label, "A4"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = labels.result()
|
||||||
|
if toASCII && p.verifyDNSLength && err == nil {
|
||||||
|
// Compute the length of the domain name minus the root label and its dot.
|
||||||
|
n := len(s)
|
||||||
|
if n > 0 && s[n-1] == '.' {
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
if len(s) < 1 || n > 253 {
|
||||||
|
err = &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalize(p *Profile, s string) (string, error) {
|
||||||
|
return norm.NFC.String(s), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateRegistration(p *Profile, s string) (string, error) {
|
||||||
|
if !norm.NFC.IsNormalString(s) {
|
||||||
|
return s, &labelError{s, "V1"}
|
||||||
|
}
|
||||||
|
for i := 0; i < len(s); {
|
||||||
|
v, sz := trie.lookupString(s[i:])
|
||||||
|
// Copy bytes not copied so far.
|
||||||
|
switch p.simplify(info(v).category()) {
|
||||||
|
// TODO: handle the NV8 defined in the Unicode idna data set to allow
|
||||||
|
// for strict conformance to IDNA2008.
|
||||||
|
case valid, deviation:
|
||||||
|
case disallowed, mapped, unknown, ignored:
|
||||||
|
r, _ := utf8.DecodeRuneInString(s[i:])
|
||||||
|
return s, runeError(r)
|
||||||
|
}
|
||||||
|
i += sz
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateAndMap(p *Profile, s string) (string, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
b []byte
|
||||||
|
k int
|
||||||
|
)
|
||||||
|
for i := 0; i < len(s); {
|
||||||
|
v, sz := trie.lookupString(s[i:])
|
||||||
|
start := i
|
||||||
|
i += sz
|
||||||
|
// Copy bytes not copied so far.
|
||||||
|
switch p.simplify(info(v).category()) {
|
||||||
|
case valid:
|
||||||
|
continue
|
||||||
|
case disallowed:
|
||||||
|
if err == nil {
|
||||||
|
r, _ := utf8.DecodeRuneInString(s[start:])
|
||||||
|
err = runeError(r)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case mapped, deviation:
|
||||||
|
b = append(b, s[k:start]...)
|
||||||
|
b = info(v).appendMapping(b, s[start:i])
|
||||||
|
case ignored:
|
||||||
|
b = append(b, s[k:start]...)
|
||||||
|
// drop the rune
|
||||||
|
case unknown:
|
||||||
|
b = append(b, s[k:start]...)
|
||||||
|
b = append(b, "\ufffd"...)
|
||||||
|
}
|
||||||
|
k = i
|
||||||
|
}
|
||||||
|
if k == 0 {
|
||||||
|
// No changes so far.
|
||||||
|
s = norm.NFC.String(s)
|
||||||
|
} else {
|
||||||
|
b = append(b, s[k:]...)
|
||||||
|
if norm.NFC.QuickSpan(b) != len(b) {
|
||||||
|
b = norm.NFC.Bytes(b)
|
||||||
|
}
|
||||||
|
// TODO: the punycode converters require strings as input.
|
||||||
|
s = string(b)
|
||||||
|
}
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// A labelIter allows iterating over domain name labels.
|
||||||
|
type labelIter struct {
|
||||||
|
orig string
|
||||||
|
slice []string
|
||||||
|
curStart int
|
||||||
|
curEnd int
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) reset() {
|
||||||
|
l.curStart = 0
|
||||||
|
l.curEnd = 0
|
||||||
|
l.i = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) done() bool {
|
||||||
|
return l.curStart >= len(l.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) result() string {
|
||||||
|
if l.slice != nil {
|
||||||
|
return strings.Join(l.slice, ".")
|
||||||
|
}
|
||||||
|
return l.orig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) label() string {
|
||||||
|
if l.slice != nil {
|
||||||
|
return l.slice[l.i]
|
||||||
|
}
|
||||||
|
p := strings.IndexByte(l.orig[l.curStart:], '.')
|
||||||
|
l.curEnd = l.curStart + p
|
||||||
|
if p == -1 {
|
||||||
|
l.curEnd = len(l.orig)
|
||||||
|
}
|
||||||
|
return l.orig[l.curStart:l.curEnd]
|
||||||
|
}
|
||||||
|
|
||||||
|
// next sets the value to the next label. It skips the last label if it is empty.
|
||||||
|
func (l *labelIter) next() {
|
||||||
|
l.i++
|
||||||
|
if l.slice != nil {
|
||||||
|
if l.i >= len(l.slice) || l.i == len(l.slice)-1 && l.slice[l.i] == "" {
|
||||||
|
l.curStart = len(l.orig)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l.curStart = l.curEnd + 1
|
||||||
|
if l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
|
||||||
|
l.curStart = len(l.orig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) set(s string) {
|
||||||
|
if l.slice == nil {
|
||||||
|
l.slice = strings.Split(l.orig, ".")
|
||||||
|
}
|
||||||
|
l.slice[l.i] = s
|
||||||
|
}
|
||||||
|
|
||||||
|
// acePrefix is the ASCII Compatible Encoding prefix.
|
||||||
|
const acePrefix = "xn--"
|
||||||
|
|
||||||
|
func (p *Profile) simplify(cat category) category {
|
||||||
|
switch cat {
|
||||||
|
case disallowedSTD3Mapped:
|
||||||
|
if p.useSTD3Rules {
|
||||||
|
cat = disallowed
|
||||||
|
} else {
|
||||||
|
cat = mapped
|
||||||
|
}
|
||||||
|
case disallowedSTD3Valid:
|
||||||
|
if p.useSTD3Rules {
|
||||||
|
cat = disallowed
|
||||||
|
} else {
|
||||||
|
cat = valid
|
||||||
|
}
|
||||||
|
case deviation:
|
||||||
|
if !p.transitional {
|
||||||
|
cat = valid
|
||||||
|
}
|
||||||
|
case validNV8, validXV8:
|
||||||
|
// TODO: handle V2008
|
||||||
|
cat = valid
|
||||||
|
}
|
||||||
|
return cat
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateFromPunycode(p *Profile, s string) error {
|
||||||
|
if !norm.NFC.IsNormalString(s) {
|
||||||
|
return &labelError{s, "V1"}
|
||||||
|
}
|
||||||
|
for i := 0; i < len(s); {
|
||||||
|
v, sz := trie.lookupString(s[i:])
|
||||||
|
if c := p.simplify(info(v).category()); c != valid && c != deviation {
|
||||||
|
return &labelError{s, "V6"}
|
||||||
|
}
|
||||||
|
i += sz
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
zwnj = "\u200c"
|
||||||
|
zwj = "\u200d"
|
||||||
|
)
|
||||||
|
|
||||||
|
type joinState int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
stateStart joinState = iota
|
||||||
|
stateVirama
|
||||||
|
stateBefore
|
||||||
|
stateBeforeVirama
|
||||||
|
stateAfter
|
||||||
|
stateFAIL
|
||||||
|
)
|
||||||
|
|
||||||
|
var joinStates = [][numJoinTypes]joinState{
|
||||||
|
stateStart: {
|
||||||
|
joiningL: stateBefore,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
joinZWNJ: stateFAIL,
|
||||||
|
joinZWJ: stateFAIL,
|
||||||
|
joinVirama: stateVirama,
|
||||||
|
},
|
||||||
|
stateVirama: {
|
||||||
|
joiningL: stateBefore,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
},
|
||||||
|
stateBefore: {
|
||||||
|
joiningL: stateBefore,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
joiningT: stateBefore,
|
||||||
|
joinZWNJ: stateAfter,
|
||||||
|
joinZWJ: stateFAIL,
|
||||||
|
joinVirama: stateBeforeVirama,
|
||||||
|
},
|
||||||
|
stateBeforeVirama: {
|
||||||
|
joiningL: stateBefore,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
joiningT: stateBefore,
|
||||||
|
},
|
||||||
|
stateAfter: {
|
||||||
|
joiningL: stateFAIL,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
joiningT: stateAfter,
|
||||||
|
joiningR: stateStart,
|
||||||
|
joinZWNJ: stateFAIL,
|
||||||
|
joinZWJ: stateFAIL,
|
||||||
|
joinVirama: stateAfter, // no-op as we can't accept joiners here
|
||||||
|
},
|
||||||
|
stateFAIL: {
|
||||||
|
0: stateFAIL,
|
||||||
|
joiningL: stateFAIL,
|
||||||
|
joiningD: stateFAIL,
|
||||||
|
joiningT: stateFAIL,
|
||||||
|
joiningR: stateFAIL,
|
||||||
|
joinZWNJ: stateFAIL,
|
||||||
|
joinZWJ: stateFAIL,
|
||||||
|
joinVirama: stateFAIL,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateLabel validates the criteria from Section 4.1. Item 1, 4, and 6 are
|
||||||
|
// already implicitly satisfied by the overall implementation.
|
||||||
|
func (p *Profile) validateLabel(s string) error {
|
||||||
|
if s == "" {
|
||||||
|
if p.verifyDNSLength {
|
||||||
|
return &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if p.bidirule != nil && !p.bidirule(s) {
|
||||||
|
return &labelError{s, "B"}
|
||||||
|
}
|
||||||
|
if p.checkHyphens {
|
||||||
|
if len(s) > 4 && s[2] == '-' && s[3] == '-' {
|
||||||
|
return &labelError{s, "V2"}
|
||||||
|
}
|
||||||
|
if s[0] == '-' || s[len(s)-1] == '-' {
|
||||||
|
return &labelError{s, "V3"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !p.checkJoiners {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
trie := p.trie // p.checkJoiners is only set if trie is set.
|
||||||
|
// TODO: merge the use of this in the trie.
|
||||||
|
v, sz := trie.lookupString(s)
|
||||||
|
x := info(v)
|
||||||
|
if x.isModifier() {
|
||||||
|
return &labelError{s, "V5"}
|
||||||
|
}
|
||||||
|
// Quickly return in the absence of zero-width (non) joiners.
|
||||||
|
if strings.Index(s, zwj) == -1 && strings.Index(s, zwnj) == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
st := stateStart
|
||||||
|
for i := 0; ; {
|
||||||
|
jt := x.joinType()
|
||||||
|
if s[i:i+sz] == zwj {
|
||||||
|
jt = joinZWJ
|
||||||
|
} else if s[i:i+sz] == zwnj {
|
||||||
|
jt = joinZWNJ
|
||||||
|
}
|
||||||
|
st = joinStates[st][jt]
|
||||||
|
if x.isViramaModifier() {
|
||||||
|
st = joinStates[st][joinVirama]
|
||||||
|
}
|
||||||
|
if i += sz; i == len(s) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
v, sz = trie.lookupString(s[i:])
|
||||||
|
x = info(v)
|
||||||
|
}
|
||||||
|
if st == stateFAIL || st == stateAfter {
|
||||||
|
return &labelError{s, "C"}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ascii(s string) bool {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] >= utf8.RuneSelf {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
|
@ -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
|
|
@ -0,0 +1,217 @@
|
||||||
|
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||||
|
|
||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
package idna
|
||||||
|
|
||||||
|
// This file implements the Punycode algorithm from RFC 3492.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// These parameter values are specified in section 5.
|
||||||
|
//
|
||||||
|
// All computation is done with int32s, so that overflow behavior is identical
|
||||||
|
// regardless of whether int is 32-bit or 64-bit.
|
||||||
|
const (
|
||||||
|
base int32 = 36
|
||||||
|
damp int32 = 700
|
||||||
|
initialBias int32 = 72
|
||||||
|
initialN int32 = 128
|
||||||
|
skew int32 = 38
|
||||||
|
tmax int32 = 26
|
||||||
|
tmin int32 = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
func punyError(s string) error { return &labelError{s, "A3"} }
|
||||||
|
|
||||||
|
// decode decodes a string as specified in section 6.2.
|
||||||
|
func decode(encoded string) (string, error) {
|
||||||
|
if encoded == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
pos := 1 + strings.LastIndex(encoded, "-")
|
||||||
|
if pos == 1 {
|
||||||
|
return "", punyError(encoded)
|
||||||
|
}
|
||||||
|
if pos == len(encoded) {
|
||||||
|
return encoded[:len(encoded)-1], nil
|
||||||
|
}
|
||||||
|
output := make([]rune, 0, len(encoded))
|
||||||
|
if pos != 0 {
|
||||||
|
for _, r := range encoded[:pos-1] {
|
||||||
|
output = append(output, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i, n, bias := int32(0), initialN, initialBias
|
||||||
|
overflow := false
|
||||||
|
for pos < len(encoded) {
|
||||||
|
oldI, w := i, int32(1)
|
||||||
|
for k := base; ; k += base {
|
||||||
|
if pos == len(encoded) {
|
||||||
|
return "", punyError(encoded)
|
||||||
|
}
|
||||||
|
digit, ok := decodeDigit(encoded[pos])
|
||||||
|
if !ok {
|
||||||
|
return "", punyError(encoded)
|
||||||
|
}
|
||||||
|
pos++
|
||||||
|
i, overflow = madd(i, digit, w)
|
||||||
|
if overflow {
|
||||||
|
return "", punyError(encoded)
|
||||||
|
}
|
||||||
|
t := k - bias
|
||||||
|
if k <= bias {
|
||||||
|
t = tmin
|
||||||
|
} else if k >= bias+tmax {
|
||||||
|
t = tmax
|
||||||
|
}
|
||||||
|
if digit < t {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
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 < 0 || n > utf8.MaxRune {
|
||||||
|
return "", punyError(encoded)
|
||||||
|
}
|
||||||
|
output = append(output, 0)
|
||||||
|
copy(output[i+1:], output[i:])
|
||||||
|
output[i] = n
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return string(output), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode encodes a string as specified in section 6.3 and prepends prefix to
|
||||||
|
// the result.
|
||||||
|
//
|
||||||
|
// The "while h < length(input)" line in the specification becomes "for
|
||||||
|
// remaining != 0" in the Go code, because len(s) in Go is in bytes, not runes.
|
||||||
|
func encode(prefix, s string) (string, error) {
|
||||||
|
output := make([]byte, len(prefix), len(prefix)+1+2*len(s))
|
||||||
|
copy(output, prefix)
|
||||||
|
delta, n, bias := int32(0), initialN, initialBias
|
||||||
|
b, remaining := int32(0), int32(0)
|
||||||
|
for _, r := range s {
|
||||||
|
if r < 0x80 {
|
||||||
|
b++
|
||||||
|
output = append(output, byte(r))
|
||||||
|
} else {
|
||||||
|
remaining++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h := b
|
||||||
|
if b > 0 {
|
||||||
|
output = append(output, '-')
|
||||||
|
}
|
||||||
|
overflow := false
|
||||||
|
for remaining != 0 {
|
||||||
|
m := int32(0x7fffffff)
|
||||||
|
for _, r := range s {
|
||||||
|
if m > r && r >= n {
|
||||||
|
m = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delta, overflow = madd(delta, m-n, h+1)
|
||||||
|
if overflow {
|
||||||
|
return "", punyError(s)
|
||||||
|
}
|
||||||
|
n = m
|
||||||
|
for _, r := range s {
|
||||||
|
if r < n {
|
||||||
|
delta++
|
||||||
|
if delta < 0 {
|
||||||
|
return "", punyError(s)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if r > n {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
q := delta
|
||||||
|
for k := base; ; k += base {
|
||||||
|
t := k - bias
|
||||||
|
if k <= bias {
|
||||||
|
t = tmin
|
||||||
|
} else if k >= bias+tmax {
|
||||||
|
t = tmax
|
||||||
|
}
|
||||||
|
if q < t {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
output = append(output, encodeDigit(t+(q-t)%(base-t)))
|
||||||
|
q = (q - t) / (base - t)
|
||||||
|
}
|
||||||
|
output = append(output, encodeDigit(q))
|
||||||
|
bias = adapt(delta, h+1, h == b)
|
||||||
|
delta = 0
|
||||||
|
h++
|
||||||
|
remaining--
|
||||||
|
}
|
||||||
|
delta++
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
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':
|
||||||
|
return int32(x - ('0' - 26)), true
|
||||||
|
case 'A' <= x && x <= 'Z':
|
||||||
|
return int32(x - 'A'), true
|
||||||
|
case 'a' <= x && x <= 'z':
|
||||||
|
return int32(x - 'a'), true
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeDigit(digit int32) byte {
|
||||||
|
switch {
|
||||||
|
case 0 <= digit && digit < 26:
|
||||||
|
return byte(digit + 'a')
|
||||||
|
case 26 <= digit && digit < 36:
|
||||||
|
return byte(digit + ('0' - 26))
|
||||||
|
}
|
||||||
|
panic("idna: internal error in punycode encoding")
|
||||||
|
}
|
||||||
|
|
||||||
|
// adapt is the bias adaptation function specified in section 6.1.
|
||||||
|
func adapt(delta, numPoints int32, firstTime bool) int32 {
|
||||||
|
if firstTime {
|
||||||
|
delta /= damp
|
||||||
|
} else {
|
||||||
|
delta /= 2
|
||||||
|
}
|
||||||
|
delta += delta / numPoints
|
||||||
|
k := int32(0)
|
||||||
|
for delta > ((base-tmin)*tmax)/2 {
|
||||||
|
delta /= base - tmin
|
||||||
|
k += base
|
||||||
|
}
|
||||||
|
return k + (base-tmin+1)*delta/(delta+skew)
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,72 @@
|
||||||
|
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||||
|
|
||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
package idna
|
||||||
|
|
||||||
|
// appendMapping appends the mapping for the respective rune. isMapped must be
|
||||||
|
// true. A mapping is a categorization of a rune as defined in UTS #46.
|
||||||
|
func (c info) appendMapping(b []byte, s string) []byte {
|
||||||
|
index := int(c >> indexShift)
|
||||||
|
if c&xorBit == 0 {
|
||||||
|
s := mappings[index:]
|
||||||
|
return append(b, s[1:s[0]+1]...)
|
||||||
|
}
|
||||||
|
b = append(b, s...)
|
||||||
|
if c&inlineXOR == inlineXOR {
|
||||||
|
// TODO: support and handle two-byte inline masks
|
||||||
|
b[len(b)-1] ^= byte(index)
|
||||||
|
} else {
|
||||||
|
for p := len(b) - int(xorData[index]); p < len(b); p++ {
|
||||||
|
index++
|
||||||
|
b[p] ^= xorData[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sparse block handling code.
|
||||||
|
|
||||||
|
type valueRange struct {
|
||||||
|
value uint16 // header: value:stride
|
||||||
|
lo, hi byte // header: lo:n
|
||||||
|
}
|
||||||
|
|
||||||
|
type sparseBlocks struct {
|
||||||
|
values []valueRange
|
||||||
|
offset []uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
var idnaSparse = sparseBlocks{
|
||||||
|
values: idnaSparseValues[:],
|
||||||
|
offset: idnaSparseOffset[:],
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't use newIdnaTrie to avoid unconditional linking in of the table.
|
||||||
|
var trie = &idnaTrie{}
|
||||||
|
|
||||||
|
// lookup determines the type of block n and looks up the value for b.
|
||||||
|
// For n < t.cutoff, the block is a simple lookup table. Otherwise, the block
|
||||||
|
// is a list of ranges with an accompanying value. Given a matching range r,
|
||||||
|
// the value for b is by r.value + (b - r.lo) * stride.
|
||||||
|
func (t *sparseBlocks) lookup(n uint32, b byte) uint16 {
|
||||||
|
offset := t.offset[n]
|
||||||
|
header := t.values[offset]
|
||||||
|
lo := offset + 1
|
||||||
|
hi := lo + uint16(header.lo)
|
||||||
|
for lo < hi {
|
||||||
|
m := lo + (hi-lo)/2
|
||||||
|
r := t.values[m]
|
||||||
|
if r.lo <= b && b <= r.hi {
|
||||||
|
return r.value + uint16(b-r.lo)*header.value
|
||||||
|
}
|
||||||
|
if b < r.lo {
|
||||||
|
hi = m
|
||||||
|
} else {
|
||||||
|
lo = m + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||||
|
|
||||||
|
package idna
|
||||||
|
|
||||||
|
// This file contains definitions for interpreting the trie value of the idna
|
||||||
|
// trie generated by "go run gen*.go". It is shared by both the generator
|
||||||
|
// program and the resultant package. Sharing is achieved by the generator
|
||||||
|
// copying gen_trieval.go to trieval.go and changing what's above this comment.
|
||||||
|
|
||||||
|
// info holds information from the IDNA mapping table for a single rune. It is
|
||||||
|
// the value returned by a trie lookup. In most cases, all information fits in
|
||||||
|
// a 16-bit value. For mappings, this value may contain an index into a slice
|
||||||
|
// with the mapped string. Such mappings can consist of the actual mapped value
|
||||||
|
// or an XOR pattern to be applied to the bytes of the UTF8 encoding of the
|
||||||
|
// input rune. This technique is used by the cases packages and reduces the
|
||||||
|
// table size significantly.
|
||||||
|
//
|
||||||
|
// The per-rune values have the following format:
|
||||||
|
//
|
||||||
|
// if mapped {
|
||||||
|
// if inlinedXOR {
|
||||||
|
// 15..13 inline XOR marker
|
||||||
|
// 12..11 unused
|
||||||
|
// 10..3 inline XOR mask
|
||||||
|
// } else {
|
||||||
|
// 15..3 index into xor or mapping table
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// 15..14 unused
|
||||||
|
// 13 mayNeedNorm
|
||||||
|
// 12..11 attributes
|
||||||
|
// 10..8 joining type
|
||||||
|
// 7..3 category type
|
||||||
|
// }
|
||||||
|
// 2 use xor pattern
|
||||||
|
// 1..0 mapped category
|
||||||
|
//
|
||||||
|
// See the definitions below for a more detailed description of the various
|
||||||
|
// bits.
|
||||||
|
type info uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
catSmallMask = 0x3
|
||||||
|
catBigMask = 0xF8
|
||||||
|
indexShift = 3
|
||||||
|
xorBit = 0x4 // interpret the index as an xor pattern
|
||||||
|
inlineXOR = 0xE000 // These bits are set if the XOR pattern is inlined.
|
||||||
|
|
||||||
|
joinShift = 8
|
||||||
|
joinMask = 0x07
|
||||||
|
|
||||||
|
// Attributes
|
||||||
|
attributesMask = 0x1800
|
||||||
|
viramaModifier = 0x1800
|
||||||
|
modifier = 0x1000
|
||||||
|
rtl = 0x0800
|
||||||
|
|
||||||
|
mayNeedNorm = 0x2000
|
||||||
|
)
|
||||||
|
|
||||||
|
// A category corresponds to a category defined in the IDNA mapping table.
|
||||||
|
type category uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
unknown category = 0 // not currently defined in unicode.
|
||||||
|
mapped category = 1
|
||||||
|
disallowedSTD3Mapped category = 2
|
||||||
|
deviation category = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
valid category = 0x08
|
||||||
|
validNV8 category = 0x18
|
||||||
|
validXV8 category = 0x28
|
||||||
|
disallowed category = 0x40
|
||||||
|
disallowedSTD3Valid category = 0x80
|
||||||
|
ignored category = 0xC0
|
||||||
|
)
|
||||||
|
|
||||||
|
// join types and additional rune information
|
||||||
|
const (
|
||||||
|
joiningL = (iota + 1)
|
||||||
|
joiningD
|
||||||
|
joiningT
|
||||||
|
joiningR
|
||||||
|
|
||||||
|
//the following types are derived during processing
|
||||||
|
joinZWJ
|
||||||
|
joinZWNJ
|
||||||
|
joinVirama
|
||||||
|
numJoinTypes
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c info) isMapped() bool {
|
||||||
|
return c&0x3 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c info) category() category {
|
||||||
|
small := c & catSmallMask
|
||||||
|
if small != 0 {
|
||||||
|
return category(small)
|
||||||
|
}
|
||||||
|
return category(c & catBigMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c info) joinType() info {
|
||||||
|
if c.isMapped() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return (c >> joinShift) & joinMask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c info) isModifier() bool {
|
||||||
|
return c&(modifier|catSmallMask) == modifier
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c info) isViramaModifier() bool {
|
||||||
|
return c&(attributesMask|catSmallMask) == viramaModifier
|
||||||
|
}
|
|
@ -5,17 +5,17 @@
|
||||||
package webdav
|
package webdav
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// slashClean is equivalent to but slightly more efficient than
|
// slashClean is equivalent to but slightly more efficient than
|
||||||
|
@ -163,6 +163,7 @@ type memFS struct {
|
||||||
// - "/", "foo", false
|
// - "/", "foo", false
|
||||||
// - "/foo/", "bar", false
|
// - "/foo/", "bar", false
|
||||||
// - "/foo/bar/", "x", true
|
// - "/foo/bar/", "x", true
|
||||||
|
//
|
||||||
// The frag argument will be empty only if dir is the root node and the walk
|
// The frag argument will be empty only if dir is the root node and the walk
|
||||||
// ends at that root node.
|
// ends at that root node.
|
||||||
func (fs *memFS) walk(op, fullname string, f func(dir *memFSNode, frag string, final bool) error) error {
|
func (fs *memFS) walk(op, fullname string, f func(dir *memFSNode, frag string, final bool) error) error {
|
||||||
|
@ -273,8 +274,14 @@ func (fs *memFS) OpenFile(ctx context.Context, name string, flag int, perm os.Fi
|
||||||
var n *memFSNode
|
var n *memFSNode
|
||||||
if dir == nil {
|
if dir == nil {
|
||||||
// We're opening the root.
|
// We're opening the root.
|
||||||
if flag&(os.O_WRONLY|os.O_RDWR) != 0 {
|
if runtime.GOOS == "zos" {
|
||||||
return nil, os.ErrPermission
|
if flag&os.O_WRONLY != 0 {
|
||||||
|
return nil, os.ErrPermission
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if flag&(os.O_WRONLY|os.O_RDWR) != 0 {
|
||||||
|
return nil, os.ErrPermission
|
||||||
|
}
|
||||||
}
|
}
|
||||||
n, frag = &fs.root, "/"
|
n, frag = &fs.root, "/"
|
||||||
|
|
||||||
|
@ -540,11 +547,11 @@ func (f *memFile) Seek(offset int64, whence int) (int64, error) {
|
||||||
npos := f.pos
|
npos := f.pos
|
||||||
// TODO: How to handle offsets greater than the size of system int?
|
// TODO: How to handle offsets greater than the size of system int?
|
||||||
switch whence {
|
switch whence {
|
||||||
case os.SEEK_SET:
|
case io.SeekStart:
|
||||||
npos = int(offset)
|
npos = int(offset)
|
||||||
case os.SEEK_CUR:
|
case io.SeekCurrent:
|
||||||
npos += int(offset)
|
npos += int(offset)
|
||||||
case os.SEEK_END:
|
case io.SeekEnd:
|
||||||
npos = len(f.n.data) + int(offset)
|
npos = len(f.n.data) + int(offset)
|
||||||
default:
|
default:
|
||||||
npos = -1
|
npos = -1
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
// Copyright 2016 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.
|
|
||||||
|
|
||||||
// +build !go1.7
|
|
||||||
|
|
||||||
package webdav
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getContext(r *http.Request) context.Context {
|
|
||||||
return context.Background()
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
// Copyright 2016 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.
|
|
||||||
|
|
||||||
// +build go1.7
|
|
||||||
|
|
||||||
package webdav
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getContext(r *http.Request) context.Context {
|
|
||||||
return r.Context()
|
|
||||||
}
|
|
|
@ -32,33 +32,33 @@ const (
|
||||||
// elements containing the data.
|
// elements containing the data.
|
||||||
//
|
//
|
||||||
// The name for the XML elements is taken from, in order of preference:
|
// The name for the XML elements is taken from, in order of preference:
|
||||||
// - the tag on the XMLName field, if the data is a struct
|
// - the tag on the XMLName field, if the data is a struct
|
||||||
// - the value of the XMLName field of type xml.Name
|
// - the value of the XMLName field of type xml.Name
|
||||||
// - the tag of the struct field used to obtain the data
|
// - the tag of the struct field used to obtain the data
|
||||||
// - the name of the struct field used to obtain the data
|
// - the name of the struct field used to obtain the data
|
||||||
// - the name of the marshalled type
|
// - the name of the marshalled type
|
||||||
//
|
//
|
||||||
// The XML element for a struct contains marshalled elements for each of the
|
// The XML element for a struct contains marshalled elements for each of the
|
||||||
// exported fields of the struct, with these exceptions:
|
// exported fields of the struct, with these exceptions:
|
||||||
// - the XMLName field, described above, is omitted.
|
// - the XMLName field, described above, is omitted.
|
||||||
// - a field with tag "-" is omitted.
|
// - a field with tag "-" is omitted.
|
||||||
// - a field with tag "name,attr" becomes an attribute with
|
// - a field with tag "name,attr" becomes an attribute with
|
||||||
// the given name in the XML element.
|
// the given name in the XML element.
|
||||||
// - a field with tag ",attr" becomes an attribute with the
|
// - a field with tag ",attr" becomes an attribute with the
|
||||||
// field name in the XML element.
|
// field name in the XML element.
|
||||||
// - a field with tag ",chardata" is written as character data,
|
// - a field with tag ",chardata" is written as character data,
|
||||||
// not as an XML element.
|
// not as an XML element.
|
||||||
// - a field with tag ",innerxml" is written verbatim, not subject
|
// - a field with tag ",innerxml" is written verbatim, not subject
|
||||||
// to the usual marshalling procedure.
|
// to the usual marshalling procedure.
|
||||||
// - a field with tag ",comment" is written as an XML comment, not
|
// - a field with tag ",comment" is written as an XML comment, not
|
||||||
// subject to the usual marshalling procedure. It must not contain
|
// subject to the usual marshalling procedure. It must not contain
|
||||||
// the "--" string within it.
|
// the "--" string within it.
|
||||||
// - a field with a tag including the "omitempty" option is omitted
|
// - a field with a tag including the "omitempty" option is omitted
|
||||||
// if the field value is empty. The empty values are false, 0, any
|
// if the field value is empty. The empty values are false, 0, any
|
||||||
// nil pointer or interface value, and any array, slice, map, or
|
// nil pointer or interface value, and any array, slice, map, or
|
||||||
// string of length zero.
|
// string of length zero.
|
||||||
// - an anonymous struct field is handled as if the fields of its
|
// - an anonymous struct field is handled as if the fields of its
|
||||||
// value were part of the outer struct.
|
// value were part of the outer struct.
|
||||||
//
|
//
|
||||||
// If a field uses a tag "a>b>c", then the element c will be nested inside
|
// If a field uses a tag "a>b>c", then the element c will be nested inside
|
||||||
// parent elements a and b. Fields that appear next to each other that name
|
// parent elements a and b. Fields that appear next to each other that name
|
||||||
|
|
|
@ -35,57 +35,57 @@ import (
|
||||||
// In the rules, the tag of a field refers to the value associated with the
|
// In the rules, the tag of a field refers to the value associated with the
|
||||||
// key 'xml' in the struct field's tag (see the example above).
|
// key 'xml' in the struct field's tag (see the example above).
|
||||||
//
|
//
|
||||||
// * If the struct has a field of type []byte or string with tag
|
// - If the struct has a field of type []byte or string with tag
|
||||||
// ",innerxml", Unmarshal accumulates the raw XML nested inside the
|
// ",innerxml", Unmarshal accumulates the raw XML nested inside the
|
||||||
// element in that field. The rest of the rules still apply.
|
// element in that field. The rest of the rules still apply.
|
||||||
//
|
//
|
||||||
// * If the struct has a field named XMLName of type xml.Name,
|
// - If the struct has a field named XMLName of type xml.Name,
|
||||||
// Unmarshal records the element name in that field.
|
// Unmarshal records the element name in that field.
|
||||||
//
|
//
|
||||||
// * If the XMLName field has an associated tag of the form
|
// - If the XMLName field has an associated tag of the form
|
||||||
// "name" or "namespace-URL name", the XML element must have
|
// "name" or "namespace-URL name", the XML element must have
|
||||||
// the given name (and, optionally, name space) or else Unmarshal
|
// the given name (and, optionally, name space) or else Unmarshal
|
||||||
// returns an error.
|
// returns an error.
|
||||||
//
|
//
|
||||||
// * If the XML element has an attribute whose name matches a
|
// - If the XML element has an attribute whose name matches a
|
||||||
// struct field name with an associated tag containing ",attr" or
|
// struct field name with an associated tag containing ",attr" or
|
||||||
// the explicit name in a struct field tag of the form "name,attr",
|
// the explicit name in a struct field tag of the form "name,attr",
|
||||||
// Unmarshal records the attribute value in that field.
|
// Unmarshal records the attribute value in that field.
|
||||||
//
|
//
|
||||||
// * If the XML element contains character data, that data is
|
// - If the XML element contains character data, that data is
|
||||||
// accumulated in the first struct field that has tag ",chardata".
|
// accumulated in the first struct field that has tag ",chardata".
|
||||||
// The struct field may have type []byte or string.
|
// The struct field may have type []byte or string.
|
||||||
// If there is no such field, the character data is discarded.
|
// If there is no such field, the character data is discarded.
|
||||||
//
|
//
|
||||||
// * If the XML element contains comments, they are accumulated in
|
// - If the XML element contains comments, they are accumulated in
|
||||||
// the first struct field that has tag ",comment". The struct
|
// the first struct field that has tag ",comment". The struct
|
||||||
// field may have type []byte or string. If there is no such
|
// field may have type []byte or string. If there is no such
|
||||||
// field, the comments are discarded.
|
// field, the comments are discarded.
|
||||||
//
|
//
|
||||||
// * If the XML element contains a sub-element whose name matches
|
// - If the XML element contains a sub-element whose name matches
|
||||||
// the prefix of a tag formatted as "a" or "a>b>c", unmarshal
|
// the prefix of a tag formatted as "a" or "a>b>c", unmarshal
|
||||||
// will descend into the XML structure looking for elements with the
|
// will descend into the XML structure looking for elements with the
|
||||||
// given names, and will map the innermost elements to that struct
|
// given names, and will map the innermost elements to that struct
|
||||||
// field. A tag starting with ">" is equivalent to one starting
|
// field. A tag starting with ">" is equivalent to one starting
|
||||||
// with the field name followed by ">".
|
// with the field name followed by ">".
|
||||||
//
|
//
|
||||||
// * If the XML element contains a sub-element whose name matches
|
// - If the XML element contains a sub-element whose name matches
|
||||||
// a struct field's XMLName tag and the struct field has no
|
// a struct field's XMLName tag and the struct field has no
|
||||||
// explicit name tag as per the previous rule, unmarshal maps
|
// explicit name tag as per the previous rule, unmarshal maps
|
||||||
// the sub-element to that struct field.
|
// the sub-element to that struct field.
|
||||||
//
|
//
|
||||||
// * If the XML element contains a sub-element whose name matches a
|
// - If the XML element contains a sub-element whose name matches a
|
||||||
// field without any mode flags (",attr", ",chardata", etc), Unmarshal
|
// field without any mode flags (",attr", ",chardata", etc), Unmarshal
|
||||||
// maps the sub-element to that struct field.
|
// maps the sub-element to that struct field.
|
||||||
//
|
//
|
||||||
// * If the XML element contains a sub-element that hasn't matched any
|
// - If the XML element contains a sub-element that hasn't matched any
|
||||||
// of the above rules and the struct has a field with tag ",any",
|
// of the above rules and the struct has a field with tag ",any",
|
||||||
// unmarshal maps the sub-element to that struct field.
|
// unmarshal maps the sub-element to that struct field.
|
||||||
//
|
//
|
||||||
// * An anonymous struct field is handled as if the fields of its
|
// - An anonymous struct field is handled as if the fields of its
|
||||||
// value were part of the outer struct.
|
// value were part of the outer struct.
|
||||||
//
|
//
|
||||||
// * A struct field with tag "-" is never unmarshalled into.
|
// - A struct field with tag "-" is never unmarshalled into.
|
||||||
//
|
//
|
||||||
// Unmarshal maps an XML element to a string or []byte by saving the
|
// Unmarshal maps an XML element to a string or []byte by saving the
|
||||||
// concatenation of that element's character data in the string or
|
// concatenation of that element's character data in the string or
|
||||||
|
@ -110,7 +110,6 @@ import (
|
||||||
//
|
//
|
||||||
// Unmarshal maps an XML element to a pointer by setting the pointer
|
// Unmarshal maps an XML element to a pointer by setting the pointer
|
||||||
// to a freshly allocated value and then mapping the element to that value.
|
// to a freshly allocated value and then mapping the element to that value.
|
||||||
//
|
|
||||||
func Unmarshal(data []byte, v interface{}) error {
|
func Unmarshal(data []byte, v interface{}) error {
|
||||||
return NewDecoder(bytes.NewReader(data)).Decode(v)
|
return NewDecoder(bytes.NewReader(data)).Decode(v)
|
||||||
}
|
}
|
||||||
|
|
|
@ -945,7 +945,7 @@ func (d *Decoder) ungetc(b byte) {
|
||||||
d.offset--
|
d.offset--
|
||||||
}
|
}
|
||||||
|
|
||||||
var entity = map[string]int{
|
var entity = map[string]rune{
|
||||||
"lt": '<',
|
"lt": '<',
|
||||||
"gt": '>',
|
"gt": '>',
|
||||||
"amp": '&',
|
"amp": '&',
|
||||||
|
@ -1040,7 +1040,7 @@ Input:
|
||||||
d.buf.WriteByte(';')
|
d.buf.WriteByte(';')
|
||||||
n, err := strconv.ParseUint(s, base, 64)
|
n, err := strconv.ParseUint(s, base, 64)
|
||||||
if err == nil && n <= unicode.MaxRune {
|
if err == nil && n <= unicode.MaxRune {
|
||||||
text = string(n)
|
text = string(rune(n))
|
||||||
haveText = true
|
haveText = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ package webdav
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -15,8 +16,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Proppatch describes a property update instruction as defined in RFC 4918.
|
// Proppatch describes a property update instruction as defined in RFC 4918.
|
||||||
|
@ -163,7 +162,7 @@ var liveProps = map[xml.Name]struct {
|
||||||
|
|
||||||
// TODO(nigeltao) merge props and allprop?
|
// TODO(nigeltao) merge props and allprop?
|
||||||
|
|
||||||
// Props returns the status of the properties named pnames for resource name.
|
// props returns the status of the properties named pnames for resource name.
|
||||||
//
|
//
|
||||||
// Each Propstat has a unique status and each property name will only be part
|
// Each Propstat has a unique status and each property name will only be part
|
||||||
// of one Propstat element.
|
// of one Propstat element.
|
||||||
|
@ -214,7 +213,7 @@ func props(ctx context.Context, fs FileSystem, ls LockSystem, name string, pname
|
||||||
return makePropstats(pstatOK, pstatNotFound), nil
|
return makePropstats(pstatOK, pstatNotFound), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Propnames returns the property names defined for resource name.
|
// propnames returns the property names defined for resource name.
|
||||||
func propnames(ctx context.Context, fs FileSystem, ls LockSystem, name string) ([]xml.Name, error) {
|
func propnames(ctx context.Context, fs FileSystem, ls LockSystem, name string) ([]xml.Name, error) {
|
||||||
f, err := fs.OpenFile(ctx, name, os.O_RDONLY, 0)
|
f, err := fs.OpenFile(ctx, name, os.O_RDONLY, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -247,7 +246,7 @@ func propnames(ctx context.Context, fs FileSystem, ls LockSystem, name string) (
|
||||||
return pnames, nil
|
return pnames, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allprop returns the properties defined for resource name and the properties
|
// allprop returns the properties defined for resource name and the properties
|
||||||
// named in include.
|
// named in include.
|
||||||
//
|
//
|
||||||
// Note that RFC 4918 defines 'allprop' to return the DAV: properties defined
|
// Note that RFC 4918 defines 'allprop' to return the DAV: properties defined
|
||||||
|
@ -273,7 +272,7 @@ func allprop(ctx context.Context, fs FileSystem, ls LockSystem, name string, inc
|
||||||
return props(ctx, fs, ls, name, pnames)
|
return props(ctx, fs, ls, name, pnames)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch patches the properties of resource name. The return values are
|
// patch patches the properties of resource name. The return values are
|
||||||
// constrained in the same manner as DeadPropsHolder.Patch.
|
// constrained in the same manner as DeadPropsHolder.Patch.
|
||||||
func patch(ctx context.Context, fs FileSystem, ls LockSystem, name string, patches []Proppatch) ([]Propstat, error) {
|
func patch(ctx context.Context, fs FileSystem, ls LockSystem, name string, patches []Proppatch) ([]Propstat, error) {
|
||||||
conflict := false
|
conflict := false
|
||||||
|
@ -426,7 +425,7 @@ func findContentType(ctx context.Context, fs FileSystem, ls LockSystem, name str
|
||||||
}
|
}
|
||||||
ctype = http.DetectContentType(buf[:n])
|
ctype = http.DetectContentType(buf[:n])
|
||||||
// Rewind file.
|
// Rewind file.
|
||||||
_, err = f.Seek(0, os.SEEK_SET)
|
_, err = f.Seek(0, io.SeekStart)
|
||||||
return ctype, err
|
return ctype, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -174,7 +175,7 @@ func (h *Handler) handleOptions(w http.ResponseWriter, r *http.Request) (status
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return status, err
|
return status, err
|
||||||
}
|
}
|
||||||
ctx := getContext(r)
|
ctx := r.Context()
|
||||||
allow := "OPTIONS, LOCK, PUT, MKCOL"
|
allow := "OPTIONS, LOCK, PUT, MKCOL"
|
||||||
if fi, err := h.FileSystem.Stat(ctx, reqPath); err == nil {
|
if fi, err := h.FileSystem.Stat(ctx, reqPath); err == nil {
|
||||||
if fi.IsDir() {
|
if fi.IsDir() {
|
||||||
|
@ -197,7 +198,7 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request) (sta
|
||||||
return status, err
|
return status, err
|
||||||
}
|
}
|
||||||
// TODO: check locks for read-only access??
|
// TODO: check locks for read-only access??
|
||||||
ctx := getContext(r)
|
ctx := r.Context()
|
||||||
f, err := h.FileSystem.OpenFile(ctx, reqPath, os.O_RDONLY, 0)
|
f, err := h.FileSystem.OpenFile(ctx, reqPath, os.O_RDONLY, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return http.StatusNotFound, err
|
return http.StatusNotFound, err
|
||||||
|
@ -231,7 +232,7 @@ func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request) (status i
|
||||||
}
|
}
|
||||||
defer release()
|
defer release()
|
||||||
|
|
||||||
ctx := getContext(r)
|
ctx := r.Context()
|
||||||
|
|
||||||
// TODO: return MultiStatus where appropriate.
|
// TODO: return MultiStatus where appropriate.
|
||||||
|
|
||||||
|
@ -262,7 +263,7 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request) (status int,
|
||||||
defer release()
|
defer release()
|
||||||
// TODO(rost): Support the If-Match, If-None-Match headers? See bradfitz'
|
// TODO(rost): Support the If-Match, If-None-Match headers? See bradfitz'
|
||||||
// comments in http.checkEtag.
|
// comments in http.checkEtag.
|
||||||
ctx := getContext(r)
|
ctx := r.Context()
|
||||||
|
|
||||||
f, err := h.FileSystem.OpenFile(ctx, reqPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
|
f, err := h.FileSystem.OpenFile(ctx, reqPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -300,7 +301,7 @@ func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request) (status in
|
||||||
}
|
}
|
||||||
defer release()
|
defer release()
|
||||||
|
|
||||||
ctx := getContext(r)
|
ctx := r.Context()
|
||||||
|
|
||||||
if r.ContentLength > 0 {
|
if r.ContentLength > 0 {
|
||||||
return http.StatusUnsupportedMediaType, nil
|
return http.StatusUnsupportedMediaType, nil
|
||||||
|
@ -323,7 +324,7 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request) (status
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return http.StatusBadRequest, errInvalidDestination
|
return http.StatusBadRequest, errInvalidDestination
|
||||||
}
|
}
|
||||||
if u.Host != r.Host {
|
if u.Host != "" && u.Host != r.Host {
|
||||||
return http.StatusBadGateway, errInvalidDestination
|
return http.StatusBadGateway, errInvalidDestination
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +345,7 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request) (status
|
||||||
return http.StatusForbidden, errDestinationEqualsSource
|
return http.StatusForbidden, errDestinationEqualsSource
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := getContext(r)
|
ctx := r.Context()
|
||||||
|
|
||||||
if r.Method == "COPY" {
|
if r.Method == "COPY" {
|
||||||
// Section 7.5.1 says that a COPY only needs to lock the destination,
|
// Section 7.5.1 says that a COPY only needs to lock the destination,
|
||||||
|
@ -399,7 +400,7 @@ func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request) (retStatus
|
||||||
return status, err
|
return status, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := getContext(r)
|
ctx := r.Context()
|
||||||
token, ld, now, created := "", LockDetails{}, time.Now(), false
|
token, ld, now, created := "", LockDetails{}, time.Now(), false
|
||||||
if li == (lockInfo{}) {
|
if li == (lockInfo{}) {
|
||||||
// An empty lockInfo means to refresh the lock.
|
// An empty lockInfo means to refresh the lock.
|
||||||
|
@ -511,7 +512,7 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return status, err
|
return status, err
|
||||||
}
|
}
|
||||||
ctx := getContext(r)
|
ctx := r.Context()
|
||||||
fi, err := h.FileSystem.Stat(ctx, reqPath)
|
fi, err := h.FileSystem.Stat(ctx, reqPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
@ -535,13 +536,14 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status
|
||||||
|
|
||||||
walkFn := func(reqPath string, info os.FileInfo, err error) error {
|
walkFn := func(reqPath string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return handlePropfindError(err, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
var pstats []Propstat
|
var pstats []Propstat
|
||||||
if pf.Propname != nil {
|
if pf.Propname != nil {
|
||||||
pnames, err := propnames(ctx, h.FileSystem, h.LockSystem, reqPath)
|
pnames, err := propnames(ctx, h.FileSystem, h.LockSystem, reqPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return handlePropfindError(err, info)
|
||||||
}
|
}
|
||||||
pstat := Propstat{Status: http.StatusOK}
|
pstat := Propstat{Status: http.StatusOK}
|
||||||
for _, xmlname := range pnames {
|
for _, xmlname := range pnames {
|
||||||
|
@ -554,9 +556,13 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status
|
||||||
pstats, err = props(ctx, h.FileSystem, h.LockSystem, reqPath, pf.Prop)
|
pstats, err = props(ctx, h.FileSystem, h.LockSystem, reqPath, pf.Prop)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return handlePropfindError(err, info)
|
||||||
}
|
}
|
||||||
return mw.write(makePropstatResponse(path.Join(h.Prefix, reqPath), pstats))
|
href := path.Join(h.Prefix, reqPath)
|
||||||
|
if href != "/" && info.IsDir() {
|
||||||
|
href += "/"
|
||||||
|
}
|
||||||
|
return mw.write(makePropstatResponse(href, pstats))
|
||||||
}
|
}
|
||||||
|
|
||||||
walkErr := walkFS(ctx, h.FileSystem, depth, reqPath, fi, walkFn)
|
walkErr := walkFS(ctx, h.FileSystem, depth, reqPath, fi, walkFn)
|
||||||
|
@ -581,7 +587,7 @@ func (h *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (statu
|
||||||
}
|
}
|
||||||
defer release()
|
defer release()
|
||||||
|
|
||||||
ctx := getContext(r)
|
ctx := r.Context()
|
||||||
|
|
||||||
if _, err := h.FileSystem.Stat(ctx, reqPath); err != nil {
|
if _, err := h.FileSystem.Stat(ctx, reqPath); err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
@ -629,6 +635,33 @@ func makePropstatResponse(href string, pstats []Propstat) *response {
|
||||||
return &resp
|
return &resp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handlePropfindError(err error, info os.FileInfo) error {
|
||||||
|
var skipResp error = nil
|
||||||
|
if info != nil && info.IsDir() {
|
||||||
|
skipResp = filepath.SkipDir
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.Is(err, os.ErrPermission) {
|
||||||
|
// If the server cannot recurse into a directory because it is not allowed,
|
||||||
|
// then there is nothing more to say about it. Just skip sending anything.
|
||||||
|
return skipResp
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := err.(*os.PathError); ok {
|
||||||
|
// If the file is just bad, it couldn't be a proper WebDAV resource. Skip it.
|
||||||
|
return skipResp
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to be careful with other errors: there is no way to abort the xml stream
|
||||||
|
// part way through while returning a valid PROPFIND response. Returning only half
|
||||||
|
// the data would be misleading, but so would be returning results tainted by errors.
|
||||||
|
// The current behaviour by returning an error here leads to the stream being aborted,
|
||||||
|
// and the parent http server complaining about writing a spurious header. We should
|
||||||
|
// consider further enhancing this error handling to more gracefully fail, or perhaps
|
||||||
|
// buffer the entire response until we've walked the tree.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
infiniteDepth = -1
|
infiniteDepth = -1
|
||||||
invalidDepth = -2
|
invalidDepth = -2
|
||||||
|
@ -638,10 +671,11 @@ const (
|
||||||
// infiniteDepth. Parsing any other string returns invalidDepth.
|
// infiniteDepth. Parsing any other string returns invalidDepth.
|
||||||
//
|
//
|
||||||
// Different WebDAV methods have further constraints on valid depths:
|
// Different WebDAV methods have further constraints on valid depths:
|
||||||
// - PROPFIND has no further restrictions, as per section 9.1.
|
// - PROPFIND has no further restrictions, as per section 9.1.
|
||||||
// - COPY accepts only "0" or "infinity", as per section 9.8.3.
|
// - COPY accepts only "0" or "infinity", as per section 9.8.3.
|
||||||
// - MOVE accepts only "infinity", as per section 9.9.2.
|
// - MOVE accepts only "infinity", as per section 9.9.2.
|
||||||
// - LOCK accepts only "0" or "infinity", as per section 9.10.3.
|
// - LOCK accepts only "0" or "infinity", as per section 9.10.3.
|
||||||
|
//
|
||||||
// These constraints are enforced by the handleXxx methods.
|
// These constraints are enforced by the handleXxx methods.
|
||||||
func parseDepth(s string) int {
|
func parseDepth(s string) int {
|
||||||
switch s {
|
switch s {
|
||||||
|
|
|
@ -113,7 +113,7 @@ func escape(s string) string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next returns the next token, if any, in the XML stream of d.
|
// next returns the next token, if any, in the XML stream of d.
|
||||||
// RFC 4918 requires to ignore comments, processing instructions
|
// RFC 4918 requires to ignore comments, processing instructions
|
||||||
// and directives.
|
// and directives.
|
||||||
// http://www.webdav.org/specs/rfc4918.html#property_values
|
// http://www.webdav.org/specs/rfc4918.html#property_values
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
# This source code refers to The Go Authors for copyright purposes.
|
|
||||||
# The master list of authors is in the main Go distribution,
|
|
||||||
# visible at http://tip.golang.org/AUTHORS.
|
|
|
@ -1,3 +0,0 @@
|
||||||
# This source code was written by the Go contributors.
|
|
||||||
# The master list of contributors is in the main Go distribution,
|
|
||||||
# visible at http://tip.golang.org/CONTRIBUTORS.
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2020 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.
|
||||||
|
|
||||||
|
// Package unsafeheader contains header declarations for the Go runtime's
|
||||||
|
// slice and string implementations.
|
||||||
|
//
|
||||||
|
// This package allows x/sys to use types equivalent to
|
||||||
|
// reflect.SliceHeader and reflect.StringHeader without introducing
|
||||||
|
// a dependency on the (relatively heavy) "reflect" package.
|
||||||
|
package unsafeheader
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Slice is the runtime representation of a slice.
|
||||||
|
// It cannot be used safely or portably and its representation may change in a later release.
|
||||||
|
type Slice struct {
|
||||||
|
Data unsafe.Pointer
|
||||||
|
Len int
|
||||||
|
Cap int
|
||||||
|
}
|
||||||
|
|
||||||
|
// String is the runtime representation of a string.
|
||||||
|
// It cannot be used safely or portably and its representation may change in a later release.
|
||||||
|
type String struct {
|
||||||
|
Data unsafe.Pointer
|
||||||
|
Len int
|
||||||
|
}
|
|
@ -76,7 +76,7 @@ arguments can be passed to the kernel. The third is for low-level use by the
|
||||||
ForkExec wrapper. Unlike the first two, it does not call into the scheduler to
|
ForkExec wrapper. Unlike the first two, it does not call into the scheduler to
|
||||||
let it know that a system call is running.
|
let it know that a system call is running.
|
||||||
|
|
||||||
When porting Go to an new architecture/OS, this file must be implemented for
|
When porting Go to a new architecture/OS, this file must be implemented for
|
||||||
each GOOS/GOARCH pair.
|
each GOOS/GOARCH pair.
|
||||||
|
|
||||||
### mksysnum
|
### mksysnum
|
||||||
|
@ -89,7 +89,7 @@ constants.
|
||||||
|
|
||||||
Adding new syscall numbers is mostly done by running the build on a sufficiently
|
Adding new syscall numbers is mostly done by running the build on a sufficiently
|
||||||
new installation of the target OS (or updating the source checkouts for the
|
new installation of the target OS (or updating the source checkouts for the
|
||||||
new build system). However, depending on the OS, you make need to update the
|
new build system). However, depending on the OS, you may need to update the
|
||||||
parsing in mksysnum.
|
parsing in mksysnum.
|
||||||
|
|
||||||
### mksyscall.go
|
### mksyscall.go
|
||||||
|
@ -107,7 +107,7 @@ prototype can be exported (capitalized) or not.
|
||||||
Adding a new syscall often just requires adding a new `//sys` function prototype
|
Adding a new syscall often just requires adding a new `//sys` function prototype
|
||||||
with the desired arguments and a capitalized name so it is exported. However, if
|
with the desired arguments and a capitalized name so it is exported. However, if
|
||||||
you want the interface to the syscall to be different, often one will make an
|
you want the interface to the syscall to be different, often one will make an
|
||||||
unexported `//sys` prototype, an then write a custom wrapper in
|
unexported `//sys` prototype, and then write a custom wrapper in
|
||||||
`syscall_${GOOS}.go`.
|
`syscall_${GOOS}.go`.
|
||||||
|
|
||||||
### types files
|
### types files
|
||||||
|
@ -137,7 +137,7 @@ some `#if/#elif` macros in your include statements.
|
||||||
|
|
||||||
This script is used to generate the system's various constants. This doesn't
|
This script is used to generate the system's various constants. This doesn't
|
||||||
just include the error numbers and error strings, but also the signal numbers
|
just include the error numbers and error strings, but also the signal numbers
|
||||||
an a wide variety of miscellaneous constants. The constants come from the list
|
and a wide variety of miscellaneous constants. The constants come from the list
|
||||||
of include files in the `includes_${uname}` variable. A regex then picks out
|
of include files in the `includes_${uname}` variable. A regex then picks out
|
||||||
the desired `#define` statements, and generates the corresponding Go constants.
|
the desired `#define` statements, and generates the corresponding Go constants.
|
||||||
The error numbers and strings are generated from `#include <errno.h>`, and the
|
The error numbers and strings are generated from `#include <errno.h>`, and the
|
||||||
|
@ -149,10 +149,21 @@ 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
|
Then, edit the regex (if necessary) to match the desired constant. Avoid making
|
||||||
the regex too broad to avoid matching unintended constants.
|
the regex too broad to avoid matching unintended constants.
|
||||||
|
|
||||||
|
### 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
|
||||||
|
into a common file for each OS.
|
||||||
|
|
||||||
|
The merge is performed in the following steps:
|
||||||
|
1. Construct the set of common code that is idential in all architecture-specific files.
|
||||||
|
2. Write this common code to the merged file.
|
||||||
|
3. Remove the common code from all architecture-specific files.
|
||||||
|
|
||||||
|
|
||||||
## Generated files
|
## Generated files
|
||||||
|
|
||||||
### `zerror_${GOOS}_${GOARCH}.go`
|
### `zerrors_${GOOS}_${GOARCH}.go`
|
||||||
|
|
||||||
A file containing all of the system's generated error numbers, error strings,
|
A file containing all of the system's generated error numbers, error strings,
|
||||||
signal numbers, and constants. Generated by `mkerrors.sh` (see above).
|
signal numbers, and constants. Generated by `mkerrors.sh` (see above).
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/bits"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -79,46 +80,7 @@ func (s *CPUSet) IsSet(cpu int) bool {
|
||||||
func (s *CPUSet) Count() int {
|
func (s *CPUSet) Count() int {
|
||||||
c := 0
|
c := 0
|
||||||
for _, b := range s {
|
for _, b := range s {
|
||||||
c += onesCount64(uint64(b))
|
c += bits.OnesCount64(uint64(b))
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// onesCount64 is a copy of Go 1.9's math/bits.OnesCount64.
|
|
||||||
// Once this package can require Go 1.9, we can delete this
|
|
||||||
// and update the caller to use bits.OnesCount64.
|
|
||||||
func onesCount64(x uint64) int {
|
|
||||||
const m0 = 0x5555555555555555 // 01010101 ...
|
|
||||||
const m1 = 0x3333333333333333 // 00110011 ...
|
|
||||||
const m2 = 0x0f0f0f0f0f0f0f0f // 00001111 ...
|
|
||||||
const m3 = 0x00ff00ff00ff00ff // etc.
|
|
||||||
const m4 = 0x0000ffff0000ffff
|
|
||||||
|
|
||||||
// Implementation: Parallel summing of adjacent bits.
|
|
||||||
// See "Hacker's Delight", Chap. 5: Counting Bits.
|
|
||||||
// The following pattern shows the general approach:
|
|
||||||
//
|
|
||||||
// x = x>>1&(m0&m) + x&(m0&m)
|
|
||||||
// x = x>>2&(m1&m) + x&(m1&m)
|
|
||||||
// x = x>>4&(m2&m) + x&(m2&m)
|
|
||||||
// x = x>>8&(m3&m) + x&(m3&m)
|
|
||||||
// x = x>>16&(m4&m) + x&(m4&m)
|
|
||||||
// x = x>>32&(m5&m) + x&(m5&m)
|
|
||||||
// return int(x)
|
|
||||||
//
|
|
||||||
// Masking (& operations) can be left away when there's no
|
|
||||||
// danger that a field's sum will carry over into the next
|
|
||||||
// field: Since the result cannot be > 64, 8 bits is enough
|
|
||||||
// and we can ignore the masks for the shifts by 8 and up.
|
|
||||||
// Per "Hacker's Delight", the first line can be simplified
|
|
||||||
// more, but it saves at best one instruction, so we leave
|
|
||||||
// it alone for clarity.
|
|
||||||
const m = 1<<64 - 1
|
|
||||||
x = x>>1&(m0&m) + x&(m0&m)
|
|
||||||
x = x>>2&(m1&m) + x&(m1&m)
|
|
||||||
x = (x>>4 + x) & (m2 & m)
|
|
||||||
x += x >> 8
|
|
||||||
x += x >> 16
|
|
||||||
x += x >> 32
|
|
||||||
return int(x) & (1<<7 - 1)
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
//go:build (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos) && go1.9
|
||||||
|
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
|
||||||
// +build go1.9
|
// +build go1.9
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !gccgo
|
//go:build gc
|
||||||
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !gccgo
|
//go:build (freebsd || netbsd || openbsd) && gc
|
||||||
|
// +build freebsd netbsd openbsd
|
||||||
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
//
|
// System call support for 386 BSD
|
||||||
// System call support for 386, FreeBSD
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
// The runtime may know about them.
|
// The runtime may know about them.
|
||||||
|
@ -22,7 +22,7 @@ TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||||
JMP syscall·Syscall9(SB)
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||||
JMP syscall·RawSyscall(SB)
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
|
@ -0,0 +1,29 @@
|
||||||
|
// 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 (darwin || dragonfly || freebsd || netbsd || openbsd) && gc
|
||||||
|
// +build darwin dragonfly freebsd netbsd openbsd
|
||||||
|
// +build gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// System call support for AMD64 BSD
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||||
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
|
@ -1,14 +1,14 @@
|
||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !gccgo
|
//go:build (freebsd || netbsd || openbsd) && gc
|
||||||
|
// +build freebsd netbsd openbsd
|
||||||
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
//
|
// System call support for ARM BSD
|
||||||
// System call support for ARM, FreeBSD
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
// The runtime may know about them.
|
// The runtime may know about them.
|
|
@ -1,14 +1,14 @@
|
||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !gccgo
|
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
||||||
|
// +build darwin freebsd netbsd openbsd
|
||||||
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
//
|
// System call support for ARM64 BSD
|
||||||
// System call support for AMD64, OpenBSD
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
// The runtime may know about them.
|
// The runtime may know about them.
|
|
@ -1,13 +1,15 @@
|
||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
// Copyright 2022 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !gccgo
|
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
||||||
|
// +build darwin freebsd netbsd openbsd
|
||||||
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
// System call support for AMD64, NetBSD
|
// System call support for ppc64, BSD
|
||||||
//
|
//
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
// Just jump to package syscall's implementation for all these functions.
|
|
@ -1,14 +1,14 @@
|
||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !gccgo
|
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
||||||
|
// +build darwin freebsd netbsd openbsd
|
||||||
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
//
|
// System call support for RISCV64 BSD
|
||||||
// System call support for AMD64, Darwin
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
// The runtime may know about them.
|
// The runtime may know about them.
|
|
@ -1,29 +0,0 @@
|
||||||
// Copyright 2009 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.
|
|
||||||
|
|
||||||
// +build !gccgo
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// System call support for 386, Darwin
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
|
||||||
JMP syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
|
||||||
JMP syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
|
||||||
JMP syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
|
||||||
JMP syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
|
||||||
JMP syscall·RawSyscall6(SB)
|
|
|
@ -1,30 +0,0 @@
|
||||||
// Copyright 2015 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.
|
|
||||||
|
|
||||||
// +build !gccgo
|
|
||||||
// +build arm,darwin
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// System call support for ARM, Darwin
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
|
||||||
B syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
|
||||||
B syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
|
||||||
B syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
|
||||||
B syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
|
||||||
B syscall·RawSyscall6(SB)
|
|
|
@ -1,30 +0,0 @@
|
||||||
// Copyright 2015 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.
|
|
||||||
|
|
||||||
// +build !gccgo
|
|
||||||
// +build arm64,darwin
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// System call support for AMD64, Darwin
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
|
||||||
B syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
|
||||||
B syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
|
||||||
B syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
|
||||||
B syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
|
||||||
B syscall·RawSyscall6(SB)
|
|
|
@ -1,29 +0,0 @@
|
||||||
// Copyright 2009 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.
|
|
||||||
|
|
||||||
// +build !gccgo
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// System call support for AMD64, DragonFly
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
|
||||||
JMP syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
|
||||||
JMP syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
|
||||||
JMP syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
|
||||||
JMP syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
|
||||||
JMP syscall·RawSyscall6(SB)
|
|
|
@ -1,29 +0,0 @@
|
||||||
// Copyright 2009 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.
|
|
||||||
|
|
||||||
// +build !gccgo
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// System call support for AMD64, FreeBSD
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
|
||||||
JMP syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
|
||||||
JMP syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
|
||||||
JMP syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
|
||||||
JMP syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
|
||||||
JMP syscall·RawSyscall6(SB)
|
|
|
@ -1,29 +0,0 @@
|
||||||
// Copyright 2018 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.
|
|
||||||
|
|
||||||
// +build !gccgo
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// System call support for ARM64, FreeBSD
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
|
||||||
JMP syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
|
||||||
JMP syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
|
||||||
JMP syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
|
||||||
JMP syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
|
||||||
JMP syscall·RawSyscall6(SB)
|
|
|
@ -2,7 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !gccgo
|
//go:build gc
|
||||||
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !gccgo
|
//go:build gc
|
||||||
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !gccgo
|
//go:build gc
|
||||||
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build linux && arm64 && gc
|
||||||
// +build linux
|
// +build linux
|
||||||
// +build arm64
|
// +build arm64
|
||||||
// +build !gccgo
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
// Copyright 2022 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 linux && loong64 && gc
|
||||||
|
// +build linux
|
||||||
|
// +build loong64
|
||||||
|
// +build gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
JAL runtime·entersyscall(SB)
|
||||||
|
MOVV a1+8(FP), R4
|
||||||
|
MOVV a2+16(FP), R5
|
||||||
|
MOVV a3+24(FP), R6
|
||||||
|
MOVV R0, R7
|
||||||
|
MOVV R0, R8
|
||||||
|
MOVV R0, R9
|
||||||
|
MOVV trap+0(FP), R11 // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVV R4, r1+32(FP)
|
||||||
|
MOVV R0, r2+40(FP) // r2 is not used. Always set to 0
|
||||||
|
JAL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
MOVV a1+8(FP), R4
|
||||||
|
MOVV a2+16(FP), R5
|
||||||
|
MOVV a3+24(FP), R6
|
||||||
|
MOVV R0, R7
|
||||||
|
MOVV R0, R8
|
||||||
|
MOVV R0, R9
|
||||||
|
MOVV trap+0(FP), R11 // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVV R4, r1+32(FP)
|
||||||
|
MOVV R0, r2+40(FP) // r2 is not used. Always set to 0
|
||||||
|
RET
|
|
@ -2,9 +2,10 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build linux && (mips64 || mips64le) && gc
|
||||||
// +build linux
|
// +build linux
|
||||||
// +build mips64 mips64le
|
// +build mips64 mips64le
|
||||||
// +build !gccgo
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build linux && (mips || mipsle) && gc
|
||||||
// +build linux
|
// +build linux
|
||||||
// +build mips mipsle
|
// +build mips mipsle
|
||||||
// +build !gccgo
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build linux && (ppc64 || ppc64le) && gc
|
||||||
// +build linux
|
// +build linux
|
||||||
// +build ppc64 ppc64le
|
// +build ppc64 ppc64le
|
||||||
// +build !gccgo
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build riscv64,!gccgo
|
//go:build riscv64 && gc
|
||||||
|
// +build riscv64
|
||||||
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
@ -23,10 +25,6 @@ TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||||
MOV a1+8(FP), A0
|
MOV a1+8(FP), A0
|
||||||
MOV a2+16(FP), A1
|
MOV a2+16(FP), A1
|
||||||
MOV a3+24(FP), A2
|
MOV a3+24(FP), A2
|
||||||
MOV $0, A3
|
|
||||||
MOV $0, A4
|
|
||||||
MOV $0, A5
|
|
||||||
MOV $0, A6
|
|
||||||
MOV trap+0(FP), A7 // syscall entry
|
MOV trap+0(FP), A7 // syscall entry
|
||||||
ECALL
|
ECALL
|
||||||
MOV A0, r1+32(FP) // r1
|
MOV A0, r1+32(FP) // r1
|
||||||
|
@ -44,9 +42,6 @@ TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||||
MOV a1+8(FP), A0
|
MOV a1+8(FP), A0
|
||||||
MOV a2+16(FP), A1
|
MOV a2+16(FP), A1
|
||||||
MOV a3+24(FP), A2
|
MOV a3+24(FP), A2
|
||||||
MOV ZERO, A3
|
|
||||||
MOV ZERO, A4
|
|
||||||
MOV ZERO, A5
|
|
||||||
MOV trap+0(FP), A7 // syscall entry
|
MOV trap+0(FP), A7 // syscall entry
|
||||||
ECALL
|
ECALL
|
||||||
MOV A0, r1+32(FP)
|
MOV A0, r1+32(FP)
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build s390x
|
//go:build linux && s390x && gc
|
||||||
// +build linux
|
// +build linux
|
||||||
// +build !gccgo
|
// +build s390x
|
||||||
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
// Copyright 2009 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.
|
|
||||||
|
|
||||||
// +build !gccgo
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// System call support for 386, NetBSD
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
|
||||||
JMP syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
|
||||||
JMP syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
|
||||||
JMP syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
|
||||||
JMP syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
|
||||||
JMP syscall·RawSyscall6(SB)
|
|
|
@ -1,29 +0,0 @@
|
||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
// +build !gccgo
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// System call support for ARM, NetBSD
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
|
||||||
B syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
|
||||||
B syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
|
||||||
B syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
|
||||||
B syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
|
||||||
B syscall·RawSyscall6(SB)
|
|
|
@ -1,29 +0,0 @@
|
||||||
// Copyright 2019 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.
|
|
||||||
|
|
||||||
// +build !gccgo
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// System call support for ARM64, NetBSD
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
|
||||||
B syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
|
||||||
B syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
|
||||||
B syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
|
||||||
B syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
|
||||||
B syscall·RawSyscall6(SB)
|
|
|
@ -1,29 +0,0 @@
|
||||||
// Copyright 2009 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.
|
|
||||||
|
|
||||||
// +build !gccgo
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// System call support for 386, OpenBSD
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
|
||||||
JMP syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
|
||||||
JMP syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
|
||||||
JMP syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
|
||||||
JMP syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
|
||||||
JMP syscall·RawSyscall6(SB)
|
|
|
@ -1,29 +0,0 @@
|
||||||
// Copyright 2017 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.
|
|
||||||
|
|
||||||
// +build !gccgo
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// System call support for ARM, OpenBSD
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
|
||||||
B syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
|
||||||
B syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
|
||||||
B syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
|
||||||
B syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
|
||||||
B syscall·RawSyscall6(SB)
|
|
|
@ -2,12 +2,13 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !gccgo
|
//go:build gc
|
||||||
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
// System call support for arm64, OpenBSD
|
// System call support for mips64, OpenBSD
|
||||||
//
|
//
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
// Just jump to package syscall's implementation for all these functions.
|
|
@ -2,7 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !gccgo
|
//go:build gc
|
||||||
|
// +build gc
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,426 @@
|
||||||
|
// Copyright 2020 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 zos && s390x && gc
|
||||||
|
// +build zos
|
||||||
|
// +build s390x
|
||||||
|
// +build gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
#define PSALAA 1208(R0)
|
||||||
|
#define GTAB64(x) 80(x)
|
||||||
|
#define LCA64(x) 88(x)
|
||||||
|
#define CAA(x) 8(x)
|
||||||
|
#define EDCHPXV(x) 1016(x) // in the CAA
|
||||||
|
#define SAVSTACK_ASYNC(x) 336(x) // in the LCA
|
||||||
|
|
||||||
|
// SS_*, where x=SAVSTACK_ASYNC
|
||||||
|
#define SS_LE(x) 0(x)
|
||||||
|
#define SS_GO(x) 8(x)
|
||||||
|
#define SS_ERRNO(x) 16(x)
|
||||||
|
#define SS_ERRNOJR(x) 20(x)
|
||||||
|
|
||||||
|
#define LE_CALL BYTE $0x0D; BYTE $0x76; // BL R7, R6
|
||||||
|
|
||||||
|
TEXT ·clearErrno(SB),NOSPLIT,$0-0
|
||||||
|
BL addrerrno<>(SB)
|
||||||
|
MOVD $0, 0(R3)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// Returns the address of errno in R3.
|
||||||
|
TEXT addrerrno<>(SB),NOSPLIT|NOFRAME,$0-0
|
||||||
|
// Get library control area (LCA).
|
||||||
|
MOVW PSALAA, R8
|
||||||
|
MOVD LCA64(R8), R8
|
||||||
|
|
||||||
|
// Get __errno FuncDesc.
|
||||||
|
MOVD CAA(R8), R9
|
||||||
|
MOVD EDCHPXV(R9), R9
|
||||||
|
ADD $(0x156*16), R9
|
||||||
|
LMG 0(R9), R5, R6
|
||||||
|
|
||||||
|
// Switch to saved LE stack.
|
||||||
|
MOVD SAVSTACK_ASYNC(R8), R9
|
||||||
|
MOVD 0(R9), R4
|
||||||
|
MOVD $0, 0(R9)
|
||||||
|
|
||||||
|
// Call __errno function.
|
||||||
|
LE_CALL
|
||||||
|
NOPH
|
||||||
|
|
||||||
|
// Switch back to Go stack.
|
||||||
|
XOR R0, R0 // Restore R0 to $0.
|
||||||
|
MOVD R4, 0(R9) // Save stack pointer.
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·syscall_syscall(SB),NOSPLIT,$0-56
|
||||||
|
BL runtime·entersyscall(SB)
|
||||||
|
MOVD a1+8(FP), R1
|
||||||
|
MOVD a2+16(FP), R2
|
||||||
|
MOVD a3+24(FP), R3
|
||||||
|
|
||||||
|
// Get library control area (LCA).
|
||||||
|
MOVW PSALAA, R8
|
||||||
|
MOVD LCA64(R8), R8
|
||||||
|
|
||||||
|
// Get function.
|
||||||
|
MOVD CAA(R8), R9
|
||||||
|
MOVD EDCHPXV(R9), R9
|
||||||
|
MOVD trap+0(FP), R5
|
||||||
|
SLD $4, R5
|
||||||
|
ADD R5, R9
|
||||||
|
LMG 0(R9), R5, R6
|
||||||
|
|
||||||
|
// Restore LE stack.
|
||||||
|
MOVD SAVSTACK_ASYNC(R8), R9
|
||||||
|
MOVD 0(R9), R4
|
||||||
|
MOVD $0, 0(R9)
|
||||||
|
|
||||||
|
// Call function.
|
||||||
|
LE_CALL
|
||||||
|
NOPH
|
||||||
|
XOR R0, R0 // Restore R0 to $0.
|
||||||
|
MOVD R4, 0(R9) // Save stack pointer.
|
||||||
|
|
||||||
|
MOVD R3, r1+32(FP)
|
||||||
|
MOVD R0, r2+40(FP)
|
||||||
|
MOVD R0, err+48(FP)
|
||||||
|
MOVW R3, R4
|
||||||
|
CMP R4, $-1
|
||||||
|
BNE done
|
||||||
|
BL addrerrno<>(SB)
|
||||||
|
MOVWZ 0(R3), R3
|
||||||
|
MOVD R3, err+48(FP)
|
||||||
|
done:
|
||||||
|
BL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·syscall_rawsyscall(SB),NOSPLIT,$0-56
|
||||||
|
MOVD a1+8(FP), R1
|
||||||
|
MOVD a2+16(FP), R2
|
||||||
|
MOVD a3+24(FP), R3
|
||||||
|
|
||||||
|
// Get library control area (LCA).
|
||||||
|
MOVW PSALAA, R8
|
||||||
|
MOVD LCA64(R8), R8
|
||||||
|
|
||||||
|
// Get function.
|
||||||
|
MOVD CAA(R8), R9
|
||||||
|
MOVD EDCHPXV(R9), R9
|
||||||
|
MOVD trap+0(FP), R5
|
||||||
|
SLD $4, R5
|
||||||
|
ADD R5, R9
|
||||||
|
LMG 0(R9), R5, R6
|
||||||
|
|
||||||
|
// Restore LE stack.
|
||||||
|
MOVD SAVSTACK_ASYNC(R8), R9
|
||||||
|
MOVD 0(R9), R4
|
||||||
|
MOVD $0, 0(R9)
|
||||||
|
|
||||||
|
// Call function.
|
||||||
|
LE_CALL
|
||||||
|
NOPH
|
||||||
|
XOR R0, R0 // Restore R0 to $0.
|
||||||
|
MOVD R4, 0(R9) // Save stack pointer.
|
||||||
|
|
||||||
|
MOVD R3, r1+32(FP)
|
||||||
|
MOVD R0, r2+40(FP)
|
||||||
|
MOVD R0, err+48(FP)
|
||||||
|
MOVW R3, R4
|
||||||
|
CMP R4, $-1
|
||||||
|
BNE done
|
||||||
|
BL addrerrno<>(SB)
|
||||||
|
MOVWZ 0(R3), R3
|
||||||
|
MOVD R3, err+48(FP)
|
||||||
|
done:
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·syscall_syscall6(SB),NOSPLIT,$0-80
|
||||||
|
BL runtime·entersyscall(SB)
|
||||||
|
MOVD a1+8(FP), R1
|
||||||
|
MOVD a2+16(FP), R2
|
||||||
|
MOVD a3+24(FP), R3
|
||||||
|
|
||||||
|
// Get library control area (LCA).
|
||||||
|
MOVW PSALAA, R8
|
||||||
|
MOVD LCA64(R8), R8
|
||||||
|
|
||||||
|
// Get function.
|
||||||
|
MOVD CAA(R8), R9
|
||||||
|
MOVD EDCHPXV(R9), R9
|
||||||
|
MOVD trap+0(FP), R5
|
||||||
|
SLD $4, R5
|
||||||
|
ADD R5, R9
|
||||||
|
LMG 0(R9), R5, R6
|
||||||
|
|
||||||
|
// Restore LE stack.
|
||||||
|
MOVD SAVSTACK_ASYNC(R8), R9
|
||||||
|
MOVD 0(R9), R4
|
||||||
|
MOVD $0, 0(R9)
|
||||||
|
|
||||||
|
// Fill in parameter list.
|
||||||
|
MOVD a4+32(FP), R12
|
||||||
|
MOVD R12, (2176+24)(R4)
|
||||||
|
MOVD a5+40(FP), R12
|
||||||
|
MOVD R12, (2176+32)(R4)
|
||||||
|
MOVD a6+48(FP), R12
|
||||||
|
MOVD R12, (2176+40)(R4)
|
||||||
|
|
||||||
|
// Call function.
|
||||||
|
LE_CALL
|
||||||
|
NOPH
|
||||||
|
XOR R0, R0 // Restore R0 to $0.
|
||||||
|
MOVD R4, 0(R9) // Save stack pointer.
|
||||||
|
|
||||||
|
MOVD R3, r1+56(FP)
|
||||||
|
MOVD R0, r2+64(FP)
|
||||||
|
MOVD R0, err+72(FP)
|
||||||
|
MOVW R3, R4
|
||||||
|
CMP R4, $-1
|
||||||
|
BNE done
|
||||||
|
BL addrerrno<>(SB)
|
||||||
|
MOVWZ 0(R3), R3
|
||||||
|
MOVD R3, err+72(FP)
|
||||||
|
done:
|
||||||
|
BL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·syscall_rawsyscall6(SB),NOSPLIT,$0-80
|
||||||
|
MOVD a1+8(FP), R1
|
||||||
|
MOVD a2+16(FP), R2
|
||||||
|
MOVD a3+24(FP), R3
|
||||||
|
|
||||||
|
// Get library control area (LCA).
|
||||||
|
MOVW PSALAA, R8
|
||||||
|
MOVD LCA64(R8), R8
|
||||||
|
|
||||||
|
// Get function.
|
||||||
|
MOVD CAA(R8), R9
|
||||||
|
MOVD EDCHPXV(R9), R9
|
||||||
|
MOVD trap+0(FP), R5
|
||||||
|
SLD $4, R5
|
||||||
|
ADD R5, R9
|
||||||
|
LMG 0(R9), R5, R6
|
||||||
|
|
||||||
|
// Restore LE stack.
|
||||||
|
MOVD SAVSTACK_ASYNC(R8), R9
|
||||||
|
MOVD 0(R9), R4
|
||||||
|
MOVD $0, 0(R9)
|
||||||
|
|
||||||
|
// Fill in parameter list.
|
||||||
|
MOVD a4+32(FP), R12
|
||||||
|
MOVD R12, (2176+24)(R4)
|
||||||
|
MOVD a5+40(FP), R12
|
||||||
|
MOVD R12, (2176+32)(R4)
|
||||||
|
MOVD a6+48(FP), R12
|
||||||
|
MOVD R12, (2176+40)(R4)
|
||||||
|
|
||||||
|
// Call function.
|
||||||
|
LE_CALL
|
||||||
|
NOPH
|
||||||
|
XOR R0, R0 // Restore R0 to $0.
|
||||||
|
MOVD R4, 0(R9) // Save stack pointer.
|
||||||
|
|
||||||
|
MOVD R3, r1+56(FP)
|
||||||
|
MOVD R0, r2+64(FP)
|
||||||
|
MOVD R0, err+72(FP)
|
||||||
|
MOVW R3, R4
|
||||||
|
CMP R4, $-1
|
||||||
|
BNE done
|
||||||
|
BL ·rrno<>(SB)
|
||||||
|
MOVWZ 0(R3), R3
|
||||||
|
MOVD R3, err+72(FP)
|
||||||
|
done:
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·syscall_syscall9(SB),NOSPLIT,$0
|
||||||
|
BL runtime·entersyscall(SB)
|
||||||
|
MOVD a1+8(FP), R1
|
||||||
|
MOVD a2+16(FP), R2
|
||||||
|
MOVD a3+24(FP), R3
|
||||||
|
|
||||||
|
// Get library control area (LCA).
|
||||||
|
MOVW PSALAA, R8
|
||||||
|
MOVD LCA64(R8), R8
|
||||||
|
|
||||||
|
// Get function.
|
||||||
|
MOVD CAA(R8), R9
|
||||||
|
MOVD EDCHPXV(R9), R9
|
||||||
|
MOVD trap+0(FP), R5
|
||||||
|
SLD $4, R5
|
||||||
|
ADD R5, R9
|
||||||
|
LMG 0(R9), R5, R6
|
||||||
|
|
||||||
|
// Restore LE stack.
|
||||||
|
MOVD SAVSTACK_ASYNC(R8), R9
|
||||||
|
MOVD 0(R9), R4
|
||||||
|
MOVD $0, 0(R9)
|
||||||
|
|
||||||
|
// Fill in parameter list.
|
||||||
|
MOVD a4+32(FP), R12
|
||||||
|
MOVD R12, (2176+24)(R4)
|
||||||
|
MOVD a5+40(FP), R12
|
||||||
|
MOVD R12, (2176+32)(R4)
|
||||||
|
MOVD a6+48(FP), R12
|
||||||
|
MOVD R12, (2176+40)(R4)
|
||||||
|
MOVD a7+56(FP), R12
|
||||||
|
MOVD R12, (2176+48)(R4)
|
||||||
|
MOVD a8+64(FP), R12
|
||||||
|
MOVD R12, (2176+56)(R4)
|
||||||
|
MOVD a9+72(FP), R12
|
||||||
|
MOVD R12, (2176+64)(R4)
|
||||||
|
|
||||||
|
// Call function.
|
||||||
|
LE_CALL
|
||||||
|
NOPH
|
||||||
|
XOR R0, R0 // Restore R0 to $0.
|
||||||
|
MOVD R4, 0(R9) // Save stack pointer.
|
||||||
|
|
||||||
|
MOVD R3, r1+80(FP)
|
||||||
|
MOVD R0, r2+88(FP)
|
||||||
|
MOVD R0, err+96(FP)
|
||||||
|
MOVW R3, R4
|
||||||
|
CMP R4, $-1
|
||||||
|
BNE done
|
||||||
|
BL addrerrno<>(SB)
|
||||||
|
MOVWZ 0(R3), R3
|
||||||
|
MOVD R3, err+96(FP)
|
||||||
|
done:
|
||||||
|
BL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·syscall_rawsyscall9(SB),NOSPLIT,$0
|
||||||
|
MOVD a1+8(FP), R1
|
||||||
|
MOVD a2+16(FP), R2
|
||||||
|
MOVD a3+24(FP), R3
|
||||||
|
|
||||||
|
// Get library control area (LCA).
|
||||||
|
MOVW PSALAA, R8
|
||||||
|
MOVD LCA64(R8), R8
|
||||||
|
|
||||||
|
// Get function.
|
||||||
|
MOVD CAA(R8), R9
|
||||||
|
MOVD EDCHPXV(R9), R9
|
||||||
|
MOVD trap+0(FP), R5
|
||||||
|
SLD $4, R5
|
||||||
|
ADD R5, R9
|
||||||
|
LMG 0(R9), R5, R6
|
||||||
|
|
||||||
|
// Restore LE stack.
|
||||||
|
MOVD SAVSTACK_ASYNC(R8), R9
|
||||||
|
MOVD 0(R9), R4
|
||||||
|
MOVD $0, 0(R9)
|
||||||
|
|
||||||
|
// Fill in parameter list.
|
||||||
|
MOVD a4+32(FP), R12
|
||||||
|
MOVD R12, (2176+24)(R4)
|
||||||
|
MOVD a5+40(FP), R12
|
||||||
|
MOVD R12, (2176+32)(R4)
|
||||||
|
MOVD a6+48(FP), R12
|
||||||
|
MOVD R12, (2176+40)(R4)
|
||||||
|
MOVD a7+56(FP), R12
|
||||||
|
MOVD R12, (2176+48)(R4)
|
||||||
|
MOVD a8+64(FP), R12
|
||||||
|
MOVD R12, (2176+56)(R4)
|
||||||
|
MOVD a9+72(FP), R12
|
||||||
|
MOVD R12, (2176+64)(R4)
|
||||||
|
|
||||||
|
// Call function.
|
||||||
|
LE_CALL
|
||||||
|
NOPH
|
||||||
|
XOR R0, R0 // Restore R0 to $0.
|
||||||
|
MOVD R4, 0(R9) // Save stack pointer.
|
||||||
|
|
||||||
|
MOVD R3, r1+80(FP)
|
||||||
|
MOVD R0, r2+88(FP)
|
||||||
|
MOVD R0, err+96(FP)
|
||||||
|
MOVW R3, R4
|
||||||
|
CMP R4, $-1
|
||||||
|
BNE done
|
||||||
|
BL addrerrno<>(SB)
|
||||||
|
MOVWZ 0(R3), R3
|
||||||
|
MOVD R3, err+96(FP)
|
||||||
|
done:
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func svcCall(fnptr unsafe.Pointer, argv *unsafe.Pointer, dsa *uint64)
|
||||||
|
TEXT ·svcCall(SB),NOSPLIT,$0
|
||||||
|
BL runtime·save_g(SB) // Save g and stack pointer
|
||||||
|
MOVW PSALAA, R8
|
||||||
|
MOVD LCA64(R8), R8
|
||||||
|
MOVD SAVSTACK_ASYNC(R8), R9
|
||||||
|
MOVD R15, 0(R9)
|
||||||
|
|
||||||
|
MOVD argv+8(FP), R1 // Move function arguments into registers
|
||||||
|
MOVD dsa+16(FP), g
|
||||||
|
MOVD fnptr+0(FP), R15
|
||||||
|
|
||||||
|
BYTE $0x0D // Branch to function
|
||||||
|
BYTE $0xEF
|
||||||
|
|
||||||
|
BL runtime·load_g(SB) // Restore g and stack pointer
|
||||||
|
MOVW PSALAA, R8
|
||||||
|
MOVD LCA64(R8), R8
|
||||||
|
MOVD SAVSTACK_ASYNC(R8), R9
|
||||||
|
MOVD 0(R9), R15
|
||||||
|
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func svcLoad(name *byte) unsafe.Pointer
|
||||||
|
TEXT ·svcLoad(SB),NOSPLIT,$0
|
||||||
|
MOVD R15, R2 // Save go stack pointer
|
||||||
|
MOVD name+0(FP), R0 // Move SVC args into registers
|
||||||
|
MOVD $0x80000000, R1
|
||||||
|
MOVD $0, R15
|
||||||
|
BYTE $0x0A // SVC 08 LOAD
|
||||||
|
BYTE $0x08
|
||||||
|
MOVW R15, R3 // Save return code from SVC
|
||||||
|
MOVD R2, R15 // Restore go stack pointer
|
||||||
|
CMP R3, $0 // Check SVC return code
|
||||||
|
BNE error
|
||||||
|
|
||||||
|
MOVD $-2, R3 // Reset last bit of entry point to zero
|
||||||
|
AND R0, R3
|
||||||
|
MOVD R3, addr+8(FP) // Return entry point returned by SVC
|
||||||
|
CMP R0, R3 // Check if last bit of entry point was set
|
||||||
|
BNE done
|
||||||
|
|
||||||
|
MOVD R15, R2 // Save go stack pointer
|
||||||
|
MOVD $0, R15 // Move SVC args into registers (entry point still in r0 from SVC 08)
|
||||||
|
BYTE $0x0A // SVC 09 DELETE
|
||||||
|
BYTE $0x09
|
||||||
|
MOVD R2, R15 // Restore go stack pointer
|
||||||
|
|
||||||
|
error:
|
||||||
|
MOVD $0, addr+8(FP) // Return 0 on failure
|
||||||
|
done:
|
||||||
|
XOR R0, R0 // Reset r0 to 0
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func svcUnload(name *byte, fnptr unsafe.Pointer) int64
|
||||||
|
TEXT ·svcUnload(SB),NOSPLIT,$0
|
||||||
|
MOVD R15, R2 // Save go stack pointer
|
||||||
|
MOVD name+0(FP), R0 // Move SVC args into registers
|
||||||
|
MOVD addr+8(FP), R15
|
||||||
|
BYTE $0x0A // SVC 09
|
||||||
|
BYTE $0x09
|
||||||
|
XOR R0, R0 // Reset r0 to 0
|
||||||
|
MOVD R15, R1 // Save SVC return code
|
||||||
|
MOVD R2, R15 // Restore go stack pointer
|
||||||
|
MOVD R1, rc+0(FP) // Return SVC return code
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func gettid() uint64
|
||||||
|
TEXT ·gettid(SB), NOSPLIT, $0
|
||||||
|
// Get library control area (LCA).
|
||||||
|
MOVW PSALAA, R8
|
||||||
|
MOVD LCA64(R8), R8
|
||||||
|
|
||||||
|
// Get CEECAATHDID
|
||||||
|
MOVD CAA(R8), R9
|
||||||
|
MOVD 0x3D0(R9), R9
|
||||||
|
MOVD R9, ret+0(FP)
|
||||||
|
|
||||||
|
RET
|
|
@ -23,6 +23,7 @@ const (
|
||||||
HCI_CHANNEL_USER = 1
|
HCI_CHANNEL_USER = 1
|
||||||
HCI_CHANNEL_MONITOR = 2
|
HCI_CHANNEL_MONITOR = 2
|
||||||
HCI_CHANNEL_CONTROL = 3
|
HCI_CHANNEL_CONTROL = 3
|
||||||
|
HCI_CHANNEL_LOGGING = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
// Socketoption Level
|
// Socketoption Level
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build freebsd
|
||||||
// +build freebsd
|
// +build freebsd
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
||||||
|
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build aix
|
//go:build aix && ppc
|
||||||
// +build ppc
|
// +build aix,ppc
|
||||||
|
|
||||||
// Functions to access/create device major and minor numbers matching the
|
// Functions to access/create device major and minor numbers matching the
|
||||||
// encoding used by AIX.
|
// encoding used by AIX.
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build aix
|
//go:build aix && ppc64
|
||||||
// +build ppc64
|
// +build aix,ppc64
|
||||||
|
|
||||||
// Functions to access/create device major and minor numbers matching the
|
// Functions to access/create device major and minor numbers matching the
|
||||||
// encoding used AIX.
|
// encoding used AIX.
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2020 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 zos && s390x
|
||||||
|
// +build zos,s390x
|
||||||
|
|
||||||
|
// Functions to access/create device major and minor numbers matching the
|
||||||
|
// encoding used by z/OS.
|
||||||
|
//
|
||||||
|
// The information below is extracted and adapted from <sys/stat.h> macros.
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
// Major returns the major component of a z/OS device number.
|
||||||
|
func Major(dev uint64) uint32 {
|
||||||
|
return uint32((dev >> 16) & 0x0000FFFF)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minor returns the minor component of a z/OS device number.
|
||||||
|
func Minor(dev uint64) uint32 {
|
||||||
|
return uint32(dev & 0x0000FFFF)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mkdev returns a z/OS device number generated from the given major and minor
|
||||||
|
// components.
|
||||||
|
func Mkdev(major, minor uint32) uint64 {
|
||||||
|
return (uint64(major) << 16) | uint64(minor)
|
||||||
|
}
|
|
@ -2,7 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris
|
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
||||||
|
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
//
|
//
|
||||||
// +build ppc64 s390x mips mips64
|
//go:build armbe || arm64be || m68k || mips || mips64 || mips64p32 || ppc || ppc64 || s390 || s390x || shbe || sparc || sparc64
|
||||||
|
// +build armbe arm64be m68k mips mips64 mips64p32 ppc ppc64 s390 s390x shbe sparc sparc64
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
//
|
//
|
||||||
// +build 386 amd64 amd64p32 arm arm64 ppc64le mipsle mips64le
|
//go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh
|
||||||
|
// +build 386 amd64 amd64p32 alpha arm arm64 loong64 mipsle mips64le mips64p32le nios2 ppc64le riscv riscv64 sh
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
||||||
|
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
|
||||||
|
|
||||||
// Unix environment variables.
|
// Unix environment variables.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,221 @@
|
||||||
|
// Copyright 2020 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 zos && s390x
|
||||||
|
// +build zos,s390x
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file simulates epoll on z/OS using poll.
|
||||||
|
|
||||||
|
// Analogous to epoll_event on Linux.
|
||||||
|
// TODO(neeilan): Pad is because the Linux kernel expects a 96-bit struct. We never pass this to the kernel; remove?
|
||||||
|
type EpollEvent struct {
|
||||||
|
Events uint32
|
||||||
|
Fd int32
|
||||||
|
Pad int32
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
EPOLLERR = 0x8
|
||||||
|
EPOLLHUP = 0x10
|
||||||
|
EPOLLIN = 0x1
|
||||||
|
EPOLLMSG = 0x400
|
||||||
|
EPOLLOUT = 0x4
|
||||||
|
EPOLLPRI = 0x2
|
||||||
|
EPOLLRDBAND = 0x80
|
||||||
|
EPOLLRDNORM = 0x40
|
||||||
|
EPOLLWRBAND = 0x200
|
||||||
|
EPOLLWRNORM = 0x100
|
||||||
|
EPOLL_CTL_ADD = 0x1
|
||||||
|
EPOLL_CTL_DEL = 0x2
|
||||||
|
EPOLL_CTL_MOD = 0x3
|
||||||
|
// The following constants are part of the epoll API, but represent
|
||||||
|
// currently unsupported functionality on z/OS.
|
||||||
|
// EPOLL_CLOEXEC = 0x80000
|
||||||
|
// EPOLLET = 0x80000000
|
||||||
|
// EPOLLONESHOT = 0x40000000
|
||||||
|
// EPOLLRDHUP = 0x2000 // Typically used with edge-triggered notis
|
||||||
|
// EPOLLEXCLUSIVE = 0x10000000 // Exclusive wake-up mode
|
||||||
|
// EPOLLWAKEUP = 0x20000000 // Relies on Linux's BLOCK_SUSPEND capability
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(neeilan): We can eliminate these epToPoll / pToEpoll calls by using identical mask values for POLL/EPOLL
|
||||||
|
// constants where possible The lower 16 bits of epoll events (uint32) can fit any system poll event (int16).
|
||||||
|
|
||||||
|
// epToPollEvt converts epoll event field to poll equivalent.
|
||||||
|
// In epoll, Events is a 32-bit field, while poll uses 16 bits.
|
||||||
|
func epToPollEvt(events uint32) int16 {
|
||||||
|
var ep2p = map[uint32]int16{
|
||||||
|
EPOLLIN: POLLIN,
|
||||||
|
EPOLLOUT: POLLOUT,
|
||||||
|
EPOLLHUP: POLLHUP,
|
||||||
|
EPOLLPRI: POLLPRI,
|
||||||
|
EPOLLERR: POLLERR,
|
||||||
|
}
|
||||||
|
|
||||||
|
var pollEvts int16 = 0
|
||||||
|
for epEvt, pEvt := range ep2p {
|
||||||
|
if (events & epEvt) != 0 {
|
||||||
|
pollEvts |= pEvt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pollEvts
|
||||||
|
}
|
||||||
|
|
||||||
|
// pToEpollEvt converts 16 bit poll event bitfields to 32-bit epoll event fields.
|
||||||
|
func pToEpollEvt(revents int16) uint32 {
|
||||||
|
var p2ep = map[int16]uint32{
|
||||||
|
POLLIN: EPOLLIN,
|
||||||
|
POLLOUT: EPOLLOUT,
|
||||||
|
POLLHUP: EPOLLHUP,
|
||||||
|
POLLPRI: EPOLLPRI,
|
||||||
|
POLLERR: EPOLLERR,
|
||||||
|
}
|
||||||
|
|
||||||
|
var epollEvts uint32 = 0
|
||||||
|
for pEvt, epEvt := range p2ep {
|
||||||
|
if (revents & pEvt) != 0 {
|
||||||
|
epollEvts |= epEvt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return epollEvts
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per-process epoll implementation.
|
||||||
|
type epollImpl struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
epfd2ep map[int]*eventPoll
|
||||||
|
nextEpfd int
|
||||||
|
}
|
||||||
|
|
||||||
|
// eventPoll holds a set of file descriptors being watched by the process. A process can have multiple epoll instances.
|
||||||
|
// On Linux, this is an in-kernel data structure accessed through a fd.
|
||||||
|
type eventPoll struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
fds map[int]*EpollEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
// epoll impl for this process.
|
||||||
|
var impl epollImpl = epollImpl{
|
||||||
|
epfd2ep: make(map[int]*eventPoll),
|
||||||
|
nextEpfd: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *epollImpl) epollcreate(size int) (epfd int, err error) {
|
||||||
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
epfd = e.nextEpfd
|
||||||
|
e.nextEpfd++
|
||||||
|
|
||||||
|
e.epfd2ep[epfd] = &eventPoll{
|
||||||
|
fds: make(map[int]*EpollEvent),
|
||||||
|
}
|
||||||
|
return epfd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *epollImpl) epollcreate1(flag int) (fd int, err error) {
|
||||||
|
return e.epollcreate(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *epollImpl) epollctl(epfd int, op int, fd int, event *EpollEvent) (err error) {
|
||||||
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
|
||||||
|
ep, ok := e.epfd2ep[epfd]
|
||||||
|
if !ok {
|
||||||
|
|
||||||
|
return EBADF
|
||||||
|
}
|
||||||
|
|
||||||
|
switch op {
|
||||||
|
case EPOLL_CTL_ADD:
|
||||||
|
// TODO(neeilan): When we make epfds and fds disjoint, detect epoll
|
||||||
|
// loops here (instances watching each other) and return ELOOP.
|
||||||
|
if _, ok := ep.fds[fd]; ok {
|
||||||
|
return EEXIST
|
||||||
|
}
|
||||||
|
ep.fds[fd] = event
|
||||||
|
case EPOLL_CTL_MOD:
|
||||||
|
if _, ok := ep.fds[fd]; !ok {
|
||||||
|
return ENOENT
|
||||||
|
}
|
||||||
|
ep.fds[fd] = event
|
||||||
|
case EPOLL_CTL_DEL:
|
||||||
|
if _, ok := ep.fds[fd]; !ok {
|
||||||
|
return ENOENT
|
||||||
|
}
|
||||||
|
delete(ep.fds, fd)
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be called while holding ep.mu
|
||||||
|
func (ep *eventPoll) getFds() []int {
|
||||||
|
fds := make([]int, len(ep.fds))
|
||||||
|
for fd := range ep.fds {
|
||||||
|
fds = append(fds, fd)
|
||||||
|
}
|
||||||
|
return fds
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *epollImpl) epollwait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||||
|
e.mu.Lock() // in [rare] case of concurrent epollcreate + epollwait
|
||||||
|
ep, ok := e.epfd2ep[epfd]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
e.mu.Unlock()
|
||||||
|
return 0, EBADF
|
||||||
|
}
|
||||||
|
|
||||||
|
pollfds := make([]PollFd, 4)
|
||||||
|
for fd, epollevt := range ep.fds {
|
||||||
|
pollfds = append(pollfds, PollFd{Fd: int32(fd), Events: epToPollEvt(epollevt.Events)})
|
||||||
|
}
|
||||||
|
e.mu.Unlock()
|
||||||
|
|
||||||
|
n, err = Poll(pollfds, msec)
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for _, pFd := range pollfds {
|
||||||
|
if pFd.Revents != 0 {
|
||||||
|
events[i] = EpollEvent{Fd: pFd.Fd, Events: pToEpollEvt(pFd.Revents)}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == n {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EpollCreate(size int) (fd int, err error) {
|
||||||
|
return impl.epollcreate(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EpollCreate1(flag int) (fd int, err error) {
|
||||||
|
return impl.epollcreate1(flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {
|
||||||
|
return impl.epollctl(epfd, op, fd, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because EpollWait mutates events, the caller is expected to coordinate
|
||||||
|
// concurrent access if calling with the same epfd from multiple goroutines.
|
||||||
|
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||||
|
return impl.epollwait(epfd, events, msec)
|
||||||
|
}
|
|
@ -1,227 +0,0 @@
|
||||||
// Copyright 2017 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.
|
|
||||||
|
|
||||||
// Constants that were deprecated or moved to enums in the FreeBSD headers. Keep
|
|
||||||
// them here for backwards compatibility.
|
|
||||||
|
|
||||||
package unix
|
|
||||||
|
|
||||||
const (
|
|
||||||
IFF_SMART = 0x20
|
|
||||||
IFT_1822 = 0x2
|
|
||||||
IFT_A12MPPSWITCH = 0x82
|
|
||||||
IFT_AAL2 = 0xbb
|
|
||||||
IFT_AAL5 = 0x31
|
|
||||||
IFT_ADSL = 0x5e
|
|
||||||
IFT_AFLANE8023 = 0x3b
|
|
||||||
IFT_AFLANE8025 = 0x3c
|
|
||||||
IFT_ARAP = 0x58
|
|
||||||
IFT_ARCNET = 0x23
|
|
||||||
IFT_ARCNETPLUS = 0x24
|
|
||||||
IFT_ASYNC = 0x54
|
|
||||||
IFT_ATM = 0x25
|
|
||||||
IFT_ATMDXI = 0x69
|
|
||||||
IFT_ATMFUNI = 0x6a
|
|
||||||
IFT_ATMIMA = 0x6b
|
|
||||||
IFT_ATMLOGICAL = 0x50
|
|
||||||
IFT_ATMRADIO = 0xbd
|
|
||||||
IFT_ATMSUBINTERFACE = 0x86
|
|
||||||
IFT_ATMVCIENDPT = 0xc2
|
|
||||||
IFT_ATMVIRTUAL = 0x95
|
|
||||||
IFT_BGPPOLICYACCOUNTING = 0xa2
|
|
||||||
IFT_BSC = 0x53
|
|
||||||
IFT_CCTEMUL = 0x3d
|
|
||||||
IFT_CEPT = 0x13
|
|
||||||
IFT_CES = 0x85
|
|
||||||
IFT_CHANNEL = 0x46
|
|
||||||
IFT_CNR = 0x55
|
|
||||||
IFT_COFFEE = 0x84
|
|
||||||
IFT_COMPOSITELINK = 0x9b
|
|
||||||
IFT_DCN = 0x8d
|
|
||||||
IFT_DIGITALPOWERLINE = 0x8a
|
|
||||||
IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba
|
|
||||||
IFT_DLSW = 0x4a
|
|
||||||
IFT_DOCSCABLEDOWNSTREAM = 0x80
|
|
||||||
IFT_DOCSCABLEMACLAYER = 0x7f
|
|
||||||
IFT_DOCSCABLEUPSTREAM = 0x81
|
|
||||||
IFT_DS0 = 0x51
|
|
||||||
IFT_DS0BUNDLE = 0x52
|
|
||||||
IFT_DS1FDL = 0xaa
|
|
||||||
IFT_DS3 = 0x1e
|
|
||||||
IFT_DTM = 0x8c
|
|
||||||
IFT_DVBASILN = 0xac
|
|
||||||
IFT_DVBASIOUT = 0xad
|
|
||||||
IFT_DVBRCCDOWNSTREAM = 0x93
|
|
||||||
IFT_DVBRCCMACLAYER = 0x92
|
|
||||||
IFT_DVBRCCUPSTREAM = 0x94
|
|
||||||
IFT_ENC = 0xf4
|
|
||||||
IFT_EON = 0x19
|
|
||||||
IFT_EPLRS = 0x57
|
|
||||||
IFT_ESCON = 0x49
|
|
||||||
IFT_ETHER = 0x6
|
|
||||||
IFT_FAITH = 0xf2
|
|
||||||
IFT_FAST = 0x7d
|
|
||||||
IFT_FASTETHER = 0x3e
|
|
||||||
IFT_FASTETHERFX = 0x45
|
|
||||||
IFT_FDDI = 0xf
|
|
||||||
IFT_FIBRECHANNEL = 0x38
|
|
||||||
IFT_FRAMERELAYINTERCONNECT = 0x3a
|
|
||||||
IFT_FRAMERELAYMPI = 0x5c
|
|
||||||
IFT_FRDLCIENDPT = 0xc1
|
|
||||||
IFT_FRELAY = 0x20
|
|
||||||
IFT_FRELAYDCE = 0x2c
|
|
||||||
IFT_FRF16MFRBUNDLE = 0xa3
|
|
||||||
IFT_FRFORWARD = 0x9e
|
|
||||||
IFT_G703AT2MB = 0x43
|
|
||||||
IFT_G703AT64K = 0x42
|
|
||||||
IFT_GIF = 0xf0
|
|
||||||
IFT_GIGABITETHERNET = 0x75
|
|
||||||
IFT_GR303IDT = 0xb2
|
|
||||||
IFT_GR303RDT = 0xb1
|
|
||||||
IFT_H323GATEKEEPER = 0xa4
|
|
||||||
IFT_H323PROXY = 0xa5
|
|
||||||
IFT_HDH1822 = 0x3
|
|
||||||
IFT_HDLC = 0x76
|
|
||||||
IFT_HDSL2 = 0xa8
|
|
||||||
IFT_HIPERLAN2 = 0xb7
|
|
||||||
IFT_HIPPI = 0x2f
|
|
||||||
IFT_HIPPIINTERFACE = 0x39
|
|
||||||
IFT_HOSTPAD = 0x5a
|
|
||||||
IFT_HSSI = 0x2e
|
|
||||||
IFT_HY = 0xe
|
|
||||||
IFT_IBM370PARCHAN = 0x48
|
|
||||||
IFT_IDSL = 0x9a
|
|
||||||
IFT_IEEE80211 = 0x47
|
|
||||||
IFT_IEEE80212 = 0x37
|
|
||||||
IFT_IEEE8023ADLAG = 0xa1
|
|
||||||
IFT_IFGSN = 0x91
|
|
||||||
IFT_IMT = 0xbe
|
|
||||||
IFT_INTERLEAVE = 0x7c
|
|
||||||
IFT_IP = 0x7e
|
|
||||||
IFT_IPFORWARD = 0x8e
|
|
||||||
IFT_IPOVERATM = 0x72
|
|
||||||
IFT_IPOVERCDLC = 0x6d
|
|
||||||
IFT_IPOVERCLAW = 0x6e
|
|
||||||
IFT_IPSWITCH = 0x4e
|
|
||||||
IFT_IPXIP = 0xf9
|
|
||||||
IFT_ISDN = 0x3f
|
|
||||||
IFT_ISDNBASIC = 0x14
|
|
||||||
IFT_ISDNPRIMARY = 0x15
|
|
||||||
IFT_ISDNS = 0x4b
|
|
||||||
IFT_ISDNU = 0x4c
|
|
||||||
IFT_ISO88022LLC = 0x29
|
|
||||||
IFT_ISO88023 = 0x7
|
|
||||||
IFT_ISO88024 = 0x8
|
|
||||||
IFT_ISO88025 = 0x9
|
|
||||||
IFT_ISO88025CRFPINT = 0x62
|
|
||||||
IFT_ISO88025DTR = 0x56
|
|
||||||
IFT_ISO88025FIBER = 0x73
|
|
||||||
IFT_ISO88026 = 0xa
|
|
||||||
IFT_ISUP = 0xb3
|
|
||||||
IFT_L3IPXVLAN = 0x89
|
|
||||||
IFT_LAPB = 0x10
|
|
||||||
IFT_LAPD = 0x4d
|
|
||||||
IFT_LAPF = 0x77
|
|
||||||
IFT_LOCALTALK = 0x2a
|
|
||||||
IFT_LOOP = 0x18
|
|
||||||
IFT_MEDIAMAILOVERIP = 0x8b
|
|
||||||
IFT_MFSIGLINK = 0xa7
|
|
||||||
IFT_MIOX25 = 0x26
|
|
||||||
IFT_MODEM = 0x30
|
|
||||||
IFT_MPC = 0x71
|
|
||||||
IFT_MPLS = 0xa6
|
|
||||||
IFT_MPLSTUNNEL = 0x96
|
|
||||||
IFT_MSDSL = 0x8f
|
|
||||||
IFT_MVL = 0xbf
|
|
||||||
IFT_MYRINET = 0x63
|
|
||||||
IFT_NFAS = 0xaf
|
|
||||||
IFT_NSIP = 0x1b
|
|
||||||
IFT_OPTICALCHANNEL = 0xc3
|
|
||||||
IFT_OPTICALTRANSPORT = 0xc4
|
|
||||||
IFT_OTHER = 0x1
|
|
||||||
IFT_P10 = 0xc
|
|
||||||
IFT_P80 = 0xd
|
|
||||||
IFT_PARA = 0x22
|
|
||||||
IFT_PFLOG = 0xf6
|
|
||||||
IFT_PFSYNC = 0xf7
|
|
||||||
IFT_PLC = 0xae
|
|
||||||
IFT_POS = 0xab
|
|
||||||
IFT_PPPMULTILINKBUNDLE = 0x6c
|
|
||||||
IFT_PROPBWAP2MP = 0xb8
|
|
||||||
IFT_PROPCNLS = 0x59
|
|
||||||
IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5
|
|
||||||
IFT_PROPDOCSWIRELESSMACLAYER = 0xb4
|
|
||||||
IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6
|
|
||||||
IFT_PROPMUX = 0x36
|
|
||||||
IFT_PROPWIRELESSP2P = 0x9d
|
|
||||||
IFT_PTPSERIAL = 0x16
|
|
||||||
IFT_PVC = 0xf1
|
|
||||||
IFT_QLLC = 0x44
|
|
||||||
IFT_RADIOMAC = 0xbc
|
|
||||||
IFT_RADSL = 0x5f
|
|
||||||
IFT_REACHDSL = 0xc0
|
|
||||||
IFT_RFC1483 = 0x9f
|
|
||||||
IFT_RS232 = 0x21
|
|
||||||
IFT_RSRB = 0x4f
|
|
||||||
IFT_SDLC = 0x11
|
|
||||||
IFT_SDSL = 0x60
|
|
||||||
IFT_SHDSL = 0xa9
|
|
||||||
IFT_SIP = 0x1f
|
|
||||||
IFT_SLIP = 0x1c
|
|
||||||
IFT_SMDSDXI = 0x2b
|
|
||||||
IFT_SMDSICIP = 0x34
|
|
||||||
IFT_SONET = 0x27
|
|
||||||
IFT_SONETOVERHEADCHANNEL = 0xb9
|
|
||||||
IFT_SONETPATH = 0x32
|
|
||||||
IFT_SONETVT = 0x33
|
|
||||||
IFT_SRP = 0x97
|
|
||||||
IFT_SS7SIGLINK = 0x9c
|
|
||||||
IFT_STACKTOSTACK = 0x6f
|
|
||||||
IFT_STARLAN = 0xb
|
|
||||||
IFT_STF = 0xd7
|
|
||||||
IFT_T1 = 0x12
|
|
||||||
IFT_TDLC = 0x74
|
|
||||||
IFT_TERMPAD = 0x5b
|
|
||||||
IFT_TR008 = 0xb0
|
|
||||||
IFT_TRANSPHDLC = 0x7b
|
|
||||||
IFT_TUNNEL = 0x83
|
|
||||||
IFT_ULTRA = 0x1d
|
|
||||||
IFT_USB = 0xa0
|
|
||||||
IFT_V11 = 0x40
|
|
||||||
IFT_V35 = 0x2d
|
|
||||||
IFT_V36 = 0x41
|
|
||||||
IFT_V37 = 0x78
|
|
||||||
IFT_VDSL = 0x61
|
|
||||||
IFT_VIRTUALIPADDRESS = 0x70
|
|
||||||
IFT_VOICEEM = 0x64
|
|
||||||
IFT_VOICEENCAP = 0x67
|
|
||||||
IFT_VOICEFXO = 0x65
|
|
||||||
IFT_VOICEFXS = 0x66
|
|
||||||
IFT_VOICEOVERATM = 0x98
|
|
||||||
IFT_VOICEOVERFRAMERELAY = 0x99
|
|
||||||
IFT_VOICEOVERIP = 0x68
|
|
||||||
IFT_X213 = 0x5d
|
|
||||||
IFT_X25 = 0x5
|
|
||||||
IFT_X25DDN = 0x4
|
|
||||||
IFT_X25HUNTGROUP = 0x7a
|
|
||||||
IFT_X25MLP = 0x79
|
|
||||||
IFT_X25PLE = 0x28
|
|
||||||
IFT_XETHER = 0x1a
|
|
||||||
IPPROTO_MAXID = 0x34
|
|
||||||
IPV6_FAITH = 0x1d
|
|
||||||
IP_FAITH = 0x16
|
|
||||||
MAP_NORESERVE = 0x40
|
|
||||||
MAP_RENAME = 0x20
|
|
||||||
NET_RT_MAXID = 0x6
|
|
||||||
RTF_PRCLONING = 0x10000
|
|
||||||
RTM_OLDADD = 0x9
|
|
||||||
RTM_OLDDEL = 0xa
|
|
||||||
SIOCADDRT = 0x8030720a
|
|
||||||
SIOCALIFADDR = 0x8118691b
|
|
||||||
SIOCDELRT = 0x8030720b
|
|
||||||
SIOCDLIFADDR = 0x8118691d
|
|
||||||
SIOCGLIFADDR = 0xc118691c
|
|
||||||
SIOCGLIFPHYADDR = 0xc118694b
|
|
||||||
SIOCSLIFPHYADDR = 0x8118694a
|
|
||||||
)
|
|
|
@ -1,227 +0,0 @@
|
||||||
// Copyright 2017 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.
|
|
||||||
|
|
||||||
// Constants that were deprecated or moved to enums in the FreeBSD headers. Keep
|
|
||||||
// them here for backwards compatibility.
|
|
||||||
|
|
||||||
package unix
|
|
||||||
|
|
||||||
const (
|
|
||||||
IFF_SMART = 0x20
|
|
||||||
IFT_1822 = 0x2
|
|
||||||
IFT_A12MPPSWITCH = 0x82
|
|
||||||
IFT_AAL2 = 0xbb
|
|
||||||
IFT_AAL5 = 0x31
|
|
||||||
IFT_ADSL = 0x5e
|
|
||||||
IFT_AFLANE8023 = 0x3b
|
|
||||||
IFT_AFLANE8025 = 0x3c
|
|
||||||
IFT_ARAP = 0x58
|
|
||||||
IFT_ARCNET = 0x23
|
|
||||||
IFT_ARCNETPLUS = 0x24
|
|
||||||
IFT_ASYNC = 0x54
|
|
||||||
IFT_ATM = 0x25
|
|
||||||
IFT_ATMDXI = 0x69
|
|
||||||
IFT_ATMFUNI = 0x6a
|
|
||||||
IFT_ATMIMA = 0x6b
|
|
||||||
IFT_ATMLOGICAL = 0x50
|
|
||||||
IFT_ATMRADIO = 0xbd
|
|
||||||
IFT_ATMSUBINTERFACE = 0x86
|
|
||||||
IFT_ATMVCIENDPT = 0xc2
|
|
||||||
IFT_ATMVIRTUAL = 0x95
|
|
||||||
IFT_BGPPOLICYACCOUNTING = 0xa2
|
|
||||||
IFT_BSC = 0x53
|
|
||||||
IFT_CCTEMUL = 0x3d
|
|
||||||
IFT_CEPT = 0x13
|
|
||||||
IFT_CES = 0x85
|
|
||||||
IFT_CHANNEL = 0x46
|
|
||||||
IFT_CNR = 0x55
|
|
||||||
IFT_COFFEE = 0x84
|
|
||||||
IFT_COMPOSITELINK = 0x9b
|
|
||||||
IFT_DCN = 0x8d
|
|
||||||
IFT_DIGITALPOWERLINE = 0x8a
|
|
||||||
IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba
|
|
||||||
IFT_DLSW = 0x4a
|
|
||||||
IFT_DOCSCABLEDOWNSTREAM = 0x80
|
|
||||||
IFT_DOCSCABLEMACLAYER = 0x7f
|
|
||||||
IFT_DOCSCABLEUPSTREAM = 0x81
|
|
||||||
IFT_DS0 = 0x51
|
|
||||||
IFT_DS0BUNDLE = 0x52
|
|
||||||
IFT_DS1FDL = 0xaa
|
|
||||||
IFT_DS3 = 0x1e
|
|
||||||
IFT_DTM = 0x8c
|
|
||||||
IFT_DVBASILN = 0xac
|
|
||||||
IFT_DVBASIOUT = 0xad
|
|
||||||
IFT_DVBRCCDOWNSTREAM = 0x93
|
|
||||||
IFT_DVBRCCMACLAYER = 0x92
|
|
||||||
IFT_DVBRCCUPSTREAM = 0x94
|
|
||||||
IFT_ENC = 0xf4
|
|
||||||
IFT_EON = 0x19
|
|
||||||
IFT_EPLRS = 0x57
|
|
||||||
IFT_ESCON = 0x49
|
|
||||||
IFT_ETHER = 0x6
|
|
||||||
IFT_FAITH = 0xf2
|
|
||||||
IFT_FAST = 0x7d
|
|
||||||
IFT_FASTETHER = 0x3e
|
|
||||||
IFT_FASTETHERFX = 0x45
|
|
||||||
IFT_FDDI = 0xf
|
|
||||||
IFT_FIBRECHANNEL = 0x38
|
|
||||||
IFT_FRAMERELAYINTERCONNECT = 0x3a
|
|
||||||
IFT_FRAMERELAYMPI = 0x5c
|
|
||||||
IFT_FRDLCIENDPT = 0xc1
|
|
||||||
IFT_FRELAY = 0x20
|
|
||||||
IFT_FRELAYDCE = 0x2c
|
|
||||||
IFT_FRF16MFRBUNDLE = 0xa3
|
|
||||||
IFT_FRFORWARD = 0x9e
|
|
||||||
IFT_G703AT2MB = 0x43
|
|
||||||
IFT_G703AT64K = 0x42
|
|
||||||
IFT_GIF = 0xf0
|
|
||||||
IFT_GIGABITETHERNET = 0x75
|
|
||||||
IFT_GR303IDT = 0xb2
|
|
||||||
IFT_GR303RDT = 0xb1
|
|
||||||
IFT_H323GATEKEEPER = 0xa4
|
|
||||||
IFT_H323PROXY = 0xa5
|
|
||||||
IFT_HDH1822 = 0x3
|
|
||||||
IFT_HDLC = 0x76
|
|
||||||
IFT_HDSL2 = 0xa8
|
|
||||||
IFT_HIPERLAN2 = 0xb7
|
|
||||||
IFT_HIPPI = 0x2f
|
|
||||||
IFT_HIPPIINTERFACE = 0x39
|
|
||||||
IFT_HOSTPAD = 0x5a
|
|
||||||
IFT_HSSI = 0x2e
|
|
||||||
IFT_HY = 0xe
|
|
||||||
IFT_IBM370PARCHAN = 0x48
|
|
||||||
IFT_IDSL = 0x9a
|
|
||||||
IFT_IEEE80211 = 0x47
|
|
||||||
IFT_IEEE80212 = 0x37
|
|
||||||
IFT_IEEE8023ADLAG = 0xa1
|
|
||||||
IFT_IFGSN = 0x91
|
|
||||||
IFT_IMT = 0xbe
|
|
||||||
IFT_INTERLEAVE = 0x7c
|
|
||||||
IFT_IP = 0x7e
|
|
||||||
IFT_IPFORWARD = 0x8e
|
|
||||||
IFT_IPOVERATM = 0x72
|
|
||||||
IFT_IPOVERCDLC = 0x6d
|
|
||||||
IFT_IPOVERCLAW = 0x6e
|
|
||||||
IFT_IPSWITCH = 0x4e
|
|
||||||
IFT_IPXIP = 0xf9
|
|
||||||
IFT_ISDN = 0x3f
|
|
||||||
IFT_ISDNBASIC = 0x14
|
|
||||||
IFT_ISDNPRIMARY = 0x15
|
|
||||||
IFT_ISDNS = 0x4b
|
|
||||||
IFT_ISDNU = 0x4c
|
|
||||||
IFT_ISO88022LLC = 0x29
|
|
||||||
IFT_ISO88023 = 0x7
|
|
||||||
IFT_ISO88024 = 0x8
|
|
||||||
IFT_ISO88025 = 0x9
|
|
||||||
IFT_ISO88025CRFPINT = 0x62
|
|
||||||
IFT_ISO88025DTR = 0x56
|
|
||||||
IFT_ISO88025FIBER = 0x73
|
|
||||||
IFT_ISO88026 = 0xa
|
|
||||||
IFT_ISUP = 0xb3
|
|
||||||
IFT_L3IPXVLAN = 0x89
|
|
||||||
IFT_LAPB = 0x10
|
|
||||||
IFT_LAPD = 0x4d
|
|
||||||
IFT_LAPF = 0x77
|
|
||||||
IFT_LOCALTALK = 0x2a
|
|
||||||
IFT_LOOP = 0x18
|
|
||||||
IFT_MEDIAMAILOVERIP = 0x8b
|
|
||||||
IFT_MFSIGLINK = 0xa7
|
|
||||||
IFT_MIOX25 = 0x26
|
|
||||||
IFT_MODEM = 0x30
|
|
||||||
IFT_MPC = 0x71
|
|
||||||
IFT_MPLS = 0xa6
|
|
||||||
IFT_MPLSTUNNEL = 0x96
|
|
||||||
IFT_MSDSL = 0x8f
|
|
||||||
IFT_MVL = 0xbf
|
|
||||||
IFT_MYRINET = 0x63
|
|
||||||
IFT_NFAS = 0xaf
|
|
||||||
IFT_NSIP = 0x1b
|
|
||||||
IFT_OPTICALCHANNEL = 0xc3
|
|
||||||
IFT_OPTICALTRANSPORT = 0xc4
|
|
||||||
IFT_OTHER = 0x1
|
|
||||||
IFT_P10 = 0xc
|
|
||||||
IFT_P80 = 0xd
|
|
||||||
IFT_PARA = 0x22
|
|
||||||
IFT_PFLOG = 0xf6
|
|
||||||
IFT_PFSYNC = 0xf7
|
|
||||||
IFT_PLC = 0xae
|
|
||||||
IFT_POS = 0xab
|
|
||||||
IFT_PPPMULTILINKBUNDLE = 0x6c
|
|
||||||
IFT_PROPBWAP2MP = 0xb8
|
|
||||||
IFT_PROPCNLS = 0x59
|
|
||||||
IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5
|
|
||||||
IFT_PROPDOCSWIRELESSMACLAYER = 0xb4
|
|
||||||
IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6
|
|
||||||
IFT_PROPMUX = 0x36
|
|
||||||
IFT_PROPWIRELESSP2P = 0x9d
|
|
||||||
IFT_PTPSERIAL = 0x16
|
|
||||||
IFT_PVC = 0xf1
|
|
||||||
IFT_QLLC = 0x44
|
|
||||||
IFT_RADIOMAC = 0xbc
|
|
||||||
IFT_RADSL = 0x5f
|
|
||||||
IFT_REACHDSL = 0xc0
|
|
||||||
IFT_RFC1483 = 0x9f
|
|
||||||
IFT_RS232 = 0x21
|
|
||||||
IFT_RSRB = 0x4f
|
|
||||||
IFT_SDLC = 0x11
|
|
||||||
IFT_SDSL = 0x60
|
|
||||||
IFT_SHDSL = 0xa9
|
|
||||||
IFT_SIP = 0x1f
|
|
||||||
IFT_SLIP = 0x1c
|
|
||||||
IFT_SMDSDXI = 0x2b
|
|
||||||
IFT_SMDSICIP = 0x34
|
|
||||||
IFT_SONET = 0x27
|
|
||||||
IFT_SONETOVERHEADCHANNEL = 0xb9
|
|
||||||
IFT_SONETPATH = 0x32
|
|
||||||
IFT_SONETVT = 0x33
|
|
||||||
IFT_SRP = 0x97
|
|
||||||
IFT_SS7SIGLINK = 0x9c
|
|
||||||
IFT_STACKTOSTACK = 0x6f
|
|
||||||
IFT_STARLAN = 0xb
|
|
||||||
IFT_STF = 0xd7
|
|
||||||
IFT_T1 = 0x12
|
|
||||||
IFT_TDLC = 0x74
|
|
||||||
IFT_TERMPAD = 0x5b
|
|
||||||
IFT_TR008 = 0xb0
|
|
||||||
IFT_TRANSPHDLC = 0x7b
|
|
||||||
IFT_TUNNEL = 0x83
|
|
||||||
IFT_ULTRA = 0x1d
|
|
||||||
IFT_USB = 0xa0
|
|
||||||
IFT_V11 = 0x40
|
|
||||||
IFT_V35 = 0x2d
|
|
||||||
IFT_V36 = 0x41
|
|
||||||
IFT_V37 = 0x78
|
|
||||||
IFT_VDSL = 0x61
|
|
||||||
IFT_VIRTUALIPADDRESS = 0x70
|
|
||||||
IFT_VOICEEM = 0x64
|
|
||||||
IFT_VOICEENCAP = 0x67
|
|
||||||
IFT_VOICEFXO = 0x65
|
|
||||||
IFT_VOICEFXS = 0x66
|
|
||||||
IFT_VOICEOVERATM = 0x98
|
|
||||||
IFT_VOICEOVERFRAMERELAY = 0x99
|
|
||||||
IFT_VOICEOVERIP = 0x68
|
|
||||||
IFT_X213 = 0x5d
|
|
||||||
IFT_X25 = 0x5
|
|
||||||
IFT_X25DDN = 0x4
|
|
||||||
IFT_X25HUNTGROUP = 0x7a
|
|
||||||
IFT_X25MLP = 0x79
|
|
||||||
IFT_X25PLE = 0x28
|
|
||||||
IFT_XETHER = 0x1a
|
|
||||||
IPPROTO_MAXID = 0x34
|
|
||||||
IPV6_FAITH = 0x1d
|
|
||||||
IP_FAITH = 0x16
|
|
||||||
MAP_NORESERVE = 0x40
|
|
||||||
MAP_RENAME = 0x20
|
|
||||||
NET_RT_MAXID = 0x6
|
|
||||||
RTF_PRCLONING = 0x10000
|
|
||||||
RTM_OLDADD = 0x9
|
|
||||||
RTM_OLDDEL = 0xa
|
|
||||||
SIOCADDRT = 0x8040720a
|
|
||||||
SIOCALIFADDR = 0x8118691b
|
|
||||||
SIOCDELRT = 0x8040720b
|
|
||||||
SIOCDLIFADDR = 0x8118691d
|
|
||||||
SIOCGLIFADDR = 0xc118691c
|
|
||||||
SIOCGLIFPHYADDR = 0xc118694b
|
|
||||||
SIOCSLIFPHYADDR = 0x8118694a
|
|
||||||
)
|
|
|
@ -1,226 +0,0 @@
|
||||||
// Copyright 2017 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.
|
|
||||||
|
|
||||||
package unix
|
|
||||||
|
|
||||||
const (
|
|
||||||
IFT_1822 = 0x2
|
|
||||||
IFT_A12MPPSWITCH = 0x82
|
|
||||||
IFT_AAL2 = 0xbb
|
|
||||||
IFT_AAL5 = 0x31
|
|
||||||
IFT_ADSL = 0x5e
|
|
||||||
IFT_AFLANE8023 = 0x3b
|
|
||||||
IFT_AFLANE8025 = 0x3c
|
|
||||||
IFT_ARAP = 0x58
|
|
||||||
IFT_ARCNET = 0x23
|
|
||||||
IFT_ARCNETPLUS = 0x24
|
|
||||||
IFT_ASYNC = 0x54
|
|
||||||
IFT_ATM = 0x25
|
|
||||||
IFT_ATMDXI = 0x69
|
|
||||||
IFT_ATMFUNI = 0x6a
|
|
||||||
IFT_ATMIMA = 0x6b
|
|
||||||
IFT_ATMLOGICAL = 0x50
|
|
||||||
IFT_ATMRADIO = 0xbd
|
|
||||||
IFT_ATMSUBINTERFACE = 0x86
|
|
||||||
IFT_ATMVCIENDPT = 0xc2
|
|
||||||
IFT_ATMVIRTUAL = 0x95
|
|
||||||
IFT_BGPPOLICYACCOUNTING = 0xa2
|
|
||||||
IFT_BSC = 0x53
|
|
||||||
IFT_CCTEMUL = 0x3d
|
|
||||||
IFT_CEPT = 0x13
|
|
||||||
IFT_CES = 0x85
|
|
||||||
IFT_CHANNEL = 0x46
|
|
||||||
IFT_CNR = 0x55
|
|
||||||
IFT_COFFEE = 0x84
|
|
||||||
IFT_COMPOSITELINK = 0x9b
|
|
||||||
IFT_DCN = 0x8d
|
|
||||||
IFT_DIGITALPOWERLINE = 0x8a
|
|
||||||
IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba
|
|
||||||
IFT_DLSW = 0x4a
|
|
||||||
IFT_DOCSCABLEDOWNSTREAM = 0x80
|
|
||||||
IFT_DOCSCABLEMACLAYER = 0x7f
|
|
||||||
IFT_DOCSCABLEUPSTREAM = 0x81
|
|
||||||
IFT_DS0 = 0x51
|
|
||||||
IFT_DS0BUNDLE = 0x52
|
|
||||||
IFT_DS1FDL = 0xaa
|
|
||||||
IFT_DS3 = 0x1e
|
|
||||||
IFT_DTM = 0x8c
|
|
||||||
IFT_DVBASILN = 0xac
|
|
||||||
IFT_DVBASIOUT = 0xad
|
|
||||||
IFT_DVBRCCDOWNSTREAM = 0x93
|
|
||||||
IFT_DVBRCCMACLAYER = 0x92
|
|
||||||
IFT_DVBRCCUPSTREAM = 0x94
|
|
||||||
IFT_ENC = 0xf4
|
|
||||||
IFT_EON = 0x19
|
|
||||||
IFT_EPLRS = 0x57
|
|
||||||
IFT_ESCON = 0x49
|
|
||||||
IFT_ETHER = 0x6
|
|
||||||
IFT_FAST = 0x7d
|
|
||||||
IFT_FASTETHER = 0x3e
|
|
||||||
IFT_FASTETHERFX = 0x45
|
|
||||||
IFT_FDDI = 0xf
|
|
||||||
IFT_FIBRECHANNEL = 0x38
|
|
||||||
IFT_FRAMERELAYINTERCONNECT = 0x3a
|
|
||||||
IFT_FRAMERELAYMPI = 0x5c
|
|
||||||
IFT_FRDLCIENDPT = 0xc1
|
|
||||||
IFT_FRELAY = 0x20
|
|
||||||
IFT_FRELAYDCE = 0x2c
|
|
||||||
IFT_FRF16MFRBUNDLE = 0xa3
|
|
||||||
IFT_FRFORWARD = 0x9e
|
|
||||||
IFT_G703AT2MB = 0x43
|
|
||||||
IFT_G703AT64K = 0x42
|
|
||||||
IFT_GIF = 0xf0
|
|
||||||
IFT_GIGABITETHERNET = 0x75
|
|
||||||
IFT_GR303IDT = 0xb2
|
|
||||||
IFT_GR303RDT = 0xb1
|
|
||||||
IFT_H323GATEKEEPER = 0xa4
|
|
||||||
IFT_H323PROXY = 0xa5
|
|
||||||
IFT_HDH1822 = 0x3
|
|
||||||
IFT_HDLC = 0x76
|
|
||||||
IFT_HDSL2 = 0xa8
|
|
||||||
IFT_HIPERLAN2 = 0xb7
|
|
||||||
IFT_HIPPI = 0x2f
|
|
||||||
IFT_HIPPIINTERFACE = 0x39
|
|
||||||
IFT_HOSTPAD = 0x5a
|
|
||||||
IFT_HSSI = 0x2e
|
|
||||||
IFT_HY = 0xe
|
|
||||||
IFT_IBM370PARCHAN = 0x48
|
|
||||||
IFT_IDSL = 0x9a
|
|
||||||
IFT_IEEE80211 = 0x47
|
|
||||||
IFT_IEEE80212 = 0x37
|
|
||||||
IFT_IEEE8023ADLAG = 0xa1
|
|
||||||
IFT_IFGSN = 0x91
|
|
||||||
IFT_IMT = 0xbe
|
|
||||||
IFT_INTERLEAVE = 0x7c
|
|
||||||
IFT_IP = 0x7e
|
|
||||||
IFT_IPFORWARD = 0x8e
|
|
||||||
IFT_IPOVERATM = 0x72
|
|
||||||
IFT_IPOVERCDLC = 0x6d
|
|
||||||
IFT_IPOVERCLAW = 0x6e
|
|
||||||
IFT_IPSWITCH = 0x4e
|
|
||||||
IFT_ISDN = 0x3f
|
|
||||||
IFT_ISDNBASIC = 0x14
|
|
||||||
IFT_ISDNPRIMARY = 0x15
|
|
||||||
IFT_ISDNS = 0x4b
|
|
||||||
IFT_ISDNU = 0x4c
|
|
||||||
IFT_ISO88022LLC = 0x29
|
|
||||||
IFT_ISO88023 = 0x7
|
|
||||||
IFT_ISO88024 = 0x8
|
|
||||||
IFT_ISO88025 = 0x9
|
|
||||||
IFT_ISO88025CRFPINT = 0x62
|
|
||||||
IFT_ISO88025DTR = 0x56
|
|
||||||
IFT_ISO88025FIBER = 0x73
|
|
||||||
IFT_ISO88026 = 0xa
|
|
||||||
IFT_ISUP = 0xb3
|
|
||||||
IFT_L3IPXVLAN = 0x89
|
|
||||||
IFT_LAPB = 0x10
|
|
||||||
IFT_LAPD = 0x4d
|
|
||||||
IFT_LAPF = 0x77
|
|
||||||
IFT_LOCALTALK = 0x2a
|
|
||||||
IFT_LOOP = 0x18
|
|
||||||
IFT_MEDIAMAILOVERIP = 0x8b
|
|
||||||
IFT_MFSIGLINK = 0xa7
|
|
||||||
IFT_MIOX25 = 0x26
|
|
||||||
IFT_MODEM = 0x30
|
|
||||||
IFT_MPC = 0x71
|
|
||||||
IFT_MPLS = 0xa6
|
|
||||||
IFT_MPLSTUNNEL = 0x96
|
|
||||||
IFT_MSDSL = 0x8f
|
|
||||||
IFT_MVL = 0xbf
|
|
||||||
IFT_MYRINET = 0x63
|
|
||||||
IFT_NFAS = 0xaf
|
|
||||||
IFT_NSIP = 0x1b
|
|
||||||
IFT_OPTICALCHANNEL = 0xc3
|
|
||||||
IFT_OPTICALTRANSPORT = 0xc4
|
|
||||||
IFT_OTHER = 0x1
|
|
||||||
IFT_P10 = 0xc
|
|
||||||
IFT_P80 = 0xd
|
|
||||||
IFT_PARA = 0x22
|
|
||||||
IFT_PFLOG = 0xf6
|
|
||||||
IFT_PFSYNC = 0xf7
|
|
||||||
IFT_PLC = 0xae
|
|
||||||
IFT_POS = 0xab
|
|
||||||
IFT_PPPMULTILINKBUNDLE = 0x6c
|
|
||||||
IFT_PROPBWAP2MP = 0xb8
|
|
||||||
IFT_PROPCNLS = 0x59
|
|
||||||
IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5
|
|
||||||
IFT_PROPDOCSWIRELESSMACLAYER = 0xb4
|
|
||||||
IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6
|
|
||||||
IFT_PROPMUX = 0x36
|
|
||||||
IFT_PROPWIRELESSP2P = 0x9d
|
|
||||||
IFT_PTPSERIAL = 0x16
|
|
||||||
IFT_PVC = 0xf1
|
|
||||||
IFT_QLLC = 0x44
|
|
||||||
IFT_RADIOMAC = 0xbc
|
|
||||||
IFT_RADSL = 0x5f
|
|
||||||
IFT_REACHDSL = 0xc0
|
|
||||||
IFT_RFC1483 = 0x9f
|
|
||||||
IFT_RS232 = 0x21
|
|
||||||
IFT_RSRB = 0x4f
|
|
||||||
IFT_SDLC = 0x11
|
|
||||||
IFT_SDSL = 0x60
|
|
||||||
IFT_SHDSL = 0xa9
|
|
||||||
IFT_SIP = 0x1f
|
|
||||||
IFT_SLIP = 0x1c
|
|
||||||
IFT_SMDSDXI = 0x2b
|
|
||||||
IFT_SMDSICIP = 0x34
|
|
||||||
IFT_SONET = 0x27
|
|
||||||
IFT_SONETOVERHEADCHANNEL = 0xb9
|
|
||||||
IFT_SONETPATH = 0x32
|
|
||||||
IFT_SONETVT = 0x33
|
|
||||||
IFT_SRP = 0x97
|
|
||||||
IFT_SS7SIGLINK = 0x9c
|
|
||||||
IFT_STACKTOSTACK = 0x6f
|
|
||||||
IFT_STARLAN = 0xb
|
|
||||||
IFT_STF = 0xd7
|
|
||||||
IFT_T1 = 0x12
|
|
||||||
IFT_TDLC = 0x74
|
|
||||||
IFT_TERMPAD = 0x5b
|
|
||||||
IFT_TR008 = 0xb0
|
|
||||||
IFT_TRANSPHDLC = 0x7b
|
|
||||||
IFT_TUNNEL = 0x83
|
|
||||||
IFT_ULTRA = 0x1d
|
|
||||||
IFT_USB = 0xa0
|
|
||||||
IFT_V11 = 0x40
|
|
||||||
IFT_V35 = 0x2d
|
|
||||||
IFT_V36 = 0x41
|
|
||||||
IFT_V37 = 0x78
|
|
||||||
IFT_VDSL = 0x61
|
|
||||||
IFT_VIRTUALIPADDRESS = 0x70
|
|
||||||
IFT_VOICEEM = 0x64
|
|
||||||
IFT_VOICEENCAP = 0x67
|
|
||||||
IFT_VOICEFXO = 0x65
|
|
||||||
IFT_VOICEFXS = 0x66
|
|
||||||
IFT_VOICEOVERATM = 0x98
|
|
||||||
IFT_VOICEOVERFRAMERELAY = 0x99
|
|
||||||
IFT_VOICEOVERIP = 0x68
|
|
||||||
IFT_X213 = 0x5d
|
|
||||||
IFT_X25 = 0x5
|
|
||||||
IFT_X25DDN = 0x4
|
|
||||||
IFT_X25HUNTGROUP = 0x7a
|
|
||||||
IFT_X25MLP = 0x79
|
|
||||||
IFT_X25PLE = 0x28
|
|
||||||
IFT_XETHER = 0x1a
|
|
||||||
|
|
||||||
// missing constants on FreeBSD-11.1-RELEASE, copied from old values in ztypes_freebsd_arm.go
|
|
||||||
IFF_SMART = 0x20
|
|
||||||
IFT_FAITH = 0xf2
|
|
||||||
IFT_IPXIP = 0xf9
|
|
||||||
IPPROTO_MAXID = 0x34
|
|
||||||
IPV6_FAITH = 0x1d
|
|
||||||
IP_FAITH = 0x16
|
|
||||||
MAP_NORESERVE = 0x40
|
|
||||||
MAP_RENAME = 0x20
|
|
||||||
NET_RT_MAXID = 0x6
|
|
||||||
RTF_PRCLONING = 0x10000
|
|
||||||
RTM_OLDADD = 0x9
|
|
||||||
RTM_OLDDEL = 0xa
|
|
||||||
SIOCADDRT = 0x8030720a
|
|
||||||
SIOCALIFADDR = 0x8118691b
|
|
||||||
SIOCDELRT = 0x8030720b
|
|
||||||
SIOCDLIFADDR = 0x8118691d
|
|
||||||
SIOCGLIFADDR = 0xc118691c
|
|
||||||
SIOCGLIFPHYADDR = 0xc118694b
|
|
||||||
SIOCSLIFPHYADDR = 0x8118694a
|
|
||||||
)
|
|
|
@ -2,6 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build dragonfly || freebsd || linux || netbsd || openbsd
|
||||||
// +build dragonfly freebsd linux netbsd openbsd
|
// +build dragonfly freebsd linux netbsd openbsd
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
@ -9,12 +10,11 @@ package unix
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
// fcntl64Syscall is usually SYS_FCNTL, but is overridden on 32-bit Linux
|
// fcntl64Syscall is usually SYS_FCNTL, but is overridden on 32-bit Linux
|
||||||
// systems by flock_linux_32bit.go to be SYS_FCNTL64.
|
// systems by fcntl_linux_32bit.go to be SYS_FCNTL64.
|
||||||
var fcntl64Syscall uintptr = SYS_FCNTL
|
var fcntl64Syscall uintptr = SYS_FCNTL
|
||||||
|
|
||||||
// FcntlInt performs a fcntl syscall on fd with the provided command and argument.
|
func fcntl(fd int, cmd, arg int) (int, error) {
|
||||||
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
|
valptr, _, errno := Syscall(fcntl64Syscall, uintptr(fd), uintptr(cmd), uintptr(arg))
|
||||||
valptr, _, errno := Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(arg))
|
|
||||||
var err error
|
var err error
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
err = errno
|
err = errno
|
||||||
|
@ -22,6 +22,11 @@ func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
|
||||||
return int(valptr), err
|
return int(valptr), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FcntlInt performs a fcntl syscall on fd with the provided command and argument.
|
||||||
|
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
|
||||||
|
return fcntl(int(fd), cmd, arg)
|
||||||
|
}
|
||||||
|
|
||||||
// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command.
|
// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command.
|
||||||
func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error {
|
func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error {
|
||||||
_, _, errno := Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(unsafe.Pointer(lk)))
|
_, _, errno := Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(unsafe.Pointer(lk)))
|
||||||
|
|
|
@ -16,3 +16,9 @@ func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error {
|
||||||
_, err := fcntl(int(fd), cmd, int(uintptr(unsafe.Pointer(lk))))
|
_, err := fcntl(int(fd), cmd, int(uintptr(unsafe.Pointer(lk))))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FcntlFstore performs a fcntl syscall for the F_PREALLOCATE command.
|
||||||
|
func FcntlFstore(fd uintptr, cmd int, fstore *Fstore_t) error {
|
||||||
|
_, err := fcntl(int(fd), cmd, int(uintptr(unsafe.Pointer(fstore))))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
// +build linux,386 linux,arm linux,mips linux,mipsle
|
|
||||||
|
|
||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build (linux && 386) || (linux && arm) || (linux && mips) || (linux && mipsle) || (linux && ppc)
|
||||||
|
// +build linux,386 linux,arm linux,mips linux,mipsle linux,ppc
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2019 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 aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
||||||
|
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
// Set adds fd to the set fds.
|
||||||
|
func (fds *FdSet) Set(fd int) {
|
||||||
|
fds.Bits[fd/NFDBITS] |= (1 << (uintptr(fd) % NFDBITS))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear removes fd from the set fds.
|
||||||
|
func (fds *FdSet) Clear(fd int) {
|
||||||
|
fds.Bits[fd/NFDBITS] &^= (1 << (uintptr(fd) % NFDBITS))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSet returns whether fd is in the set fds.
|
||||||
|
func (fds *FdSet) IsSet(fd int) bool {
|
||||||
|
return fds.Bits[fd/NFDBITS]&(1<<(uintptr(fd)%NFDBITS)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero clears the set fds.
|
||||||
|
func (fds *FdSet) Zero() {
|
||||||
|
for i := range fds.Bits {
|
||||||
|
fds.Bits[i] = 0
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,164 @@
|
||||||
|
// Copyright 2020 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 zos && s390x
|
||||||
|
// +build zos,s390x
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file simulates fstatfs on z/OS using fstatvfs and w_getmntent.
|
||||||
|
|
||||||
|
func Fstatfs(fd int, stat *Statfs_t) (err error) {
|
||||||
|
var stat_v Statvfs_t
|
||||||
|
err = Fstatvfs(fd, &stat_v)
|
||||||
|
if err == nil {
|
||||||
|
// populate stat
|
||||||
|
stat.Type = 0
|
||||||
|
stat.Bsize = stat_v.Bsize
|
||||||
|
stat.Blocks = stat_v.Blocks
|
||||||
|
stat.Bfree = stat_v.Bfree
|
||||||
|
stat.Bavail = stat_v.Bavail
|
||||||
|
stat.Files = stat_v.Files
|
||||||
|
stat.Ffree = stat_v.Ffree
|
||||||
|
stat.Fsid = stat_v.Fsid
|
||||||
|
stat.Namelen = stat_v.Namemax
|
||||||
|
stat.Frsize = stat_v.Frsize
|
||||||
|
stat.Flags = stat_v.Flag
|
||||||
|
for passn := 0; passn < 5; passn++ {
|
||||||
|
switch passn {
|
||||||
|
case 0:
|
||||||
|
err = tryGetmntent64(stat)
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
err = tryGetmntent128(stat)
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
err = tryGetmntent256(stat)
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
err = tryGetmntent512(stat)
|
||||||
|
break
|
||||||
|
case 4:
|
||||||
|
err = tryGetmntent1024(stat)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
//proceed to return if: err is nil (found), err is nonnil but not ERANGE (another error occurred)
|
||||||
|
if err == nil || err != nil && err != ERANGE {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryGetmntent64(stat *Statfs_t) (err error) {
|
||||||
|
var mnt_ent_buffer struct {
|
||||||
|
header W_Mnth
|
||||||
|
filesys_info [64]W_Mntent
|
||||||
|
}
|
||||||
|
var buffer_size int = int(unsafe.Sizeof(mnt_ent_buffer))
|
||||||
|
fs_count, err := W_Getmntent((*byte)(unsafe.Pointer(&mnt_ent_buffer)), buffer_size)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ERANGE //return ERANGE if no match is found in this batch
|
||||||
|
for i := 0; i < fs_count; i++ {
|
||||||
|
if stat.Fsid == uint64(mnt_ent_buffer.filesys_info[i].Dev) {
|
||||||
|
stat.Type = uint32(mnt_ent_buffer.filesys_info[i].Fstname[0])
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryGetmntent128(stat *Statfs_t) (err error) {
|
||||||
|
var mnt_ent_buffer struct {
|
||||||
|
header W_Mnth
|
||||||
|
filesys_info [128]W_Mntent
|
||||||
|
}
|
||||||
|
var buffer_size int = int(unsafe.Sizeof(mnt_ent_buffer))
|
||||||
|
fs_count, err := W_Getmntent((*byte)(unsafe.Pointer(&mnt_ent_buffer)), buffer_size)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ERANGE //return ERANGE if no match is found in this batch
|
||||||
|
for i := 0; i < fs_count; i++ {
|
||||||
|
if stat.Fsid == uint64(mnt_ent_buffer.filesys_info[i].Dev) {
|
||||||
|
stat.Type = uint32(mnt_ent_buffer.filesys_info[i].Fstname[0])
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryGetmntent256(stat *Statfs_t) (err error) {
|
||||||
|
var mnt_ent_buffer struct {
|
||||||
|
header W_Mnth
|
||||||
|
filesys_info [256]W_Mntent
|
||||||
|
}
|
||||||
|
var buffer_size int = int(unsafe.Sizeof(mnt_ent_buffer))
|
||||||
|
fs_count, err := W_Getmntent((*byte)(unsafe.Pointer(&mnt_ent_buffer)), buffer_size)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ERANGE //return ERANGE if no match is found in this batch
|
||||||
|
for i := 0; i < fs_count; i++ {
|
||||||
|
if stat.Fsid == uint64(mnt_ent_buffer.filesys_info[i].Dev) {
|
||||||
|
stat.Type = uint32(mnt_ent_buffer.filesys_info[i].Fstname[0])
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryGetmntent512(stat *Statfs_t) (err error) {
|
||||||
|
var mnt_ent_buffer struct {
|
||||||
|
header W_Mnth
|
||||||
|
filesys_info [512]W_Mntent
|
||||||
|
}
|
||||||
|
var buffer_size int = int(unsafe.Sizeof(mnt_ent_buffer))
|
||||||
|
fs_count, err := W_Getmntent((*byte)(unsafe.Pointer(&mnt_ent_buffer)), buffer_size)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ERANGE //return ERANGE if no match is found in this batch
|
||||||
|
for i := 0; i < fs_count; i++ {
|
||||||
|
if stat.Fsid == uint64(mnt_ent_buffer.filesys_info[i].Dev) {
|
||||||
|
stat.Type = uint32(mnt_ent_buffer.filesys_info[i].Fstname[0])
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryGetmntent1024(stat *Statfs_t) (err error) {
|
||||||
|
var mnt_ent_buffer struct {
|
||||||
|
header W_Mnth
|
||||||
|
filesys_info [1024]W_Mntent
|
||||||
|
}
|
||||||
|
var buffer_size int = int(unsafe.Sizeof(mnt_ent_buffer))
|
||||||
|
fs_count, err := W_Getmntent((*byte)(unsafe.Pointer(&mnt_ent_buffer)), buffer_size)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ERANGE //return ERANGE if no match is found in this batch
|
||||||
|
for i := 0; i < fs_count; i++ {
|
||||||
|
if stat.Fsid == uint64(mnt_ent_buffer.filesys_info[i].Dev) {
|
||||||
|
stat.Type = uint32(mnt_ent_buffer.filesys_info[i].Fstname[0])
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
|
@ -2,8 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build gccgo
|
//go:build gccgo && !aix && !hurd
|
||||||
// +build !aix
|
// +build gccgo,!aix,!hurd
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
|
@ -12,10 +12,8 @@ import "syscall"
|
||||||
// We can't use the gc-syntax .s files for gccgo. On the plus side
|
// We can't use the gc-syntax .s files for gccgo. On the plus side
|
||||||
// much of the functionality can be written directly in Go.
|
// much of the functionality can be written directly in Go.
|
||||||
|
|
||||||
//extern gccgoRealSyscallNoError
|
|
||||||
func realSyscallNoError(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r uintptr)
|
func realSyscallNoError(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r uintptr)
|
||||||
|
|
||||||
//extern gccgoRealSyscall
|
|
||||||
func realSyscall(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r, errno uintptr)
|
func realSyscall(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r, errno uintptr)
|
||||||
|
|
||||||
func SyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr) {
|
func SyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr) {
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build gccgo
|
//go:build gccgo && !aix && !hurd
|
||||||
// +build !aix
|
// +build gccgo,!aix,!hurd
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
@ -21,6 +21,9 @@ struct ret {
|
||||||
uintptr_t err;
|
uintptr_t err;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ret gccgoRealSyscall(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9)
|
||||||
|
__asm__(GOSYM_PREFIX GOPKGPATH ".realSyscall");
|
||||||
|
|
||||||
struct ret
|
struct ret
|
||||||
gccgoRealSyscall(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9)
|
gccgoRealSyscall(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9)
|
||||||
{
|
{
|
||||||
|
@ -32,6 +35,9 @@ gccgoRealSyscall(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintp
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uintptr_t gccgoRealSyscallNoError(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9)
|
||||||
|
__asm__(GOSYM_PREFIX GOPKGPATH ".realSyscallNoError");
|
||||||
|
|
||||||
uintptr_t
|
uintptr_t
|
||||||
gccgoRealSyscallNoError(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9)
|
gccgoRealSyscallNoError(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gccgo && linux && amd64
|
||||||
// +build gccgo,linux,amd64
|
// +build gccgo,linux,amd64
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
// 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 linux
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Helpers for dealing with ifreq since it contains a union and thus requires a
|
||||||
|
// lot of unsafe.Pointer casts to use properly.
|
||||||
|
|
||||||
|
// An Ifreq is a type-safe wrapper around the raw ifreq struct. An Ifreq
|
||||||
|
// contains an interface name and a union of arbitrary data which can be
|
||||||
|
// accessed using the Ifreq's methods. To create an Ifreq, use the NewIfreq
|
||||||
|
// function.
|
||||||
|
//
|
||||||
|
// Use the Name method to access the stored interface name. The union data
|
||||||
|
// fields can be get and set using the following methods:
|
||||||
|
// - Uint16/SetUint16: flags
|
||||||
|
// - Uint32/SetUint32: ifindex, metric, mtu
|
||||||
|
type Ifreq struct{ raw ifreq }
|
||||||
|
|
||||||
|
// NewIfreq creates an Ifreq with the input network interface name after
|
||||||
|
// validating the name does not exceed IFNAMSIZ-1 (trailing NULL required)
|
||||||
|
// bytes.
|
||||||
|
func NewIfreq(name string) (*Ifreq, error) {
|
||||||
|
// Leave room for terminating NULL byte.
|
||||||
|
if len(name) >= IFNAMSIZ {
|
||||||
|
return nil, EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
var ifr ifreq
|
||||||
|
copy(ifr.Ifrn[:], name)
|
||||||
|
|
||||||
|
return &Ifreq{raw: ifr}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(mdlayher): get/set methods for hardware address sockaddr, char array, etc.
|
||||||
|
|
||||||
|
// Name returns the interface name associated with the Ifreq.
|
||||||
|
func (ifr *Ifreq) Name() string {
|
||||||
|
return ByteSliceToString(ifr.raw.Ifrn[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// According to netdevice(7), only AF_INET addresses are returned for numerous
|
||||||
|
// sockaddr ioctls. For convenience, we expose these as Inet4Addr since the Port
|
||||||
|
// field and other data is always empty.
|
||||||
|
|
||||||
|
// Inet4Addr returns the Ifreq union data from an embedded sockaddr as a C
|
||||||
|
// in_addr/Go []byte (4-byte IPv4 address) value. If the sockaddr family is not
|
||||||
|
// AF_INET, an error is returned.
|
||||||
|
func (ifr *Ifreq) Inet4Addr() ([]byte, error) {
|
||||||
|
raw := *(*RawSockaddrInet4)(unsafe.Pointer(&ifr.raw.Ifru[:SizeofSockaddrInet4][0]))
|
||||||
|
if raw.Family != AF_INET {
|
||||||
|
// Cannot safely interpret raw.Addr bytes as an IPv4 address.
|
||||||
|
return nil, EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
return raw.Addr[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInet4Addr sets a C in_addr/Go []byte (4-byte IPv4 address) value in an
|
||||||
|
// embedded sockaddr within the Ifreq's union data. v must be 4 bytes in length
|
||||||
|
// or an error will be returned.
|
||||||
|
func (ifr *Ifreq) SetInet4Addr(v []byte) error {
|
||||||
|
if len(v) != 4 {
|
||||||
|
return EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
var addr [4]byte
|
||||||
|
copy(addr[:], v)
|
||||||
|
|
||||||
|
ifr.clear()
|
||||||
|
*(*RawSockaddrInet4)(
|
||||||
|
unsafe.Pointer(&ifr.raw.Ifru[:SizeofSockaddrInet4][0]),
|
||||||
|
) = RawSockaddrInet4{
|
||||||
|
// Always set IP family as ioctls would require it anyway.
|
||||||
|
Family: AF_INET,
|
||||||
|
Addr: addr,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint16 returns the Ifreq union data as a C short/Go uint16 value.
|
||||||
|
func (ifr *Ifreq) Uint16() uint16 {
|
||||||
|
return *(*uint16)(unsafe.Pointer(&ifr.raw.Ifru[:2][0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUint16 sets a C short/Go uint16 value as the Ifreq's union data.
|
||||||
|
func (ifr *Ifreq) SetUint16(v uint16) {
|
||||||
|
ifr.clear()
|
||||||
|
*(*uint16)(unsafe.Pointer(&ifr.raw.Ifru[:2][0])) = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32 returns the Ifreq union data as a C int/Go uint32 value.
|
||||||
|
func (ifr *Ifreq) Uint32() uint32 {
|
||||||
|
return *(*uint32)(unsafe.Pointer(&ifr.raw.Ifru[:4][0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUint32 sets a C int/Go uint32 value as the Ifreq's union data.
|
||||||
|
func (ifr *Ifreq) SetUint32(v uint32) {
|
||||||
|
ifr.clear()
|
||||||
|
*(*uint32)(unsafe.Pointer(&ifr.raw.Ifru[:4][0])) = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear zeroes the ifreq's union field to prevent trailing garbage data from
|
||||||
|
// being sent to the kernel if an ifreq is reused.
|
||||||
|
func (ifr *Ifreq) clear() {
|
||||||
|
for i := range ifr.raw.Ifru {
|
||||||
|
ifr.raw.Ifru[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(mdlayher): export as IfreqData? For now we can provide helpers such as
|
||||||
|
// IoctlGetEthtoolDrvinfo which use these APIs under the hood.
|
||||||
|
|
||||||
|
// An ifreqData is an Ifreq which carries pointer data. To produce an ifreqData,
|
||||||
|
// use the Ifreq.withData method.
|
||||||
|
type ifreqData struct {
|
||||||
|
name [IFNAMSIZ]byte
|
||||||
|
// A type separate from ifreq is required in order to comply with the
|
||||||
|
// unsafe.Pointer rules since the "pointer-ness" of data would not be
|
||||||
|
// preserved if it were cast into the byte array of a raw ifreq.
|
||||||
|
data unsafe.Pointer
|
||||||
|
// Pad to the same size as ifreq.
|
||||||
|
_ [len(ifreq{}.Ifru) - SizeofPtr]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// withData produces an ifreqData with the pointer p set for ioctls which require
|
||||||
|
// arbitrary pointer data.
|
||||||
|
func (ifr Ifreq) withData(p unsafe.Pointer) ifreqData {
|
||||||
|
return ifreqData{
|
||||||
|
name: ifr.raw.Ifrn,
|
||||||
|
data: p,
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,11 +2,33 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
//go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris
|
||||||
|
// +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
import "runtime"
|
import (
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ioctl itself should not be exposed directly, but additional get/set
|
||||||
|
// functions for specific types are permissible.
|
||||||
|
|
||||||
|
// IoctlSetInt performs an ioctl operation which sets an integer value
|
||||||
|
// on fd, using the specified request number.
|
||||||
|
func IoctlSetInt(fd int, req uint, value int) error {
|
||||||
|
return ioctl(fd, req, uintptr(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IoctlSetPointerInt performs an ioctl operation which sets an
|
||||||
|
// integer value on fd, using the specified request number. The ioctl
|
||||||
|
// argument is called with a pointer to the integer value, rather than
|
||||||
|
// passing the integer value directly.
|
||||||
|
func IoctlSetPointerInt(fd int, req uint, value int) error {
|
||||||
|
v := int32(value)
|
||||||
|
return ioctl(fd, req, uintptr(unsafe.Pointer(&v)))
|
||||||
|
}
|
||||||
|
|
||||||
// IoctlSetWinsize performs an ioctl on fd with a *Winsize argument.
|
// IoctlSetWinsize performs an ioctl on fd with a *Winsize argument.
|
||||||
//
|
//
|
||||||
|
@ -14,7 +36,7 @@ import "runtime"
|
||||||
func IoctlSetWinsize(fd int, req uint, value *Winsize) error {
|
func IoctlSetWinsize(fd int, req uint, value *Winsize) error {
|
||||||
// TODO: if we get the chance, remove the req parameter and
|
// TODO: if we get the chance, remove the req parameter and
|
||||||
// hardcode TIOCSWINSZ.
|
// hardcode TIOCSWINSZ.
|
||||||
err := ioctlSetWinsize(fd, req, value)
|
err := ioctl(fd, req, uintptr(unsafe.Pointer(value)))
|
||||||
runtime.KeepAlive(value)
|
runtime.KeepAlive(value)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -24,7 +46,30 @@ func IoctlSetWinsize(fd int, req uint, value *Winsize) error {
|
||||||
// The req value will usually be TCSETA or TIOCSETA.
|
// The req value will usually be TCSETA or TIOCSETA.
|
||||||
func IoctlSetTermios(fd int, req uint, value *Termios) error {
|
func IoctlSetTermios(fd int, req uint, value *Termios) error {
|
||||||
// TODO: if we get the chance, remove the req parameter.
|
// TODO: if we get the chance, remove the req parameter.
|
||||||
err := ioctlSetTermios(fd, req, value)
|
err := ioctl(fd, req, uintptr(unsafe.Pointer(value)))
|
||||||
runtime.KeepAlive(value)
|
runtime.KeepAlive(value)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IoctlGetInt performs an ioctl operation which gets an integer value
|
||||||
|
// from fd, using the specified request number.
|
||||||
|
//
|
||||||
|
// A few ioctl requests use the return value as an output parameter;
|
||||||
|
// for those, IoctlRetInt should be used instead of this function.
|
||||||
|
func IoctlGetInt(fd int, req uint) (int, error) {
|
||||||
|
var value int
|
||||||
|
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value)))
|
||||||
|
return value, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func IoctlGetWinsize(fd int, req uint) (*Winsize, error) {
|
||||||
|
var value Winsize
|
||||||
|
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value)))
|
||||||
|
return &value, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func IoctlGetTermios(fd int, req uint) (*Termios, error) {
|
||||||
|
var value Termios
|
||||||
|
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value)))
|
||||||
|
return &value, err
|
||||||
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue