From 4e033ed5f37c47ec8d477bd0ee71c935ddfc8938 Mon Sep 17 00:00:00 2001 From: "xiaobing.wang" Date: Fri, 9 Aug 2024 11:37:49 +0800 Subject: [PATCH] feat: update traefik sdk --- sdk/traefik/.traefik.yml | 2 +- sdk/traefik/README.md | 2 +- sdk/traefik/go.mod | 6 +- sdk/traefik/safeline.go | 80 ++++-- .../vendor/github.com/chaitin/t1k-go/License | 201 ++++++++++++++ .../github.com/chaitin/t1k-go/README.md | 5 + .../vendor/github.com/chaitin/t1k-go/conn.go | 101 +++++++ .../github.com/chaitin/t1k-go/detect.go | 253 ++++++++++++++++++ .../chaitin/t1k-go/detection/context.go | 88 ++++++ .../chaitin/t1k-go/detection/extra.go | 93 +++++++ .../chaitin/t1k-go/detection/request.go | 132 +++++++++ .../chaitin/t1k-go/detection/response.go | 72 +++++ .../chaitin/t1k-go/detection/result.go | 76 ++++++ .../github.com/chaitin/t1k-go/heartbeat.go | 17 ++ .../chaitin/t1k-go/misc/dump_hex.go | 81 ++++++ .../github.com/chaitin/t1k-go/misc/errors.go | 58 ++++ .../chaitin/t1k-go/misc/gen_uuid.go | 17 ++ .../github.com/chaitin/t1k-go/misc/mt19937.go | 102 +++++++ .../github.com/chaitin/t1k-go/misc/now.go | 9 + .../github.com/chaitin/t1k-go/server.go | 184 +++++++++++++ .../github.com/chaitin/t1k-go/t1k/header.go | 35 +++ .../github.com/chaitin/t1k-go/t1k/section.go | 95 +++++++ .../github.com/chaitin/t1k-go/t1k/tag.go | 39 +++ .../vendor/golang.org/x/xerrors/LICENSE | 27 ++ .../vendor/golang.org/x/xerrors/PATENTS | 22 ++ .../vendor/golang.org/x/xerrors/README | 2 + .../vendor/golang.org/x/xerrors/adaptor.go | 193 +++++++++++++ .../golang.org/x/xerrors/codereview.cfg | 1 + .../vendor/golang.org/x/xerrors/doc.go | 23 ++ .../vendor/golang.org/x/xerrors/errors.go | 33 +++ .../vendor/golang.org/x/xerrors/fmt.go | 190 +++++++++++++ .../vendor/golang.org/x/xerrors/format.go | 34 +++ .../vendor/golang.org/x/xerrors/frame.go | 56 ++++ .../golang.org/x/xerrors/internal/internal.go | 8 + .../vendor/golang.org/x/xerrors/wrap.go | 112 ++++++++ sdk/traefik/vendor/modules.txt | 14 +- 36 files changed, 2426 insertions(+), 37 deletions(-) create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/License create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/README.md create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/conn.go create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/detect.go create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/context.go create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/extra.go create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/request.go create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/response.go create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/result.go create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/heartbeat.go create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/dump_hex.go create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/errors.go create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/gen_uuid.go create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/mt19937.go create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/now.go create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/server.go create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/t1k/header.go create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/t1k/section.go create mode 100644 sdk/traefik/vendor/github.com/chaitin/t1k-go/t1k/tag.go create mode 100644 sdk/traefik/vendor/golang.org/x/xerrors/LICENSE create mode 100644 sdk/traefik/vendor/golang.org/x/xerrors/PATENTS create mode 100644 sdk/traefik/vendor/golang.org/x/xerrors/README create mode 100644 sdk/traefik/vendor/golang.org/x/xerrors/adaptor.go create mode 100644 sdk/traefik/vendor/golang.org/x/xerrors/codereview.cfg create mode 100644 sdk/traefik/vendor/golang.org/x/xerrors/doc.go create mode 100644 sdk/traefik/vendor/golang.org/x/xerrors/errors.go create mode 100644 sdk/traefik/vendor/golang.org/x/xerrors/fmt.go create mode 100644 sdk/traefik/vendor/golang.org/x/xerrors/format.go create mode 100644 sdk/traefik/vendor/golang.org/x/xerrors/frame.go create mode 100644 sdk/traefik/vendor/golang.org/x/xerrors/internal/internal.go create mode 100644 sdk/traefik/vendor/golang.org/x/xerrors/wrap.go diff --git a/sdk/traefik/.traefik.yml b/sdk/traefik/.traefik.yml index 6a612725..6e8b402f 100644 --- a/sdk/traefik/.traefik.yml +++ b/sdk/traefik/.traefik.yml @@ -1,7 +1,7 @@ displayName: Chaitin Safeline WAF type: middleware -import: github.com/xbingW/traefik-safeline +import: github.com/chaitin/traefik-safeline summary: 'Traefik plugin to proxy requests to safeline waf.t serves as a reverse proxy access to protect your website from network attacks that including OWASP attacks, zero-day attacks, web crawlers, vulnerability scanning, vulnerability exploit, http flood and so on.' diff --git a/sdk/traefik/README.md b/sdk/traefik/README.md index 5a189516..6aeac670 100644 --- a/sdk/traefik/README.md +++ b/sdk/traefik/README.md @@ -29,7 +29,7 @@ The following declaration (given here in YAML) defines a plugin: experimental: plugins: safeline: - moduleName: github.com/xbingW/traefik-safeline + moduleName: github.com/chaitin/traefik-safeline version: v1.0.0 ``` diff --git a/sdk/traefik/go.mod b/sdk/traefik/go.mod index 3af65602..3d7d490c 100644 --- a/sdk/traefik/go.mod +++ b/sdk/traefik/go.mod @@ -1,5 +1,7 @@ -module github.com/xbingW/traefik-safeline +module github.com/chaitin/traefik-safeline go 1.17 -require github.com/xbingW/t1k v1.2.1 +require golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + +require github.com/chaitin/t1k-go v1.5.0 diff --git a/sdk/traefik/safeline.go b/sdk/traefik/safeline.go index 2311012d..5a2253de 100644 --- a/sdk/traefik/safeline.go +++ b/sdk/traefik/safeline.go @@ -2,12 +2,13 @@ package traefik_safeline import ( "context" - "encoding/json" + "fmt" "log" "net/http" "os" + "sync" - "github.com/xbingW/t1k" + t1k "github.com/chaitin/t1k-go" ) // Package example a example plugin. @@ -15,61 +16,84 @@ import ( // Config the plugin configuration. type Config struct { // Addr is the address for the detector - Addr string `yaml:"addr"` - // Get ip from header, if not set, get ip from remote addr - IpHeader string `yaml:"ipHeader"` - // When ip_header has multiple ip, use this to get the ip - // - //for example, X-Forwarded-For: ip1, ip2, ip3 - // when ip_last_index is 0, the client ip is ip3 - // when ip_last_index is 1, the client ip is ip2 - // when ip_last_index is 2, the client ip is ip1 - IPRightIndex uint `yaml:"ipRightIndex"` + Addr string `yaml:"addr"` + PoolSize int `yaml:"pool_size"` } // CreateConfig creates the default plugin configuration. func CreateConfig() *Config { return &Config{ - Addr: "", - IpHeader: "", - IPRightIndex: 0, + Addr: "", + PoolSize: 100, } } // Safeline a plugin. type Safeline struct { next http.Handler + server *t1k.Server name string config *Config logger *log.Logger + mu sync.Mutex } // New created a new plugin. func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) { + logger := log.New(os.Stdout, "safeline", log.LstdFlags) + logger.Printf("config: %+v", config) return &Safeline{ next: next, name: name, config: config, - logger: log.New(os.Stdout, "safeline", log.LstdFlags), + logger: logger, }, nil } +func (s *Safeline) initServer() error { + if s.server != nil { + return nil + } + s.mu.Lock() + defer s.mu.Unlock() + if s.server == nil { + server, err := t1k.NewWithPoolSize(s.config.Addr, s.config.PoolSize) + if err != nil { + return err + } + s.server = server + } + return nil +} + func (s *Safeline) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - d := t1k.NewDetector(t1k.Config{ - Addr: s.config.Addr, - IpHeader: s.config.IpHeader, - IPRightIndex: s.config.IPRightIndex, - }) - resp, err := d.DetectorRequest(req) + defer func() { + if r := recover(); r != nil { + s.logger.Printf("panic: %s", r) + } + }() + if err := s.initServer(); err != nil { + s.logger.Printf("error in initServer: %s", err) + s.next.ServeHTTP(rw, req) + return + } + rw.Header().Set("X-Chaitin-waf", "safeline") + result, err := s.server.DetectHttpRequest(req) if err != nil { - s.logger.Printf("Failed to detect request: %v", err) + s.logger.Printf("error in detection: \n%+v\n", err) + s.next.ServeHTTP(rw, req) + return } - if resp != nil && !resp.Allowed() { - rw.WriteHeader(resp.StatusCode()) - if err := json.NewEncoder(rw).Encode(resp.BlockMessage()); err != nil { - s.logger.Printf("Failed to encode block message: %v", err) - } + if result.Blocked() { + rw.WriteHeader(result.StatusCode()) + msg := fmt.Sprintf(`{"code": %d, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "%s"}`, + result.StatusCode(), + result.EventID(), + ) + _, _ = rw.Write([]byte(msg)) return } s.next.ServeHTTP(rw, req) + //rw.WriteHeader(http.StatusForbidden) + //_, _ = rw.Write([]byte("Inject by safeline\n")) } diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/License b/sdk/traefik/vendor/github.com/chaitin/t1k-go/License new file mode 100644 index 00000000..e2cfd4f8 --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/License @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2023 Beijing Chaitin Technology Co., Ltd. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/README.md b/sdk/traefik/vendor/github.com/chaitin/t1k-go/README.md new file mode 100644 index 00000000..db2bc7eb --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/README.md @@ -0,0 +1,5 @@ +# t1k-go + +Go implementation of the T1K protocol for [Chaitin/SafeLine](https://github.com/chaitin/safeline) Web Application Firewall. + + diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/conn.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/conn.go new file mode 100644 index 00000000..58958325 --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/conn.go @@ -0,0 +1,101 @@ +package t1k + +import ( + "net" + "net/http" + + "github.com/chaitin/t1k-go/detection" + "github.com/chaitin/t1k-go/t1k" + + "github.com/chaitin/t1k-go/misc" +) + +type conn struct { + socket net.Conn + server *Server + failing bool +} + +func makeConn(socket net.Conn, server *Server) *conn { + return &conn{ + socket: socket, + server: server, + failing: false, + } +} + +func (c *conn) onErr(err error) { + if err != nil { + // re-open socket to recover from possible error state + c.socket.Close() + sock, errConnect := c.server.socketFactory() + if errConnect != nil { + c.failing = true + return + } + c.socket = sock + } +} + +func (c *conn) Close() { + if c.socket == nil { + return + } + c.socket.Close() +} + +func (c *conn) DetectRequestInCtx(dc *detection.DetectionContext) (*detection.Result, error) { + ret, err := DetectRequestInCtx(c.socket, dc) + c.onErr(err) + return ret, err +} + +func (c *conn) DetectResponseInCtx(dc *detection.DetectionContext) (*detection.Result, error) { + ret, err := DetectResponseInCtx(c.socket, dc) + c.onErr(err) + return ret, misc.ErrorWrap(err, "") +} + +func (c *conn) Detect(dc *detection.DetectionContext) (*detection.Result, *detection.Result, error) { + retReq, retRsp, err := Detect(c.socket, dc) + c.onErr(err) + return retReq, retRsp, misc.ErrorWrap(err, "") +} + +func (c *conn) DetectHttpRequest(req *http.Request) (*detection.Result, error) { + ret, err := DetectHttpRequest(c.socket, req) + c.onErr(err) + return ret, err +} + +func (c *conn) DetectRequest(req detection.Request) (*detection.Result, error) { + ret, err := DetectRequest(c.socket, req) + c.onErr(err) + return ret, err +} + +func (c *conn) Heartbeat() { + err := DoHeartbeat(c.socket) + c.onErr(err) +} + +func (c *conn) WriteSection(sec t1k.Section) error { + err := t1k.WriteSection(sec, c.socket) + return misc.ErrorWrap(err, "") +} + +func (c *conn) ReadSection() (t1k.Section, error) { + sec, err := t1k.ReadSection(c.socket) + if err != nil { + return nil, misc.ErrorWrap(err, "") + } + return sec, nil +} + +func (c *conn) ReadFullSection() (t1k.Section, error) { + sec, err := t1k.ReadFullSection(c.socket) + if err != nil { + return nil, misc.ErrorWrap(err, "") + } + return sec, nil +} diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/detect.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/detect.go new file mode 100644 index 00000000..9bbad796 --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/detect.go @@ -0,0 +1,253 @@ +package t1k + +import ( + "bytes" + "fmt" + "io" + "net/http" + + "github.com/chaitin/t1k-go/detection" + "github.com/chaitin/t1k-go/t1k" + + "github.com/chaitin/t1k-go/misc" +) + +func writeDetectionRequest(w io.Writer, req detection.Request) error { + { + data, err := req.Header() + if err != nil { + return err + } + sec := t1k.MakeSimpleSection(t1k.TAG_HEADER|t1k.MASK_FIRST, data) + err = t1k.WriteSection(sec, w) + if err != nil { + return err + } + } + { + bodySize, bodyReadCloser, err := req.Body() + if err == nil { + defer bodyReadCloser.Close() + sec := t1k.MakeReaderSection(t1k.TAG_BODY, bodySize, bodyReadCloser) + err = t1k.WriteSection(sec, w) + if err != nil { + return err + } + } + } + { + data, err := req.Extra() + if err != nil { + return err + } + sec := t1k.MakeSimpleSection(t1k.TAG_EXTRA, data) + err = t1k.WriteSection(sec, w) + if err != nil { + return err + } + } + { + sec := t1k.MakeSimpleSection(t1k.TAG_VERSION|t1k.MASK_LAST, []byte("Proto:2\n")) + err := t1k.WriteSection(sec, w) + if err != nil { + return err + } + } + return nil +} + +func writeDetectionResponse(w io.Writer, rsp detection.Response) error { + { + data, err := rsp.RequestHeader() + if err != nil { + return misc.ErrorWrap(err, "") + } + sec := t1k.MakeSimpleSection(t1k.TAG_HEADER|t1k.MASK_FIRST, data) + err = t1k.WriteSection(sec, w) + if err != nil { + return misc.ErrorWrap(err, "") + } + } + { + data, err := rsp.Header() + if err != nil { + return misc.ErrorWrap(err, "") + } + sec := t1k.MakeSimpleSection(t1k.TAG_RSP_HEADER, data) + err = t1k.WriteSection(sec, w) + if err != nil { + return misc.ErrorWrap(err, "") + } + } + { + bodySize, bodyReadCloser, err := rsp.Body() + if err == nil { + defer bodyReadCloser.Close() + sec := t1k.MakeReaderSection(t1k.TAG_RSP_BODY, bodySize, bodyReadCloser) + err = t1k.WriteSection(sec, w) + if err != nil { + return err + } + } + } + { + data, err := rsp.Extra() + if err != nil { + return misc.ErrorWrap(err, "") + } + sec := t1k.MakeSimpleSection(t1k.TAG_RSP_EXTRA, data) + err = t1k.WriteSection(sec, w) + if err != nil { + return misc.ErrorWrap(err, "") + } + } + { + sec := t1k.MakeSimpleSection(t1k.TAG_VERSION, []byte("Proto:2\n")) + err := t1k.WriteSection(sec, w) + if err != nil { + return misc.ErrorWrap(err, "") + } + } + { + data, err := rsp.T1KContext() + if err != nil { + return misc.ErrorWrap(err, "") + } + sec := t1k.MakeSimpleSection(t1k.TAG_CONTEXT|t1k.MASK_LAST, data) + err = t1k.WriteSection(sec, w) + if err != nil { + return misc.ErrorWrap(err, "") + } + } + return nil +} + +func readDetectionResult(r io.Reader) (*detection.Result, error) { + var ret detection.Result + parseSection := func(sec t1k.Section) error { + var buf bytes.Buffer + err := sec.WriteBody(&buf) + if err != nil { + return misc.ErrorWrap(err, "") + } + tag := sec.Header().Tag.Strip() + switch tag { + case t1k.TAG_HEADER: + if len(buf.Bytes()) != 1 { + return fmt.Errorf("len(T1K_HEADER) != 1") + } + ret.Head = buf.Bytes()[0] + case t1k.TAG_BODY: + ret.Body = buf.Bytes() + case t1k.TAG_ALOG: + ret.Alog = buf.Bytes() + case t1k.TAG_EXTRA_HEADER: + ret.ExtraHeader = buf.Bytes() + case t1k.TAG_EXTRA_BODY: + ret.ExtraBody = buf.Bytes() + case t1k.TAG_CONTEXT: + ret.T1KContext = buf.Bytes() + case t1k.TAG_COOKIE: + ret.Cookie = buf.Bytes() + case t1k.TAG_WEB_LOG: + ret.WebLog = buf.Bytes() + } + return nil + } + sec, err := t1k.ReadFullSection(r) + if err != nil { + return nil, misc.ErrorWrap(err, "") + } + if !sec.Header().Tag.IsFirst() { + return nil, fmt.Errorf("first section IsFirst != true, middle of another msg or corrupt stream, with <%x>", sec.Header().Tag) + } + for { + err = parseSection(sec) + if err != nil { + return nil, misc.ErrorWrap(err, "") + } + if sec.Header().Tag.IsLast() { + break + } + sec, err = t1k.ReadSection(r) + if err != nil { + return nil, misc.ErrorWrap(err, "") + } + } + return &ret, nil +} + +func doDetectRequest(s io.ReadWriter, req detection.Request) (*detection.Result, error) { + err := writeDetectionRequest(s, req) + if err != nil { + return nil, misc.ErrorWrap(err, "") + } + ret, err := readDetectionResult(s) + if err != nil { + return nil, misc.ErrorWrap(err, "") + } + ret.Objective = detection.RO_REQUEST + return ret, nil +} + +func doDetectResponse(s io.ReadWriter, rsp detection.Response) (*detection.Result, error) { + err := writeDetectionResponse(s, rsp) + if err != nil { + return nil, misc.ErrorWrap(err, "") + } + ret, err := readDetectionResult(s) + if err != nil { + return nil, misc.ErrorWrap(err, "") + } + ret.Objective = detection.RO_RESPONSE + return ret, nil +} + +func DetectRequestInCtx(s io.ReadWriter, dc *detection.DetectionContext) (*detection.Result, error) { + ret, err := doDetectRequest(s, dc.Request) + if err != nil { + return nil, err + } + dc.ProcessResult(ret) + return ret, nil +} + +func DetectResponseInCtx(s io.ReadWriter, dc *detection.DetectionContext) (*detection.Result, error) { + ret, err := doDetectResponse(s, dc.Response) + if err != nil { + return nil, misc.ErrorWrap(err, "") + } + dc.ProcessResult(ret) + return ret, nil +} + +func Detect(s io.ReadWriter, dc *detection.DetectionContext) (*detection.Result, *detection.Result, error) { + var reqResult *detection.Result + var rspResult *detection.Result + if dc.Request != nil { + ret, err := doDetectRequest(s, dc.Request) + if err != nil { + return nil, nil, misc.ErrorWrap(err, "") + } + reqResult = ret + dc.ProcessResult(reqResult) + } + if dc.Response != nil { + ret, err := doDetectResponse(s, dc.Response) + if err != nil { + return nil, nil, misc.ErrorWrap(err, "") + } + rspResult = ret + dc.ProcessResult(rspResult) + } + return reqResult, rspResult, nil +} + +func DetectHttpRequest(s io.ReadWriter, req *http.Request) (*detection.Result, error) { + dc, _ := detection.MakeContextWithRequest(req) + return doDetectRequest(s, detection.MakeHttpRequestInCtx(req, dc)) +} + +func DetectRequest(s io.ReadWriter, req detection.Request) (*detection.Result, error) { + return doDetectRequest(s, req) +} diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/context.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/context.go new file mode 100644 index 00000000..13702b2b --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/context.go @@ -0,0 +1,88 @@ +package detection + +import ( + "errors" + "net/http" + + "github.com/chaitin/t1k-go/misc" +) + +type DetectionContext struct { + UUID string + Scheme string + ProxyName string + RemoteAddr string + Protocol string + RemotePort uint16 + LocalAddr string + LocalPort uint16 + ReqBeginTime int64 + RspBeginTime int64 + + T1KContext []byte + + Request Request + Response Response +} + +func New() *DetectionContext { + return &DetectionContext{ + UUID: misc.GenUUID(), + Scheme: "http", + ProxyName: "go-sdk", + RemoteAddr: "127.0.0.1", + RemotePort: 30001, + LocalAddr: "127.0.0.1", + LocalPort: 80, + Protocol: "HTTP/1.1", + } +} + +func MakeContextWithRequest(req *http.Request) (*DetectionContext, error) { + if req == nil { + return nil, errors.New("nil http.request or response") + } + wrapReq := &HttpRequest{ + req: req, + } + + // ignore GetRemoteIP error,not sure request record remote ip + remoteIP, _ := wrapReq.GetRemoteIP() + remotePort, _ := wrapReq.GetRemotePort() + + localAddr, err := wrapReq.GetUpstreamAddress() + if err != nil { + return nil, err + } + + localPort, err := wrapReq.GetUpstreamPort() + if err != nil { + return nil, err + } + + scheme := "http" + if req.TLS != nil { + scheme = "https" + } + + context := &DetectionContext{ + UUID: misc.GenUUID(), + Scheme: scheme, + ProxyName: "go-sdk", + RemoteAddr: remoteIP, + RemotePort: remotePort, + LocalAddr: localAddr, + LocalPort: localPort, + ReqBeginTime: misc.Now(), + Request: wrapReq, + Protocol: req.Proto, + } + wrapReq.dc = context + return context, nil +} + +func (dc *DetectionContext) ProcessResult(r *Result) { + if r.Objective == RO_REQUEST { + dc.T1KContext = r.T1KContext + } +} diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/extra.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/extra.go new file mode 100644 index 00000000..3176dab8 --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/extra.go @@ -0,0 +1,93 @@ +package detection + +import ( + "fmt" + + "github.com/chaitin/t1k-go/misc" +) + +func MakeRequestExtra( + scheme string, + proxyName string, + remoteAddr string, + remotePort uint16, + localAddr string, + localPort uint16, + uuid string, + hasRspIfOK string, + hasRspIfBlock string, + reqBeginTime int64, +) []byte { + format := "Scheme:%s\n" + + "ProxyName:%s\n" + + "RemoteAddr:%s\n" + + "RemotePort:%d\n" + + "LocalAddr:%s\n" + + "LocalPort:%d\n" + + "UUID:%s\n" + + "HasRspIfOK:%s\n" + + "HasRspIfBlock:%s\n" + + "ReqBeginTime:%d\n" + + return []byte(fmt.Sprintf( + format, + scheme, + proxyName, + remoteAddr, + remotePort, + localAddr, + localPort, + uuid, + hasRspIfOK, + hasRspIfBlock, + reqBeginTime, + )) +} + +func MakeResponseExtra( + scheme string, + proxyName string, + remoteAddr string, + remotePort uint16, + localAddr string, + localPort uint16, + uuid string, + rspBeginTime int64, +) []byte { + format := "Scheme:%s\n" + + "ProxyName:%s\n" + + "RemoteAddr:%s\n" + + "RemotePort:%d\n" + + "LocalAddr:%s\n" + + "LocalPort:%d\n" + + "UUID:%s\n" + + "RspBeginTime:%d\n" + + return []byte(fmt.Sprintf( + format, + scheme, + proxyName, + remoteAddr, + remotePort, + localAddr, + localPort, + uuid, + rspBeginTime, + )) +} + +func PlaceholderRequestExtra(uuid string) []byte { + return MakeRequestExtra("http", "go-sdk", "127.0.0.1", 30001, "127.0.0.1", 80, uuid, "n", "n", misc.Now()) +} + +func GenRequestExtra(dc *DetectionContext) []byte { + hasRsp := "u" + if dc.Response != nil { + hasRsp = "y" + } + return MakeRequestExtra(dc.Scheme, dc.ProxyName, dc.RemoteAddr, dc.RemotePort, dc.LocalAddr, dc.LocalPort, dc.UUID, hasRsp, hasRsp, dc.ReqBeginTime) +} + +func GenResponseExtra(dc *DetectionContext) []byte { + return MakeResponseExtra(dc.Scheme, dc.ProxyName, dc.RemoteAddr, dc.RemotePort, dc.LocalAddr, dc.LocalPort, dc.UUID, dc.RspBeginTime) +} diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/request.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/request.go new file mode 100644 index 00000000..044203b5 --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/request.go @@ -0,0 +1,132 @@ +package detection + +import ( + "bytes" + "errors" + "fmt" + "io" + "net" + "net/http" + "strconv" + + "github.com/chaitin/t1k-go/misc" +) + +type Request interface { + Header() ([]byte, error) + Body() (uint32, io.ReadCloser, error) + Extra() ([]byte, error) +} + +type HttpRequest struct { + req *http.Request + dc *DetectionContext // this is optional +} + +func MakeHttpRequest(req *http.Request) *HttpRequest { + return &HttpRequest{ + req: req, + } +} + +func MakeHttpRequestInCtx(req *http.Request, dc *DetectionContext) *HttpRequest { + ret := &HttpRequest{ + req: req, + dc: dc, + } + dc.Request = ret + + if dc.ReqBeginTime == 0 { + dc.ReqBeginTime = misc.Now() + } + + return ret +} + +func (r *HttpRequest) GetUpstreamAddress() (string, error) { + if r.req.Host == "" { + return "", errors.New("empty Host in request") + } + host, _, err := net.SplitHostPort(r.req.Host) + if err != nil { + return r.req.Host, nil // OK; there probably was no port + } + return host, nil +} + +func (r *HttpRequest) GetUpstreamPort() (uint16, error) { + _, port, err := net.SplitHostPort(r.req.Host) + if err != nil { + if r.req.TLS != nil { + return 443, nil + } else { + return 80, nil + } + } + if portNum, err := strconv.Atoi(port); err == nil { + return uint16(portNum), nil + } + return 0, errors.New("wrong value of port") +} + +func (r *HttpRequest) GetRemoteIP() (string, error) { + host, _, err := net.SplitHostPort(r.req.RemoteAddr) + if err != nil { + return r.req.RemoteAddr, nil + } + return host, nil +} + +func (r *HttpRequest) GetRemotePort() (uint16, error) { + _, port, _ := net.SplitHostPort(r.req.RemoteAddr) + if portNum, err := strconv.Atoi(port); err == nil { + return uint16(portNum), nil + } + return 0, errors.New("wrong value of port") +} + +func (r *HttpRequest) Header() ([]byte, error) { + var buf bytes.Buffer + proto := r.req.Proto + if r.dc != nil { + if r.dc.Protocol != "" { + proto = r.dc.Protocol + } else { + r.dc.Protocol = proto + } + } + startLine := fmt.Sprintf("%s %s %s\r\n", r.req.Method, r.req.URL.RequestURI(), proto) + _, err := buf.Write([]byte(startLine)) + if err != nil { + return nil, err + } + _, err = buf.Write([]byte(fmt.Sprintf("Host: %s\r\n", r.req.Host))) + if err != nil { + return nil, err + } + err = r.req.Header.Write(&buf) + if err != nil { + return nil, err + } + _, err = buf.Write([]byte("\r\n")) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func (r *HttpRequest) Body() (uint32, io.ReadCloser, error) { + bodyBytes, err := io.ReadAll(r.req.Body) + if err != nil { + return 0, nil, err + } + r.req.Body = io.NopCloser(bytes.NewReader(bodyBytes)) + return uint32(len(bodyBytes)), io.NopCloser(bytes.NewReader(bodyBytes)), nil +} + +func (r *HttpRequest) Extra() ([]byte, error) { + if r.dc == nil { + return PlaceholderRequestExtra(misc.GenUUID()), nil + } + return GenRequestExtra(r.dc), nil +} diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/response.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/response.go new file mode 100644 index 00000000..644294a9 --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/response.go @@ -0,0 +1,72 @@ +package detection + +import ( + "bytes" + "fmt" + "io" + "net/http" + + "github.com/chaitin/t1k-go/misc" +) + +type Response interface { + RequestHeader() ([]byte, error) + Header() ([]byte, error) + Body() (uint32, io.ReadCloser, error) + Extra() ([]byte, error) + T1KContext() ([]byte, error) +} + +type HttpResponse struct { + rsp *http.Response + dc *DetectionContext // this is a must-have +} + +func MakeHttpResponseInCtx(rsp *http.Response, dc *DetectionContext) *HttpResponse { + ret := &HttpResponse{ + rsp: rsp, + dc: dc, + } + dc.Response = ret + dc.RspBeginTime = misc.Now() + return ret +} + +func (r *HttpResponse) RequestHeader() ([]byte, error) { + return r.dc.Request.Header() +} + +func (r *HttpResponse) Header() ([]byte, error) { + var buf bytes.Buffer + statusLine := fmt.Sprintf("HTTP/1.1 %s\n", r.rsp.Status) + _, err := buf.Write([]byte(statusLine)) + if err != nil { + return nil, err + } + err = r.rsp.Header.Write(&buf) + if err != nil { + return nil, err + } + _, err = buf.Write([]byte("\r\n")) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func (r *HttpResponse) Body() (uint32, io.ReadCloser, error) { + bodyBytes, err := io.ReadAll(r.rsp.Body) + if err != nil { + return 0, nil, misc.ErrorWrapf(err, "get body size %d", len(bodyBytes)) + } + r.rsp.Body = io.NopCloser(bytes.NewReader(bodyBytes)) + return uint32(len(bodyBytes)), io.NopCloser(bytes.NewReader(bodyBytes)), nil +} + +func (r *HttpResponse) Extra() ([]byte, error) { + return GenResponseExtra(r.dc), nil +} + +func (r *HttpResponse) T1KContext() ([]byte, error) { + return r.dc.T1KContext, nil +} diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/result.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/result.go new file mode 100644 index 00000000..bffcacd9 --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/detection/result.go @@ -0,0 +1,76 @@ +package detection + +import ( + "log" + "net/http" + "regexp" + "strconv" +) + +type ResultObjective int + +const ( + RO_REQUEST ResultObjective = 0 + RO_RESPONSE ResultObjective = 1 +) + +type Result struct { + Objective ResultObjective + Head byte + Body []byte + Alog []byte + ExtraHeader []byte + ExtraBody []byte + T1KContext []byte + Cookie []byte + WebLog []byte +} + +func (r *Result) Passed() bool { + return r.Head == '.' +} + +func (r *Result) Blocked() bool { + return !r.Passed() +} + +func (r *Result) StatusCode() int { + str := string(r.Body) + if str == "" { + return http.StatusForbidden + } + code, err := strconv.Atoi(str) + if err != nil { + log.Printf("t1k convert status code failed: %v", err) + return http.StatusForbidden + } + return code +} + +func (r *Result) BlockMessage() map[string]interface{} { + return map[string]interface{}{ + "status": r.StatusCode(), + "success": false, + "message": "blocked by Chaitin SafeLine Web Application Firewall", + "event_id": r.EventID(), + } +} + +func (r *Result) EventID() string { + extra := string(r.ExtraBody) + if extra == "" { + return "" + } + // + re, err := regexp.Compile(`<\!--\s*event_id:\s*([a-zA-Z0-9]+)\s*-->\s*`) + if err != nil { + log.Printf("t1k compile regexp failed: %v", err) + return "" + } + matches := re.FindStringSubmatch(extra) + if len(matches) < 2 { + log.Printf("t1k regexp not match event id: %s", extra) + return "" + } + return matches[1] +} diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/heartbeat.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/heartbeat.go new file mode 100644 index 00000000..d6fdff8c --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/heartbeat.go @@ -0,0 +1,17 @@ +package t1k + +import ( + "io" + + "github.com/chaitin/t1k-go/t1k" +) + +func DoHeartbeat(s io.ReadWriter) error { + h := t1k.MakeHeader(t1k.MASK_FIRST|t1k.MASK_LAST, 0) + _, err := s.Write(h.Serialize()) + if err != nil { + return err + } + _, err = readDetectionResult(s) + return err +} diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/dump_hex.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/dump_hex.go new file mode 100644 index 00000000..783b868f --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/dump_hex.go @@ -0,0 +1,81 @@ +package misc + +import ( + "fmt" + "io" + "os" +) + +var asciiPrintableMap map[byte]bool + +func init() { + asciiPrintableMap = make(map[byte]bool) + for i := 0; i < 256; i++ { + asciiPrintableMap[byte(i)] = false + } + s := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ " + for _, ch := range []byte(s) { + asciiPrintableMap[ch] = true + } +} + +func isAsciiPrintable(b byte) bool { + return asciiPrintableMap[b] +} + +func DumpHex(w io.Writer, b []byte) error { + lines := len(b) / 16 + for i := 0; i < lines; i += 1 { + srep := "" + for j := 0; j < 16; j += 1 { + it := b[16*i+j] + _, err := w.Write([]byte(fmt.Sprintf("%02x ", it))) + if err != nil { + return err + } + if isAsciiPrintable(it) { + srep += string([]byte{it}) + } else { + srep += "." + } + } + _, err := w.Write([]byte("| " + srep + "\n")) + if err != nil { + return err + } + } + remain := len(b) - 16*lines + srep := "" + j := 0 + for ; j < remain; j += 1 { + it := b[16*lines+j] + _, err := w.Write([]byte(fmt.Sprintf("%02x ", it))) + if err != nil { + return err + } + if isAsciiPrintable(it) { + srep += string([]byte{it}) + } else { + srep += "." + } + } + for ; j < 16; j++ { + _, err := w.Write([]byte(" ")) + if err != nil { + return err + } + srep += " " + } + _, err := w.Write([]byte("| " + srep + "\n")) + if err != nil { + return err + } + return nil +} + +func PrintHex(b []byte) { + err := DumpHex(os.Stdout, b) + if err != nil { + fmt.Printf("error in PrintHex() : %s\n", err.Error()) + } +} diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/errors.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/errors.go new file mode 100644 index 00000000..ca40adaa --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/errors.go @@ -0,0 +1,58 @@ +package misc + +import ( + "fmt" + + "golang.org/x/xerrors" +) + +type wrapError struct { + message string + next error + frame xerrors.Frame +} + +func (e *wrapError) Unwrap() error { + return e.next +} + +func (e *wrapError) Error() string { + if e.next == nil { + return e.message + } + return fmt.Sprintf("%s: %v", e.message, e.next) +} + +func (e *wrapError) Format(f fmt.State, c rune) { + xerrors.FormatError(e, f, c) +} + +func (e *wrapError) FormatError(p xerrors.Printer) error { + p.Print(e.message) + if p.Detail() { + e.frame.Format(p) + } + return e.next +} + +func wrap(err error, message string, skip int) error { + if err == nil { + return nil + } + return &wrapError{ + message: message, + next: err, + frame: xerrors.Caller(skip), + } +} + +// Wrap returns a error annotating `err` with `message` and the caller's frame. +// Wrap returns nil if `err` is nil. +func ErrorWrap(err error, message string) error { + return wrap(err, message, 2) +} + +// Wrapf returns a error annotating `err` with `message` formatted and the caller's frame. +func ErrorWrapf(err error, message string, args ...interface{}) error { + return wrap(err, fmt.Sprintf(message, args...), 2) +} diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/gen_uuid.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/gen_uuid.go new file mode 100644 index 00000000..feb41087 --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/gen_uuid.go @@ -0,0 +1,17 @@ +package misc + +import ( + "encoding/hex" +) + +var ( + rng *MT19937 = NewMT19937() +) + +func GenUUID() string { + u := make([]byte, 16) + rng.RandBytes(u) + u[6] = (u[6] | 0x40) & 0x4F + u[8] = (u[8] | 0x80) & 0xBF + return hex.EncodeToString(u) +} diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/mt19937.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/mt19937.go new file mode 100644 index 00000000..ebbb17fc --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/mt19937.go @@ -0,0 +1,102 @@ +package misc + +import ( + "crypto/rand" + "encoding/binary" + "sync" + "time" +) + +const ( + n = 312 + m = 156 + seedMask = 170298 + hiMask uint64 = 0xffffffff80000000 + loMask uint64 = 0x000000007fffffff + matrixA uint64 = 0xB5026F5AA96619E9 +) + +type MT19937 struct { + state []uint64 + index int + mut sync.Mutex +} + +func NewMT19937WithSeed(seed int64) *MT19937 { + mt := &MT19937{ + state: make([]uint64, n), + index: n, + } + + mt.mut.Lock() + defer mt.mut.Unlock() + + mt.state[0] = uint64(seed) + for i := uint64(1); i < n; i++ { + mt.state[i] = 6364136223846793005*(mt.state[i-1]^(mt.state[i-1]>>62)) + i + } + + return mt +} + +func NewMT19937() *MT19937 { + var seed int64 + + b := make([]byte, 8) + if _, err := rand.Read(b); err == nil { + seed = int64(binary.LittleEndian.Uint64(b[:])) + } + + seed = seed ^ time.Now().UnixNano() ^ seedMask + + return NewMT19937WithSeed(seed) +} + +func (mt *MT19937) Uint64() uint64 { + mt.mut.Lock() + defer mt.mut.Unlock() + + x := mt.state + if mt.index >= n { + for i := 0; i < n-m; i++ { + y := (x[i] & hiMask) | (x[i+1] & loMask) + x[i] = x[i+m] ^ (y >> 1) ^ ((y & 1) * matrixA) + } + for i := n - m; i < n-1; i++ { + y := (x[i] & hiMask) | (x[i+1] & loMask) + x[i] = x[i+(m-n)] ^ (y >> 1) ^ ((y & 1) * matrixA) + } + y := (x[n-1] & hiMask) | (x[0] & loMask) + x[n-1] = x[m-1] ^ (y >> 1) ^ ((y & 1) * matrixA) + mt.index = 0 + } + y := x[mt.index] + y ^= (y >> 29) & 0x5555555555555555 + y ^= (y << 17) & 0x71D67FFFEDA60000 + y ^= (y << 37) & 0xFFF7EEE000000000 + y ^= (y >> 43) + mt.index++ + return y +} + +func (mt *MT19937) RandBytes(p []byte) { + for len(p) >= 8 { + val := mt.Uint64() + p[0] = byte(val) + p[1] = byte(val >> 8) + p[2] = byte(val >> 16) + p[3] = byte(val >> 24) + p[4] = byte(val >> 32) + p[5] = byte(val >> 40) + p[6] = byte(val >> 48) + p[7] = byte(val >> 56) + p = p[8:] + } + if len(p) > 0 { + val := mt.Uint64() + for i := 0; i < len(p); i++ { + p[i] = byte(val) + val >>= 8 + } + } +} diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/now.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/now.go new file mode 100644 index 00000000..9bdb97be --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/misc/now.go @@ -0,0 +1,9 @@ +package misc + +import ( + "time" +) + +func Now() int64 { + return time.Now().UnixNano() / 1e3 +} diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/server.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/server.go new file mode 100644 index 00000000..e862600a --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/server.go @@ -0,0 +1,184 @@ +package t1k + +import ( + "log" + "net" + "net/http" + "os" + "sync" + "time" + + "github.com/chaitin/t1k-go/detection" + + "github.com/chaitin/t1k-go/misc" +) + +const ( + DEFAULT_POOL_SIZE = 8 + HEARTBEAT_INTERVAL = 20 +) + +type Server struct { + socketFactory func() (net.Conn, error) + poolCh chan *conn + poolSize int + count int + closeCh chan struct{} + logger *log.Logger + mu sync.Mutex +} + +func (s *Server) newConn() error { + s.mu.Lock() + defer s.mu.Unlock() + sock, err := s.socketFactory() + if err != nil { + return err + } + s.count += 1 + s.poolCh <- makeConn(sock, s) + return nil +} + +func (s *Server) GetConn() (*conn, error) { + if s.count < s.poolSize { + for i := 0; i < (s.poolSize - s.count); i++ { + err := s.newConn() + if err != nil { + return nil, err + } + } + } + c := <-s.poolCh + return c, nil +} + +func (s *Server) PutConn(c *conn) { + s.mu.Lock() + defer s.mu.Unlock() + if c.failing { + s.count -= 1 + c.Close() + } else { + s.poolCh <- c + } +} + +func (s *Server) broadcastHeartbeat() { + l := len(s.poolCh) + for i := 0; i < l; i++ { + select { + case c := <-s.poolCh: + c.Heartbeat() + s.PutConn(c) + default: + return + } + } +} + +func (s *Server) runHeartbeatCo() { + for { + timer := time.NewTimer(HEARTBEAT_INTERVAL * time.Second) + select { + case <-s.closeCh: + return + case <-timer.C: + } + s.broadcastHeartbeat() + } +} + +func NewFromSocketFactoryWithPoolSize(socketFactory func() (net.Conn, error), poolSize int) (*Server, error) { + ret := &Server{ + socketFactory: socketFactory, + poolCh: make(chan *conn, poolSize), + poolSize: poolSize, + closeCh: make(chan struct{}), + logger: log.New(os.Stdout, "snserver", log.LstdFlags), + mu: sync.Mutex{}, + } + for i := 0; i < poolSize; i++ { + err := ret.newConn() + if err != nil { + return nil, err + } + } + go ret.runHeartbeatCo() + return ret, nil +} + +func NewFromSocketFactory(socketFactory func() (net.Conn, error)) (*Server, error) { + return NewFromSocketFactoryWithPoolSize(socketFactory, DEFAULT_POOL_SIZE) +} + +func NewWithPoolSize(addr string, poolSize int) (*Server, error) { + return NewFromSocketFactoryWithPoolSize(func() (net.Conn, error) { + return net.Dial("tcp", addr) + }, poolSize) +} + +func New(addr string) (*Server, error) { + return NewWithPoolSize(addr, DEFAULT_POOL_SIZE) +} + +func (s *Server) DetectRequestInCtx(dc *detection.DetectionContext) (*detection.Result, error) { + c, err := s.GetConn() + if err != nil { + return nil, err + } + defer s.PutConn(c) + return c.DetectRequestInCtx(dc) +} + +func (s *Server) DetectResponseInCtx(dc *detection.DetectionContext) (*detection.Result, error) { + c, err := s.GetConn() + if err != nil { + return nil, misc.ErrorWrap(err, "") + } + defer s.PutConn(c) + return c.DetectResponseInCtx(dc) +} + +func (s *Server) Detect(dc *detection.DetectionContext) (*detection.Result, *detection.Result, error) { + c, err := s.GetConn() + if err != nil { + return nil, nil, misc.ErrorWrap(err, "") + } + + reqResult, rspResult, err := c.Detect(dc) + if err == nil { + s.PutConn(c) + } + return reqResult, rspResult, err +} + +func (s *Server) DetectHttpRequest(req *http.Request) (*detection.Result, error) { + c, err := s.GetConn() + if err != nil { + return nil, err + } + defer s.PutConn(c) + return c.DetectHttpRequest(req) +} + +func (s *Server) DetectRequest(req detection.Request) (*detection.Result, error) { + c, err := s.GetConn() + if err != nil { + return nil, err + } + defer s.PutConn(c) + return c.DetectRequest(req) +} + +// blocks until all pending detection is completed +func (s *Server) Close() { + close(s.closeCh) + for i := 0; i < s.count; i++ { + c, err := s.GetConn() + if err != nil { + return + } + c.Close() + } +} diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/t1k/header.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/t1k/header.go new file mode 100644 index 00000000..bb4b9402 --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/t1k/header.go @@ -0,0 +1,35 @@ +package t1k + +import ( + "encoding/binary" +) + +const ( + T1K_HEADER_SIZE uint64 = 5 +) + +type Header struct { + Tag Tag + Size uint32 +} + +func MakeHeader(tag Tag, size uint32) Header { + return Header{ + Tag: tag, + Size: size, + } +} + +func (h Header) Serialize() []byte { + b := make([]byte, 5) + b[0] = byte(uint8(h.Tag)) + binary.LittleEndian.PutUint32(b[1:], h.Size) + return b +} + +func DeserializeHeader(b []byte) Header { + return MakeHeader( + Tag(uint8(b[0])), + binary.LittleEndian.Uint32(b[1:]), + ) +} diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/t1k/section.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/t1k/section.go new file mode 100644 index 00000000..f71a15dd --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/t1k/section.go @@ -0,0 +1,95 @@ +package t1k + +import ( + "bytes" + "io" + + "github.com/chaitin/t1k-go/misc" +) + +type Section interface { + Header() Header + WriteBody(io.Writer) error +} + +type SimpleSection struct { + tag Tag + body []byte +} + +func MakeSimpleSection(tag Tag, body []byte) *SimpleSection { + return &SimpleSection{ + tag: tag, + body: body, + } +} + +func (msg *SimpleSection) Header() Header { + return MakeHeader(msg.tag, uint32(len(msg.body))) +} + +func (msg *SimpleSection) WriteBody(w io.Writer) error { + _, err := w.Write(msg.body) + return err +} + +type ReaderSection struct { + tag Tag + size uint32 + reader io.Reader +} + +func MakeReaderSection(tag Tag, size uint32, reader io.Reader) *ReaderSection { + return &ReaderSection{ + tag: tag, + size: size, + reader: reader, + } +} + +func (msg *ReaderSection) Header() Header { + return MakeHeader(msg.tag, msg.size) +} + +func (msg *ReaderSection) WriteBody(w io.Writer) error { + _, err := io.CopyN(w, msg.reader, int64(msg.size)) + return misc.ErrorWrap(err, "") +} + +func WriteSection(s Section, w io.Writer) error { + h := s.Header() + _, err := w.Write(h.Serialize()) + if err != nil { + return misc.ErrorWrap(err, "") + } + return s.WriteBody(w) +} + +// returns a *ReaderSection, must call its WriteBody +// before next call to r +func ReadSection(r io.Reader) (Section, error) { + bHeader := make([]byte, T1K_HEADER_SIZE) + _, err := io.ReadFull(r, bHeader) + if err != nil { + return nil, err + } + h := DeserializeHeader(bHeader) + bodyReader := io.LimitReader(r, int64(h.Size)) + return MakeReaderSection(h.Tag, h.Size, bodyReader), nil +} + +// returns a *SimpleSection +func ReadFullSection(r io.Reader) (Section, error) { + bHeader := make([]byte, T1K_HEADER_SIZE) + _, err := io.ReadFull(r, bHeader) + if err != nil { + return nil, misc.ErrorWrap(err, "") + } + h := DeserializeHeader(bHeader) + var buf bytes.Buffer + _, err = io.CopyN(&buf, r, int64(h.Size)) + if err != nil { + return nil, misc.ErrorWrap(err, "") + } + return MakeSimpleSection(h.Tag, buf.Bytes()), nil +} diff --git a/sdk/traefik/vendor/github.com/chaitin/t1k-go/t1k/tag.go b/sdk/traefik/vendor/github.com/chaitin/t1k-go/t1k/tag.go new file mode 100644 index 00000000..feaf447b --- /dev/null +++ b/sdk/traefik/vendor/github.com/chaitin/t1k-go/t1k/tag.go @@ -0,0 +1,39 @@ +package t1k + +type Tag uint8 + +const ( + TAG_HEADER Tag = 0x01 + TAG_BODY Tag = 0x02 + TAG_EXTRA Tag = 0x03 + TAG_RSP_HEADER Tag = 0x11 + TAG_RSP_BODY Tag = 0x12 + TAG_RSP_EXTRA Tag = 0x13 + TAG_VERSION Tag = 0x20 + TAG_ALOG Tag = 0x21 + TAG_STAT Tag = 0x22 + TAG_EXTRA_HEADER Tag = 0x23 + TAG_EXTRA_BODY Tag = 0x24 + TAG_CONTEXT Tag = 0x25 + TAG_COOKIE Tag = 0x26 + TAG_WEB_LOG Tag = 0x27 + TAG_USER_DATA Tag = 0x28 + TAG_BOT_QUERY Tag = 0x29 + TAG_BOT_BODY Tag = 0x30 + + MASK_TAG Tag = 0x3f + MASK_FIRST Tag = 0x40 + MASK_LAST Tag = 0x80 +) + +func (t Tag) IsFirst() bool { + return !(0 == (t & MASK_FIRST)) +} + +func (t Tag) IsLast() bool { + return !(0 == (t & MASK_LAST)) +} + +func (t Tag) Strip() Tag { + return t & MASK_TAG +} diff --git a/sdk/traefik/vendor/golang.org/x/xerrors/LICENSE b/sdk/traefik/vendor/golang.org/x/xerrors/LICENSE new file mode 100644 index 00000000..e4a47e17 --- /dev/null +++ b/sdk/traefik/vendor/golang.org/x/xerrors/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2019 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/sdk/traefik/vendor/golang.org/x/xerrors/PATENTS b/sdk/traefik/vendor/golang.org/x/xerrors/PATENTS new file mode 100644 index 00000000..73309904 --- /dev/null +++ b/sdk/traefik/vendor/golang.org/x/xerrors/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/sdk/traefik/vendor/golang.org/x/xerrors/README b/sdk/traefik/vendor/golang.org/x/xerrors/README new file mode 100644 index 00000000..aac7867a --- /dev/null +++ b/sdk/traefik/vendor/golang.org/x/xerrors/README @@ -0,0 +1,2 @@ +This repository holds the transition packages for the new Go 1.13 error values. +See golang.org/design/29934-error-values. diff --git a/sdk/traefik/vendor/golang.org/x/xerrors/adaptor.go b/sdk/traefik/vendor/golang.org/x/xerrors/adaptor.go new file mode 100644 index 00000000..4317f248 --- /dev/null +++ b/sdk/traefik/vendor/golang.org/x/xerrors/adaptor.go @@ -0,0 +1,193 @@ +// 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. + +package xerrors + +import ( + "bytes" + "fmt" + "io" + "reflect" + "strconv" +) + +// FormatError calls the FormatError method of f with an errors.Printer +// configured according to s and verb, and writes the result to s. +func FormatError(f Formatter, s fmt.State, verb rune) { + // Assuming this function is only called from the Format method, and given + // that FormatError takes precedence over Format, it cannot be called from + // any package that supports errors.Formatter. It is therefore safe to + // disregard that State may be a specific printer implementation and use one + // of our choice instead. + + // limitations: does not support printing error as Go struct. + + var ( + sep = " " // separator before next error + p = &state{State: s} + direct = true + ) + + var err error = f + + switch verb { + // Note that this switch must match the preference order + // for ordinary string printing (%#v before %+v, and so on). + + case 'v': + if s.Flag('#') { + if stringer, ok := err.(fmt.GoStringer); ok { + io.WriteString(&p.buf, stringer.GoString()) + goto exit + } + // proceed as if it were %v + } else if s.Flag('+') { + p.printDetail = true + sep = "\n - " + } + case 's': + case 'q', 'x', 'X': + // Use an intermediate buffer in the rare cases that precision, + // truncation, or one of the alternative verbs (q, x, and X) are + // specified. + direct = false + + default: + p.buf.WriteString("%!") + p.buf.WriteRune(verb) + p.buf.WriteByte('(') + switch { + case err != nil: + p.buf.WriteString(reflect.TypeOf(f).String()) + default: + p.buf.WriteString("") + } + p.buf.WriteByte(')') + io.Copy(s, &p.buf) + return + } + +loop: + for { + switch v := err.(type) { + case Formatter: + err = v.FormatError((*printer)(p)) + case fmt.Formatter: + v.Format(p, 'v') + break loop + default: + io.WriteString(&p.buf, v.Error()) + break loop + } + if err == nil { + break + } + if p.needColon || !p.printDetail { + p.buf.WriteByte(':') + p.needColon = false + } + p.buf.WriteString(sep) + p.inDetail = false + p.needNewline = false + } + +exit: + width, okW := s.Width() + prec, okP := s.Precision() + + if !direct || (okW && width > 0) || okP { + // Construct format string from State s. + format := []byte{'%'} + if s.Flag('-') { + format = append(format, '-') + } + if s.Flag('+') { + format = append(format, '+') + } + if s.Flag(' ') { + format = append(format, ' ') + } + if okW { + format = strconv.AppendInt(format, int64(width), 10) + } + if okP { + format = append(format, '.') + format = strconv.AppendInt(format, int64(prec), 10) + } + format = append(format, string(verb)...) + fmt.Fprintf(s, string(format), p.buf.String()) + } else { + io.Copy(s, &p.buf) + } +} + +var detailSep = []byte("\n ") + +// state tracks error printing state. It implements fmt.State. +type state struct { + fmt.State + buf bytes.Buffer + + printDetail bool + inDetail bool + needColon bool + needNewline bool +} + +func (s *state) Write(b []byte) (n int, err error) { + if s.printDetail { + if len(b) == 0 { + return 0, nil + } + if s.inDetail && s.needColon { + s.needNewline = true + if b[0] == '\n' { + b = b[1:] + } + } + k := 0 + for i, c := range b { + if s.needNewline { + if s.inDetail && s.needColon { + s.buf.WriteByte(':') + s.needColon = false + } + s.buf.Write(detailSep) + s.needNewline = false + } + if c == '\n' { + s.buf.Write(b[k:i]) + k = i + 1 + s.needNewline = true + } + } + s.buf.Write(b[k:]) + if !s.inDetail { + s.needColon = true + } + } else if !s.inDetail { + s.buf.Write(b) + } + return len(b), nil +} + +// printer wraps a state to implement an xerrors.Printer. +type printer state + +func (s *printer) Print(args ...interface{}) { + if !s.inDetail || s.printDetail { + fmt.Fprint((*state)(s), args...) + } +} + +func (s *printer) Printf(format string, args ...interface{}) { + if !s.inDetail || s.printDetail { + fmt.Fprintf((*state)(s), format, args...) + } +} + +func (s *printer) Detail() bool { + s.inDetail = true + return s.printDetail +} diff --git a/sdk/traefik/vendor/golang.org/x/xerrors/codereview.cfg b/sdk/traefik/vendor/golang.org/x/xerrors/codereview.cfg new file mode 100644 index 00000000..3f8b14b6 --- /dev/null +++ b/sdk/traefik/vendor/golang.org/x/xerrors/codereview.cfg @@ -0,0 +1 @@ +issuerepo: golang/go diff --git a/sdk/traefik/vendor/golang.org/x/xerrors/doc.go b/sdk/traefik/vendor/golang.org/x/xerrors/doc.go new file mode 100644 index 00000000..2ef99f5a --- /dev/null +++ b/sdk/traefik/vendor/golang.org/x/xerrors/doc.go @@ -0,0 +1,23 @@ +// 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 xerrors implements functions to manipulate errors. +// +// This package is based on the Go 2 proposal for error values: +// +// https://golang.org/design/29934-error-values +// +// These functions were incorporated into the standard library's errors package +// in Go 1.13: +// - Is +// - As +// - Unwrap +// +// Also, Errorf's %w verb was incorporated into fmt.Errorf. +// +// Use this package to get equivalent behavior in all supported Go versions. +// +// No other features of this package were included in Go 1.13, and at present +// there are no plans to include any of them. +package xerrors // import "golang.org/x/xerrors" diff --git a/sdk/traefik/vendor/golang.org/x/xerrors/errors.go b/sdk/traefik/vendor/golang.org/x/xerrors/errors.go new file mode 100644 index 00000000..e88d3772 --- /dev/null +++ b/sdk/traefik/vendor/golang.org/x/xerrors/errors.go @@ -0,0 +1,33 @@ +// Copyright 2011 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 xerrors + +import "fmt" + +// errorString is a trivial implementation of error. +type errorString struct { + s string + frame Frame +} + +// New returns an error that formats as the given text. +// +// The returned error contains a Frame set to the caller's location and +// implements Formatter to show this information when printed with details. +func New(text string) error { + return &errorString{text, Caller(1)} +} + +func (e *errorString) Error() string { + return e.s +} + +func (e *errorString) Format(s fmt.State, v rune) { FormatError(e, s, v) } + +func (e *errorString) FormatError(p Printer) (next error) { + p.Print(e.s) + e.frame.Format(p) + return nil +} diff --git a/sdk/traefik/vendor/golang.org/x/xerrors/fmt.go b/sdk/traefik/vendor/golang.org/x/xerrors/fmt.go new file mode 100644 index 00000000..27a5d70b --- /dev/null +++ b/sdk/traefik/vendor/golang.org/x/xerrors/fmt.go @@ -0,0 +1,190 @@ +// 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. + +package xerrors + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" + + "golang.org/x/xerrors/internal" +) + +const percentBangString = "%!" + +// Errorf formats according to a format specifier and returns the string as a +// value that satisfies error. +// +// The returned error includes the file and line number of the caller when +// formatted with additional detail enabled. If the last argument is an error +// the returned error's Format method will return it if the format string ends +// with ": %s", ": %v", or ": %w". If the last argument is an error and the +// format string ends with ": %w", the returned error implements an Unwrap +// method returning it. +// +// If the format specifier includes a %w verb with an error operand in a +// position other than at the end, the returned error will still implement an +// Unwrap method returning the operand, but the error's Format method will not +// return the wrapped error. +// +// It is invalid to include more than one %w verb or to supply it with an +// operand that does not implement the error interface. The %w verb is otherwise +// a synonym for %v. +// +// Note that as of Go 1.13, the fmt.Errorf function will do error formatting, +// but it will not capture a stack backtrace. +func Errorf(format string, a ...interface{}) error { + format = formatPlusW(format) + // Support a ": %[wsv]" suffix, which works well with xerrors.Formatter. + wrap := strings.HasSuffix(format, ": %w") + idx, format2, ok := parsePercentW(format) + percentWElsewhere := !wrap && idx >= 0 + if !percentWElsewhere && (wrap || strings.HasSuffix(format, ": %s") || strings.HasSuffix(format, ": %v")) { + err := errorAt(a, len(a)-1) + if err == nil { + return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)} + } + // TODO: this is not entirely correct. The error value could be + // printed elsewhere in format if it mixes numbered with unnumbered + // substitutions. With relatively small changes to doPrintf we can + // have it optionally ignore extra arguments and pass the argument + // list in its entirety. + msg := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...) + frame := Frame{} + if internal.EnableTrace { + frame = Caller(1) + } + if wrap { + return &wrapError{msg, err, frame} + } + return &noWrapError{msg, err, frame} + } + // Support %w anywhere. + // TODO: don't repeat the wrapped error's message when %w occurs in the middle. + msg := fmt.Sprintf(format2, a...) + if idx < 0 { + return &noWrapError{msg, nil, Caller(1)} + } + err := errorAt(a, idx) + if !ok || err == nil { + // Too many %ws or argument of %w is not an error. Approximate the Go + // 1.13 fmt.Errorf message. + return &noWrapError{fmt.Sprintf("%sw(%s)", percentBangString, msg), nil, Caller(1)} + } + frame := Frame{} + if internal.EnableTrace { + frame = Caller(1) + } + return &wrapError{msg, err, frame} +} + +func errorAt(args []interface{}, i int) error { + if i < 0 || i >= len(args) { + return nil + } + err, ok := args[i].(error) + if !ok { + return nil + } + return err +} + +// formatPlusW is used to avoid the vet check that will barf at %w. +func formatPlusW(s string) string { + return s +} + +// Return the index of the only %w in format, or -1 if none. +// Also return a rewritten format string with %w replaced by %v, and +// false if there is more than one %w. +// TODO: handle "%[N]w". +func parsePercentW(format string) (idx int, newFormat string, ok bool) { + // Loosely copied from golang.org/x/tools/go/analysis/passes/printf/printf.go. + idx = -1 + ok = true + n := 0 + sz := 0 + var isW bool + for i := 0; i < len(format); i += sz { + if format[i] != '%' { + sz = 1 + continue + } + // "%%" is not a format directive. + if i+1 < len(format) && format[i+1] == '%' { + sz = 2 + continue + } + sz, isW = parsePrintfVerb(format[i:]) + if isW { + if idx >= 0 { + ok = false + } else { + idx = n + } + // "Replace" the last character, the 'w', with a 'v'. + p := i + sz - 1 + format = format[:p] + "v" + format[p+1:] + } + n++ + } + return idx, format, ok +} + +// Parse the printf verb starting with a % at s[0]. +// Return how many bytes it occupies and whether the verb is 'w'. +func parsePrintfVerb(s string) (int, bool) { + // Assume only that the directive is a sequence of non-letters followed by a single letter. + sz := 0 + var r rune + for i := 1; i < len(s); i += sz { + r, sz = utf8.DecodeRuneInString(s[i:]) + if unicode.IsLetter(r) { + return i + sz, r == 'w' + } + } + return len(s), false +} + +type noWrapError struct { + msg string + err error + frame Frame +} + +func (e *noWrapError) Error() string { + return fmt.Sprint(e) +} + +func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } + +func (e *noWrapError) FormatError(p Printer) (next error) { + p.Print(e.msg) + e.frame.Format(p) + return e.err +} + +type wrapError struct { + msg string + err error + frame Frame +} + +func (e *wrapError) Error() string { + return fmt.Sprint(e) +} + +func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } + +func (e *wrapError) FormatError(p Printer) (next error) { + p.Print(e.msg) + e.frame.Format(p) + return e.err +} + +func (e *wrapError) Unwrap() error { + return e.err +} diff --git a/sdk/traefik/vendor/golang.org/x/xerrors/format.go b/sdk/traefik/vendor/golang.org/x/xerrors/format.go new file mode 100644 index 00000000..1bc9c26b --- /dev/null +++ b/sdk/traefik/vendor/golang.org/x/xerrors/format.go @@ -0,0 +1,34 @@ +// 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. + +package xerrors + +// A Formatter formats error messages. +type Formatter interface { + error + + // FormatError prints the receiver's first error and returns the next error in + // the error chain, if any. + FormatError(p Printer) (next error) +} + +// A Printer formats error messages. +// +// The most common implementation of Printer is the one provided by package fmt +// during Printf (as of Go 1.13). Localization packages such as golang.org/x/text/message +// typically provide their own implementations. +type Printer interface { + // Print appends args to the message output. + Print(args ...interface{}) + + // Printf writes a formatted string. + Printf(format string, args ...interface{}) + + // Detail reports whether error detail is requested. + // After the first call to Detail, all text written to the Printer + // is formatted as additional detail, or ignored when + // detail has not been requested. + // If Detail returns false, the caller can avoid printing the detail at all. + Detail() bool +} diff --git a/sdk/traefik/vendor/golang.org/x/xerrors/frame.go b/sdk/traefik/vendor/golang.org/x/xerrors/frame.go new file mode 100644 index 00000000..0de628ec --- /dev/null +++ b/sdk/traefik/vendor/golang.org/x/xerrors/frame.go @@ -0,0 +1,56 @@ +// 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. + +package xerrors + +import ( + "runtime" +) + +// A Frame contains part of a call stack. +type Frame struct { + // Make room for three PCs: the one we were asked for, what it called, + // and possibly a PC for skipPleaseUseCallersFrames. See: + // https://go.googlesource.com/go/+/032678e0fb/src/runtime/extern.go#169 + frames [3]uintptr +} + +// Caller returns a Frame that describes a frame on the caller's stack. +// The argument skip is the number of frames to skip over. +// Caller(0) returns the frame for the caller of Caller. +func Caller(skip int) Frame { + var s Frame + runtime.Callers(skip+1, s.frames[:]) + return s +} + +// location reports the file, line, and function of a frame. +// +// The returned function may be "" even if file and line are not. +func (f Frame) location() (function, file string, line int) { + frames := runtime.CallersFrames(f.frames[:]) + if _, ok := frames.Next(); !ok { + return "", "", 0 + } + fr, ok := frames.Next() + if !ok { + return "", "", 0 + } + return fr.Function, fr.File, fr.Line +} + +// Format prints the stack as error detail. +// It should be called from an error's Format implementation +// after printing any other error detail. +func (f Frame) Format(p Printer) { + if p.Detail() { + function, file, line := f.location() + if function != "" { + p.Printf("%s\n ", function) + } + if file != "" { + p.Printf("%s:%d\n", file, line) + } + } +} diff --git a/sdk/traefik/vendor/golang.org/x/xerrors/internal/internal.go b/sdk/traefik/vendor/golang.org/x/xerrors/internal/internal.go new file mode 100644 index 00000000..89f4eca5 --- /dev/null +++ b/sdk/traefik/vendor/golang.org/x/xerrors/internal/internal.go @@ -0,0 +1,8 @@ +// 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. + +package internal + +// EnableTrace indicates whether stack information should be recorded in errors. +var EnableTrace = true diff --git a/sdk/traefik/vendor/golang.org/x/xerrors/wrap.go b/sdk/traefik/vendor/golang.org/x/xerrors/wrap.go new file mode 100644 index 00000000..9842758c --- /dev/null +++ b/sdk/traefik/vendor/golang.org/x/xerrors/wrap.go @@ -0,0 +1,112 @@ +// 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. + +package xerrors + +import ( + "reflect" +) + +// A Wrapper provides context around another error. +type Wrapper interface { + // Unwrap returns the next error in the error chain. + // If there is no next error, Unwrap returns nil. + Unwrap() error +} + +// Opaque returns an error with the same error formatting as err +// but that does not match err and cannot be unwrapped. +func Opaque(err error) error { + return noWrapper{err} +} + +type noWrapper struct { + error +} + +func (e noWrapper) FormatError(p Printer) (next error) { + if f, ok := e.error.(Formatter); ok { + return f.FormatError(p) + } + p.Print(e.error) + return nil +} + +// Unwrap returns the result of calling the Unwrap method on err, if err implements +// Unwrap. Otherwise, Unwrap returns nil. +// +// Deprecated: As of Go 1.13, use errors.Unwrap instead. +func Unwrap(err error) error { + u, ok := err.(Wrapper) + if !ok { + return nil + } + return u.Unwrap() +} + +// Is reports whether any error in err's chain matches target. +// +// An error is considered to match a target if it is equal to that target or if +// it implements a method Is(error) bool such that Is(target) returns true. +// +// Deprecated: As of Go 1.13, use errors.Is instead. +func Is(err, target error) bool { + if target == nil { + return err == target + } + + isComparable := reflect.TypeOf(target).Comparable() + for { + if isComparable && err == target { + return true + } + if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) { + return true + } + // TODO: consider supporing target.Is(err). This would allow + // user-definable predicates, but also may allow for coping with sloppy + // APIs, thereby making it easier to get away with them. + if err = Unwrap(err); err == nil { + return false + } + } +} + +// As finds the first error in err's chain that matches the type to which target +// points, and if so, sets the target to its value and returns true. An error +// matches a type if it is assignable to the target type, or if it has a method +// As(interface{}) bool such that As(target) returns true. As will panic if target +// is not a non-nil pointer to a type which implements error or is of interface type. +// +// The As method should set the target to its value and return true if err +// matches the type to which target points. +// +// Deprecated: As of Go 1.13, use errors.As instead. +func As(err error, target interface{}) bool { + if target == nil { + panic("errors: target cannot be nil") + } + val := reflect.ValueOf(target) + typ := val.Type() + if typ.Kind() != reflect.Ptr || val.IsNil() { + panic("errors: target must be a non-nil pointer") + } + if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) { + panic("errors: *target must be interface or implement error") + } + targetType := typ.Elem() + for err != nil { + if reflect.TypeOf(err).AssignableTo(targetType) { + val.Elem().Set(reflect.ValueOf(err)) + return true + } + if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) { + return true + } + err = Unwrap(err) + } + return false +} + +var errorType = reflect.TypeOf((*error)(nil)).Elem() diff --git a/sdk/traefik/vendor/modules.txt b/sdk/traefik/vendor/modules.txt index 035f24cc..0fae7488 100644 --- a/sdk/traefik/vendor/modules.txt +++ b/sdk/traefik/vendor/modules.txt @@ -1,6 +1,10 @@ -# github.com/xbingW/t1k v1.2.1 +# github.com/chaitin/t1k-go v1.5.0 ## explicit; go 1.17 -github.com/xbingW/t1k -github.com/xbingW/t1k/pkg/datetime -github.com/xbingW/t1k/pkg/rand -github.com/xbingW/t1k/pkg/t1k +github.com/chaitin/t1k-go +github.com/chaitin/t1k-go/detection +github.com/chaitin/t1k-go/misc +github.com/chaitin/t1k-go/t1k +# golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 +## explicit; go 1.18 +golang.org/x/xerrors +golang.org/x/xerrors/internal