pax_global_header00006660000000000000000000000064141544213230014511gustar00rootroot0000000000000052 comment=e822df7b2e9ddb20303963151e3975c7b10fde1e golang-golang-x-net-0.0+git20211209.491a49a+dfsg/000077500000000000000000000000001415442132300204275ustar00rootroot00000000000000golang-golang-x-net-0.0+git20211209.491a49a+dfsg/.gitattributes000066400000000000000000000005311415442132300233210ustar00rootroot00000000000000# Treat all files in this repo as binary, with no git magic updating # line endings. Windows users contributing to Go will need to use a # modern version of git and editors capable of LF line endings. # # We'll prevent accidental CRLF line endings from entering the repo # via the git-review gofmt checks. # # See golang.org/issue/9281 * -text golang-golang-x-net-0.0+git20211209.491a49a+dfsg/.gitignore000066400000000000000000000001251415442132300224150ustar00rootroot00000000000000# Add no patterns to .gitignore except for files generated by the build. last-change golang-golang-x-net-0.0+git20211209.491a49a+dfsg/AUTHORS000066400000000000000000000002551415442132300215010ustar00rootroot00000000000000# 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. golang-golang-x-net-0.0+git20211209.491a49a+dfsg/CONTRIBUTING.md000066400000000000000000000016211415442132300226600ustar00rootroot00000000000000# Contributing to Go Go is an open source project. It is the work of hundreds of contributors. We appreciate your help! ## Filing issues When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions: 1. What version of Go are you using (`go version`)? 2. What operating system and processor architecture are you using? 3. What did you do? 4. What did you expect to see? 5. What did you see instead? General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker. The gophers there will answer or ask you to file an issue if you've tripped over a bug. ## Contributing code Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html) before sending patches. Unless otherwise noted, the Go source files are distributed under the BSD-style license found in the LICENSE file. golang-golang-x-net-0.0+git20211209.491a49a+dfsg/CONTRIBUTORS000066400000000000000000000002521415442132300223060ustar00rootroot00000000000000# 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. golang-golang-x-net-0.0+git20211209.491a49a+dfsg/LICENSE000066400000000000000000000027071415442132300214420ustar00rootroot00000000000000Copyright (c) 2009 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. golang-golang-x-net-0.0+git20211209.491a49a+dfsg/PATENTS000066400000000000000000000024271415442132300214750ustar00rootroot00000000000000Additional 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. golang-golang-x-net-0.0+git20211209.491a49a+dfsg/README.md000066400000000000000000000012741415442132300217120ustar00rootroot00000000000000# Go Networking [![Go Reference](https://pkg.go.dev/badge/golang.org/x/net.svg)](https://pkg.go.dev/golang.org/x/net) This repository holds supplementary Go networking libraries. ## Download/Install The easiest way to install is to run `go get -u golang.org/x/net`. You can also manually git clone the repository to `$GOPATH/src/golang.org/x/net`. ## Report Issues / Send Patches This repository uses Gerrit for code changes. To learn how to submit changes to this repository, see https://golang.org/doc/contribute.html. The main issue tracker for the net repository is located at https://github.com/golang/go/issues. Prefix your issue with "x/net:" in the subject line, so it is easy to find. golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/000077500000000000000000000000001415442132300211765ustar00rootroot00000000000000golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/asm.go000066400000000000000000000023631415442132300223110ustar00rootroot00000000000000// 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 bpf import "fmt" // Assemble converts insts into raw instructions suitable for loading // into a BPF virtual machine. // // Currently, no optimization is attempted, the assembled program flow // is exactly as provided. func Assemble(insts []Instruction) ([]RawInstruction, error) { ret := make([]RawInstruction, len(insts)) var err error for i, inst := range insts { ret[i], err = inst.Assemble() if err != nil { return nil, fmt.Errorf("assembling instruction %d: %s", i+1, err) } } return ret, nil } // Disassemble attempts to parse raw back into // Instructions. Unrecognized RawInstructions are assumed to be an // extension not implemented by this package, and are passed through // unchanged to the output. The allDecoded value reports whether insts // contains no RawInstructions. func Disassemble(raw []RawInstruction) (insts []Instruction, allDecoded bool) { insts = make([]Instruction, len(raw)) allDecoded = true for i, r := range raw { insts[i] = r.Disassemble() if _, ok := insts[i].(RawInstruction); ok { allDecoded = false } } return insts, allDecoded } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/constants.go000066400000000000000000000140541415442132300235450ustar00rootroot00000000000000// 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 bpf // A Register is a register of the BPF virtual machine. type Register uint16 const ( // RegA is the accumulator register. RegA is always the // destination register of ALU operations. RegA Register = iota // RegX is the indirection register, used by LoadIndirect // operations. RegX ) // An ALUOp is an arithmetic or logic operation. type ALUOp uint16 // ALU binary operation types. const ( ALUOpAdd ALUOp = iota << 4 ALUOpSub ALUOpMul ALUOpDiv ALUOpOr ALUOpAnd ALUOpShiftLeft ALUOpShiftRight aluOpNeg // Not exported because it's the only unary ALU operation, and gets its own instruction type. ALUOpMod ALUOpXor ) // A JumpTest is a comparison operator used in conditional jumps. type JumpTest uint16 // Supported operators for conditional jumps. // K can be RegX for JumpIfX const ( // K == A JumpEqual JumpTest = iota // K != A JumpNotEqual // K > A JumpGreaterThan // K < A JumpLessThan // K >= A JumpGreaterOrEqual // K <= A JumpLessOrEqual // K & A != 0 JumpBitsSet // K & A == 0 JumpBitsNotSet ) // An Extension is a function call provided by the kernel that // performs advanced operations that are expensive or impossible // within the BPF virtual machine. // // Extensions are only implemented by the Linux kernel. // // TODO: should we prune this list? Some of these extensions seem // either broken or near-impossible to use correctly, whereas other // (len, random, ifindex) are quite useful. type Extension int // Extension functions available in the Linux kernel. const ( // extOffset is the negative maximum number of instructions used // to load instructions by overloading the K argument. extOffset = -0x1000 // ExtLen returns the length of the packet. ExtLen Extension = 1 // ExtProto returns the packet's L3 protocol type. ExtProto Extension = 0 // ExtType returns the packet's type (skb->pkt_type in the kernel) // // TODO: better documentation. How nice an API do we want to // provide for these esoteric extensions? ExtType Extension = 4 // ExtPayloadOffset returns the offset of the packet payload, or // the first protocol header that the kernel does not know how to // parse. ExtPayloadOffset Extension = 52 // ExtInterfaceIndex returns the index of the interface on which // the packet was received. ExtInterfaceIndex Extension = 8 // ExtNetlinkAttr returns the netlink attribute of type X at // offset A. ExtNetlinkAttr Extension = 12 // ExtNetlinkAttrNested returns the nested netlink attribute of // type X at offset A. ExtNetlinkAttrNested Extension = 16 // ExtMark returns the packet's mark value. ExtMark Extension = 20 // ExtQueue returns the packet's assigned hardware queue. ExtQueue Extension = 24 // ExtLinkLayerType returns the packet's hardware address type // (e.g. Ethernet, Infiniband). ExtLinkLayerType Extension = 28 // ExtRXHash returns the packets receive hash. // // TODO: figure out what this rxhash actually is. ExtRXHash Extension = 32 // ExtCPUID returns the ID of the CPU processing the current // packet. ExtCPUID Extension = 36 // ExtVLANTag returns the packet's VLAN tag. ExtVLANTag Extension = 44 // ExtVLANTagPresent returns non-zero if the packet has a VLAN // tag. // // TODO: I think this might be a lie: it reads bit 0x1000 of the // VLAN header, which changed meaning in recent revisions of the // spec - this extension may now return meaningless information. ExtVLANTagPresent Extension = 48 // ExtVLANProto returns 0x8100 if the frame has a VLAN header, // 0x88a8 if the frame has a "Q-in-Q" double VLAN header, or some // other value if no VLAN information is present. ExtVLANProto Extension = 60 // ExtRand returns a uniformly random uint32. ExtRand Extension = 56 ) // The following gives names to various bit patterns used in opcode construction. const ( opMaskCls uint16 = 0x7 // opClsLoad masks opMaskLoadDest = 0x01 opMaskLoadWidth = 0x18 opMaskLoadMode = 0xe0 // opClsALU & opClsJump opMaskOperand = 0x08 opMaskOperator = 0xf0 ) const ( // +---------------+-----------------+---+---+---+ // | AddrMode (3b) | LoadWidth (2b) | 0 | 0 | 0 | // +---------------+-----------------+---+---+---+ opClsLoadA uint16 = iota // +---------------+-----------------+---+---+---+ // | AddrMode (3b) | LoadWidth (2b) | 0 | 0 | 1 | // +---------------+-----------------+---+---+---+ opClsLoadX // +---+---+---+---+---+---+---+---+ // | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | // +---+---+---+---+---+---+---+---+ opClsStoreA // +---+---+---+---+---+---+---+---+ // | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | // +---+---+---+---+---+---+---+---+ opClsStoreX // +---------------+-----------------+---+---+---+ // | Operator (4b) | OperandSrc (1b) | 1 | 0 | 0 | // +---------------+-----------------+---+---+---+ opClsALU // +-----------------------------+---+---+---+---+ // | TestOperator (4b) | 0 | 1 | 0 | 1 | // +-----------------------------+---+---+---+---+ opClsJump // +---+-------------------------+---+---+---+---+ // | 0 | 0 | 0 | RetSrc (1b) | 0 | 1 | 1 | 0 | // +---+-------------------------+---+---+---+---+ opClsReturn // +---+-------------------------+---+---+---+---+ // | 0 | 0 | 0 | TXAorTAX (1b) | 0 | 1 | 1 | 1 | // +---+-------------------------+---+---+---+---+ opClsMisc ) const ( opAddrModeImmediate uint16 = iota << 5 opAddrModeAbsolute opAddrModeIndirect opAddrModeScratch opAddrModePacketLen // actually an extension, not an addressing mode. opAddrModeMemShift ) const ( opLoadWidth4 uint16 = iota << 3 opLoadWidth2 opLoadWidth1 ) // Operand for ALU and Jump instructions type opOperand uint16 // Supported operand sources. const ( opOperandConstant opOperand = iota << 3 opOperandX ) // An jumpOp is a conditional jump condition. type jumpOp uint16 // Supported jump conditions. const ( opJumpAlways jumpOp = iota << 4 opJumpEqual opJumpGT opJumpGE opJumpSet ) const ( opRetSrcConstant uint16 = iota << 4 opRetSrcA ) const ( opMiscTAX = 0x00 opMiscTXA = 0x80 ) golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/doc.go000066400000000000000000000062171415442132300223000ustar00rootroot00000000000000// 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 bpf implements marshaling and unmarshaling of programs for the Berkeley Packet Filter virtual machine, and provides a Go implementation of the virtual machine. BPF's main use is to specify a packet filter for network taps, so that the kernel doesn't have to expensively copy every packet it sees to userspace. However, it's been repurposed to other areas where running user code in-kernel is needed. For example, Linux's seccomp uses BPF to apply security policies to system calls. For simplicity, this documentation refers only to packets, but other uses of BPF have their own data payloads. BPF programs run in a restricted virtual machine. It has almost no access to kernel functions, and while conditional branches are allowed, they can only jump forwards, to guarantee that there are no infinite loops. The virtual machine The BPF VM is an accumulator machine. Its main register, called register A, is an implicit source and destination in all arithmetic and logic operations. The machine also has 16 scratch registers for temporary storage, and an indirection register (register X) for indirect memory access. All registers are 32 bits wide. Each run of a BPF program is given one packet, which is placed in the VM's read-only "main memory". LoadAbsolute and LoadIndirect instructions can fetch up to 32 bits at a time into register A for examination. The goal of a BPF program is to produce and return a verdict (uint32), which tells the kernel what to do with the packet. In the context of packet filtering, the returned value is the number of bytes of the packet to forward to userspace, or 0 to ignore the packet. Other contexts like seccomp define their own return values. In order to simplify programs, attempts to read past the end of the packet terminate the program execution with a verdict of 0 (ignore packet). This means that the vast majority of BPF programs don't need to do any explicit bounds checking. In addition to the bytes of the packet, some BPF programs have access to extensions, which are essentially calls to kernel utility functions. Currently, the only extensions supported by this package are the Linux packet filter extensions. Examples This packet filter selects all ARP packets. bpf.Assemble([]bpf.Instruction{ // Load "EtherType" field from the ethernet header. bpf.LoadAbsolute{Off: 12, Size: 2}, // Skip over the next instruction if EtherType is not ARP. bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x0806, SkipTrue: 1}, // Verdict is "send up to 4k of the packet to userspace." bpf.RetConstant{Val: 4096}, // Verdict is "ignore packet." bpf.RetConstant{Val: 0}, }) This packet filter captures a random 1% sample of traffic. bpf.Assemble([]bpf.Instruction{ // Get a 32-bit random number from the Linux kernel. bpf.LoadExtension{Num: bpf.ExtRand}, // 1% dice roll? bpf.JumpIf{Cond: bpf.JumpLessThan, Val: 2^32/100, SkipFalse: 1}, // Capture. bpf.RetConstant{Val: 4096}, // Ignore. bpf.RetConstant{Val: 0}, }) */ package bpf // import "golang.org/x/net/bpf" golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/instructions.go000066400000000000000000000432671415442132300243050ustar00rootroot00000000000000// 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 bpf import "fmt" // An Instruction is one instruction executed by the BPF virtual // machine. type Instruction interface { // Assemble assembles the Instruction into a RawInstruction. Assemble() (RawInstruction, error) } // A RawInstruction is a raw BPF virtual machine instruction. type RawInstruction struct { // Operation to execute. Op uint16 // For conditional jump instructions, the number of instructions // to skip if the condition is true/false. Jt uint8 Jf uint8 // Constant parameter. The meaning depends on the Op. K uint32 } // Assemble implements the Instruction Assemble method. func (ri RawInstruction) Assemble() (RawInstruction, error) { return ri, nil } // Disassemble parses ri into an Instruction and returns it. If ri is // not recognized by this package, ri itself is returned. func (ri RawInstruction) Disassemble() Instruction { switch ri.Op & opMaskCls { case opClsLoadA, opClsLoadX: reg := Register(ri.Op & opMaskLoadDest) sz := 0 switch ri.Op & opMaskLoadWidth { case opLoadWidth4: sz = 4 case opLoadWidth2: sz = 2 case opLoadWidth1: sz = 1 default: return ri } switch ri.Op & opMaskLoadMode { case opAddrModeImmediate: if sz != 4 { return ri } return LoadConstant{Dst: reg, Val: ri.K} case opAddrModeScratch: if sz != 4 || ri.K > 15 { return ri } return LoadScratch{Dst: reg, N: int(ri.K)} case opAddrModeAbsolute: if ri.K > extOffset+0xffffffff { return LoadExtension{Num: Extension(-extOffset + ri.K)} } return LoadAbsolute{Size: sz, Off: ri.K} case opAddrModeIndirect: return LoadIndirect{Size: sz, Off: ri.K} case opAddrModePacketLen: if sz != 4 { return ri } return LoadExtension{Num: ExtLen} case opAddrModeMemShift: return LoadMemShift{Off: ri.K} default: return ri } case opClsStoreA: if ri.Op != opClsStoreA || ri.K > 15 { return ri } return StoreScratch{Src: RegA, N: int(ri.K)} case opClsStoreX: if ri.Op != opClsStoreX || ri.K > 15 { return ri } return StoreScratch{Src: RegX, N: int(ri.K)} case opClsALU: switch op := ALUOp(ri.Op & opMaskOperator); op { case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor: switch operand := opOperand(ri.Op & opMaskOperand); operand { case opOperandX: return ALUOpX{Op: op} case opOperandConstant: return ALUOpConstant{Op: op, Val: ri.K} default: return ri } case aluOpNeg: return NegateA{} default: return ri } case opClsJump: switch op := jumpOp(ri.Op & opMaskOperator); op { case opJumpAlways: return Jump{Skip: ri.K} case opJumpEqual, opJumpGT, opJumpGE, opJumpSet: cond, skipTrue, skipFalse := jumpOpToTest(op, ri.Jt, ri.Jf) switch operand := opOperand(ri.Op & opMaskOperand); operand { case opOperandX: return JumpIfX{Cond: cond, SkipTrue: skipTrue, SkipFalse: skipFalse} case opOperandConstant: return JumpIf{Cond: cond, Val: ri.K, SkipTrue: skipTrue, SkipFalse: skipFalse} default: return ri } default: return ri } case opClsReturn: switch ri.Op { case opClsReturn | opRetSrcA: return RetA{} case opClsReturn | opRetSrcConstant: return RetConstant{Val: ri.K} default: return ri } case opClsMisc: switch ri.Op { case opClsMisc | opMiscTAX: return TAX{} case opClsMisc | opMiscTXA: return TXA{} default: return ri } default: panic("unreachable") // switch is exhaustive on the bit pattern } } func jumpOpToTest(op jumpOp, skipTrue uint8, skipFalse uint8) (JumpTest, uint8, uint8) { var test JumpTest // Decode "fake" jump conditions that don't appear in machine code // Ensures the Assemble -> Disassemble stage recreates the same instructions // See https://github.com/golang/go/issues/18470 if skipTrue == 0 { switch op { case opJumpEqual: test = JumpNotEqual case opJumpGT: test = JumpLessOrEqual case opJumpGE: test = JumpLessThan case opJumpSet: test = JumpBitsNotSet } return test, skipFalse, 0 } switch op { case opJumpEqual: test = JumpEqual case opJumpGT: test = JumpGreaterThan case opJumpGE: test = JumpGreaterOrEqual case opJumpSet: test = JumpBitsSet } return test, skipTrue, skipFalse } // LoadConstant loads Val into register Dst. type LoadConstant struct { Dst Register Val uint32 } // Assemble implements the Instruction Assemble method. func (a LoadConstant) Assemble() (RawInstruction, error) { return assembleLoad(a.Dst, 4, opAddrModeImmediate, a.Val) } // String returns the instruction in assembler notation. func (a LoadConstant) String() string { switch a.Dst { case RegA: return fmt.Sprintf("ld #%d", a.Val) case RegX: return fmt.Sprintf("ldx #%d", a.Val) default: return fmt.Sprintf("unknown instruction: %#v", a) } } // LoadScratch loads scratch[N] into register Dst. type LoadScratch struct { Dst Register N int // 0-15 } // Assemble implements the Instruction Assemble method. func (a LoadScratch) Assemble() (RawInstruction, error) { if a.N < 0 || a.N > 15 { return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N) } return assembleLoad(a.Dst, 4, opAddrModeScratch, uint32(a.N)) } // String returns the instruction in assembler notation. func (a LoadScratch) String() string { switch a.Dst { case RegA: return fmt.Sprintf("ld M[%d]", a.N) case RegX: return fmt.Sprintf("ldx M[%d]", a.N) default: return fmt.Sprintf("unknown instruction: %#v", a) } } // LoadAbsolute loads packet[Off:Off+Size] as an integer value into // register A. type LoadAbsolute struct { Off uint32 Size int // 1, 2 or 4 } // Assemble implements the Instruction Assemble method. func (a LoadAbsolute) Assemble() (RawInstruction, error) { return assembleLoad(RegA, a.Size, opAddrModeAbsolute, a.Off) } // String returns the instruction in assembler notation. func (a LoadAbsolute) String() string { switch a.Size { case 1: // byte return fmt.Sprintf("ldb [%d]", a.Off) case 2: // half word return fmt.Sprintf("ldh [%d]", a.Off) case 4: // word if a.Off > extOffset+0xffffffff { return LoadExtension{Num: Extension(a.Off + 0x1000)}.String() } return fmt.Sprintf("ld [%d]", a.Off) default: return fmt.Sprintf("unknown instruction: %#v", a) } } // LoadIndirect loads packet[X+Off:X+Off+Size] as an integer value // into register A. type LoadIndirect struct { Off uint32 Size int // 1, 2 or 4 } // Assemble implements the Instruction Assemble method. func (a LoadIndirect) Assemble() (RawInstruction, error) { return assembleLoad(RegA, a.Size, opAddrModeIndirect, a.Off) } // String returns the instruction in assembler notation. func (a LoadIndirect) String() string { switch a.Size { case 1: // byte return fmt.Sprintf("ldb [x + %d]", a.Off) case 2: // half word return fmt.Sprintf("ldh [x + %d]", a.Off) case 4: // word return fmt.Sprintf("ld [x + %d]", a.Off) default: return fmt.Sprintf("unknown instruction: %#v", a) } } // LoadMemShift multiplies the first 4 bits of the byte at packet[Off] // by 4 and stores the result in register X. // // This instruction is mainly useful to load into X the length of an // IPv4 packet header in a single instruction, rather than have to do // the arithmetic on the header's first byte by hand. type LoadMemShift struct { Off uint32 } // Assemble implements the Instruction Assemble method. func (a LoadMemShift) Assemble() (RawInstruction, error) { return assembleLoad(RegX, 1, opAddrModeMemShift, a.Off) } // String returns the instruction in assembler notation. func (a LoadMemShift) String() string { return fmt.Sprintf("ldx 4*([%d]&0xf)", a.Off) } // LoadExtension invokes a linux-specific extension and stores the // result in register A. type LoadExtension struct { Num Extension } // Assemble implements the Instruction Assemble method. func (a LoadExtension) Assemble() (RawInstruction, error) { if a.Num == ExtLen { return assembleLoad(RegA, 4, opAddrModePacketLen, 0) } return assembleLoad(RegA, 4, opAddrModeAbsolute, uint32(extOffset+a.Num)) } // String returns the instruction in assembler notation. func (a LoadExtension) String() string { switch a.Num { case ExtLen: return "ld #len" case ExtProto: return "ld #proto" case ExtType: return "ld #type" case ExtPayloadOffset: return "ld #poff" case ExtInterfaceIndex: return "ld #ifidx" case ExtNetlinkAttr: return "ld #nla" case ExtNetlinkAttrNested: return "ld #nlan" case ExtMark: return "ld #mark" case ExtQueue: return "ld #queue" case ExtLinkLayerType: return "ld #hatype" case ExtRXHash: return "ld #rxhash" case ExtCPUID: return "ld #cpu" case ExtVLANTag: return "ld #vlan_tci" case ExtVLANTagPresent: return "ld #vlan_avail" case ExtVLANProto: return "ld #vlan_tpid" case ExtRand: return "ld #rand" default: return fmt.Sprintf("unknown instruction: %#v", a) } } // StoreScratch stores register Src into scratch[N]. type StoreScratch struct { Src Register N int // 0-15 } // Assemble implements the Instruction Assemble method. func (a StoreScratch) Assemble() (RawInstruction, error) { if a.N < 0 || a.N > 15 { return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N) } var op uint16 switch a.Src { case RegA: op = opClsStoreA case RegX: op = opClsStoreX default: return RawInstruction{}, fmt.Errorf("invalid source register %v", a.Src) } return RawInstruction{ Op: op, K: uint32(a.N), }, nil } // String returns the instruction in assembler notation. func (a StoreScratch) String() string { switch a.Src { case RegA: return fmt.Sprintf("st M[%d]", a.N) case RegX: return fmt.Sprintf("stx M[%d]", a.N) default: return fmt.Sprintf("unknown instruction: %#v", a) } } // ALUOpConstant executes A = A Val. type ALUOpConstant struct { Op ALUOp Val uint32 } // Assemble implements the Instruction Assemble method. func (a ALUOpConstant) Assemble() (RawInstruction, error) { return RawInstruction{ Op: opClsALU | uint16(opOperandConstant) | uint16(a.Op), K: a.Val, }, nil } // String returns the instruction in assembler notation. func (a ALUOpConstant) String() string { switch a.Op { case ALUOpAdd: return fmt.Sprintf("add #%d", a.Val) case ALUOpSub: return fmt.Sprintf("sub #%d", a.Val) case ALUOpMul: return fmt.Sprintf("mul #%d", a.Val) case ALUOpDiv: return fmt.Sprintf("div #%d", a.Val) case ALUOpMod: return fmt.Sprintf("mod #%d", a.Val) case ALUOpAnd: return fmt.Sprintf("and #%d", a.Val) case ALUOpOr: return fmt.Sprintf("or #%d", a.Val) case ALUOpXor: return fmt.Sprintf("xor #%d", a.Val) case ALUOpShiftLeft: return fmt.Sprintf("lsh #%d", a.Val) case ALUOpShiftRight: return fmt.Sprintf("rsh #%d", a.Val) default: return fmt.Sprintf("unknown instruction: %#v", a) } } // ALUOpX executes A = A X type ALUOpX struct { Op ALUOp } // Assemble implements the Instruction Assemble method. func (a ALUOpX) Assemble() (RawInstruction, error) { return RawInstruction{ Op: opClsALU | uint16(opOperandX) | uint16(a.Op), }, nil } // String returns the instruction in assembler notation. func (a ALUOpX) String() string { switch a.Op { case ALUOpAdd: return "add x" case ALUOpSub: return "sub x" case ALUOpMul: return "mul x" case ALUOpDiv: return "div x" case ALUOpMod: return "mod x" case ALUOpAnd: return "and x" case ALUOpOr: return "or x" case ALUOpXor: return "xor x" case ALUOpShiftLeft: return "lsh x" case ALUOpShiftRight: return "rsh x" default: return fmt.Sprintf("unknown instruction: %#v", a) } } // NegateA executes A = -A. type NegateA struct{} // Assemble implements the Instruction Assemble method. func (a NegateA) Assemble() (RawInstruction, error) { return RawInstruction{ Op: opClsALU | uint16(aluOpNeg), }, nil } // String returns the instruction in assembler notation. func (a NegateA) String() string { return fmt.Sprintf("neg") } // Jump skips the following Skip instructions in the program. type Jump struct { Skip uint32 } // Assemble implements the Instruction Assemble method. func (a Jump) Assemble() (RawInstruction, error) { return RawInstruction{ Op: opClsJump | uint16(opJumpAlways), K: a.Skip, }, nil } // String returns the instruction in assembler notation. func (a Jump) String() string { return fmt.Sprintf("ja %d", a.Skip) } // JumpIf skips the following Skip instructions in the program if A // Val is true. type JumpIf struct { Cond JumpTest Val uint32 SkipTrue uint8 SkipFalse uint8 } // Assemble implements the Instruction Assemble method. func (a JumpIf) Assemble() (RawInstruction, error) { return jumpToRaw(a.Cond, opOperandConstant, a.Val, a.SkipTrue, a.SkipFalse) } // String returns the instruction in assembler notation. func (a JumpIf) String() string { return jumpToString(a.Cond, fmt.Sprintf("#%d", a.Val), a.SkipTrue, a.SkipFalse) } // JumpIfX skips the following Skip instructions in the program if A // X is true. type JumpIfX struct { Cond JumpTest SkipTrue uint8 SkipFalse uint8 } // Assemble implements the Instruction Assemble method. func (a JumpIfX) Assemble() (RawInstruction, error) { return jumpToRaw(a.Cond, opOperandX, 0, a.SkipTrue, a.SkipFalse) } // String returns the instruction in assembler notation. func (a JumpIfX) String() string { return jumpToString(a.Cond, "x", a.SkipTrue, a.SkipFalse) } // jumpToRaw assembles a jump instruction into a RawInstruction func jumpToRaw(test JumpTest, operand opOperand, k uint32, skipTrue, skipFalse uint8) (RawInstruction, error) { var ( cond jumpOp flip bool ) switch test { case JumpEqual: cond = opJumpEqual case JumpNotEqual: cond, flip = opJumpEqual, true case JumpGreaterThan: cond = opJumpGT case JumpLessThan: cond, flip = opJumpGE, true case JumpGreaterOrEqual: cond = opJumpGE case JumpLessOrEqual: cond, flip = opJumpGT, true case JumpBitsSet: cond = opJumpSet case JumpBitsNotSet: cond, flip = opJumpSet, true default: return RawInstruction{}, fmt.Errorf("unknown JumpTest %v", test) } jt, jf := skipTrue, skipFalse if flip { jt, jf = jf, jt } return RawInstruction{ Op: opClsJump | uint16(cond) | uint16(operand), Jt: jt, Jf: jf, K: k, }, nil } // jumpToString converts a jump instruction to assembler notation func jumpToString(cond JumpTest, operand string, skipTrue, skipFalse uint8) string { switch cond { // K == A case JumpEqual: return conditionalJump(operand, skipTrue, skipFalse, "jeq", "jneq") // K != A case JumpNotEqual: return fmt.Sprintf("jneq %s,%d", operand, skipTrue) // K > A case JumpGreaterThan: return conditionalJump(operand, skipTrue, skipFalse, "jgt", "jle") // K < A case JumpLessThan: return fmt.Sprintf("jlt %s,%d", operand, skipTrue) // K >= A case JumpGreaterOrEqual: return conditionalJump(operand, skipTrue, skipFalse, "jge", "jlt") // K <= A case JumpLessOrEqual: return fmt.Sprintf("jle %s,%d", operand, skipTrue) // K & A != 0 case JumpBitsSet: if skipFalse > 0 { return fmt.Sprintf("jset %s,%d,%d", operand, skipTrue, skipFalse) } return fmt.Sprintf("jset %s,%d", operand, skipTrue) // K & A == 0, there is no assembler instruction for JumpBitNotSet, use JumpBitSet and invert skips case JumpBitsNotSet: return jumpToString(JumpBitsSet, operand, skipFalse, skipTrue) default: return fmt.Sprintf("unknown JumpTest %#v", cond) } } func conditionalJump(operand string, skipTrue, skipFalse uint8, positiveJump, negativeJump string) string { if skipTrue > 0 { if skipFalse > 0 { return fmt.Sprintf("%s %s,%d,%d", positiveJump, operand, skipTrue, skipFalse) } return fmt.Sprintf("%s %s,%d", positiveJump, operand, skipTrue) } return fmt.Sprintf("%s %s,%d", negativeJump, operand, skipFalse) } // RetA exits the BPF program, returning the value of register A. type RetA struct{} // Assemble implements the Instruction Assemble method. func (a RetA) Assemble() (RawInstruction, error) { return RawInstruction{ Op: opClsReturn | opRetSrcA, }, nil } // String returns the instruction in assembler notation. func (a RetA) String() string { return fmt.Sprintf("ret a") } // RetConstant exits the BPF program, returning a constant value. type RetConstant struct { Val uint32 } // Assemble implements the Instruction Assemble method. func (a RetConstant) Assemble() (RawInstruction, error) { return RawInstruction{ Op: opClsReturn | opRetSrcConstant, K: a.Val, }, nil } // String returns the instruction in assembler notation. func (a RetConstant) String() string { return fmt.Sprintf("ret #%d", a.Val) } // TXA copies the value of register X to register A. type TXA struct{} // Assemble implements the Instruction Assemble method. func (a TXA) Assemble() (RawInstruction, error) { return RawInstruction{ Op: opClsMisc | opMiscTXA, }, nil } // String returns the instruction in assembler notation. func (a TXA) String() string { return fmt.Sprintf("txa") } // TAX copies the value of register A to register X. type TAX struct{} // Assemble implements the Instruction Assemble method. func (a TAX) Assemble() (RawInstruction, error) { return RawInstruction{ Op: opClsMisc | opMiscTAX, }, nil } // String returns the instruction in assembler notation. func (a TAX) String() string { return fmt.Sprintf("tax") } func assembleLoad(dst Register, loadSize int, mode uint16, k uint32) (RawInstruction, error) { var ( cls uint16 sz uint16 ) switch dst { case RegA: cls = opClsLoadA case RegX: cls = opClsLoadX default: return RawInstruction{}, fmt.Errorf("invalid target register %v", dst) } switch loadSize { case 1: sz = opLoadWidth1 case 2: sz = opLoadWidth2 case 4: sz = opLoadWidth4 default: return RawInstruction{}, fmt.Errorf("invalid load byte length %d", sz) } return RawInstruction{ Op: cls | sz | mode, K: k, }, nil } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/instructions_test.go000066400000000000000000000362661415442132300253450ustar00rootroot00000000000000// 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 bpf import ( "fmt" "io/ioutil" "reflect" "strconv" "strings" "testing" ) // This is a direct translation of the program in // testdata/all_instructions.txt. var allInstructions = []Instruction{ LoadConstant{Dst: RegA, Val: 42}, LoadConstant{Dst: RegX, Val: 42}, LoadScratch{Dst: RegA, N: 3}, LoadScratch{Dst: RegX, N: 3}, LoadAbsolute{Off: 42, Size: 1}, LoadAbsolute{Off: 42, Size: 2}, LoadAbsolute{Off: 42, Size: 4}, LoadIndirect{Off: 42, Size: 1}, LoadIndirect{Off: 42, Size: 2}, LoadIndirect{Off: 42, Size: 4}, LoadMemShift{Off: 42}, LoadExtension{Num: ExtLen}, LoadExtension{Num: ExtProto}, LoadExtension{Num: ExtType}, LoadExtension{Num: ExtRand}, StoreScratch{Src: RegA, N: 3}, StoreScratch{Src: RegX, N: 3}, ALUOpConstant{Op: ALUOpAdd, Val: 42}, ALUOpConstant{Op: ALUOpSub, Val: 42}, ALUOpConstant{Op: ALUOpMul, Val: 42}, ALUOpConstant{Op: ALUOpDiv, Val: 42}, ALUOpConstant{Op: ALUOpOr, Val: 42}, ALUOpConstant{Op: ALUOpAnd, Val: 42}, ALUOpConstant{Op: ALUOpShiftLeft, Val: 42}, ALUOpConstant{Op: ALUOpShiftRight, Val: 42}, ALUOpConstant{Op: ALUOpMod, Val: 42}, ALUOpConstant{Op: ALUOpXor, Val: 42}, ALUOpX{Op: ALUOpAdd}, ALUOpX{Op: ALUOpSub}, ALUOpX{Op: ALUOpMul}, ALUOpX{Op: ALUOpDiv}, ALUOpX{Op: ALUOpOr}, ALUOpX{Op: ALUOpAnd}, ALUOpX{Op: ALUOpShiftLeft}, ALUOpX{Op: ALUOpShiftRight}, ALUOpX{Op: ALUOpMod}, ALUOpX{Op: ALUOpXor}, NegateA{}, Jump{Skip: 17}, JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 15, SkipFalse: 16}, JumpIf{Cond: JumpNotEqual, Val: 42, SkipTrue: 15}, JumpIf{Cond: JumpLessThan, Val: 42, SkipTrue: 14}, JumpIf{Cond: JumpLessOrEqual, Val: 42, SkipTrue: 13}, JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 11, SkipFalse: 12}, JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 10, SkipFalse: 11}, JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 9, SkipFalse: 10}, JumpIfX{Cond: JumpEqual, SkipTrue: 8, SkipFalse: 9}, JumpIfX{Cond: JumpNotEqual, SkipTrue: 8}, JumpIfX{Cond: JumpLessThan, SkipTrue: 7}, JumpIfX{Cond: JumpLessOrEqual, SkipTrue: 6}, JumpIfX{Cond: JumpGreaterThan, SkipTrue: 4, SkipFalse: 5}, JumpIfX{Cond: JumpGreaterOrEqual, SkipTrue: 3, SkipFalse: 4}, JumpIfX{Cond: JumpBitsSet, SkipTrue: 2, SkipFalse: 3}, TAX{}, TXA{}, RetA{}, RetConstant{Val: 42}, } var allInstructionsExpected = "testdata/all_instructions.bpf" // Check that we produce the same output as the canonical bpf_asm // linux kernel tool. func TestInterop(t *testing.T) { out, err := Assemble(allInstructions) if err != nil { t.Fatalf("assembly of allInstructions program failed: %s", err) } t.Logf("Assembled program is %d instructions long", len(out)) bs, err := ioutil.ReadFile(allInstructionsExpected) if err != nil { t.Fatalf("reading %s: %s", allInstructionsExpected, err) } // First statement is the number of statements, last statement is // empty. We just ignore both and rely on slice length. stmts := strings.Split(string(bs), ",") if len(stmts)-2 != len(out) { t.Fatalf("test program lengths don't match: %s has %d, Go implementation has %d", allInstructionsExpected, len(stmts)-2, len(allInstructions)) } for i, stmt := range stmts[1 : len(stmts)-2] { nums := strings.Split(stmt, " ") if len(nums) != 4 { t.Fatalf("malformed instruction %d in %s: %s", i+1, allInstructionsExpected, stmt) } actual := out[i] op, err := strconv.ParseUint(nums[0], 10, 16) if err != nil { t.Fatalf("malformed opcode %s in instruction %d of %s", nums[0], i+1, allInstructionsExpected) } if actual.Op != uint16(op) { t.Errorf("opcode mismatch on instruction %d (%#v): got 0x%02x, want 0x%02x", i+1, allInstructions[i], actual.Op, op) } jt, err := strconv.ParseUint(nums[1], 10, 8) if err != nil { t.Fatalf("malformed jt offset %s in instruction %d of %s", nums[1], i+1, allInstructionsExpected) } if actual.Jt != uint8(jt) { t.Errorf("jt mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.Jt, jt) } jf, err := strconv.ParseUint(nums[2], 10, 8) if err != nil { t.Fatalf("malformed jf offset %s in instruction %d of %s", nums[2], i+1, allInstructionsExpected) } if actual.Jf != uint8(jf) { t.Errorf("jf mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.Jf, jf) } k, err := strconv.ParseUint(nums[3], 10, 32) if err != nil { t.Fatalf("malformed constant %s in instruction %d of %s", nums[3], i+1, allInstructionsExpected) } if actual.K != uint32(k) { t.Errorf("constant mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.K, k) } } } // Check that assembly and disassembly match each other. func TestAsmDisasm(t *testing.T) { prog1, err := Assemble(allInstructions) if err != nil { t.Fatalf("assembly of allInstructions program failed: %s", err) } t.Logf("Assembled program is %d instructions long", len(prog1)) got, allDecoded := Disassemble(prog1) if !allDecoded { t.Errorf("Disassemble(Assemble(allInstructions)) produced unrecognized instructions:") for i, inst := range got { if r, ok := inst.(RawInstruction); ok { t.Logf(" insn %d, %#v --> %#v", i+1, allInstructions[i], r) } } } if len(allInstructions) != len(got) { t.Fatalf("disassembly changed program size: %d insns before, %d insns after", len(allInstructions), len(got)) } if !reflect.DeepEqual(allInstructions, got) { t.Errorf("program mutated by disassembly:") for i := range got { if !reflect.DeepEqual(allInstructions[i], got[i]) { t.Logf(" insn %d, s: %#v, p1: %#v, got: %#v", i+1, allInstructions[i], prog1[i], got[i]) } } } } type InvalidInstruction struct{} func (a InvalidInstruction) Assemble() (RawInstruction, error) { return RawInstruction{}, fmt.Errorf("Invalid Instruction") } func (a InvalidInstruction) String() string { return fmt.Sprintf("unknown instruction: %#v", a) } func TestString(t *testing.T) { testCases := []struct { instruction Instruction assembler string }{ { instruction: LoadConstant{Dst: RegA, Val: 42}, assembler: "ld #42", }, { instruction: LoadConstant{Dst: RegX, Val: 42}, assembler: "ldx #42", }, { instruction: LoadConstant{Dst: 0xffff, Val: 42}, assembler: "unknown instruction: bpf.LoadConstant{Dst:0xffff, Val:0x2a}", }, { instruction: LoadScratch{Dst: RegA, N: 3}, assembler: "ld M[3]", }, { instruction: LoadScratch{Dst: RegX, N: 3}, assembler: "ldx M[3]", }, { instruction: LoadScratch{Dst: 0xffff, N: 3}, assembler: "unknown instruction: bpf.LoadScratch{Dst:0xffff, N:3}", }, { instruction: LoadAbsolute{Off: 42, Size: 1}, assembler: "ldb [42]", }, { instruction: LoadAbsolute{Off: 42, Size: 2}, assembler: "ldh [42]", }, { instruction: LoadAbsolute{Off: 42, Size: 4}, assembler: "ld [42]", }, { instruction: LoadAbsolute{Off: 42, Size: -1}, assembler: "unknown instruction: bpf.LoadAbsolute{Off:0x2a, Size:-1}", }, { instruction: LoadIndirect{Off: 42, Size: 1}, assembler: "ldb [x + 42]", }, { instruction: LoadIndirect{Off: 42, Size: 2}, assembler: "ldh [x + 42]", }, { instruction: LoadIndirect{Off: 42, Size: 4}, assembler: "ld [x + 42]", }, { instruction: LoadIndirect{Off: 42, Size: -1}, assembler: "unknown instruction: bpf.LoadIndirect{Off:0x2a, Size:-1}", }, { instruction: LoadMemShift{Off: 42}, assembler: "ldx 4*([42]&0xf)", }, { instruction: LoadExtension{Num: ExtLen}, assembler: "ld #len", }, { instruction: LoadExtension{Num: ExtProto}, assembler: "ld #proto", }, { instruction: LoadExtension{Num: ExtType}, assembler: "ld #type", }, { instruction: LoadExtension{Num: ExtPayloadOffset}, assembler: "ld #poff", }, { instruction: LoadExtension{Num: ExtInterfaceIndex}, assembler: "ld #ifidx", }, { instruction: LoadExtension{Num: ExtNetlinkAttr}, assembler: "ld #nla", }, { instruction: LoadExtension{Num: ExtNetlinkAttrNested}, assembler: "ld #nlan", }, { instruction: LoadExtension{Num: ExtMark}, assembler: "ld #mark", }, { instruction: LoadExtension{Num: ExtQueue}, assembler: "ld #queue", }, { instruction: LoadExtension{Num: ExtLinkLayerType}, assembler: "ld #hatype", }, { instruction: LoadExtension{Num: ExtRXHash}, assembler: "ld #rxhash", }, { instruction: LoadExtension{Num: ExtCPUID}, assembler: "ld #cpu", }, { instruction: LoadExtension{Num: ExtVLANTag}, assembler: "ld #vlan_tci", }, { instruction: LoadExtension{Num: ExtVLANTagPresent}, assembler: "ld #vlan_avail", }, { instruction: LoadExtension{Num: ExtVLANProto}, assembler: "ld #vlan_tpid", }, { instruction: LoadExtension{Num: ExtRand}, assembler: "ld #rand", }, { instruction: LoadAbsolute{Off: 0xfffff038, Size: 4}, assembler: "ld #rand", }, { instruction: LoadExtension{Num: 0xfff}, assembler: "unknown instruction: bpf.LoadExtension{Num:4095}", }, { instruction: StoreScratch{Src: RegA, N: 3}, assembler: "st M[3]", }, { instruction: StoreScratch{Src: RegX, N: 3}, assembler: "stx M[3]", }, { instruction: StoreScratch{Src: 0xffff, N: 3}, assembler: "unknown instruction: bpf.StoreScratch{Src:0xffff, N:3}", }, { instruction: ALUOpConstant{Op: ALUOpAdd, Val: 42}, assembler: "add #42", }, { instruction: ALUOpConstant{Op: ALUOpSub, Val: 42}, assembler: "sub #42", }, { instruction: ALUOpConstant{Op: ALUOpMul, Val: 42}, assembler: "mul #42", }, { instruction: ALUOpConstant{Op: ALUOpDiv, Val: 42}, assembler: "div #42", }, { instruction: ALUOpConstant{Op: ALUOpOr, Val: 42}, assembler: "or #42", }, { instruction: ALUOpConstant{Op: ALUOpAnd, Val: 42}, assembler: "and #42", }, { instruction: ALUOpConstant{Op: ALUOpShiftLeft, Val: 42}, assembler: "lsh #42", }, { instruction: ALUOpConstant{Op: ALUOpShiftRight, Val: 42}, assembler: "rsh #42", }, { instruction: ALUOpConstant{Op: ALUOpMod, Val: 42}, assembler: "mod #42", }, { instruction: ALUOpConstant{Op: ALUOpXor, Val: 42}, assembler: "xor #42", }, { instruction: ALUOpConstant{Op: 0xffff, Val: 42}, assembler: "unknown instruction: bpf.ALUOpConstant{Op:0xffff, Val:0x2a}", }, { instruction: ALUOpX{Op: ALUOpAdd}, assembler: "add x", }, { instruction: ALUOpX{Op: ALUOpSub}, assembler: "sub x", }, { instruction: ALUOpX{Op: ALUOpMul}, assembler: "mul x", }, { instruction: ALUOpX{Op: ALUOpDiv}, assembler: "div x", }, { instruction: ALUOpX{Op: ALUOpOr}, assembler: "or x", }, { instruction: ALUOpX{Op: ALUOpAnd}, assembler: "and x", }, { instruction: ALUOpX{Op: ALUOpShiftLeft}, assembler: "lsh x", }, { instruction: ALUOpX{Op: ALUOpShiftRight}, assembler: "rsh x", }, { instruction: ALUOpX{Op: ALUOpMod}, assembler: "mod x", }, { instruction: ALUOpX{Op: ALUOpXor}, assembler: "xor x", }, { instruction: ALUOpX{Op: 0xffff}, assembler: "unknown instruction: bpf.ALUOpX{Op:0xffff}", }, { instruction: NegateA{}, assembler: "neg", }, { instruction: Jump{Skip: 10}, assembler: "ja 10", }, { instruction: JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 8, SkipFalse: 9}, assembler: "jeq #42,8,9", }, { instruction: JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 8}, assembler: "jeq #42,8", }, { instruction: JumpIf{Cond: JumpEqual, Val: 42, SkipFalse: 8}, assembler: "jneq #42,8", }, { instruction: JumpIf{Cond: JumpNotEqual, Val: 42, SkipTrue: 8}, assembler: "jneq #42,8", }, { instruction: JumpIf{Cond: JumpLessThan, Val: 42, SkipTrue: 7}, assembler: "jlt #42,7", }, { instruction: JumpIf{Cond: JumpLessOrEqual, Val: 42, SkipTrue: 6}, assembler: "jle #42,6", }, { instruction: JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 4, SkipFalse: 5}, assembler: "jgt #42,4,5", }, { instruction: JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 4}, assembler: "jgt #42,4", }, { instruction: JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 3, SkipFalse: 4}, assembler: "jge #42,3,4", }, { instruction: JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 3}, assembler: "jge #42,3", }, { instruction: JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 2, SkipFalse: 3}, assembler: "jset #42,2,3", }, { instruction: JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 2}, assembler: "jset #42,2", }, { instruction: JumpIf{Cond: JumpBitsNotSet, Val: 42, SkipTrue: 2, SkipFalse: 3}, assembler: "jset #42,3,2", }, { instruction: JumpIf{Cond: JumpBitsNotSet, Val: 42, SkipTrue: 2}, assembler: "jset #42,0,2", }, { instruction: JumpIf{Cond: 0xffff, Val: 42, SkipTrue: 1, SkipFalse: 2}, assembler: "unknown JumpTest 0xffff", }, { instruction: JumpIfX{Cond: JumpEqual, SkipTrue: 8, SkipFalse: 9}, assembler: "jeq x,8,9", }, { instruction: JumpIfX{Cond: JumpEqual, SkipTrue: 8}, assembler: "jeq x,8", }, { instruction: JumpIfX{Cond: JumpEqual, SkipFalse: 8}, assembler: "jneq x,8", }, { instruction: JumpIfX{Cond: JumpNotEqual, SkipTrue: 8}, assembler: "jneq x,8", }, { instruction: JumpIfX{Cond: JumpLessThan, SkipTrue: 7}, assembler: "jlt x,7", }, { instruction: JumpIfX{Cond: JumpLessOrEqual, SkipTrue: 6}, assembler: "jle x,6", }, { instruction: JumpIfX{Cond: JumpGreaterThan, SkipTrue: 4, SkipFalse: 5}, assembler: "jgt x,4,5", }, { instruction: JumpIfX{Cond: JumpGreaterThan, SkipTrue: 4}, assembler: "jgt x,4", }, { instruction: JumpIfX{Cond: JumpGreaterOrEqual, SkipTrue: 3, SkipFalse: 4}, assembler: "jge x,3,4", }, { instruction: JumpIfX{Cond: JumpGreaterOrEqual, SkipTrue: 3}, assembler: "jge x,3", }, { instruction: JumpIfX{Cond: JumpBitsSet, SkipTrue: 2, SkipFalse: 3}, assembler: "jset x,2,3", }, { instruction: JumpIfX{Cond: JumpBitsSet, SkipTrue: 2}, assembler: "jset x,2", }, { instruction: JumpIfX{Cond: JumpBitsNotSet, SkipTrue: 2, SkipFalse: 3}, assembler: "jset x,3,2", }, { instruction: JumpIfX{Cond: JumpBitsNotSet, SkipTrue: 2}, assembler: "jset x,0,2", }, { instruction: JumpIfX{Cond: 0xffff, SkipTrue: 1, SkipFalse: 2}, assembler: "unknown JumpTest 0xffff", }, { instruction: TAX{}, assembler: "tax", }, { instruction: TXA{}, assembler: "txa", }, { instruction: RetA{}, assembler: "ret a", }, { instruction: RetConstant{Val: 42}, assembler: "ret #42", }, // Invalid instruction { instruction: InvalidInstruction{}, assembler: "unknown instruction: bpf.InvalidInstruction{}", }, } for _, testCase := range testCases { if input, ok := testCase.instruction.(fmt.Stringer); ok { got := input.String() if got != testCase.assembler { t.Errorf("String did not return expected assembler notation, expected: %s, got: %s", testCase.assembler, got) } } else { t.Errorf("Instruction %#v is not a fmt.Stringer", testCase.instruction) } } } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/setter.go000066400000000000000000000004661415442132300230410ustar00rootroot00000000000000// 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 bpf // A Setter is a type which can attach a compiled BPF filter to itself. type Setter interface { SetBPF(filter []RawInstruction) error } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/testdata/000077500000000000000000000000001415442132300230075ustar00rootroot00000000000000golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/testdata/all_instructions.bpf000066400000000000000000000011121415442132300270670ustar00rootroot0000000000000057,0 0 0 42,1 0 0 42,96 0 0 3,97 0 0 3,48 0 0 42,40 0 0 42,32 0 0 42,80 0 0 42,72 0 0 42,64 0 0 42,177 0 0 42,128 0 0 0,32 0 0 4294963200,32 0 0 4294963204,32 0 0 4294963256,2 0 0 3,3 0 0 3,4 0 0 42,20 0 0 42,36 0 0 42,52 0 0 42,68 0 0 42,84 0 0 42,100 0 0 42,116 0 0 42,148 0 0 42,164 0 0 42,12 0 0 0,28 0 0 0,44 0 0 0,60 0 0 0,76 0 0 0,92 0 0 0,108 0 0 0,124 0 0 0,156 0 0 0,172 0 0 0,132 0 0 0,5 0 0 17,21 15 16 42,21 0 15 42,53 0 14 42,37 0 13 42,37 11 12 42,53 10 11 42,69 9 10 42,29 8 9 0,29 0 8 0,61 0 7 0,45 0 6 0,45 4 5 0,61 3 4 0,77 2 3 0,7 0 0 0,135 0 0 0,22 0 0 0,6 0 0 42, golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/testdata/all_instructions.txt000066400000000000000000000016521415442132300271500ustar00rootroot00000000000000# This filter is compiled to all_instructions.bpf by the `bpf_asm` # tool, which can be found in the linux kernel source tree under # tools/bpf. # Load immediate ld #42 ldx #42 # Load scratch ld M[3] ldx M[3] # Load absolute ldb [42] ldh [42] ld [42] # Load indirect ldb [x + 42] ldh [x + 42] ld [x + 42] # Load IPv4 header length ldx 4*([42]&0xf) # Run extension function ld #len ld #proto ld #type ld #rand # Store scratch st M[3] stx M[3] # A constant add #42 sub #42 mul #42 div #42 or #42 and #42 lsh #42 rsh #42 mod #42 xor #42 # A X add x sub x mul x div x or x and x lsh x rsh x mod x xor x # !A neg # Jump A constant ja end jeq #42,prev,end jne #42,end jlt #42,end jle #42,end jgt #42,prev,end jge #42,prev,end jset #42,prev,end # Jump A X jeq x,prev,end jne x,end jlt x,end jle x,end jgt x,prev,end jge x,prev,end jset x,prev,end # Register transfers tax txa # Returns prev: ret a end: ret #42 golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/vm.go000066400000000000000000000100401415442132300221420ustar00rootroot00000000000000// 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 bpf import ( "errors" "fmt" ) // A VM is an emulated BPF virtual machine. type VM struct { filter []Instruction } // NewVM returns a new VM using the input BPF program. func NewVM(filter []Instruction) (*VM, error) { if len(filter) == 0 { return nil, errors.New("one or more Instructions must be specified") } for i, ins := range filter { check := len(filter) - (i + 1) switch ins := ins.(type) { // Check for out-of-bounds jumps in instructions case Jump: if check <= int(ins.Skip) { return nil, fmt.Errorf("cannot jump %d instructions; jumping past program bounds", ins.Skip) } case JumpIf: if check <= int(ins.SkipTrue) { return nil, fmt.Errorf("cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue) } if check <= int(ins.SkipFalse) { return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse) } case JumpIfX: if check <= int(ins.SkipTrue) { return nil, fmt.Errorf("cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue) } if check <= int(ins.SkipFalse) { return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse) } // Check for division or modulus by zero case ALUOpConstant: if ins.Val != 0 { break } switch ins.Op { case ALUOpDiv, ALUOpMod: return nil, errors.New("cannot divide by zero using ALUOpConstant") } // Check for unknown extensions case LoadExtension: switch ins.Num { case ExtLen: default: return nil, fmt.Errorf("extension %d not implemented", ins.Num) } } } // Make sure last instruction is a return instruction switch filter[len(filter)-1].(type) { case RetA, RetConstant: default: return nil, errors.New("BPF program must end with RetA or RetConstant") } // Though our VM works using disassembled instructions, we // attempt to assemble the input filter anyway to ensure it is compatible // with an operating system VM. _, err := Assemble(filter) return &VM{ filter: filter, }, err } // Run runs the VM's BPF program against the input bytes. // Run returns the number of bytes accepted by the BPF program, and any errors // which occurred while processing the program. func (v *VM) Run(in []byte) (int, error) { var ( // Registers of the virtual machine regA uint32 regX uint32 regScratch [16]uint32 // OK is true if the program should continue processing the next // instruction, or false if not, causing the loop to break ok = true ) // TODO(mdlayher): implement: // - NegateA: // - would require a change from uint32 registers to int32 // registers // TODO(mdlayher): add interop tests that check signedness of ALU // operations against kernel implementation, and make sure Go // implementation matches behavior for i := 0; i < len(v.filter) && ok; i++ { ins := v.filter[i] switch ins := ins.(type) { case ALUOpConstant: regA = aluOpConstant(ins, regA) case ALUOpX: regA, ok = aluOpX(ins, regA, regX) case Jump: i += int(ins.Skip) case JumpIf: jump := jumpIf(ins, regA) i += jump case JumpIfX: jump := jumpIfX(ins, regA, regX) i += jump case LoadAbsolute: regA, ok = loadAbsolute(ins, in) case LoadConstant: regA, regX = loadConstant(ins, regA, regX) case LoadExtension: regA = loadExtension(ins, in) case LoadIndirect: regA, ok = loadIndirect(ins, in, regX) case LoadMemShift: regX, ok = loadMemShift(ins, in) case LoadScratch: regA, regX = loadScratch(ins, regScratch, regA, regX) case RetA: return int(regA), nil case RetConstant: return int(ins.Val), nil case StoreScratch: regScratch = storeScratch(ins, regScratch, regA, regX) case TAX: regX = regA case TXA: regA = regX default: return 0, fmt.Errorf("unknown Instruction at index %d: %T", i, ins) } } return 0, nil } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/vm_aluop_test.go000066400000000000000000000232451415442132300244140ustar00rootroot00000000000000// 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 bpf_test import ( "testing" "golang.org/x/net/bpf" ) func TestVMALUOpAdd(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.ALUOpConstant{ Op: bpf.ALUOpAdd, Val: 3, }, bpf.RetA{}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 8, 2, 3, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 3, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMALUOpSub(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.TAX{}, bpf.ALUOpX{ Op: bpf.ALUOpSub, }, bpf.RetA{}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1, 2, 3, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 0, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMALUOpMul(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.ALUOpConstant{ Op: bpf.ALUOpMul, Val: 2, }, bpf.RetA{}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 6, 2, 3, 4, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 4, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMALUOpDiv(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.ALUOpConstant{ Op: bpf.ALUOpDiv, Val: 2, }, bpf.RetA{}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 20, 2, 3, 4, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 2, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMALUOpDivByZeroALUOpConstant(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{ bpf.ALUOpConstant{ Op: bpf.ALUOpDiv, Val: 0, }, bpf.RetA{}, }) if errStr(err) != "cannot divide by zero using ALUOpConstant" { t.Fatalf("unexpected error: %v", err) } } func TestVMALUOpDivByZeroALUOpX(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ // Load byte 0 into X bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.TAX{}, // Load byte 1 into A bpf.LoadAbsolute{ Off: 9, Size: 1, }, // Attempt to perform 1/0 bpf.ALUOpX{ Op: bpf.ALUOpDiv, }, // Return 4 bytes if program does not terminate bpf.LoadConstant{ Val: 12, }, bpf.RetA{}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, 3, 4, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 0, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMALUOpOr(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 2, }, bpf.ALUOpConstant{ Op: bpf.ALUOpOr, Val: 0x01, }, bpf.RetA{}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x10, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 9, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMALUOpAnd(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 2, }, bpf.ALUOpConstant{ Op: bpf.ALUOpAnd, Val: 0x0019, }, bpf.RetA{}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0x09, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 1, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMALUOpShiftLeft(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.ALUOpConstant{ Op: bpf.ALUOpShiftLeft, Val: 0x01, }, bpf.JumpIf{ Cond: bpf.JumpEqual, Val: 0x02, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 9, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xaa, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 1, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMALUOpShiftRight(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.ALUOpConstant{ Op: bpf.ALUOpShiftRight, Val: 0x01, }, bpf.JumpIf{ Cond: bpf.JumpEqual, Val: 0x04, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 9, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0xff, 0xff, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 1, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMALUOpMod(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.ALUOpConstant{ Op: bpf.ALUOpMod, Val: 20, }, bpf.RetA{}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 30, 0, 0, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 2, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMALUOpModByZeroALUOpConstant(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.ALUOpConstant{ Op: bpf.ALUOpMod, Val: 0, }, bpf.RetA{}, }) if errStr(err) != "cannot divide by zero using ALUOpConstant" { t.Fatalf("unexpected error: %v", err) } } func TestVMALUOpModByZeroALUOpX(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ // Load byte 0 into X bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.TAX{}, // Load byte 1 into A bpf.LoadAbsolute{ Off: 9, Size: 1, }, // Attempt to perform 1%0 bpf.ALUOpX{ Op: bpf.ALUOpMod, }, // Return 4 bytes if program does not terminate bpf.LoadConstant{ Val: 12, }, bpf.RetA{}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, 3, 4, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 0, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMALUOpXor(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.ALUOpConstant{ Op: bpf.ALUOpXor, Val: 0x0a, }, bpf.JumpIf{ Cond: bpf.JumpEqual, Val: 0x01, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 9, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 1, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMALUOpUnknown(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.ALUOpConstant{ Op: bpf.ALUOpAdd, Val: 1, }, // Verify that an unknown operation is a no-op bpf.ALUOpConstant{ Op: 100, }, bpf.JumpIf{ Cond: bpf.JumpEqual, Val: 0x02, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 9, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 1, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/vm_bpf_test.go000066400000000000000000000120741415442132300240410ustar00rootroot00000000000000// 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 bpf_test import ( "net" "runtime" "testing" "time" "golang.org/x/net/bpf" "golang.org/x/net/ipv4" "golang.org/x/net/ipv6" "golang.org/x/net/nettest" ) // A virtualMachine is a BPF virtual machine which can process an // input packet against a BPF program and render a verdict. type virtualMachine interface { Run(in []byte) (int, error) } // canUseOSVM indicates if the OS BPF VM is available on this platform. func canUseOSVM() bool { // OS BPF VM can only be used on platforms where x/net/ipv4 supports // attaching a BPF program to a socket. switch runtime.GOOS { case "linux": return true } return false } // All BPF tests against both the Go VM and OS VM are assumed to // be used with a UDP socket. As a result, the entire contents // of a UDP datagram is sent through the BPF program, but only // the body after the UDP header will ever be returned in output. // testVM sets up a Go BPF VM, and if available, a native OS BPF VM // for integration testing. func testVM(t *testing.T, filter []bpf.Instruction) (virtualMachine, func(), error) { goVM, err := bpf.NewVM(filter) if err != nil { // Some tests expect an error, so this error must be returned // instead of fatally exiting the test return nil, nil, err } mvm := &multiVirtualMachine{ goVM: goVM, t: t, } // If available, add the OS VM for tests which verify that both the Go // VM and OS VM have exactly the same output for the same input program // and packet. done := func() {} if canUseOSVM() { osVM, osVMDone := testOSVM(t, filter) done = func() { osVMDone() } mvm.osVM = osVM } return mvm, done, nil } // udpHeaderLen is the length of a UDP header. const udpHeaderLen = 8 // A multiVirtualMachine is a virtualMachine which can call out to both the Go VM // and the native OS VM, if the OS VM is available. type multiVirtualMachine struct { goVM virtualMachine osVM virtualMachine t *testing.T } func (mvm *multiVirtualMachine) Run(in []byte) (int, error) { if len(in) < udpHeaderLen { mvm.t.Fatalf("input must be at least length of UDP header (%d), got: %d", udpHeaderLen, len(in)) } // All tests have a UDP header as part of input, because the OS VM // packets always will. For the Go VM, this output is trimmed before // being sent back to tests. goOut, goErr := mvm.goVM.Run(in) if goOut >= udpHeaderLen { goOut -= udpHeaderLen } // If Go output is larger than the size of the packet, packet filtering // interop tests must trim the output bytes to the length of the packet. // The BPF VM should not do this on its own, as other uses of it do // not trim the output byte count. trim := len(in) - udpHeaderLen if goOut > trim { goOut = trim } // When the OS VM is not available, process using the Go VM alone if mvm.osVM == nil { return goOut, goErr } // The OS VM will apply its own UDP header, so remove the pseudo header // that the Go VM needs. osOut, err := mvm.osVM.Run(in[udpHeaderLen:]) if err != nil { mvm.t.Fatalf("error while running OS VM: %v", err) } // Verify both VMs return same number of bytes var mismatch bool if goOut != osOut { mismatch = true mvm.t.Logf("output byte count does not match:\n- go: %v\n- os: %v", goOut, osOut) } if mismatch { mvm.t.Fatal("Go BPF and OS BPF packet outputs do not match") } return goOut, goErr } // An osVirtualMachine is a virtualMachine which uses the OS's BPF VM for // processing BPF programs. type osVirtualMachine struct { l net.PacketConn s net.Conn } // testOSVM creates a virtualMachine which uses the OS's BPF VM by injecting // packets into a UDP listener with a BPF program attached to it. func testOSVM(t *testing.T, filter []bpf.Instruction) (virtualMachine, func()) { l, err := nettest.NewLocalPacketListener("udp") if err != nil { t.Fatalf("failed to open OS VM UDP listener: %v", err) } prog, err := bpf.Assemble(filter) if err != nil { t.Fatalf("failed to compile BPF program: %v", err) } ip := l.LocalAddr().(*net.UDPAddr).IP if ip.To4() != nil && ip.To16() == nil { err = ipv4.NewPacketConn(l).SetBPF(prog) } else { err = ipv6.NewPacketConn(l).SetBPF(prog) } if err != nil { t.Fatalf("failed to attach BPF program to listener: %v", err) } s, err := net.Dial(l.LocalAddr().Network(), l.LocalAddr().String()) if err != nil { t.Fatalf("failed to dial connection to listener: %v", err) } done := func() { _ = s.Close() _ = l.Close() } return &osVirtualMachine{ l: l, s: s, }, done } // Run sends the input bytes into the OS's BPF VM and returns its verdict. func (vm *osVirtualMachine) Run(in []byte) (int, error) { go func() { _, _ = vm.s.Write(in) }() vm.l.SetDeadline(time.Now().Add(50 * time.Millisecond)) var b [512]byte n, _, err := vm.l.ReadFrom(b[:]) if err != nil { // A timeout indicates that BPF filtered out the packet, and thus, // no input should be returned. if nerr, ok := err.(net.Error); ok && nerr.Timeout() { return n, nil } return n, err } return n, nil } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/vm_extension_test.go000066400000000000000000000020121415442132300252750ustar00rootroot00000000000000// 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 bpf_test import ( "testing" "golang.org/x/net/bpf" ) func TestVMLoadExtensionNotImplemented(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{ bpf.LoadExtension{ Num: 100, }, bpf.RetA{}, }) if errStr(err) != "extension 100 not implemented" { t.Fatalf("unexpected error: %v", err) } } func TestVMLoadExtensionExtLen(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadExtension{ Num: bpf.ExtLen, }, bpf.RetA{}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, 2, 3, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 4, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/vm_instructions.go000066400000000000000000000076061415442132300250040ustar00rootroot00000000000000// 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 bpf import ( "encoding/binary" "fmt" ) func aluOpConstant(ins ALUOpConstant, regA uint32) uint32 { return aluOpCommon(ins.Op, regA, ins.Val) } func aluOpX(ins ALUOpX, regA uint32, regX uint32) (uint32, bool) { // Guard against division or modulus by zero by terminating // the program, as the OS BPF VM does if regX == 0 { switch ins.Op { case ALUOpDiv, ALUOpMod: return 0, false } } return aluOpCommon(ins.Op, regA, regX), true } func aluOpCommon(op ALUOp, regA uint32, value uint32) uint32 { switch op { case ALUOpAdd: return regA + value case ALUOpSub: return regA - value case ALUOpMul: return regA * value case ALUOpDiv: // Division by zero not permitted by NewVM and aluOpX checks return regA / value case ALUOpOr: return regA | value case ALUOpAnd: return regA & value case ALUOpShiftLeft: return regA << value case ALUOpShiftRight: return regA >> value case ALUOpMod: // Modulus by zero not permitted by NewVM and aluOpX checks return regA % value case ALUOpXor: return regA ^ value default: return regA } } func jumpIf(ins JumpIf, regA uint32) int { return jumpIfCommon(ins.Cond, ins.SkipTrue, ins.SkipFalse, regA, ins.Val) } func jumpIfX(ins JumpIfX, regA uint32, regX uint32) int { return jumpIfCommon(ins.Cond, ins.SkipTrue, ins.SkipFalse, regA, regX) } func jumpIfCommon(cond JumpTest, skipTrue, skipFalse uint8, regA uint32, value uint32) int { var ok bool switch cond { case JumpEqual: ok = regA == value case JumpNotEqual: ok = regA != value case JumpGreaterThan: ok = regA > value case JumpLessThan: ok = regA < value case JumpGreaterOrEqual: ok = regA >= value case JumpLessOrEqual: ok = regA <= value case JumpBitsSet: ok = (regA & value) != 0 case JumpBitsNotSet: ok = (regA & value) == 0 } if ok { return int(skipTrue) } return int(skipFalse) } func loadAbsolute(ins LoadAbsolute, in []byte) (uint32, bool) { offset := int(ins.Off) size := int(ins.Size) return loadCommon(in, offset, size) } func loadConstant(ins LoadConstant, regA uint32, regX uint32) (uint32, uint32) { switch ins.Dst { case RegA: regA = ins.Val case RegX: regX = ins.Val } return regA, regX } func loadExtension(ins LoadExtension, in []byte) uint32 { switch ins.Num { case ExtLen: return uint32(len(in)) default: panic(fmt.Sprintf("unimplemented extension: %d", ins.Num)) } } func loadIndirect(ins LoadIndirect, in []byte, regX uint32) (uint32, bool) { offset := int(ins.Off) + int(regX) size := int(ins.Size) return loadCommon(in, offset, size) } func loadMemShift(ins LoadMemShift, in []byte) (uint32, bool) { offset := int(ins.Off) // Size of LoadMemShift is always 1 byte if !inBounds(len(in), offset, 1) { return 0, false } // Mask off high 4 bits and multiply low 4 bits by 4 return uint32(in[offset]&0x0f) * 4, true } func inBounds(inLen int, offset int, size int) bool { return offset+size <= inLen } func loadCommon(in []byte, offset int, size int) (uint32, bool) { if !inBounds(len(in), offset, size) { return 0, false } switch size { case 1: return uint32(in[offset]), true case 2: return uint32(binary.BigEndian.Uint16(in[offset : offset+size])), true case 4: return uint32(binary.BigEndian.Uint32(in[offset : offset+size])), true default: panic(fmt.Sprintf("invalid load size: %d", size)) } } func loadScratch(ins LoadScratch, regScratch [16]uint32, regA uint32, regX uint32) (uint32, uint32) { switch ins.Dst { case RegA: regA = regScratch[ins.N] case RegX: regX = regScratch[ins.N] } return regA, regX } func storeScratch(ins StoreScratch, regScratch [16]uint32, regA uint32, regX uint32) [16]uint32 { switch ins.Src { case RegA: regScratch[ins.N] = regA case RegX: regScratch[ins.N] = regX } return regScratch } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/vm_jump_test.go000066400000000000000000000330371415442132300242470ustar00rootroot00000000000000// 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 bpf_test import ( "testing" "golang.org/x/net/bpf" ) func TestVMJumpOne(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.Jump{ Skip: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 9, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 1, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMJumpOutOfProgram(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{ bpf.Jump{ Skip: 1, }, bpf.RetA{}, }) if errStr(err) != "cannot jump 1 instructions; jumping past program bounds" { t.Fatalf("unexpected error: %v", err) } } func TestVMJumpIfTrueOutOfProgram(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{ bpf.JumpIf{ Cond: bpf.JumpEqual, SkipTrue: 2, }, bpf.RetA{}, }) if errStr(err) != "cannot jump 2 instructions in true case; jumping past program bounds" { t.Fatalf("unexpected error: %v", err) } } func TestVMJumpIfFalseOutOfProgram(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{ bpf.JumpIf{ Cond: bpf.JumpEqual, SkipFalse: 3, }, bpf.RetA{}, }) if errStr(err) != "cannot jump 3 instructions in false case; jumping past program bounds" { t.Fatalf("unexpected error: %v", err) } } func TestVMJumpIfXTrueOutOfProgram(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{ bpf.JumpIfX{ Cond: bpf.JumpEqual, SkipTrue: 2, }, bpf.RetA{}, }) if errStr(err) != "cannot jump 2 instructions in true case; jumping past program bounds" { t.Fatalf("unexpected error: %v", err) } } func TestVMJumpIfXFalseOutOfProgram(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{ bpf.JumpIfX{ Cond: bpf.JumpEqual, SkipFalse: 3, }, bpf.RetA{}, }) if errStr(err) != "cannot jump 3 instructions in false case; jumping past program bounds" { t.Fatalf("unexpected error: %v", err) } } func TestVMJumpIfEqual(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.JumpIf{ Cond: bpf.JumpEqual, Val: 1, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 9, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 1, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMJumpIfNotEqual(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.JumpIf{ Cond: bpf.JumpNotEqual, Val: 1, SkipFalse: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 9, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 1, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMJumpIfGreaterThan(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 4, }, bpf.JumpIf{ Cond: bpf.JumpGreaterThan, Val: 0x00010202, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 12, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, 2, 3, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 4, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMJumpIfLessThan(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 4, }, bpf.JumpIf{ Cond: bpf.JumpLessThan, Val: 0xff010203, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 12, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, 2, 3, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 4, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMJumpIfGreaterOrEqual(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 4, }, bpf.JumpIf{ Cond: bpf.JumpGreaterOrEqual, Val: 0x00010203, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 12, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, 2, 3, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 4, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMJumpIfLessOrEqual(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 4, }, bpf.JumpIf{ Cond: bpf.JumpLessOrEqual, Val: 0xff010203, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 12, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, 2, 3, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 4, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMJumpIfBitsSet(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 2, }, bpf.JumpIf{ Cond: bpf.JumpBitsSet, Val: 0x1122, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 10, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x02, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 2, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMJumpIfBitsNotSet(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 2, }, bpf.JumpIf{ Cond: bpf.JumpBitsNotSet, Val: 0x1221, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 10, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x02, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 2, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMJumpIfXEqual(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.LoadConstant{ Dst: bpf.RegX, Val: 1, }, bpf.JumpIfX{ Cond: bpf.JumpEqual, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 9, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 1, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMJumpIfXNotEqual(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.LoadConstant{ Dst: bpf.RegX, Val: 1, }, bpf.JumpIfX{ Cond: bpf.JumpNotEqual, SkipFalse: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 9, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 1, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMJumpIfXGreaterThan(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 4, }, bpf.LoadConstant{ Dst: bpf.RegX, Val: 0x00010202, }, bpf.JumpIfX{ Cond: bpf.JumpGreaterThan, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 12, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, 2, 3, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 4, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMJumpIfXLessThan(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 4, }, bpf.LoadConstant{ Dst: bpf.RegX, Val: 0xff010203, }, bpf.JumpIfX{ Cond: bpf.JumpLessThan, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 12, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, 2, 3, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 4, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMJumpIfXGreaterOrEqual(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 4, }, bpf.LoadConstant{ Dst: bpf.RegX, Val: 0x00010203, }, bpf.JumpIfX{ Cond: bpf.JumpGreaterOrEqual, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 12, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, 2, 3, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 4, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMJumpIfXLessOrEqual(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 4, }, bpf.LoadConstant{ Dst: bpf.RegX, Val: 0xff010203, }, bpf.JumpIfX{ Cond: bpf.JumpLessOrEqual, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 12, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, 2, 3, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 4, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMJumpIfXBitsSet(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 2, }, bpf.LoadConstant{ Dst: bpf.RegX, Val: 0x1122, }, bpf.JumpIfX{ Cond: bpf.JumpBitsSet, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 10, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x02, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 2, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMJumpIfXBitsNotSet(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 2, }, bpf.LoadConstant{ Dst: bpf.RegX, Val: 0x1221, }, bpf.JumpIfX{ Cond: bpf.JumpBitsNotSet, SkipTrue: 1, }, bpf.RetConstant{ Val: 0, }, bpf.RetConstant{ Val: 10, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x02, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 2, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/vm_load_test.go000066400000000000000000000130731415442132300242110ustar00rootroot00000000000000// 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 bpf_test import ( "net" "testing" "golang.org/x/net/bpf" "golang.org/x/net/ipv4" ) func TestVMLoadAbsoluteOffsetOutOfBounds(t *testing.T) { pkt := []byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, 2, 3, } vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: uint32(len(pkt)), Size: 1, }, // Out of bounds should return 0, return 1 to tell if execution continued bpf.RetConstant{Val: 1}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run(pkt) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 0, out; want != got { t.Fatalf("unexpected result:\n- want: %d\n- got: %d", want, got) } } func TestVMLoadAbsoluteOffsetPlusSizeOutOfBounds(t *testing.T) { pkt := []byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, } vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: uint32(len(pkt) - 1), Size: 2, }, // Out of bounds should return 0, return 1 to tell if execution continued bpf.RetConstant{Val: 1}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run(pkt) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 0, out; want != got { t.Fatalf("unexpected result:\n- want: %d\n- got: %d", want, got) } } func TestVMLoadAbsoluteBadInstructionSize(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Size: 5, }, bpf.RetA{}, }) if errStr(err) != "assembling instruction 1: invalid load byte length 0" { t.Fatalf("unexpected error: %v", err) } } func TestVMLoadConstantOK(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadConstant{ Dst: bpf.RegX, Val: 9, }, bpf.TXA{}, bpf.RetA{}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 1, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMLoadIndirectOutOfBounds(t *testing.T) { pkt := []byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, } vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadIndirect{ Off: uint32(len(pkt)), Size: 1, }, // Out of bounds should return 0, return 1 to tell if execution continued bpf.RetConstant{Val: 1}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run(pkt) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 0, out; want != got { t.Fatalf("unexpected result:\n- want: %d\n- got: %d", want, got) } } func TestVMLoadMemShiftOutOfBounds(t *testing.T) { pkt := []byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, } vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadMemShift{ Off: uint32(len(pkt)), }, // Out of bounds should return 0, return 1 to tell if execution continued bpf.RetConstant{Val: 1}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run(pkt) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 0, out; want != got { t.Fatalf("unexpected result:\n- want: %d\n- got: %d", want, got) } } const ( dhcp4Port = 53 ) func TestVMLoadMemShiftLoadIndirectNoResult(t *testing.T) { vm, in, done := testDHCPv4(t) defer done() // Append mostly empty UDP header with incorrect DHCPv4 port in = append(in, []byte{ 0, 0, 0, dhcp4Port + 1, 0, 0, 0, 0, }...) out, err := vm.Run(in) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 0, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMLoadMemShiftLoadIndirectOK(t *testing.T) { vm, in, done := testDHCPv4(t) defer done() // Append mostly empty UDP header with correct DHCPv4 port in = append(in, []byte{ 0, 0, 0, dhcp4Port, 0, 0, 0, 0, }...) out, err := vm.Run(in) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := len(in)-8, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func testDHCPv4(t *testing.T) (virtualMachine, []byte, func()) { // DHCPv4 test data courtesy of David Anderson: // https://github.com/google/netboot/blob/master/dhcp4/conn_linux.go#L59-L70 vm, done, err := testVM(t, []bpf.Instruction{ // Load IPv4 packet length bpf.LoadMemShift{Off: 8}, // Get UDP dport bpf.LoadIndirect{Off: 8 + 2, Size: 2}, // Correct dport? bpf.JumpIf{Cond: bpf.JumpEqual, Val: dhcp4Port, SkipFalse: 1}, // Accept bpf.RetConstant{Val: 1500}, // Ignore bpf.RetConstant{Val: 0}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } // Minimal requirements to make a valid IPv4 header h := &ipv4.Header{ Len: ipv4.HeaderLen, Src: net.IPv4(192, 168, 1, 1), Dst: net.IPv4(192, 168, 1, 2), } hb, err := h.Marshal() if err != nil { t.Fatalf("failed to marshal IPv4 header: %v", err) } hb = append([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }, hb...) return vm, hb, done } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/vm_ret_test.go000066400000000000000000000044711415442132300240660ustar00rootroot00000000000000// 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 bpf_test import ( "testing" "golang.org/x/net/bpf" ) func TestVMRetA(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 1, }, bpf.RetA{}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 9, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 1, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMRetALargerThanInput(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadAbsolute{ Off: 8, Size: 2, }, bpf.RetA{}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 255, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 2, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMRetConstant(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.RetConstant{ Val: 9, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 1, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMRetConstantLargerThanInput(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.RetConstant{ Val: 16, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 2, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/vm_scratch_test.go000066400000000000000000000113571415442132300247240ustar00rootroot00000000000000// 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 bpf_test import ( "testing" "golang.org/x/net/bpf" ) func TestVMStoreScratchInvalidScratchRegisterTooSmall(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{ bpf.StoreScratch{ Src: bpf.RegA, N: -1, }, bpf.RetA{}, }) if errStr(err) != "assembling instruction 1: invalid scratch slot -1" { t.Fatalf("unexpected error: %v", err) } } func TestVMStoreScratchInvalidScratchRegisterTooLarge(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{ bpf.StoreScratch{ Src: bpf.RegA, N: 16, }, bpf.RetA{}, }) if errStr(err) != "assembling instruction 1: invalid scratch slot 16" { t.Fatalf("unexpected error: %v", err) } } func TestVMStoreScratchUnknownSourceRegister(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{ bpf.StoreScratch{ Src: 100, N: 0, }, bpf.RetA{}, }) if errStr(err) != "assembling instruction 1: invalid source register 100" { t.Fatalf("unexpected error: %v", err) } } func TestVMLoadScratchInvalidScratchRegisterTooSmall(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{ bpf.LoadScratch{ Dst: bpf.RegX, N: -1, }, bpf.RetA{}, }) if errStr(err) != "assembling instruction 1: invalid scratch slot -1" { t.Fatalf("unexpected error: %v", err) } } func TestVMLoadScratchInvalidScratchRegisterTooLarge(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{ bpf.LoadScratch{ Dst: bpf.RegX, N: 16, }, bpf.RetA{}, }) if errStr(err) != "assembling instruction 1: invalid scratch slot 16" { t.Fatalf("unexpected error: %v", err) } } func TestVMLoadScratchUnknownDestinationRegister(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{ bpf.LoadScratch{ Dst: 100, N: 0, }, bpf.RetA{}, }) if errStr(err) != "assembling instruction 1: invalid target register 100" { t.Fatalf("unexpected error: %v", err) } } func TestVMStoreScratchLoadScratchOneValue(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ // Load byte 255 bpf.LoadAbsolute{ Off: 8, Size: 1, }, // Copy to X and store in scratch[0] bpf.TAX{}, bpf.StoreScratch{ Src: bpf.RegX, N: 0, }, // Load byte 1 bpf.LoadAbsolute{ Off: 9, Size: 1, }, // Overwrite 1 with 255 from scratch[0] bpf.LoadScratch{ Dst: bpf.RegA, N: 0, }, // Return 255 bpf.RetA{}, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 255, 1, 2, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 3, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } func TestVMStoreScratchLoadScratchMultipleValues(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ // Load byte 10 bpf.LoadAbsolute{ Off: 8, Size: 1, }, // Store in scratch[0] bpf.StoreScratch{ Src: bpf.RegA, N: 0, }, // Load byte 20 bpf.LoadAbsolute{ Off: 9, Size: 1, }, // Store in scratch[1] bpf.StoreScratch{ Src: bpf.RegA, N: 1, }, // Load byte 30 bpf.LoadAbsolute{ Off: 10, Size: 1, }, // Store in scratch[2] bpf.StoreScratch{ Src: bpf.RegA, N: 2, }, // Load byte 1 bpf.LoadAbsolute{ Off: 11, Size: 1, }, // Store in scratch[3] bpf.StoreScratch{ Src: bpf.RegA, N: 3, }, // Load in byte 10 to X bpf.LoadScratch{ Dst: bpf.RegX, N: 0, }, // Copy X -> A bpf.TXA{}, // Verify value is 10 bpf.JumpIf{ Cond: bpf.JumpEqual, Val: 10, SkipTrue: 1, }, // Fail test if incorrect bpf.RetConstant{ Val: 0, }, // Load in byte 20 to A bpf.LoadScratch{ Dst: bpf.RegA, N: 1, }, // Verify value is 20 bpf.JumpIf{ Cond: bpf.JumpEqual, Val: 20, SkipTrue: 1, }, // Fail test if incorrect bpf.RetConstant{ Val: 0, }, // Load in byte 30 to A bpf.LoadScratch{ Dst: bpf.RegA, N: 2, }, // Verify value is 30 bpf.JumpIf{ Cond: bpf.JumpEqual, Val: 30, SkipTrue: 1, }, // Fail test if incorrect bpf.RetConstant{ Val: 0, }, // Return first two bytes on success bpf.RetConstant{ Val: 10, }, }) if err != nil { t.Fatalf("failed to load BPF program: %v", err) } defer done() out, err := vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 10, 20, 30, 1, }) if err != nil { t.Fatalf("unexpected error while running program: %v", err) } if want, got := 2, out; want != got { t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", want, got) } } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/bpf/vm_test.go000066400000000000000000000064041415442132300232120ustar00rootroot00000000000000// 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 bpf_test import ( "fmt" "testing" "golang.org/x/net/bpf" ) var _ bpf.Instruction = unknown{} type unknown struct{} func (unknown) Assemble() (bpf.RawInstruction, error) { return bpf.RawInstruction{}, nil } func TestVMUnknownInstruction(t *testing.T) { vm, done, err := testVM(t, []bpf.Instruction{ bpf.LoadConstant{ Dst: bpf.RegA, Val: 100, }, // Should terminate the program with an error immediately unknown{}, bpf.RetA{}, }) if err != nil { t.Fatalf("unexpected error: %v", err) } defer done() _, err = vm.Run([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, }) if errStr(err) != "unknown Instruction at index 1: bpf_test.unknown" { t.Fatalf("unexpected error while running program: %v", err) } } func TestVMNoReturnInstruction(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{ bpf.LoadConstant{ Dst: bpf.RegA, Val: 1, }, }) if errStr(err) != "BPF program must end with RetA or RetConstant" { t.Fatalf("unexpected error: %v", err) } } func TestVMNoInputInstructions(t *testing.T) { _, _, err := testVM(t, []bpf.Instruction{}) if errStr(err) != "one or more Instructions must be specified" { t.Fatalf("unexpected error: %v", err) } } // ExampleNewVM demonstrates usage of a VM, using an Ethernet frame // as input and checking its EtherType to determine if it should be accepted. func ExampleNewVM() { // Offset | Length | Comment // ------------------------- // 00 | 06 | Ethernet destination MAC address // 06 | 06 | Ethernet source MAC address // 12 | 02 | Ethernet EtherType const ( etOff = 12 etLen = 2 etARP = 0x0806 ) // Set up a VM to filter traffic based on if its EtherType // matches the ARP EtherType. vm, err := bpf.NewVM([]bpf.Instruction{ // Load EtherType value from Ethernet header bpf.LoadAbsolute{ Off: etOff, Size: etLen, }, // If EtherType is equal to the ARP EtherType, jump to allow // packet to be accepted bpf.JumpIf{ Cond: bpf.JumpEqual, Val: etARP, SkipTrue: 1, }, // EtherType does not match the ARP EtherType bpf.RetConstant{ Val: 0, }, // EtherType matches the ARP EtherType, accept up to 1500 // bytes of packet bpf.RetConstant{ Val: 1500, }, }) if err != nil { panic(fmt.Sprintf("failed to load BPF program: %v", err)) } // Create an Ethernet frame with the ARP EtherType for testing frame := []byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x08, 0x06, // Payload omitted for brevity } // Run our VM's BPF program using the Ethernet frame as input out, err := vm.Run(frame) if err != nil { panic(fmt.Sprintf("failed to accept Ethernet frame: %v", err)) } // BPF VM can return a byte count greater than the number of input // bytes, so trim the output to match the input byte length if out > len(frame) { out = len(frame) } fmt.Printf("out: %d bytes", out) // Output: // out: 14 bytes } // errStr returns the string representation of an error, or // "" if it is nil. func errStr(err error) string { if err == nil { return "" } return err.Error() } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/codereview.cfg000066400000000000000000000000251415442132300232410ustar00rootroot00000000000000issuerepo: golang/go golang-golang-x-net-0.0+git20211209.491a49a+dfsg/context/000077500000000000000000000000001415442132300221135ustar00rootroot00000000000000golang-golang-x-net-0.0+git20211209.491a49a+dfsg/context/context.go000066400000000000000000000046401415442132300241320ustar00rootroot00000000000000// 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 } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/context/context_test.go000066400000000000000000000351751415442132300252000ustar00rootroot00000000000000// 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. //go:build !go1.7 // +build !go1.7 package context import ( "fmt" "math/rand" "runtime" "strings" "sync" "testing" "time" ) // otherContext is a Context that's not one of the types defined in context.go. // This lets us test code paths that differ based on the underlying type of the // Context. type otherContext struct { Context } func TestBackground(t *testing.T) { c := Background() if c == nil { t.Fatalf("Background returned nil") } select { case x := <-c.Done(): t.Errorf("<-c.Done() == %v want nothing (it should block)", x) default: } if got, want := fmt.Sprint(c), "context.Background"; got != want { t.Errorf("Background().String() = %q want %q", got, want) } } func TestTODO(t *testing.T) { c := TODO() if c == nil { t.Fatalf("TODO returned nil") } select { case x := <-c.Done(): t.Errorf("<-c.Done() == %v want nothing (it should block)", x) default: } if got, want := fmt.Sprint(c), "context.TODO"; got != want { t.Errorf("TODO().String() = %q want %q", got, want) } } func TestWithCancel(t *testing.T) { c1, cancel := WithCancel(Background()) if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want { t.Errorf("c1.String() = %q want %q", got, want) } o := otherContext{c1} c2, _ := WithCancel(o) contexts := []Context{c1, o, c2} for i, c := range contexts { if d := c.Done(); d == nil { t.Errorf("c[%d].Done() == %v want non-nil", i, d) } if e := c.Err(); e != nil { t.Errorf("c[%d].Err() == %v want nil", i, e) } select { case x := <-c.Done(): t.Errorf("<-c.Done() == %v want nothing (it should block)", x) default: } } cancel() time.Sleep(100 * time.Millisecond) // let cancelation propagate for i, c := range contexts { select { case <-c.Done(): default: t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i) } if e := c.Err(); e != Canceled { t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled) } } } func TestParentFinishesChild(t *testing.T) { // Context tree: // parent -> cancelChild // parent -> valueChild -> timerChild parent, cancel := WithCancel(Background()) cancelChild, stop := WithCancel(parent) defer stop() valueChild := WithValue(parent, "key", "value") timerChild, stop := WithTimeout(valueChild, 10000*time.Hour) defer stop() select { case x := <-parent.Done(): t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) case x := <-cancelChild.Done(): t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x) case x := <-timerChild.Done(): t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x) case x := <-valueChild.Done(): t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x) default: } // The parent's children should contain the two cancelable children. pc := parent.(*cancelCtx) cc := cancelChild.(*cancelCtx) tc := timerChild.(*timerCtx) pc.mu.Lock() if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] { t.Errorf("bad linkage: pc.children = %v, want %v and %v", pc.children, cc, tc) } pc.mu.Unlock() if p, ok := parentCancelCtx(cc.Context); !ok || p != pc { t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc) } if p, ok := parentCancelCtx(tc.Context); !ok || p != pc { t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc) } cancel() pc.mu.Lock() if len(pc.children) != 0 { t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children) } pc.mu.Unlock() // parent and children should all be finished. check := func(ctx Context, name string) { select { case <-ctx.Done(): default: t.Errorf("<-%s.Done() blocked, but shouldn't have", name) } if e := ctx.Err(); e != Canceled { t.Errorf("%s.Err() == %v want %v", name, e, Canceled) } } check(parent, "parent") check(cancelChild, "cancelChild") check(valueChild, "valueChild") check(timerChild, "timerChild") // WithCancel should return a canceled context on a canceled parent. precanceledChild := WithValue(parent, "key", "value") select { case <-precanceledChild.Done(): default: t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have") } if e := precanceledChild.Err(); e != Canceled { t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled) } } func TestChildFinishesFirst(t *testing.T) { cancelable, stop := WithCancel(Background()) defer stop() for _, parent := range []Context{Background(), cancelable} { child, cancel := WithCancel(parent) select { case x := <-parent.Done(): t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) case x := <-child.Done(): t.Errorf("<-child.Done() == %v want nothing (it should block)", x) default: } cc := child.(*cancelCtx) pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background() if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) { t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok) } if pcok { pc.mu.Lock() if len(pc.children) != 1 || !pc.children[cc] { t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc) } pc.mu.Unlock() } cancel() if pcok { pc.mu.Lock() if len(pc.children) != 0 { t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children) } pc.mu.Unlock() } // child should be finished. select { case <-child.Done(): default: t.Errorf("<-child.Done() blocked, but shouldn't have") } if e := child.Err(); e != Canceled { t.Errorf("child.Err() == %v want %v", e, Canceled) } // parent should not be finished. select { case x := <-parent.Done(): t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) default: } if e := parent.Err(); e != nil { t.Errorf("parent.Err() == %v want nil", e) } } } func testDeadline(c Context, wait time.Duration, t *testing.T) { select { case <-time.After(wait): t.Fatalf("context should have timed out") case <-c.Done(): } if e := c.Err(); e != DeadlineExceeded { t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded) } } func TestDeadline(t *testing.T) { t.Parallel() const timeUnit = 500 * time.Millisecond c, _ := WithDeadline(Background(), time.Now().Add(1*timeUnit)) if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { t.Errorf("c.String() = %q want prefix %q", got, prefix) } testDeadline(c, 2*timeUnit, t) c, _ = WithDeadline(Background(), time.Now().Add(1*timeUnit)) o := otherContext{c} testDeadline(o, 2*timeUnit, t) c, _ = WithDeadline(Background(), time.Now().Add(1*timeUnit)) o = otherContext{c} c, _ = WithDeadline(o, time.Now().Add(3*timeUnit)) testDeadline(c, 2*timeUnit, t) } func TestTimeout(t *testing.T) { t.Parallel() const timeUnit = 500 * time.Millisecond c, _ := WithTimeout(Background(), 1*timeUnit) if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { t.Errorf("c.String() = %q want prefix %q", got, prefix) } testDeadline(c, 2*timeUnit, t) c, _ = WithTimeout(Background(), 1*timeUnit) o := otherContext{c} testDeadline(o, 2*timeUnit, t) c, _ = WithTimeout(Background(), 1*timeUnit) o = otherContext{c} c, _ = WithTimeout(o, 3*timeUnit) testDeadline(c, 2*timeUnit, t) } func TestCanceledTimeout(t *testing.T) { t.Parallel() const timeUnit = 500 * time.Millisecond c, _ := WithTimeout(Background(), 2*timeUnit) o := otherContext{c} c, cancel := WithTimeout(o, 4*timeUnit) cancel() time.Sleep(1 * timeUnit) // let cancelation propagate select { case <-c.Done(): default: t.Errorf("<-c.Done() blocked, but shouldn't have") } if e := c.Err(); e != Canceled { t.Errorf("c.Err() == %v want %v", e, Canceled) } } type key1 int type key2 int var k1 = key1(1) var k2 = key2(1) // same int as k1, different type var k3 = key2(3) // same type as k2, different int func TestValues(t *testing.T) { check := func(c Context, nm, v1, v2, v3 string) { if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 { t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0) } if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 { t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0) } if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 { t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0) } } c0 := Background() check(c0, "c0", "", "", "") c1 := WithValue(Background(), k1, "c1k1") check(c1, "c1", "c1k1", "", "") if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want { t.Errorf("c.String() = %q want %q", got, want) } c2 := WithValue(c1, k2, "c2k2") check(c2, "c2", "c1k1", "c2k2", "") c3 := WithValue(c2, k3, "c3k3") check(c3, "c2", "c1k1", "c2k2", "c3k3") c4 := WithValue(c3, k1, nil) check(c4, "c4", "", "c2k2", "c3k3") o0 := otherContext{Background()} check(o0, "o0", "", "", "") o1 := otherContext{WithValue(Background(), k1, "c1k1")} check(o1, "o1", "c1k1", "", "") o2 := WithValue(o1, k2, "o2k2") check(o2, "o2", "c1k1", "o2k2", "") o3 := otherContext{c4} check(o3, "o3", "", "c2k2", "c3k3") o4 := WithValue(o3, k3, nil) check(o4, "o4", "", "c2k2", "") } func TestAllocs(t *testing.T) { bg := Background() for _, test := range []struct { desc string f func() limit float64 gccgoLimit float64 }{ { desc: "Background()", f: func() { Background() }, limit: 0, gccgoLimit: 0, }, { desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1), f: func() { c := WithValue(bg, k1, nil) c.Value(k1) }, limit: 3, gccgoLimit: 3, }, { desc: "WithTimeout(bg, 15*time.Millisecond)", f: func() { c, _ := WithTimeout(bg, 15*time.Millisecond) <-c.Done() }, limit: 8, gccgoLimit: 16, }, { desc: "WithCancel(bg)", f: func() { c, cancel := WithCancel(bg) cancel() <-c.Done() }, limit: 5, gccgoLimit: 8, }, { desc: "WithTimeout(bg, 100*time.Millisecond)", f: func() { c, cancel := WithTimeout(bg, 100*time.Millisecond) cancel() <-c.Done() }, limit: 8, gccgoLimit: 25, }, } { limit := test.limit if runtime.Compiler == "gccgo" { // gccgo does not yet do escape analysis. // TODO(iant): Remove this when gccgo does do escape analysis. limit = test.gccgoLimit } if n := testing.AllocsPerRun(100, test.f); n > limit { t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit)) } } } func TestSimultaneousCancels(t *testing.T) { root, cancel := WithCancel(Background()) m := map[Context]CancelFunc{root: cancel} q := []Context{root} // Create a tree of contexts. for len(q) != 0 && len(m) < 100 { parent := q[0] q = q[1:] for i := 0; i < 4; i++ { ctx, cancel := WithCancel(parent) m[ctx] = cancel q = append(q, ctx) } } // Start all the cancels in a random order. var wg sync.WaitGroup wg.Add(len(m)) for _, cancel := range m { go func(cancel CancelFunc) { cancel() wg.Done() }(cancel) } // Wait on all the contexts in a random order. for ctx := range m { select { case <-ctx.Done(): case <-time.After(1 * time.Second): buf := make([]byte, 10<<10) n := runtime.Stack(buf, true) t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n]) } } // Wait for all the cancel functions to return. done := make(chan struct{}) go func() { wg.Wait() close(done) }() select { case <-done: case <-time.After(1 * time.Second): buf := make([]byte, 10<<10) n := runtime.Stack(buf, true) t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n]) } } func TestInterlockedCancels(t *testing.T) { parent, cancelParent := WithCancel(Background()) child, cancelChild := WithCancel(parent) go func() { parent.Done() cancelChild() }() cancelParent() select { case <-child.Done(): case <-time.After(1 * time.Second): buf := make([]byte, 10<<10) n := runtime.Stack(buf, true) t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n]) } } func TestLayersCancel(t *testing.T) { testLayers(t, time.Now().UnixNano(), false) } func TestLayersTimeout(t *testing.T) { testLayers(t, time.Now().UnixNano(), true) } func testLayers(t *testing.T, seed int64, testTimeout bool) { rand.Seed(seed) errorf := func(format string, a ...interface{}) { t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...) } const ( timeout = 200 * time.Millisecond minLayers = 30 ) type value int var ( vals []*value cancels []CancelFunc numTimers int ctx = Background() ) for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ { switch rand.Intn(3) { case 0: v := new(value) ctx = WithValue(ctx, v, v) vals = append(vals, v) case 1: var cancel CancelFunc ctx, cancel = WithCancel(ctx) cancels = append(cancels, cancel) case 2: var cancel CancelFunc ctx, cancel = WithTimeout(ctx, timeout) cancels = append(cancels, cancel) numTimers++ } } checkValues := func(when string) { for _, key := range vals { if val := ctx.Value(key).(*value); key != val { errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key) } } } select { case <-ctx.Done(): errorf("ctx should not be canceled yet") default: } if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) { t.Errorf("ctx.String() = %q want prefix %q", s, prefix) } t.Log(ctx) checkValues("before cancel") if testTimeout { select { case <-ctx.Done(): case <-time.After(timeout + 100*time.Millisecond): errorf("ctx should have timed out") } checkValues("after timeout") } else { cancel := cancels[rand.Intn(len(cancels))] cancel() select { case <-ctx.Done(): default: errorf("ctx should be canceled") } checkValues("after cancel") } } func TestCancelRemoves(t *testing.T) { checkChildren := func(when string, ctx Context, want int) { if got := len(ctx.(*cancelCtx).children); got != want { t.Errorf("%s: context has %d children, want %d", when, got, want) } } ctx, _ := WithCancel(Background()) checkChildren("after creation", ctx, 0) _, cancel := WithCancel(ctx) checkChildren("with WithCancel child ", ctx, 1) cancel() checkChildren("after cancelling WithCancel child", ctx, 0) ctx, _ = WithCancel(Background()) checkChildren("after creation", ctx, 0) _, cancel = WithTimeout(ctx, 60*time.Minute) checkChildren("with WithTimeout child ", ctx, 1) cancel() checkChildren("after cancelling WithTimeout child", ctx, 0) } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/context/ctxhttp/000077500000000000000000000000001415442132300236115ustar00rootroot00000000000000golang-golang-x-net-0.0+git20211209.491a49a+dfsg/context/ctxhttp/ctxhttp.go000066400000000000000000000041451415442132300256420ustar00rootroot00000000000000// 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 ctxhttp provides helper functions for performing context-aware HTTP requests. package ctxhttp // import "golang.org/x/net/context/ctxhttp" import ( "context" "io" "net/http" "net/url" "strings" ) // Do sends an HTTP request with the provided http.Client and returns // an HTTP response. // // If the client is nil, http.DefaultClient is used. // // The provided ctx must be non-nil. If it is canceled or times out, // ctx.Err() will be returned. func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) { if client == nil { client = http.DefaultClient } resp, err := client.Do(req.WithContext(ctx)) // If we got an error, and the context has been canceled, // the context's error is probably more useful. if err != nil { select { case <-ctx.Done(): err = ctx.Err() default: } } return resp, err } // Get issues a GET request via the Do function. func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) { req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } return Do(ctx, client, req) } // Head issues a HEAD request via the Do function. func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) { req, err := http.NewRequest("HEAD", url, nil) if err != nil { return nil, err } return Do(ctx, client, req) } // Post issues a POST request via the Do function. func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) { req, err := http.NewRequest("POST", url, body) if err != nil { return nil, err } req.Header.Set("Content-Type", bodyType) return Do(ctx, client, req) } // PostForm issues a POST request via the Do function. func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) { return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/context/ctxhttp/ctxhttp_test.go000066400000000000000000000052061415442132300267000ustar00rootroot00000000000000// 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. //go:build !plan9 // +build !plan9 package ctxhttp import ( "context" "io" "io/ioutil" "net/http" "net/http/httptest" "testing" "time" ) func TestGo17Context(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { io.WriteString(w, "ok") })) defer ts.Close() ctx := context.Background() resp, err := Get(ctx, http.DefaultClient, ts.URL) if resp == nil || err != nil { t.Fatalf("error received from client: %v %v", err, resp) } resp.Body.Close() } const ( requestDuration = 100 * time.Millisecond requestBody = "ok" ) func okHandler(w http.ResponseWriter, r *http.Request) { time.Sleep(requestDuration) io.WriteString(w, requestBody) } func TestNoTimeout(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(okHandler)) defer ts.Close() ctx := context.Background() res, err := Get(ctx, nil, ts.URL) if err != nil { t.Fatal(err) } defer res.Body.Close() slurp, err := ioutil.ReadAll(res.Body) if err != nil { t.Fatal(err) } if string(slurp) != requestBody { t.Errorf("body = %q; want %q", slurp, requestBody) } } func TestCancelBeforeHeaders(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) blockServer := make(chan struct{}) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { cancel() <-blockServer io.WriteString(w, requestBody) })) defer ts.Close() defer close(blockServer) res, err := Get(ctx, nil, ts.URL) if err == nil { res.Body.Close() t.Fatal("Get returned unexpected nil error") } if err != context.Canceled { t.Errorf("err = %v; want %v", err, context.Canceled) } } func TestCancelAfterHangingRequest(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.(http.Flusher).Flush() <-w.(http.CloseNotifier).CloseNotify() })) defer ts.Close() ctx, cancel := context.WithCancel(context.Background()) resp, err := Get(ctx, nil, ts.URL) if err != nil { t.Fatalf("unexpected error in Get: %v", err) } // Cancel befer reading the body. // Reading Request.Body should fail, since the request was // canceled before anything was written. cancel() done := make(chan struct{}) go func() { b, err := ioutil.ReadAll(resp.Body) if len(b) != 0 || err == nil { t.Errorf(`Read got (%q, %v); want ("", error)`, b, err) } close(done) }() select { case <-time.After(1 * time.Second): t.Errorf("Test timed out") case <-done: } } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/context/go17.go000066400000000000000000000054771415442132300232340ustar00rootroot00000000000000// 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.7 // +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) } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/context/go19.go000066400000000000000000000012671415442132300232270ustar00rootroot00000000000000// 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. //go:build go1.9 // +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 golang-golang-x-net-0.0+git20211209.491a49a+dfsg/context/pre_go17.go000066400000000000000000000177151415442132300241000ustar00rootroot00000000000000// 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. //go:build !go1.7 // +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) } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/context/pre_go19.go000066400000000000000000000077411415442132300241000ustar00rootroot00000000000000// 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. //go:build !go1.9 // +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() golang-golang-x-net-0.0+git20211209.491a49a+dfsg/context/withtimeout_test.go000066400000000000000000000014761415442132300260730ustar00rootroot00000000000000// 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_test import ( "fmt" "time" "golang.org/x/net/context" ) // This example passes a context with a timeout to tell a blocking function that // it should abandon its work after the timeout elapses. func ExampleWithTimeout() { // Pass a context with a timeout to tell a blocking function that it // should abandon its work after the timeout elapses. ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) defer cancel() select { case <-time.After(1 * time.Second): fmt.Println("overslept") case <-ctx.Done(): fmt.Println(ctx.Err()) // prints "context deadline exceeded" } // Output: // context deadline exceeded } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/dict/000077500000000000000000000000001415442132300213525ustar00rootroot00000000000000golang-golang-x-net-0.0+git20211209.491a49a+dfsg/dict/dict.go000066400000000000000000000110571415442132300226300ustar00rootroot00000000000000// Copyright 2010 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 dict implements the Dictionary Server Protocol // as defined in RFC 2229. // // The dict package is frozen and is not accepting new features. package dict // import "golang.org/x/net/dict" import ( "net/textproto" "strconv" "strings" ) // A Client represents a client connection to a dictionary server. type Client struct { text *textproto.Conn } // Dial returns a new client connected to a dictionary server at // addr on the given network. func Dial(network, addr string) (*Client, error) { text, err := textproto.Dial(network, addr) if err != nil { return nil, err } _, _, err = text.ReadCodeLine(220) if err != nil { text.Close() return nil, err } return &Client{text: text}, nil } // Close closes the connection to the dictionary server. func (c *Client) Close() error { return c.text.Close() } // A Dict represents a dictionary available on the server. type Dict struct { Name string // short name of dictionary Desc string // long description } // Dicts returns a list of the dictionaries available on the server. func (c *Client) Dicts() ([]Dict, error) { id, err := c.text.Cmd("SHOW DB") if err != nil { return nil, err } c.text.StartResponse(id) defer c.text.EndResponse(id) _, _, err = c.text.ReadCodeLine(110) if err != nil { return nil, err } lines, err := c.text.ReadDotLines() if err != nil { return nil, err } _, _, err = c.text.ReadCodeLine(250) dicts := make([]Dict, len(lines)) for i := range dicts { d := &dicts[i] a, _ := fields(lines[i]) if len(a) < 2 { return nil, textproto.ProtocolError("invalid dictionary: " + lines[i]) } d.Name = a[0] d.Desc = a[1] } return dicts, err } // A Defn represents a definition. type Defn struct { Dict Dict // Dict where definition was found Word string // Word being defined Text []byte // Definition text, typically multiple lines } // Define requests the definition of the given word. // The argument dict names the dictionary to use, // the Name field of a Dict returned by Dicts. // // The special dictionary name "*" means to look in all the // server's dictionaries. // The special dictionary name "!" means to look in all the // server's dictionaries in turn, stopping after finding the word // in one of them. func (c *Client) Define(dict, word string) ([]*Defn, error) { id, err := c.text.Cmd("DEFINE %s %q", dict, word) if err != nil { return nil, err } c.text.StartResponse(id) defer c.text.EndResponse(id) _, line, err := c.text.ReadCodeLine(150) if err != nil { return nil, err } a, _ := fields(line) if len(a) < 1 { return nil, textproto.ProtocolError("malformed response: " + line) } n, err := strconv.Atoi(a[0]) if err != nil { return nil, textproto.ProtocolError("invalid definition count: " + a[0]) } def := make([]*Defn, n) for i := 0; i < n; i++ { _, line, err = c.text.ReadCodeLine(151) if err != nil { return nil, err } a, _ := fields(line) if len(a) < 3 { // skip it, to keep protocol in sync i-- n-- def = def[0:n] continue } d := &Defn{Word: a[0], Dict: Dict{a[1], a[2]}} d.Text, err = c.text.ReadDotBytes() if err != nil { return nil, err } def[i] = d } _, _, err = c.text.ReadCodeLine(250) return def, err } // Fields returns the fields in s. // Fields are space separated unquoted words // or quoted with single or double quote. func fields(s string) ([]string, error) { var v []string i := 0 for { for i < len(s) && (s[i] == ' ' || s[i] == '\t') { i++ } if i >= len(s) { break } if s[i] == '"' || s[i] == '\'' { q := s[i] // quoted string var j int for j = i + 1; ; j++ { if j >= len(s) { return nil, textproto.ProtocolError("malformed quoted string") } if s[j] == '\\' { j++ continue } if s[j] == q { j++ break } } v = append(v, unquote(s[i+1:j-1])) i = j } else { // atom var j int for j = i; j < len(s); j++ { if s[j] == ' ' || s[j] == '\t' || s[j] == '\\' || s[j] == '"' || s[j] == '\'' { break } } v = append(v, s[i:j]) i = j } if i < len(s) { c := s[i] if c != ' ' && c != '\t' { return nil, textproto.ProtocolError("quotes not on word boundaries") } } } return v, nil } func unquote(s string) string { if strings.Index(s, "\\") < 0 { return s } b := []byte(s) w := 0 for r := 0; r < len(b); r++ { c := b[r] if c == '\\' { r++ c = b[r] } b[w] = c w++ } return string(b[0:w]) } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/dns/000077500000000000000000000000001415442132300212135ustar00rootroot00000000000000golang-golang-x-net-0.0+git20211209.491a49a+dfsg/dns/dnsmessage/000077500000000000000000000000001415442132300233445ustar00rootroot00000000000000golang-golang-x-net-0.0+git20211209.491a49a+dfsg/dns/dnsmessage/example_test.go000066400000000000000000000051201415442132300263630ustar00rootroot00000000000000// 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 dnsmessage_test import ( "fmt" "net" "strings" "golang.org/x/net/dns/dnsmessage" ) func mustNewName(name string) dnsmessage.Name { n, err := dnsmessage.NewName(name) if err != nil { panic(err) } return n } func ExampleParser() { msg := dnsmessage.Message{ Header: dnsmessage.Header{Response: true, Authoritative: true}, Questions: []dnsmessage.Question{ { Name: mustNewName("foo.bar.example.com."), Type: dnsmessage.TypeA, Class: dnsmessage.ClassINET, }, { Name: mustNewName("bar.example.com."), Type: dnsmessage.TypeA, Class: dnsmessage.ClassINET, }, }, Answers: []dnsmessage.Resource{ { Header: dnsmessage.ResourceHeader{ Name: mustNewName("foo.bar.example.com."), Type: dnsmessage.TypeA, Class: dnsmessage.ClassINET, }, Body: &dnsmessage.AResource{A: [4]byte{127, 0, 0, 1}}, }, { Header: dnsmessage.ResourceHeader{ Name: mustNewName("bar.example.com."), Type: dnsmessage.TypeA, Class: dnsmessage.ClassINET, }, Body: &dnsmessage.AResource{A: [4]byte{127, 0, 0, 2}}, }, }, } buf, err := msg.Pack() if err != nil { panic(err) } wantName := "bar.example.com." var p dnsmessage.Parser if _, err := p.Start(buf); err != nil { panic(err) } for { q, err := p.Question() if err == dnsmessage.ErrSectionDone { break } if err != nil { panic(err) } if q.Name.String() != wantName { continue } fmt.Println("Found question for name", wantName) if err := p.SkipAllQuestions(); err != nil { panic(err) } break } var gotIPs []net.IP for { h, err := p.AnswerHeader() if err == dnsmessage.ErrSectionDone { break } if err != nil { panic(err) } if (h.Type != dnsmessage.TypeA && h.Type != dnsmessage.TypeAAAA) || h.Class != dnsmessage.ClassINET { continue } if !strings.EqualFold(h.Name.String(), wantName) { if err := p.SkipAnswer(); err != nil { panic(err) } continue } switch h.Type { case dnsmessage.TypeA: r, err := p.AResource() if err != nil { panic(err) } gotIPs = append(gotIPs, r.A[:]) case dnsmessage.TypeAAAA: r, err := p.AAAAResource() if err != nil { panic(err) } gotIPs = append(gotIPs, r.AAAA[:]) } } fmt.Printf("Found A/AAAA records for name %s: %v\n", wantName, gotIPs) // Output: // Found question for name bar.example.com. // Found A/AAAA records for name bar.example.com.: [127.0.0.2] } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/dns/dnsmessage/message.go000066400000000000000000002070441415442132300253260ustar00rootroot00000000000000// 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. // Package dnsmessage provides a mostly RFC 1035 compliant implementation of // DNS message packing and unpacking. // // The package also supports messages with Extension Mechanisms for DNS // (EDNS(0)) as defined in RFC 6891. // // This implementation is designed to minimize heap allocations and avoid // unnecessary packing and unpacking as much as possible. package dnsmessage import ( "errors" ) // Message formats // A Type is a type of DNS request and response. type Type uint16 const ( // ResourceHeader.Type and Question.Type TypeA Type = 1 TypeNS Type = 2 TypeCNAME Type = 5 TypeSOA Type = 6 TypePTR Type = 12 TypeMX Type = 15 TypeTXT Type = 16 TypeAAAA Type = 28 TypeSRV Type = 33 TypeOPT Type = 41 // Question.Type TypeWKS Type = 11 TypeHINFO Type = 13 TypeMINFO Type = 14 TypeAXFR Type = 252 TypeALL Type = 255 ) var typeNames = map[Type]string{ TypeA: "TypeA", TypeNS: "TypeNS", TypeCNAME: "TypeCNAME", TypeSOA: "TypeSOA", TypePTR: "TypePTR", TypeMX: "TypeMX", TypeTXT: "TypeTXT", TypeAAAA: "TypeAAAA", TypeSRV: "TypeSRV", TypeOPT: "TypeOPT", TypeWKS: "TypeWKS", TypeHINFO: "TypeHINFO", TypeMINFO: "TypeMINFO", TypeAXFR: "TypeAXFR", TypeALL: "TypeALL", } // String implements fmt.Stringer.String. func (t Type) String() string { if n, ok := typeNames[t]; ok { return n } return printUint16(uint16(t)) } // GoString implements fmt.GoStringer.GoString. func (t Type) GoString() string { if n, ok := typeNames[t]; ok { return "dnsmessage." + n } return printUint16(uint16(t)) } // A Class is a type of network. type Class uint16 const ( // ResourceHeader.Class and Question.Class ClassINET Class = 1 ClassCSNET Class = 2 ClassCHAOS Class = 3 ClassHESIOD Class = 4 // Question.Class ClassANY Class = 255 ) var classNames = map[Class]string{ ClassINET: "ClassINET", ClassCSNET: "ClassCSNET", ClassCHAOS: "ClassCHAOS", ClassHESIOD: "ClassHESIOD", ClassANY: "ClassANY", } // String implements fmt.Stringer.String. func (c Class) String() string { if n, ok := classNames[c]; ok { return n } return printUint16(uint16(c)) } // GoString implements fmt.GoStringer.GoString. func (c Class) GoString() string { if n, ok := classNames[c]; ok { return "dnsmessage." + n } return printUint16(uint16(c)) } // An OpCode is a DNS operation code. type OpCode uint16 // GoString implements fmt.GoStringer.GoString. func (o OpCode) GoString() string { return printUint16(uint16(o)) } // An RCode is a DNS response status code. type RCode uint16 // Header.RCode values. const ( RCodeSuccess RCode = 0 // NoError RCodeFormatError RCode = 1 // FormErr RCodeServerFailure RCode = 2 // ServFail RCodeNameError RCode = 3 // NXDomain RCodeNotImplemented RCode = 4 // NotImp RCodeRefused RCode = 5 // Refused ) var rCodeNames = map[RCode]string{ RCodeSuccess: "RCodeSuccess", RCodeFormatError: "RCodeFormatError", RCodeServerFailure: "RCodeServerFailure", RCodeNameError: "RCodeNameError", RCodeNotImplemented: "RCodeNotImplemented", RCodeRefused: "RCodeRefused", } // String implements fmt.Stringer.String. func (r RCode) String() string { if n, ok := rCodeNames[r]; ok { return n } return printUint16(uint16(r)) } // GoString implements fmt.GoStringer.GoString. func (r RCode) GoString() string { if n, ok := rCodeNames[r]; ok { return "dnsmessage." + n } return printUint16(uint16(r)) } func printPaddedUint8(i uint8) string { b := byte(i) return string([]byte{ b/100 + '0', b/10%10 + '0', b%10 + '0', }) } func printUint8Bytes(buf []byte, i uint8) []byte { b := byte(i) if i >= 100 { buf = append(buf, b/100+'0') } if i >= 10 { buf = append(buf, b/10%10+'0') } return append(buf, b%10+'0') } func printByteSlice(b []byte) string { if len(b) == 0 { return "" } buf := make([]byte, 0, 5*len(b)) buf = printUint8Bytes(buf, uint8(b[0])) for _, n := range b[1:] { buf = append(buf, ',', ' ') buf = printUint8Bytes(buf, uint8(n)) } return string(buf) } const hexDigits = "0123456789abcdef" func printString(str []byte) string { buf := make([]byte, 0, len(str)) for i := 0; i < len(str); i++ { c := str[i] if c == '.' || c == '-' || c == ' ' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { buf = append(buf, c) continue } upper := c >> 4 lower := (c << 4) >> 4 buf = append( buf, '\\', 'x', hexDigits[upper], hexDigits[lower], ) } return string(buf) } func printUint16(i uint16) string { return printUint32(uint32(i)) } func printUint32(i uint32) string { // Max value is 4294967295. buf := make([]byte, 10) for b, d := buf, uint32(1000000000); d > 0; d /= 10 { b[0] = byte(i/d%10 + '0') if b[0] == '0' && len(b) == len(buf) && len(buf) > 1 { buf = buf[1:] } b = b[1:] i %= d } return string(buf) } func printBool(b bool) string { if b { return "true" } return "false" } var ( // ErrNotStarted indicates that the prerequisite information isn't // available yet because the previous records haven't been appropriately // parsed, skipped or finished. ErrNotStarted = errors.New("parsing/packing of this type isn't available yet") // ErrSectionDone indicated that all records in the section have been // parsed or finished. ErrSectionDone = errors.New("parsing/packing of this section has completed") errBaseLen = errors.New("insufficient data for base length type") errCalcLen = errors.New("insufficient data for calculated length type") errReserved = errors.New("segment prefix is reserved") errTooManyPtr = errors.New("too many pointers (>10)") errInvalidPtr = errors.New("invalid pointer") errNilResouceBody = errors.New("nil resource body") errResourceLen = errors.New("insufficient data for resource body length") errSegTooLong = errors.New("segment length too long") errZeroSegLen = errors.New("zero length segment") errResTooLong = errors.New("resource length too long") errTooManyQuestions = errors.New("too many Questions to pack (>65535)") errTooManyAnswers = errors.New("too many Answers to pack (>65535)") errTooManyAuthorities = errors.New("too many Authorities to pack (>65535)") errTooManyAdditionals = errors.New("too many Additionals to pack (>65535)") errNonCanonicalName = errors.New("name is not in canonical format (it must end with a .)") errStringTooLong = errors.New("character string exceeds maximum length (255)") errCompressedSRV = errors.New("compressed name in SRV resource data") ) // Internal constants. const ( // packStartingCap is the default initial buffer size allocated during // packing. // // The starting capacity doesn't matter too much, but most DNS responses // Will be <= 512 bytes as it is the limit for DNS over UDP. packStartingCap = 512 // uint16Len is the length (in bytes) of a uint16. uint16Len = 2 // uint32Len is the length (in bytes) of a uint32. uint32Len = 4 // headerLen is the length (in bytes) of a DNS header. // // A header is comprised of 6 uint16s and no padding. headerLen = 6 * uint16Len ) type nestedError struct { // s is the current level's error message. s string // err is the nested error. err error } // nestedError implements error.Error. func (e *nestedError) Error() string { return e.s + ": " + e.err.Error() } // Header is a representation of a DNS message header. type Header struct { ID uint16 Response bool OpCode OpCode Authoritative bool Truncated bool RecursionDesired bool RecursionAvailable bool RCode RCode } func (m *Header) pack() (id uint16, bits uint16) { id = m.ID bits = uint16(m.OpCode)<<11 | uint16(m.RCode) if m.RecursionAvailable { bits |= headerBitRA } if m.RecursionDesired { bits |= headerBitRD } if m.Truncated { bits |= headerBitTC } if m.Authoritative { bits |= headerBitAA } if m.Response { bits |= headerBitQR } return } // GoString implements fmt.GoStringer.GoString. func (m *Header) GoString() string { return "dnsmessage.Header{" + "ID: " + printUint16(m.ID) + ", " + "Response: " + printBool(m.Response) + ", " + "OpCode: " + m.OpCode.GoString() + ", " + "Authoritative: " + printBool(m.Authoritative) + ", " + "Truncated: " + printBool(m.Truncated) + ", " + "RecursionDesired: " + printBool(m.RecursionDesired) + ", " + "RecursionAvailable: " + printBool(m.RecursionAvailable) + ", " + "RCode: " + m.RCode.GoString() + "}" } // Message is a representation of a DNS message. type Message struct { Header Questions []Question Answers []Resource Authorities []Resource Additionals []Resource } type section uint8 const ( sectionNotStarted section = iota sectionHeader sectionQuestions sectionAnswers sectionAuthorities sectionAdditionals sectionDone headerBitQR = 1 << 15 // query/response (response=1) headerBitAA = 1 << 10 // authoritative headerBitTC = 1 << 9 // truncated headerBitRD = 1 << 8 // recursion desired headerBitRA = 1 << 7 // recursion available ) var sectionNames = map[section]string{ sectionHeader: "header", sectionQuestions: "Question", sectionAnswers: "Answer", sectionAuthorities: "Authority", sectionAdditionals: "Additional", } // header is the wire format for a DNS message header. type header struct { id uint16 bits uint16 questions uint16 answers uint16 authorities uint16 additionals uint16 } func (h *header) count(sec section) uint16 { switch sec { case sectionQuestions: return h.questions case sectionAnswers: return h.answers case sectionAuthorities: return h.authorities case sectionAdditionals: return h.additionals } return 0 } // pack appends the wire format of the header to msg. func (h *header) pack(msg []byte) []byte { msg = packUint16(msg, h.id) msg = packUint16(msg, h.bits) msg = packUint16(msg, h.questions) msg = packUint16(msg, h.answers) msg = packUint16(msg, h.authorities) return packUint16(msg, h.additionals) } func (h *header) unpack(msg []byte, off int) (int, error) { newOff := off var err error if h.id, newOff, err = unpackUint16(msg, newOff); err != nil { return off, &nestedError{"id", err} } if h.bits, newOff, err = unpackUint16(msg, newOff); err != nil { return off, &nestedError{"bits", err} } if h.questions, newOff, err = unpackUint16(msg, newOff); err != nil { return off, &nestedError{"questions", err} } if h.answers, newOff, err = unpackUint16(msg, newOff); err != nil { return off, &nestedError{"answers", err} } if h.authorities, newOff, err = unpackUint16(msg, newOff); err != nil { return off, &nestedError{"authorities", err} } if h.additionals, newOff, err = unpackUint16(msg, newOff); err != nil { return off, &nestedError{"additionals", err} } return newOff, nil } func (h *header) header() Header { return Header{ ID: h.id, Response: (h.bits & headerBitQR) != 0, OpCode: OpCode(h.bits>>11) & 0xF, Authoritative: (h.bits & headerBitAA) != 0, Truncated: (h.bits & headerBitTC) != 0, RecursionDesired: (h.bits & headerBitRD) != 0, RecursionAvailable: (h.bits & headerBitRA) != 0, RCode: RCode(h.bits & 0xF), } } // A Resource is a DNS resource record. type Resource struct { Header ResourceHeader Body ResourceBody } func (r *Resource) GoString() string { return "dnsmessage.Resource{" + "Header: " + r.Header.GoString() + ", Body: &" + r.Body.GoString() + "}" } // A ResourceBody is a DNS resource record minus the header. type ResourceBody interface { // pack packs a Resource except for its header. pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) // realType returns the actual type of the Resource. This is used to // fill in the header Type field. realType() Type // GoString implements fmt.GoStringer.GoString. GoString() string } // pack appends the wire format of the Resource to msg. func (r *Resource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { if r.Body == nil { return msg, errNilResouceBody } oldMsg := msg r.Header.Type = r.Body.realType() msg, lenOff, err := r.Header.pack(msg, compression, compressionOff) if err != nil { return msg, &nestedError{"ResourceHeader", err} } preLen := len(msg) msg, err = r.Body.pack(msg, compression, compressionOff) if err != nil { return msg, &nestedError{"content", err} } if err := r.Header.fixLen(msg, lenOff, preLen); err != nil { return oldMsg, err } return msg, nil } // A Parser allows incrementally parsing a DNS message. // // When parsing is started, the Header is parsed. Next, each Question can be // either parsed or skipped. Alternatively, all Questions can be skipped at // once. When all Questions have been parsed, attempting to parse Questions // will return (nil, nil) and attempting to skip Questions will return // (true, nil). After all Questions have been either parsed or skipped, all // Answers, Authorities and Additionals can be either parsed or skipped in the // same way, and each type of Resource must be fully parsed or skipped before // proceeding to the next type of Resource. // // Note that there is no requirement to fully skip or parse the message. type Parser struct { msg []byte header header section section off int index int resHeaderValid bool resHeader ResourceHeader } // Start parses the header and enables the parsing of Questions. func (p *Parser) Start(msg []byte) (Header, error) { if p.msg != nil { *p = Parser{} } p.msg = msg var err error if p.off, err = p.header.unpack(msg, 0); err != nil { return Header{}, &nestedError{"unpacking header", err} } p.section = sectionQuestions return p.header.header(), nil } func (p *Parser) checkAdvance(sec section) error { if p.section < sec { return ErrNotStarted } if p.section > sec { return ErrSectionDone } p.resHeaderValid = false if p.index == int(p.header.count(sec)) { p.index = 0 p.section++ return ErrSectionDone } return nil } func (p *Parser) resource(sec section) (Resource, error) { var r Resource var err error r.Header, err = p.resourceHeader(sec) if err != nil { return r, err } p.resHeaderValid = false r.Body, p.off, err = unpackResourceBody(p.msg, p.off, r.Header) if err != nil { return Resource{}, &nestedError{"unpacking " + sectionNames[sec], err} } p.index++ return r, nil } func (p *Parser) resourceHeader(sec section) (ResourceHeader, error) { if p.resHeaderValid { return p.resHeader, nil } if err := p.checkAdvance(sec); err != nil { return ResourceHeader{}, err } var hdr ResourceHeader off, err := hdr.unpack(p.msg, p.off) if err != nil { return ResourceHeader{}, err } p.resHeaderValid = true p.resHeader = hdr p.off = off return hdr, nil } func (p *Parser) skipResource(sec section) error { if p.resHeaderValid { newOff := p.off + int(p.resHeader.Length) if newOff > len(p.msg) { return errResourceLen } p.off = newOff p.resHeaderValid = false p.index++ return nil } if err := p.checkAdvance(sec); err != nil { return err } var err error p.off, err = skipResource(p.msg, p.off) if err != nil { return &nestedError{"skipping: " + sectionNames[sec], err} } p.index++ return nil } // Question parses a single Question. func (p *Parser) Question() (Question, error) { if err := p.checkAdvance(sectionQuestions); err != nil { return Question{}, err } var name Name off, err := name.unpack(p.msg, p.off) if err != nil { return Question{}, &nestedError{"unpacking Question.Name", err} } typ, off, err := unpackType(p.msg, off) if err != nil { return Question{}, &nestedError{"unpacking Question.Type", err} } class, off, err := unpackClass(p.msg, off) if err != nil { return Question{}, &nestedError{"unpacking Question.Class", err} } p.off = off p.index++ return Question{name, typ, class}, nil } // AllQuestions parses all Questions. func (p *Parser) AllQuestions() ([]Question, error) { // Multiple questions are valid according to the spec, // but servers don't actually support them. There will // be at most one question here. // // Do not pre-allocate based on info in p.header, since // the data is untrusted. qs := []Question{} for { q, err := p.Question() if err == ErrSectionDone { return qs, nil } if err != nil { return nil, err } qs = append(qs, q) } } // SkipQuestion skips a single Question. func (p *Parser) SkipQuestion() error { if err := p.checkAdvance(sectionQuestions); err != nil { return err } off, err := skipName(p.msg, p.off) if err != nil { return &nestedError{"skipping Question Name", err} } if off, err = skipType(p.msg, off); err != nil { return &nestedError{"skipping Question Type", err} } if off, err = skipClass(p.msg, off); err != nil { return &nestedError{"skipping Question Class", err} } p.off = off p.index++ return nil } // SkipAllQuestions skips all Questions. func (p *Parser) SkipAllQuestions() error { for { if err := p.SkipQuestion(); err == ErrSectionDone { return nil } else if err != nil { return err } } } // AnswerHeader parses a single Answer ResourceHeader. func (p *Parser) AnswerHeader() (ResourceHeader, error) { return p.resourceHeader(sectionAnswers) } // Answer parses a single Answer Resource. func (p *Parser) Answer() (Resource, error) { return p.resource(sectionAnswers) } // AllAnswers parses all Answer Resources. func (p *Parser) AllAnswers() ([]Resource, error) { // The most common query is for A/AAAA, which usually returns // a handful of IPs. // // Pre-allocate up to a certain limit, since p.header is // untrusted data. n := int(p.header.answers) if n > 20 { n = 20 } as := make([]Resource, 0, n) for { a, err := p.Answer() if err == ErrSectionDone { return as, nil } if err != nil { return nil, err } as = append(as, a) } } // SkipAnswer skips a single Answer Resource. func (p *Parser) SkipAnswer() error { return p.skipResource(sectionAnswers) } // SkipAllAnswers skips all Answer Resources. func (p *Parser) SkipAllAnswers() error { for { if err := p.SkipAnswer(); err == ErrSectionDone { return nil } else if err != nil { return err } } } // AuthorityHeader parses a single Authority ResourceHeader. func (p *Parser) AuthorityHeader() (ResourceHeader, error) { return p.resourceHeader(sectionAuthorities) } // Authority parses a single Authority Resource. func (p *Parser) Authority() (Resource, error) { return p.resource(sectionAuthorities) } // AllAuthorities parses all Authority Resources. func (p *Parser) AllAuthorities() ([]Resource, error) { // Authorities contains SOA in case of NXDOMAIN and friends, // otherwise it is empty. // // Pre-allocate up to a certain limit, since p.header is // untrusted data. n := int(p.header.authorities) if n > 10 { n = 10 } as := make([]Resource, 0, n) for { a, err := p.Authority() if err == ErrSectionDone { return as, nil } if err != nil { return nil, err } as = append(as, a) } } // SkipAuthority skips a single Authority Resource. func (p *Parser) SkipAuthority() error { return p.skipResource(sectionAuthorities) } // SkipAllAuthorities skips all Authority Resources. func (p *Parser) SkipAllAuthorities() error { for { if err := p.SkipAuthority(); err == ErrSectionDone { return nil } else if err != nil { return err } } } // AdditionalHeader parses a single Additional ResourceHeader. func (p *Parser) AdditionalHeader() (ResourceHeader, error) { return p.resourceHeader(sectionAdditionals) } // Additional parses a single Additional Resource. func (p *Parser) Additional() (Resource, error) { return p.resource(sectionAdditionals) } // AllAdditionals parses all Additional Resources. func (p *Parser) AllAdditionals() ([]Resource, error) { // Additionals usually contain OPT, and sometimes A/AAAA // glue records. // // Pre-allocate up to a certain limit, since p.header is // untrusted data. n := int(p.header.additionals) if n > 10 { n = 10 } as := make([]Resource, 0, n) for { a, err := p.Additional() if err == ErrSectionDone { return as, nil } if err != nil { return nil, err } as = append(as, a) } } // SkipAdditional skips a single Additional Resource. func (p *Parser) SkipAdditional() error { return p.skipResource(sectionAdditionals) } // SkipAllAdditionals skips all Additional Resources. func (p *Parser) SkipAllAdditionals() error { for { if err := p.SkipAdditional(); err == ErrSectionDone { return nil } else if err != nil { return err } } } // CNAMEResource parses a single CNAMEResource. // // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) CNAMEResource() (CNAMEResource, error) { if !p.resHeaderValid || p.resHeader.Type != TypeCNAME { return CNAMEResource{}, ErrNotStarted } r, err := unpackCNAMEResource(p.msg, p.off) if err != nil { return CNAMEResource{}, err } p.off += int(p.resHeader.Length) p.resHeaderValid = false p.index++ return r, nil } // MXResource parses a single MXResource. // // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) MXResource() (MXResource, error) { if !p.resHeaderValid || p.resHeader.Type != TypeMX { return MXResource{}, ErrNotStarted } r, err := unpackMXResource(p.msg, p.off) if err != nil { return MXResource{}, err } p.off += int(p.resHeader.Length) p.resHeaderValid = false p.index++ return r, nil } // NSResource parses a single NSResource. // // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) NSResource() (NSResource, error) { if !p.resHeaderValid || p.resHeader.Type != TypeNS { return NSResource{}, ErrNotStarted } r, err := unpackNSResource(p.msg, p.off) if err != nil { return NSResource{}, err } p.off += int(p.resHeader.Length) p.resHeaderValid = false p.index++ return r, nil } // PTRResource parses a single PTRResource. // // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) PTRResource() (PTRResource, error) { if !p.resHeaderValid || p.resHeader.Type != TypePTR { return PTRResource{}, ErrNotStarted } r, err := unpackPTRResource(p.msg, p.off) if err != nil { return PTRResource{}, err } p.off += int(p.resHeader.Length) p.resHeaderValid = false p.index++ return r, nil } // SOAResource parses a single SOAResource. // // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) SOAResource() (SOAResource, error) { if !p.resHeaderValid || p.resHeader.Type != TypeSOA { return SOAResource{}, ErrNotStarted } r, err := unpackSOAResource(p.msg, p.off) if err != nil { return SOAResource{}, err } p.off += int(p.resHeader.Length) p.resHeaderValid = false p.index++ return r, nil } // TXTResource parses a single TXTResource. // // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) TXTResource() (TXTResource, error) { if !p.resHeaderValid || p.resHeader.Type != TypeTXT { return TXTResource{}, ErrNotStarted } r, err := unpackTXTResource(p.msg, p.off, p.resHeader.Length) if err != nil { return TXTResource{}, err } p.off += int(p.resHeader.Length) p.resHeaderValid = false p.index++ return r, nil } // SRVResource parses a single SRVResource. // // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) SRVResource() (SRVResource, error) { if !p.resHeaderValid || p.resHeader.Type != TypeSRV { return SRVResource{}, ErrNotStarted } r, err := unpackSRVResource(p.msg, p.off) if err != nil { return SRVResource{}, err } p.off += int(p.resHeader.Length) p.resHeaderValid = false p.index++ return r, nil } // AResource parses a single AResource. // // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) AResource() (AResource, error) { if !p.resHeaderValid || p.resHeader.Type != TypeA { return AResource{}, ErrNotStarted } r, err := unpackAResource(p.msg, p.off) if err != nil { return AResource{}, err } p.off += int(p.resHeader.Length) p.resHeaderValid = false p.index++ return r, nil } // AAAAResource parses a single AAAAResource. // // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) AAAAResource() (AAAAResource, error) { if !p.resHeaderValid || p.resHeader.Type != TypeAAAA { return AAAAResource{}, ErrNotStarted } r, err := unpackAAAAResource(p.msg, p.off) if err != nil { return AAAAResource{}, err } p.off += int(p.resHeader.Length) p.resHeaderValid = false p.index++ return r, nil } // OPTResource parses a single OPTResource. // // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) OPTResource() (OPTResource, error) { if !p.resHeaderValid || p.resHeader.Type != TypeOPT { return OPTResource{}, ErrNotStarted } r, err := unpackOPTResource(p.msg, p.off, p.resHeader.Length) if err != nil { return OPTResource{}, err } p.off += int(p.resHeader.Length) p.resHeaderValid = false p.index++ return r, nil } // UnknownResource parses a single UnknownResource. // // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) UnknownResource() (UnknownResource, error) { if !p.resHeaderValid { return UnknownResource{}, ErrNotStarted } r, err := unpackUnknownResource(p.resHeader.Type, p.msg, p.off, p.resHeader.Length) if err != nil { return UnknownResource{}, err } p.off += int(p.resHeader.Length) p.resHeaderValid = false p.index++ return r, nil } // Unpack parses a full Message. func (m *Message) Unpack(msg []byte) error { var p Parser var err error if m.Header, err = p.Start(msg); err != nil { return err } if m.Questions, err = p.AllQuestions(); err != nil { return err } if m.Answers, err = p.AllAnswers(); err != nil { return err } if m.Authorities, err = p.AllAuthorities(); err != nil { return err } if m.Additionals, err = p.AllAdditionals(); err != nil { return err } return nil } // Pack packs a full Message. func (m *Message) Pack() ([]byte, error) { return m.AppendPack(make([]byte, 0, packStartingCap)) } // AppendPack is like Pack but appends the full Message to b and returns the // extended buffer. func (m *Message) AppendPack(b []byte) ([]byte, error) { // Validate the lengths. It is very unlikely that anyone will try to // pack more than 65535 of any particular type, but it is possible and // we should fail gracefully. if len(m.Questions) > int(^uint16(0)) { return nil, errTooManyQuestions } if len(m.Answers) > int(^uint16(0)) { return nil, errTooManyAnswers } if len(m.Authorities) > int(^uint16(0)) { return nil, errTooManyAuthorities } if len(m.Additionals) > int(^uint16(0)) { return nil, errTooManyAdditionals } var h header h.id, h.bits = m.Header.pack() h.questions = uint16(len(m.Questions)) h.answers = uint16(len(m.Answers)) h.authorities = uint16(len(m.Authorities)) h.additionals = uint16(len(m.Additionals)) compressionOff := len(b) msg := h.pack(b) // RFC 1035 allows (but does not require) compression for packing. RFC // 1035 requires unpacking implementations to support compression, so // unconditionally enabling it is fine. // // DNS lookups are typically done over UDP, and RFC 1035 states that UDP // DNS messages can be a maximum of 512 bytes long. Without compression, // many DNS response messages are over this limit, so enabling // compression will help ensure compliance. compression := map[string]int{} for i := range m.Questions { var err error if msg, err = m.Questions[i].pack(msg, compression, compressionOff); err != nil { return nil, &nestedError{"packing Question", err} } } for i := range m.Answers { var err error if msg, err = m.Answers[i].pack(msg, compression, compressionOff); err != nil { return nil, &nestedError{"packing Answer", err} } } for i := range m.Authorities { var err error if msg, err = m.Authorities[i].pack(msg, compression, compressionOff); err != nil { return nil, &nestedError{"packing Authority", err} } } for i := range m.Additionals { var err error if msg, err = m.Additionals[i].pack(msg, compression, compressionOff); err != nil { return nil, &nestedError{"packing Additional", err} } } return msg, nil } // GoString implements fmt.GoStringer.GoString. func (m *Message) GoString() string { s := "dnsmessage.Message{Header: " + m.Header.GoString() + ", " + "Questions: []dnsmessage.Question{" if len(m.Questions) > 0 { s += m.Questions[0].GoString() for _, q := range m.Questions[1:] { s += ", " + q.GoString() } } s += "}, Answers: []dnsmessage.Resource{" if len(m.Answers) > 0 { s += m.Answers[0].GoString() for _, a := range m.Answers[1:] { s += ", " + a.GoString() } } s += "}, Authorities: []dnsmessage.Resource{" if len(m.Authorities) > 0 { s += m.Authorities[0].GoString() for _, a := range m.Authorities[1:] { s += ", " + a.GoString() } } s += "}, Additionals: []dnsmessage.Resource{" if len(m.Additionals) > 0 { s += m.Additionals[0].GoString() for _, a := range m.Additionals[1:] { s += ", " + a.GoString() } } return s + "}}" } // A Builder allows incrementally packing a DNS message. // // Example usage: // buf := make([]byte, 2, 514) // b := NewBuilder(buf, Header{...}) // b.EnableCompression() // // Optionally start a section and add things to that section. // // Repeat adding sections as necessary. // buf, err := b.Finish() // // If err is nil, buf[2:] will contain the built bytes. type Builder struct { // msg is the storage for the message being built. msg []byte // section keeps track of the current section being built. section section // header keeps track of what should go in the header when Finish is // called. header header // start is the starting index of the bytes allocated in msg for header. start int // compression is a mapping from name suffixes to their starting index // in msg. compression map[string]int } // NewBuilder creates a new builder with compression disabled. // // Note: Most users will want to immediately enable compression with the // EnableCompression method. See that method's comment for why you may or may // not want to enable compression. // // The DNS message is appended to the provided initial buffer buf (which may be // nil) as it is built. The final message is returned by the (*Builder).Finish // method, which includes buf[:len(buf)] and may return the same underlying // array if there was sufficient capacity in the slice. func NewBuilder(buf []byte, h Header) Builder { if buf == nil { buf = make([]byte, 0, packStartingCap) } b := Builder{msg: buf, start: len(buf)} b.header.id, b.header.bits = h.pack() var hb [headerLen]byte b.msg = append(b.msg, hb[:]...) b.section = sectionHeader return b } // EnableCompression enables compression in the Builder. // // Leaving compression disabled avoids compression related allocations, but can // result in larger message sizes. Be careful with this mode as it can cause // messages to exceed the UDP size limit. // // According to RFC 1035, section 4.1.4, the use of compression is optional, but // all implementations must accept both compressed and uncompressed DNS // messages. // // Compression should be enabled before any sections are added for best results. func (b *Builder) EnableCompression() { b.compression = map[string]int{} } func (b *Builder) startCheck(s section) error { if b.section <= sectionNotStarted { return ErrNotStarted } if b.section > s { return ErrSectionDone } return nil } // StartQuestions prepares the builder for packing Questions. func (b *Builder) StartQuestions() error { if err := b.startCheck(sectionQuestions); err != nil { return err } b.section = sectionQuestions return nil } // StartAnswers prepares the builder for packing Answers. func (b *Builder) StartAnswers() error { if err := b.startCheck(sectionAnswers); err != nil { return err } b.section = sectionAnswers return nil } // StartAuthorities prepares the builder for packing Authorities. func (b *Builder) StartAuthorities() error { if err := b.startCheck(sectionAuthorities); err != nil { return err } b.section = sectionAuthorities return nil } // StartAdditionals prepares the builder for packing Additionals. func (b *Builder) StartAdditionals() error { if err := b.startCheck(sectionAdditionals); err != nil { return err } b.section = sectionAdditionals return nil } func (b *Builder) incrementSectionCount() error { var count *uint16 var err error switch b.section { case sectionQuestions: count = &b.header.questions err = errTooManyQuestions case sectionAnswers: count = &b.header.answers err = errTooManyAnswers case sectionAuthorities: count = &b.header.authorities err = errTooManyAuthorities case sectionAdditionals: count = &b.header.additionals err = errTooManyAdditionals } if *count == ^uint16(0) { return err } *count++ return nil } // Question adds a single Question. func (b *Builder) Question(q Question) error { if b.section < sectionQuestions { return ErrNotStarted } if b.section > sectionQuestions { return ErrSectionDone } msg, err := q.pack(b.msg, b.compression, b.start) if err != nil { return err } if err := b.incrementSectionCount(); err != nil { return err } b.msg = msg return nil } func (b *Builder) checkResourceSection() error { if b.section < sectionAnswers { return ErrNotStarted } if b.section > sectionAdditionals { return ErrSectionDone } return nil } // CNAMEResource adds a single CNAMEResource. func (b *Builder) CNAMEResource(h ResourceHeader, r CNAMEResource) error { if err := b.checkResourceSection(); err != nil { return err } h.Type = r.realType() msg, lenOff, err := h.pack(b.msg, b.compression, b.start) if err != nil { return &nestedError{"ResourceHeader", err} } preLen := len(msg) if msg, err = r.pack(msg, b.compression, b.start); err != nil { return &nestedError{"CNAMEResource body", err} } if err := h.fixLen(msg, lenOff, preLen); err != nil { return err } if err := b.incrementSectionCount(); err != nil { return err } b.msg = msg return nil } // MXResource adds a single MXResource. func (b *Builder) MXResource(h ResourceHeader, r MXResource) error { if err := b.checkResourceSection(); err != nil { return err } h.Type = r.realType() msg, lenOff, err := h.pack(b.msg, b.compression, b.start) if err != nil { return &nestedError{"ResourceHeader", err} } preLen := len(msg) if msg, err = r.pack(msg, b.compression, b.start); err != nil { return &nestedError{"MXResource body", err} } if err := h.fixLen(msg, lenOff, preLen); err != nil { return err } if err := b.incrementSectionCount(); err != nil { return err } b.msg = msg return nil } // NSResource adds a single NSResource. func (b *Builder) NSResource(h ResourceHeader, r NSResource) error { if err := b.checkResourceSection(); err != nil { return err } h.Type = r.realType() msg, lenOff, err := h.pack(b.msg, b.compression, b.start) if err != nil { return &nestedError{"ResourceHeader", err} } preLen := len(msg) if msg, err = r.pack(msg, b.compression, b.start); err != nil { return &nestedError{"NSResource body", err} } if err := h.fixLen(msg, lenOff, preLen); err != nil { return err } if err := b.incrementSectionCount(); err != nil { return err } b.msg = msg return nil } // PTRResource adds a single PTRResource. func (b *Builder) PTRResource(h ResourceHeader, r PTRResource) error { if err := b.checkResourceSection(); err != nil { return err } h.Type = r.realType() msg, lenOff, err := h.pack(b.msg, b.compression, b.start) if err != nil { return &nestedError{"ResourceHeader", err} } preLen := len(msg) if msg, err = r.pack(msg, b.compression, b.start); err != nil { return &nestedError{"PTRResource body", err} } if err := h.fixLen(msg, lenOff, preLen); err != nil { return err } if err := b.incrementSectionCount(); err != nil { return err } b.msg = msg return nil } // SOAResource adds a single SOAResource. func (b *Builder) SOAResource(h ResourceHeader, r SOAResource) error { if err := b.checkResourceSection(); err != nil { return err } h.Type = r.realType() msg, lenOff, err := h.pack(b.msg, b.compression, b.start) if err != nil { return &nestedError{"ResourceHeader", err} } preLen := len(msg) if msg, err = r.pack(msg, b.compression, b.start); err != nil { return &nestedError{"SOAResource body", err} } if err := h.fixLen(msg, lenOff, preLen); err != nil { return err } if err := b.incrementSectionCount(); err != nil { return err } b.msg = msg return nil } // TXTResource adds a single TXTResource. func (b *Builder) TXTResource(h ResourceHeader, r TXTResource) error { if err := b.checkResourceSection(); err != nil { return err } h.Type = r.realType() msg, lenOff, err := h.pack(b.msg, b.compression, b.start) if err != nil { return &nestedError{"ResourceHeader", err} } preLen := len(msg) if msg, err = r.pack(msg, b.compression, b.start); err != nil { return &nestedError{"TXTResource body", err} } if err := h.fixLen(msg, lenOff, preLen); err != nil { return err } if err := b.incrementSectionCount(); err != nil { return err } b.msg = msg return nil } // SRVResource adds a single SRVResource. func (b *Builder) SRVResource(h ResourceHeader, r SRVResource) error { if err := b.checkResourceSection(); err != nil { return err } h.Type = r.realType() msg, lenOff, err := h.pack(b.msg, b.compression, b.start) if err != nil { return &nestedError{"ResourceHeader", err} } preLen := len(msg) if msg, err = r.pack(msg, b.compression, b.start); err != nil { return &nestedError{"SRVResource body", err} } if err := h.fixLen(msg, lenOff, preLen); err != nil { return err } if err := b.incrementSectionCount(); err != nil { return err } b.msg = msg return nil } // AResource adds a single AResource. func (b *Builder) AResource(h ResourceHeader, r AResource) error { if err := b.checkResourceSection(); err != nil { return err } h.Type = r.realType() msg, lenOff, err := h.pack(b.msg, b.compression, b.start) if err != nil { return &nestedError{"ResourceHeader", err} } preLen := len(msg) if msg, err = r.pack(msg, b.compression, b.start); err != nil { return &nestedError{"AResource body", err} } if err := h.fixLen(msg, lenOff, preLen); err != nil { return err } if err := b.incrementSectionCount(); err != nil { return err } b.msg = msg return nil } // AAAAResource adds a single AAAAResource. func (b *Builder) AAAAResource(h ResourceHeader, r AAAAResource) error { if err := b.checkResourceSection(); err != nil { return err } h.Type = r.realType() msg, lenOff, err := h.pack(b.msg, b.compression, b.start) if err != nil { return &nestedError{"ResourceHeader", err} } preLen := len(msg) if msg, err = r.pack(msg, b.compression, b.start); err != nil { return &nestedError{"AAAAResource body", err} } if err := h.fixLen(msg, lenOff, preLen); err != nil { return err } if err := b.incrementSectionCount(); err != nil { return err } b.msg = msg return nil } // OPTResource adds a single OPTResource. func (b *Builder) OPTResource(h ResourceHeader, r OPTResource) error { if err := b.checkResourceSection(); err != nil { return err } h.Type = r.realType() msg, lenOff, err := h.pack(b.msg, b.compression, b.start) if err != nil { return &nestedError{"ResourceHeader", err} } preLen := len(msg) if msg, err = r.pack(msg, b.compression, b.start); err != nil { return &nestedError{"OPTResource body", err} } if err := h.fixLen(msg, lenOff, preLen); err != nil { return err } if err := b.incrementSectionCount(); err != nil { return err } b.msg = msg return nil } // UnknownResource adds a single UnknownResource. func (b *Builder) UnknownResource(h ResourceHeader, r UnknownResource) error { if err := b.checkResourceSection(); err != nil { return err } h.Type = r.realType() msg, lenOff, err := h.pack(b.msg, b.compression, b.start) if err != nil { return &nestedError{"ResourceHeader", err} } preLen := len(msg) if msg, err = r.pack(msg, b.compression, b.start); err != nil { return &nestedError{"UnknownResource body", err} } if err := h.fixLen(msg, lenOff, preLen); err != nil { return err } if err := b.incrementSectionCount(); err != nil { return err } b.msg = msg return nil } // Finish ends message building and generates a binary message. func (b *Builder) Finish() ([]byte, error) { if b.section < sectionHeader { return nil, ErrNotStarted } b.section = sectionDone // Space for the header was allocated in NewBuilder. b.header.pack(b.msg[b.start:b.start]) return b.msg, nil } // A ResourceHeader is the header of a DNS resource record. There are // many types of DNS resource records, but they all share the same header. type ResourceHeader struct { // Name is the domain name for which this resource record pertains. Name Name // Type is the type of DNS resource record. // // This field will be set automatically during packing. Type Type // Class is the class of network to which this DNS resource record // pertains. Class Class // TTL is the length of time (measured in seconds) which this resource // record is valid for (time to live). All Resources in a set should // have the same TTL (RFC 2181 Section 5.2). TTL uint32 // Length is the length of data in the resource record after the header. // // This field will be set automatically during packing. Length uint16 } // GoString implements fmt.GoStringer.GoString. func (h *ResourceHeader) GoString() string { return "dnsmessage.ResourceHeader{" + "Name: " + h.Name.GoString() + ", " + "Type: " + h.Type.GoString() + ", " + "Class: " + h.Class.GoString() + ", " + "TTL: " + printUint32(h.TTL) + ", " + "Length: " + printUint16(h.Length) + "}" } // pack appends the wire format of the ResourceHeader to oldMsg. // // lenOff is the offset in msg where the Length field was packed. func (h *ResourceHeader) pack(oldMsg []byte, compression map[string]int, compressionOff int) (msg []byte, lenOff int, err error) { msg = oldMsg if msg, err = h.Name.pack(msg, compression, compressionOff); err != nil { return oldMsg, 0, &nestedError{"Name", err} } msg = packType(msg, h.Type) msg = packClass(msg, h.Class) msg = packUint32(msg, h.TTL) lenOff = len(msg) msg = packUint16(msg, h.Length) return msg, lenOff, nil } func (h *ResourceHeader) unpack(msg []byte, off int) (int, error) { newOff := off var err error if newOff, err = h.Name.unpack(msg, newOff); err != nil { return off, &nestedError{"Name", err} } if h.Type, newOff, err = unpackType(msg, newOff); err != nil { return off, &nestedError{"Type", err} } if h.Class, newOff, err = unpackClass(msg, newOff); err != nil { return off, &nestedError{"Class", err} } if h.TTL, newOff, err = unpackUint32(msg, newOff); err != nil { return off, &nestedError{"TTL", err} } if h.Length, newOff, err = unpackUint16(msg, newOff); err != nil { return off, &nestedError{"Length", err} } return newOff, nil } // fixLen updates a packed ResourceHeader to include the length of the // ResourceBody. // // lenOff is the offset of the ResourceHeader.Length field in msg. // // preLen is the length that msg was before the ResourceBody was packed. func (h *ResourceHeader) fixLen(msg []byte, lenOff int, preLen int) error { conLen := len(msg) - preLen if conLen > int(^uint16(0)) { return errResTooLong } // Fill in the length now that we know how long the content is. packUint16(msg[lenOff:lenOff], uint16(conLen)) h.Length = uint16(conLen) return nil } // EDNS(0) wire constants. const ( edns0Version = 0 edns0DNSSECOK = 0x00008000 ednsVersionMask = 0x00ff0000 edns0DNSSECOKMask = 0x00ff8000 ) // SetEDNS0 configures h for EDNS(0). // // The provided extRCode must be an extended RCode. func (h *ResourceHeader) SetEDNS0(udpPayloadLen int, extRCode RCode, dnssecOK bool) error { h.Name = Name{Data: [nameLen]byte{'.'}, Length: 1} // RFC 6891 section 6.1.2 h.Type = TypeOPT h.Class = Class(udpPayloadLen) h.TTL = uint32(extRCode) >> 4 << 24 if dnssecOK { h.TTL |= edns0DNSSECOK } return nil } // DNSSECAllowed reports whether the DNSSEC OK bit is set. func (h *ResourceHeader) DNSSECAllowed() bool { return h.TTL&edns0DNSSECOKMask == edns0DNSSECOK // RFC 6891 section 6.1.3 } // ExtendedRCode returns an extended RCode. // // The provided rcode must be the RCode in DNS message header. func (h *ResourceHeader) ExtendedRCode(rcode RCode) RCode { if h.TTL&ednsVersionMask == edns0Version { // RFC 6891 section 6.1.3 return RCode(h.TTL>>24<<4) | rcode } return rcode } func skipResource(msg []byte, off int) (int, error) { newOff, err := skipName(msg, off) if err != nil { return off, &nestedError{"Name", err} } if newOff, err = skipType(msg, newOff); err != nil { return off, &nestedError{"Type", err} } if newOff, err = skipClass(msg, newOff); err != nil { return off, &nestedError{"Class", err} } if newOff, err = skipUint32(msg, newOff); err != nil { return off, &nestedError{"TTL", err} } length, newOff, err := unpackUint16(msg, newOff) if err != nil { return off, &nestedError{"Length", err} } if newOff += int(length); newOff > len(msg) { return off, errResourceLen } return newOff, nil } // packUint16 appends the wire format of field to msg. func packUint16(msg []byte, field uint16) []byte { return append(msg, byte(field>>8), byte(field)) } func unpackUint16(msg []byte, off int) (uint16, int, error) { if off+uint16Len > len(msg) { return 0, off, errBaseLen } return uint16(msg[off])<<8 | uint16(msg[off+1]), off + uint16Len, nil } func skipUint16(msg []byte, off int) (int, error) { if off+uint16Len > len(msg) { return off, errBaseLen } return off + uint16Len, nil } // packType appends the wire format of field to msg. func packType(msg []byte, field Type) []byte { return packUint16(msg, uint16(field)) } func unpackType(msg []byte, off int) (Type, int, error) { t, o, err := unpackUint16(msg, off) return Type(t), o, err } func skipType(msg []byte, off int) (int, error) { return skipUint16(msg, off) } // packClass appends the wire format of field to msg. func packClass(msg []byte, field Class) []byte { return packUint16(msg, uint16(field)) } func unpackClass(msg []byte, off int) (Class, int, error) { c, o, err := unpackUint16(msg, off) return Class(c), o, err } func skipClass(msg []byte, off int) (int, error) { return skipUint16(msg, off) } // packUint32 appends the wire format of field to msg. func packUint32(msg []byte, field uint32) []byte { return append( msg, byte(field>>24), byte(field>>16), byte(field>>8), byte(field), ) } func unpackUint32(msg []byte, off int) (uint32, int, error) { if off+uint32Len > len(msg) { return 0, off, errBaseLen } v := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]) return v, off + uint32Len, nil } func skipUint32(msg []byte, off int) (int, error) { if off+uint32Len > len(msg) { return off, errBaseLen } return off + uint32Len, nil } // packText appends the wire format of field to msg. func packText(msg []byte, field string) ([]byte, error) { l := len(field) if l > 255 { return nil, errStringTooLong } msg = append(msg, byte(l)) msg = append(msg, field...) return msg, nil } func unpackText(msg []byte, off int) (string, int, error) { if off >= len(msg) { return "", off, errBaseLen } beginOff := off + 1 endOff := beginOff + int(msg[off]) if endOff > len(msg) { return "", off, errCalcLen } return string(msg[beginOff:endOff]), endOff, nil } // packBytes appends the wire format of field to msg. func packBytes(msg []byte, field []byte) []byte { return append(msg, field...) } func unpackBytes(msg []byte, off int, field []byte) (int, error) { newOff := off + len(field) if newOff > len(msg) { return off, errBaseLen } copy(field, msg[off:newOff]) return newOff, nil } const nameLen = 255 // A Name is a non-encoded domain name. It is used instead of strings to avoid // allocations. type Name struct { Data [nameLen]byte // 255 bytes Length uint8 } // NewName creates a new Name from a string. func NewName(name string) (Name, error) { if len([]byte(name)) > nameLen { return Name{}, errCalcLen } n := Name{Length: uint8(len(name))} copy(n.Data[:], []byte(name)) return n, nil } // MustNewName creates a new Name from a string and panics on error. func MustNewName(name string) Name { n, err := NewName(name) if err != nil { panic("creating name: " + err.Error()) } return n } // String implements fmt.Stringer.String. func (n Name) String() string { return string(n.Data[:n.Length]) } // GoString implements fmt.GoStringer.GoString. func (n *Name) GoString() string { return `dnsmessage.MustNewName("` + printString(n.Data[:n.Length]) + `")` } // pack appends the wire format of the Name to msg. // // Domain names are a sequence of counted strings split at the dots. They end // with a zero-length string. Compression can be used to reuse domain suffixes. // // The compression map will be updated with new domain suffixes. If compression // is nil, compression will not be used. func (n *Name) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { oldMsg := msg // Add a trailing dot to canonicalize name. if n.Length == 0 || n.Data[n.Length-1] != '.' { return oldMsg, errNonCanonicalName } // Allow root domain. if n.Data[0] == '.' && n.Length == 1 { return append(msg, 0), nil } // Emit sequence of counted strings, chopping at dots. for i, begin := 0, 0; i < int(n.Length); i++ { // Check for the end of the segment. if n.Data[i] == '.' { // The two most significant bits have special meaning. // It isn't allowed for segments to be long enough to // need them. if i-begin >= 1<<6 { return oldMsg, errSegTooLong } // Segments must have a non-zero length. if i-begin == 0 { return oldMsg, errZeroSegLen } msg = append(msg, byte(i-begin)) for j := begin; j < i; j++ { msg = append(msg, n.Data[j]) } begin = i + 1 continue } // We can only compress domain suffixes starting with a new // segment. A pointer is two bytes with the two most significant // bits set to 1 to indicate that it is a pointer. if (i == 0 || n.Data[i-1] == '.') && compression != nil { if ptr, ok := compression[string(n.Data[i:])]; ok { // Hit. Emit a pointer instead of the rest of // the domain. return append(msg, byte(ptr>>8|0xC0), byte(ptr)), nil } // Miss. Add the suffix to the compression table if the // offset can be stored in the available 14 bytes. if len(msg) <= int(^uint16(0)>>2) { compression[string(n.Data[i:])] = len(msg) - compressionOff } } } return append(msg, 0), nil } // unpack unpacks a domain name. func (n *Name) unpack(msg []byte, off int) (int, error) { return n.unpackCompressed(msg, off, true /* allowCompression */) } func (n *Name) unpackCompressed(msg []byte, off int, allowCompression bool) (int, error) { // currOff is the current working offset. currOff := off // newOff is the offset where the next record will start. Pointers lead // to data that belongs to other names and thus doesn't count towards to // the usage of this name. newOff := off // ptr is the number of pointers followed. var ptr int // Name is a slice representation of the name data. name := n.Data[:0] Loop: for { if currOff >= len(msg) { return off, errBaseLen } c := int(msg[currOff]) currOff++ switch c & 0xC0 { case 0x00: // String segment if c == 0x00 { // A zero length signals the end of the name. break Loop } endOff := currOff + c if endOff > len(msg) { return off, errCalcLen } name = append(name, msg[currOff:endOff]...) name = append(name, '.') currOff = endOff case 0xC0: // Pointer if !allowCompression { return off, errCompressedSRV } if currOff >= len(msg) { return off, errInvalidPtr } c1 := msg[currOff] currOff++ if ptr == 0 { newOff = currOff } // Don't follow too many pointers, maybe there's a loop. if ptr++; ptr > 10 { return off, errTooManyPtr } currOff = (c^0xC0)<<8 | int(c1) default: // Prefixes 0x80 and 0x40 are reserved. return off, errReserved } } if len(name) == 0 { name = append(name, '.') } if len(name) > len(n.Data) { return off, errCalcLen } n.Length = uint8(len(name)) if ptr == 0 { newOff = currOff } return newOff, nil } func skipName(msg []byte, off int) (int, error) { // newOff is the offset where the next record will start. Pointers lead // to data that belongs to other names and thus doesn't count towards to // the usage of this name. newOff := off Loop: for { if newOff >= len(msg) { return off, errBaseLen } c := int(msg[newOff]) newOff++ switch c & 0xC0 { case 0x00: if c == 0x00 { // A zero length signals the end of the name. break Loop } // literal string newOff += c if newOff > len(msg) { return off, errCalcLen } case 0xC0: // Pointer to somewhere else in msg. // Pointers are two bytes. newOff++ // Don't follow the pointer as the data here has ended. break Loop default: // Prefixes 0x80 and 0x40 are reserved. return off, errReserved } } return newOff, nil } // A Question is a DNS query. type Question struct { Name Name Type Type Class Class } // pack appends the wire format of the Question to msg. func (q *Question) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { msg, err := q.Name.pack(msg, compression, compressionOff) if err != nil { return msg, &nestedError{"Name", err} } msg = packType(msg, q.Type) return packClass(msg, q.Class), nil } // GoString implements fmt.GoStringer.GoString. func (q *Question) GoString() string { return "dnsmessage.Question{" + "Name: " + q.Name.GoString() + ", " + "Type: " + q.Type.GoString() + ", " + "Class: " + q.Class.GoString() + "}" } func unpackResourceBody(msg []byte, off int, hdr ResourceHeader) (ResourceBody, int, error) { var ( r ResourceBody err error name string ) switch hdr.Type { case TypeA: var rb AResource rb, err = unpackAResource(msg, off) r = &rb name = "A" case TypeNS: var rb NSResource rb, err = unpackNSResource(msg, off) r = &rb name = "NS" case TypeCNAME: var rb CNAMEResource rb, err = unpackCNAMEResource(msg, off) r = &rb name = "CNAME" case TypeSOA: var rb SOAResource rb, err = unpackSOAResource(msg, off) r = &rb name = "SOA" case TypePTR: var rb PTRResource rb, err = unpackPTRResource(msg, off) r = &rb name = "PTR" case TypeMX: var rb MXResource rb, err = unpackMXResource(msg, off) r = &rb name = "MX" case TypeTXT: var rb TXTResource rb, err = unpackTXTResource(msg, off, hdr.Length) r = &rb name = "TXT" case TypeAAAA: var rb AAAAResource rb, err = unpackAAAAResource(msg, off) r = &rb name = "AAAA" case TypeSRV: var rb SRVResource rb, err = unpackSRVResource(msg, off) r = &rb name = "SRV" case TypeOPT: var rb OPTResource rb, err = unpackOPTResource(msg, off, hdr.Length) r = &rb name = "OPT" default: var rb UnknownResource rb, err = unpackUnknownResource(hdr.Type, msg, off, hdr.Length) r = &rb name = "Unknown" } if err != nil { return nil, off, &nestedError{name + " record", err} } return r, off + int(hdr.Length), nil } // A CNAMEResource is a CNAME Resource record. type CNAMEResource struct { CNAME Name } func (r *CNAMEResource) realType() Type { return TypeCNAME } // pack appends the wire format of the CNAMEResource to msg. func (r *CNAMEResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { return r.CNAME.pack(msg, compression, compressionOff) } // GoString implements fmt.GoStringer.GoString. func (r *CNAMEResource) GoString() string { return "dnsmessage.CNAMEResource{CNAME: " + r.CNAME.GoString() + "}" } func unpackCNAMEResource(msg []byte, off int) (CNAMEResource, error) { var cname Name if _, err := cname.unpack(msg, off); err != nil { return CNAMEResource{}, err } return CNAMEResource{cname}, nil } // An MXResource is an MX Resource record. type MXResource struct { Pref uint16 MX Name } func (r *MXResource) realType() Type { return TypeMX } // pack appends the wire format of the MXResource to msg. func (r *MXResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { oldMsg := msg msg = packUint16(msg, r.Pref) msg, err := r.MX.pack(msg, compression, compressionOff) if err != nil { return oldMsg, &nestedError{"MXResource.MX", err} } return msg, nil } // GoString implements fmt.GoStringer.GoString. func (r *MXResource) GoString() string { return "dnsmessage.MXResource{" + "Pref: " + printUint16(r.Pref) + ", " + "MX: " + r.MX.GoString() + "}" } func unpackMXResource(msg []byte, off int) (MXResource, error) { pref, off, err := unpackUint16(msg, off) if err != nil { return MXResource{}, &nestedError{"Pref", err} } var mx Name if _, err := mx.unpack(msg, off); err != nil { return MXResource{}, &nestedError{"MX", err} } return MXResource{pref, mx}, nil } // An NSResource is an NS Resource record. type NSResource struct { NS Name } func (r *NSResource) realType() Type { return TypeNS } // pack appends the wire format of the NSResource to msg. func (r *NSResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { return r.NS.pack(msg, compression, compressionOff) } // GoString implements fmt.GoStringer.GoString. func (r *NSResource) GoString() string { return "dnsmessage.NSResource{NS: " + r.NS.GoString() + "}" } func unpackNSResource(msg []byte, off int) (NSResource, error) { var ns Name if _, err := ns.unpack(msg, off); err != nil { return NSResource{}, err } return NSResource{ns}, nil } // A PTRResource is a PTR Resource record. type PTRResource struct { PTR Name } func (r *PTRResource) realType() Type { return TypePTR } // pack appends the wire format of the PTRResource to msg. func (r *PTRResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { return r.PTR.pack(msg, compression, compressionOff) } // GoString implements fmt.GoStringer.GoString. func (r *PTRResource) GoString() string { return "dnsmessage.PTRResource{PTR: " + r.PTR.GoString() + "}" } func unpackPTRResource(msg []byte, off int) (PTRResource, error) { var ptr Name if _, err := ptr.unpack(msg, off); err != nil { return PTRResource{}, err } return PTRResource{ptr}, nil } // An SOAResource is an SOA Resource record. type SOAResource struct { NS Name MBox Name Serial uint32 Refresh uint32 Retry uint32 Expire uint32 // MinTTL the is the default TTL of Resources records which did not // contain a TTL value and the TTL of negative responses. (RFC 2308 // Section 4) MinTTL uint32 } func (r *SOAResource) realType() Type { return TypeSOA } // pack appends the wire format of the SOAResource to msg. func (r *SOAResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { oldMsg := msg msg, err := r.NS.pack(msg, compression, compressionOff) if err != nil { return oldMsg, &nestedError{"SOAResource.NS", err} } msg, err = r.MBox.pack(msg, compression, compressionOff) if err != nil { return oldMsg, &nestedError{"SOAResource.MBox", err} } msg = packUint32(msg, r.Serial) msg = packUint32(msg, r.Refresh) msg = packUint32(msg, r.Retry) msg = packUint32(msg, r.Expire) return packUint32(msg, r.MinTTL), nil } // GoString implements fmt.GoStringer.GoString. func (r *SOAResource) GoString() string { return "dnsmessage.SOAResource{" + "NS: " + r.NS.GoString() + ", " + "MBox: " + r.MBox.GoString() + ", " + "Serial: " + printUint32(r.Serial) + ", " + "Refresh: " + printUint32(r.Refresh) + ", " + "Retry: " + printUint32(r.Retry) + ", " + "Expire: " + printUint32(r.Expire) + ", " + "MinTTL: " + printUint32(r.MinTTL) + "}" } func unpackSOAResource(msg []byte, off int) (SOAResource, error) { var ns Name off, err := ns.unpack(msg, off) if err != nil { return SOAResource{}, &nestedError{"NS", err} } var mbox Name if off, err = mbox.unpack(msg, off); err != nil { return SOAResource{}, &nestedError{"MBox", err} } serial, off, err := unpackUint32(msg, off) if err != nil { return SOAResource{}, &nestedError{"Serial", err} } refresh, off, err := unpackUint32(msg, off) if err != nil { return SOAResource{}, &nestedError{"Refresh", err} } retry, off, err := unpackUint32(msg, off) if err != nil { return SOAResource{}, &nestedError{"Retry", err} } expire, off, err := unpackUint32(msg, off) if err != nil { return SOAResource{}, &nestedError{"Expire", err} } minTTL, _, err := unpackUint32(msg, off) if err != nil { return SOAResource{}, &nestedError{"MinTTL", err} } return SOAResource{ns, mbox, serial, refresh, retry, expire, minTTL}, nil } // A TXTResource is a TXT Resource record. type TXTResource struct { TXT []string } func (r *TXTResource) realType() Type { return TypeTXT } // pack appends the wire format of the TXTResource to msg. func (r *TXTResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { oldMsg := msg for _, s := range r.TXT { var err error msg, err = packText(msg, s) if err != nil { return oldMsg, err } } return msg, nil } // GoString implements fmt.GoStringer.GoString. func (r *TXTResource) GoString() string { s := "dnsmessage.TXTResource{TXT: []string{" if len(r.TXT) == 0 { return s + "}}" } s += `"` + printString([]byte(r.TXT[0])) for _, t := range r.TXT[1:] { s += `", "` + printString([]byte(t)) } return s + `"}}` } func unpackTXTResource(msg []byte, off int, length uint16) (TXTResource, error) { txts := make([]string, 0, 1) for n := uint16(0); n < length; { var t string var err error if t, off, err = unpackText(msg, off); err != nil { return TXTResource{}, &nestedError{"text", err} } // Check if we got too many bytes. if length-n < uint16(len(t))+1 { return TXTResource{}, errCalcLen } n += uint16(len(t)) + 1 txts = append(txts, t) } return TXTResource{txts}, nil } // An SRVResource is an SRV Resource record. type SRVResource struct { Priority uint16 Weight uint16 Port uint16 Target Name // Not compressed as per RFC 2782. } func (r *SRVResource) realType() Type { return TypeSRV } // pack appends the wire format of the SRVResource to msg. func (r *SRVResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { oldMsg := msg msg = packUint16(msg, r.Priority) msg = packUint16(msg, r.Weight) msg = packUint16(msg, r.Port) msg, err := r.Target.pack(msg, nil, compressionOff) if err != nil { return oldMsg, &nestedError{"SRVResource.Target", err} } return msg, nil } // GoString implements fmt.GoStringer.GoString. func (r *SRVResource) GoString() string { return "dnsmessage.SRVResource{" + "Priority: " + printUint16(r.Priority) + ", " + "Weight: " + printUint16(r.Weight) + ", " + "Port: " + printUint16(r.Port) + ", " + "Target: " + r.Target.GoString() + "}" } func unpackSRVResource(msg []byte, off int) (SRVResource, error) { priority, off, err := unpackUint16(msg, off) if err != nil { return SRVResource{}, &nestedError{"Priority", err} } weight, off, err := unpackUint16(msg, off) if err != nil { return SRVResource{}, &nestedError{"Weight", err} } port, off, err := unpackUint16(msg, off) if err != nil { return SRVResource{}, &nestedError{"Port", err} } var target Name if _, err := target.unpackCompressed(msg, off, false /* allowCompression */); err != nil { return SRVResource{}, &nestedError{"Target", err} } return SRVResource{priority, weight, port, target}, nil } // An AResource is an A Resource record. type AResource struct { A [4]byte } func (r *AResource) realType() Type { return TypeA } // pack appends the wire format of the AResource to msg. func (r *AResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { return packBytes(msg, r.A[:]), nil } // GoString implements fmt.GoStringer.GoString. func (r *AResource) GoString() string { return "dnsmessage.AResource{" + "A: [4]byte{" + printByteSlice(r.A[:]) + "}}" } func unpackAResource(msg []byte, off int) (AResource, error) { var a [4]byte if _, err := unpackBytes(msg, off, a[:]); err != nil { return AResource{}, err } return AResource{a}, nil } // An AAAAResource is an AAAA Resource record. type AAAAResource struct { AAAA [16]byte } func (r *AAAAResource) realType() Type { return TypeAAAA } // GoString implements fmt.GoStringer.GoString. func (r *AAAAResource) GoString() string { return "dnsmessage.AAAAResource{" + "AAAA: [16]byte{" + printByteSlice(r.AAAA[:]) + "}}" } // pack appends the wire format of the AAAAResource to msg. func (r *AAAAResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { return packBytes(msg, r.AAAA[:]), nil } func unpackAAAAResource(msg []byte, off int) (AAAAResource, error) { var aaaa [16]byte if _, err := unpackBytes(msg, off, aaaa[:]); err != nil { return AAAAResource{}, err } return AAAAResource{aaaa}, nil } // An OPTResource is an OPT pseudo Resource record. // // The pseudo resource record is part of the extension mechanisms for DNS // as defined in RFC 6891. type OPTResource struct { Options []Option } // An Option represents a DNS message option within OPTResource. // // The message option is part of the extension mechanisms for DNS as // defined in RFC 6891. type Option struct { Code uint16 // option code Data []byte } // GoString implements fmt.GoStringer.GoString. func (o *Option) GoString() string { return "dnsmessage.Option{" + "Code: " + printUint16(o.Code) + ", " + "Data: []byte{" + printByteSlice(o.Data) + "}}" } func (r *OPTResource) realType() Type { return TypeOPT } func (r *OPTResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { for _, opt := range r.Options { msg = packUint16(msg, opt.Code) l := uint16(len(opt.Data)) msg = packUint16(msg, l) msg = packBytes(msg, opt.Data) } return msg, nil } // GoString implements fmt.GoStringer.GoString. func (r *OPTResource) GoString() string { s := "dnsmessage.OPTResource{Options: []dnsmessage.Option{" if len(r.Options) == 0 { return s + "}}" } s += r.Options[0].GoString() for _, o := range r.Options[1:] { s += ", " + o.GoString() } return s + "}}" } func unpackOPTResource(msg []byte, off int, length uint16) (OPTResource, error) { var opts []Option for oldOff := off; off < oldOff+int(length); { var err error var o Option o.Code, off, err = unpackUint16(msg, off) if err != nil { return OPTResource{}, &nestedError{"Code", err} } var l uint16 l, off, err = unpackUint16(msg, off) if err != nil { return OPTResource{}, &nestedError{"Data", err} } o.Data = make([]byte, l) if copy(o.Data, msg[off:]) != int(l) { return OPTResource{}, &nestedError{"Data", errCalcLen} } off += int(l) opts = append(opts, o) } return OPTResource{opts}, nil } // An UnknownResource is a catch-all container for unknown record types. type UnknownResource struct { Type Type Data []byte } func (r *UnknownResource) realType() Type { return r.Type } // pack appends the wire format of the UnknownResource to msg. func (r *UnknownResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { return packBytes(msg, r.Data[:]), nil } // GoString implements fmt.GoStringer.GoString. func (r *UnknownResource) GoString() string { return "dnsmessage.UnknownResource{" + "Type: " + r.Type.GoString() + ", " + "Data: []byte{" + printByteSlice(r.Data) + "}}" } func unpackUnknownResource(recordType Type, msg []byte, off int, length uint16) (UnknownResource, error) { parsed := UnknownResource{ Type: recordType, Data: make([]byte, length), } if _, err := unpackBytes(msg, off, parsed.Data); err != nil { return UnknownResource{}, err } return parsed, nil } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/dns/dnsmessage/message_test.go000066400000000000000000001311431415442132300263610ustar00rootroot00000000000000// 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. package dnsmessage import ( "bytes" "fmt" "io/ioutil" "path/filepath" "reflect" "strings" "testing" ) const ( // This type was selected randomly from the IANA-assigned private use // range of RR TYPEs. privateUseType Type = 65362 ) func TestPrintPaddedUint8(t *testing.T) { tests := []struct { num uint8 want string }{ {0, "000"}, {1, "001"}, {9, "009"}, {10, "010"}, {99, "099"}, {100, "100"}, {124, "124"}, {104, "104"}, {120, "120"}, {255, "255"}, } for _, test := range tests { if got := printPaddedUint8(test.num); got != test.want { t.Errorf("got printPaddedUint8(%d) = %s, want = %s", test.num, got, test.want) } } } func TestPrintUint8Bytes(t *testing.T) { tests := []uint8{ 0, 1, 9, 10, 99, 100, 124, 104, 120, 255, } for _, test := range tests { if got, want := string(printUint8Bytes(nil, test)), fmt.Sprint(test); got != want { t.Errorf("got printUint8Bytes(%d) = %s, want = %s", test, got, want) } } } func TestPrintUint16(t *testing.T) { tests := []uint16{ 65535, 0, 1, 10, 100, 1000, 10000, 324, 304, 320, } for _, test := range tests { if got, want := printUint16(test), fmt.Sprint(test); got != want { t.Errorf("got printUint16(%d) = %s, want = %s", test, got, want) } } } func TestPrintUint32(t *testing.T) { tests := []uint32{ 4294967295, 65535, 0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 324, 304, 320, } for _, test := range tests { if got, want := printUint32(test), fmt.Sprint(test); got != want { t.Errorf("got printUint32(%d) = %s, want = %s", test, got, want) } } } func mustEDNS0ResourceHeader(l int, extrc RCode, do bool) ResourceHeader { h := ResourceHeader{Class: ClassINET} if err := h.SetEDNS0(l, extrc, do); err != nil { panic(err) } return h } func (m *Message) String() string { s := fmt.Sprintf("Message: %#v\n", &m.Header) if len(m.Questions) > 0 { s += "-- Questions\n" for _, q := range m.Questions { s += fmt.Sprintf("%#v\n", q) } } if len(m.Answers) > 0 { s += "-- Answers\n" for _, a := range m.Answers { s += fmt.Sprintf("%#v\n", a) } } if len(m.Authorities) > 0 { s += "-- Authorities\n" for _, ns := range m.Authorities { s += fmt.Sprintf("%#v\n", ns) } } if len(m.Additionals) > 0 { s += "-- Additionals\n" for _, e := range m.Additionals { s += fmt.Sprintf("%#v\n", e) } } return s } func TestNameString(t *testing.T) { want := "foo" name := MustNewName(want) if got := fmt.Sprint(name); got != want { t.Errorf("got fmt.Sprint(%#v) = %s, want = %s", name, got, want) } } func TestQuestionPackUnpack(t *testing.T) { want := Question{ Name: MustNewName("."), Type: TypeA, Class: ClassINET, } buf, err := want.pack(make([]byte, 1, 50), map[string]int{}, 1) if err != nil { t.Fatal("Question.pack() =", err) } var p Parser p.msg = buf p.header.questions = 1 p.section = sectionQuestions p.off = 1 got, err := p.Question() if err != nil { t.Fatalf("Parser{%q}.Question() = %v", string(buf[1:]), err) } if p.off != len(buf) { t.Errorf("unpacked different amount than packed: got = %d, want = %d", p.off, len(buf)) } if !reflect.DeepEqual(got, want) { t.Errorf("got from Parser.Question() = %+v, want = %+v", got, want) } } func TestName(t *testing.T) { tests := []string{ "", ".", "google..com", "google.com", "google..com.", "google.com.", ".google.com.", "www..google.com.", "www.google.com.", } for _, test := range tests { n, err := NewName(test) if err != nil { t.Errorf("NewName(%q) = %v", test, err) continue } if ns := n.String(); ns != test { t.Errorf("got %#v.String() = %q, want = %q", n, ns, test) continue } } } func TestNamePackUnpack(t *testing.T) { tests := []struct { in string want string err error }{ {"", "", errNonCanonicalName}, {".", ".", nil}, {"google..com", "", errNonCanonicalName}, {"google.com", "", errNonCanonicalName}, {"google..com.", "", errZeroSegLen}, {"google.com.", "google.com.", nil}, {".google.com.", "", errZeroSegLen}, {"www..google.com.", "", errZeroSegLen}, {"www.google.com.", "www.google.com.", nil}, } for _, test := range tests { in := MustNewName(test.in) want := MustNewName(test.want) buf, err := in.pack(make([]byte, 0, 30), map[string]int{}, 0) if err != test.err { t.Errorf("got %q.pack() = %v, want = %v", test.in, err, test.err) continue } if test.err != nil { continue } var got Name n, err := got.unpack(buf, 0) if err != nil { t.Errorf("%q.unpack() = %v", test.in, err) continue } if n != len(buf) { t.Errorf( "unpacked different amount than packed for %q: got = %d, want = %d", test.in, n, len(buf), ) } if got != want { t.Errorf("unpacking packing of %q: got = %#v, want = %#v", test.in, got, want) } } } func TestIncompressibleName(t *testing.T) { name := MustNewName("example.com.") compression := map[string]int{} buf, err := name.pack(make([]byte, 0, 100), compression, 0) if err != nil { t.Fatal("first Name.pack() =", err) } buf, err = name.pack(buf, compression, 0) if err != nil { t.Fatal("second Name.pack() =", err) } var n1 Name off, err := n1.unpackCompressed(buf, 0, false /* allowCompression */) if err != nil { t.Fatal("unpacking incompressible name without pointers failed:", err) } var n2 Name if _, err := n2.unpackCompressed(buf, off, false /* allowCompression */); err != errCompressedSRV { t.Errorf("unpacking compressed incompressible name with pointers: got %v, want = %v", err, errCompressedSRV) } } func checkErrorPrefix(err error, prefix string) bool { e, ok := err.(*nestedError) return ok && e.s == prefix } func TestHeaderUnpackError(t *testing.T) { wants := []string{ "id", "bits", "questions", "answers", "authorities", "additionals", } var buf []byte var h header for _, want := range wants { n, err := h.unpack(buf, 0) if n != 0 || !checkErrorPrefix(err, want) { t.Errorf("got header.unpack([%d]byte, 0) = %d, %v, want = 0, %s", len(buf), n, err, want) } buf = append(buf, 0, 0) } } func TestParserStart(t *testing.T) { const want = "unpacking header" var p Parser for i := 0; i <= 1; i++ { _, err := p.Start([]byte{}) if !checkErrorPrefix(err, want) { t.Errorf("got Parser.Start(nil) = _, %v, want = _, %s", err, want) } } } func TestResourceNotStarted(t *testing.T) { tests := []struct { name string fn func(*Parser) error }{ {"CNAMEResource", func(p *Parser) error { _, err := p.CNAMEResource(); return err }}, {"MXResource", func(p *Parser) error { _, err := p.MXResource(); return err }}, {"NSResource", func(p *Parser) error { _, err := p.NSResource(); return err }}, {"PTRResource", func(p *Parser) error { _, err := p.PTRResource(); return err }}, {"SOAResource", func(p *Parser) error { _, err := p.SOAResource(); return err }}, {"TXTResource", func(p *Parser) error { _, err := p.TXTResource(); return err }}, {"SRVResource", func(p *Parser) error { _, err := p.SRVResource(); return err }}, {"AResource", func(p *Parser) error { _, err := p.AResource(); return err }}, {"AAAAResource", func(p *Parser) error { _, err := p.AAAAResource(); return err }}, {"UnknownResource", func(p *Parser) error { _, err := p.UnknownResource(); return err }}, } for _, test := range tests { if err := test.fn(&Parser{}); err != ErrNotStarted { t.Errorf("got Parser.%s() = _ , %v, want = _, %v", test.name, err, ErrNotStarted) } } } func TestDNSPackUnpack(t *testing.T) { wants := []Message{ { Questions: []Question{ { Name: MustNewName("."), Type: TypeAAAA, Class: ClassINET, }, }, Answers: []Resource{}, Authorities: []Resource{}, Additionals: []Resource{}, }, largeTestMsg(), } for i, want := range wants { b, err := want.Pack() if err != nil { t.Fatalf("%d: Message.Pack() = %v", i, err) } var got Message err = got.Unpack(b) if err != nil { t.Fatalf("%d: Message.Unapck() = %v", i, err) } if !reflect.DeepEqual(got, want) { t.Errorf("%d: Message.Pack/Unpack() roundtrip: got = %+v, want = %+v", i, &got, &want) } } } func TestDNSAppendPackUnpack(t *testing.T) { wants := []Message{ { Questions: []Question{ { Name: MustNewName("."), Type: TypeAAAA, Class: ClassINET, }, }, Answers: []Resource{}, Authorities: []Resource{}, Additionals: []Resource{}, }, largeTestMsg(), } for i, want := range wants { b := make([]byte, 2, 514) b, err := want.AppendPack(b) if err != nil { t.Fatalf("%d: Message.AppendPack() = %v", i, err) } b = b[2:] var got Message err = got.Unpack(b) if err != nil { t.Fatalf("%d: Message.Unapck() = %v", i, err) } if !reflect.DeepEqual(got, want) { t.Errorf("%d: Message.AppendPack/Unpack() roundtrip: got = %+v, want = %+v", i, &got, &want) } } } func TestSkipAll(t *testing.T) { msg := largeTestMsg() buf, err := msg.Pack() if err != nil { t.Fatal("Message.Pack() =", err) } var p Parser if _, err := p.Start(buf); err != nil { t.Fatal("Parser.Start(non-nil) =", err) } tests := []struct { name string f func() error }{ {"SkipAllQuestions", p.SkipAllQuestions}, {"SkipAllAnswers", p.SkipAllAnswers}, {"SkipAllAuthorities", p.SkipAllAuthorities}, {"SkipAllAdditionals", p.SkipAllAdditionals}, } for _, test := range tests { for i := 1; i <= 3; i++ { if err := test.f(); err != nil { t.Errorf("%d: Parser.%s() = %v", i, test.name, err) } } } } func TestSkipEach(t *testing.T) { msg := smallTestMsg() buf, err := msg.Pack() if err != nil { t.Fatal("Message.Pack() =", err) } var p Parser if _, err := p.Start(buf); err != nil { t.Fatal("Parser.Start(non-nil) =", err) } tests := []struct { name string f func() error }{ {"SkipQuestion", p.SkipQuestion}, {"SkipAnswer", p.SkipAnswer}, {"SkipAuthority", p.SkipAuthority}, {"SkipAdditional", p.SkipAdditional}, } for _, test := range tests { if err := test.f(); err != nil { t.Errorf("first Parser.%s() = %v, want = nil", test.name, err) } if err := test.f(); err != ErrSectionDone { t.Errorf("second Parser.%s() = %v, want = %v", test.name, err, ErrSectionDone) } } } func TestSkipAfterRead(t *testing.T) { msg := smallTestMsg() buf, err := msg.Pack() if err != nil { t.Fatal("Message.Pack() =", err) } var p Parser if _, err := p.Start(buf); err != nil { t.Fatal("Parser.Srart(non-nil) =", err) } tests := []struct { name string skip func() error read func() error }{ {"Question", p.SkipQuestion, func() error { _, err := p.Question(); return err }}, {"Answer", p.SkipAnswer, func() error { _, err := p.Answer(); return err }}, {"Authority", p.SkipAuthority, func() error { _, err := p.Authority(); return err }}, {"Additional", p.SkipAdditional, func() error { _, err := p.Additional(); return err }}, } for _, test := range tests { if err := test.read(); err != nil { t.Errorf("got Parser.%s() = _, %v, want = _, nil", test.name, err) } if err := test.skip(); err != ErrSectionDone { t.Errorf("got Parser.Skip%s() = %v, want = %v", test.name, err, ErrSectionDone) } } } func TestSkipNotStarted(t *testing.T) { var p Parser tests := []struct { name string f func() error }{ {"SkipAllQuestions", p.SkipAllQuestions}, {"SkipAllAnswers", p.SkipAllAnswers}, {"SkipAllAuthorities", p.SkipAllAuthorities}, {"SkipAllAdditionals", p.SkipAllAdditionals}, } for _, test := range tests { if err := test.f(); err != ErrNotStarted { t.Errorf("got Parser.%s() = %v, want = %v", test.name, err, ErrNotStarted) } } } func TestTooManyRecords(t *testing.T) { const recs = int(^uint16(0)) + 1 tests := []struct { name string msg Message want error }{ { "Questions", Message{ Questions: make([]Question, recs), }, errTooManyQuestions, }, { "Answers", Message{ Answers: make([]Resource, recs), }, errTooManyAnswers, }, { "Authorities", Message{ Authorities: make([]Resource, recs), }, errTooManyAuthorities, }, { "Additionals", Message{ Additionals: make([]Resource, recs), }, errTooManyAdditionals, }, } for _, test := range tests { if _, got := test.msg.Pack(); got != test.want { t.Errorf("got Message.Pack() for %d %s = %v, want = %v", recs, test.name, got, test.want) } } } func TestVeryLongTxt(t *testing.T) { want := Resource{ ResourceHeader{ Name: MustNewName("foo.bar.example.com."), Type: TypeTXT, Class: ClassINET, }, &TXTResource{[]string{ "", "", "foo bar", "", "www.example.com", "www.example.com.", strings.Repeat(".", 255), }}, } buf, err := want.pack(make([]byte, 0, 8000), map[string]int{}, 0) if err != nil { t.Fatal("Resource.pack() =", err) } var got Resource off, err := got.Header.unpack(buf, 0) if err != nil { t.Fatal("ResourceHeader.unpack() =", err) } body, n, err := unpackResourceBody(buf, off, got.Header) if err != nil { t.Fatal("unpackResourceBody() =", err) } got.Body = body if n != len(buf) { t.Errorf("unpacked different amount than packed: got = %d, want = %d", n, len(buf)) } if !reflect.DeepEqual(got, want) { t.Errorf("Resource.pack/unpack() roundtrip: got = %#v, want = %#v", got, want) } } func TestTooLongTxt(t *testing.T) { rb := TXTResource{[]string{strings.Repeat(".", 256)}} if _, err := rb.pack(make([]byte, 0, 8000), map[string]int{}, 0); err != errStringTooLong { t.Errorf("packing TXTResource with 256 character string: got err = %v, want = %v", err, errStringTooLong) } } func TestStartAppends(t *testing.T) { buf := make([]byte, 2, 514) wantBuf := []byte{4, 44} copy(buf, wantBuf) b := NewBuilder(buf, Header{}) b.EnableCompression() buf, err := b.Finish() if err != nil { t.Fatal("Builder.Finish() =", err) } if got, want := len(buf), headerLen+2; got != want { t.Errorf("got len(buf) = %d, want = %d", got, want) } if string(buf[:2]) != string(wantBuf) { t.Errorf("original data not preserved, got = %#v, want = %#v", buf[:2], wantBuf) } } func TestStartError(t *testing.T) { tests := []struct { name string fn func(*Builder) error }{ {"Questions", func(b *Builder) error { return b.StartQuestions() }}, {"Answers", func(b *Builder) error { return b.StartAnswers() }}, {"Authorities", func(b *Builder) error { return b.StartAuthorities() }}, {"Additionals", func(b *Builder) error { return b.StartAdditionals() }}, } envs := []struct { name string fn func() *Builder want error }{ {"sectionNotStarted", func() *Builder { return &Builder{section: sectionNotStarted} }, ErrNotStarted}, {"sectionDone", func() *Builder { return &Builder{section: sectionDone} }, ErrSectionDone}, } for _, env := range envs { for _, test := range tests { if got := test.fn(env.fn()); got != env.want { t.Errorf("got Builder{%s}.Start%s() = %v, want = %v", env.name, test.name, got, env.want) } } } } func TestBuilderResourceError(t *testing.T) { tests := []struct { name string fn func(*Builder) error }{ {"CNAMEResource", func(b *Builder) error { return b.CNAMEResource(ResourceHeader{}, CNAMEResource{}) }}, {"MXResource", func(b *Builder) error { return b.MXResource(ResourceHeader{}, MXResource{}) }}, {"NSResource", func(b *Builder) error { return b.NSResource(ResourceHeader{}, NSResource{}) }}, {"PTRResource", func(b *Builder) error { return b.PTRResource(ResourceHeader{}, PTRResource{}) }}, {"SOAResource", func(b *Builder) error { return b.SOAResource(ResourceHeader{}, SOAResource{}) }}, {"TXTResource", func(b *Builder) error { return b.TXTResource(ResourceHeader{}, TXTResource{}) }}, {"SRVResource", func(b *Builder) error { return b.SRVResource(ResourceHeader{}, SRVResource{}) }}, {"AResource", func(b *Builder) error { return b.AResource(ResourceHeader{}, AResource{}) }}, {"AAAAResource", func(b *Builder) error { return b.AAAAResource(ResourceHeader{}, AAAAResource{}) }}, {"OPTResource", func(b *Builder) error { return b.OPTResource(ResourceHeader{}, OPTResource{}) }}, {"UnknownResource", func(b *Builder) error { return b.UnknownResource(ResourceHeader{}, UnknownResource{}) }}, } envs := []struct { name string fn func() *Builder want error }{ {"sectionNotStarted", func() *Builder { return &Builder{section: sectionNotStarted} }, ErrNotStarted}, {"sectionHeader", func() *Builder { return &Builder{section: sectionHeader} }, ErrNotStarted}, {"sectionQuestions", func() *Builder { return &Builder{section: sectionQuestions} }, ErrNotStarted}, {"sectionDone", func() *Builder { return &Builder{section: sectionDone} }, ErrSectionDone}, } for _, env := range envs { for _, test := range tests { if got := test.fn(env.fn()); got != env.want { t.Errorf("got Builder{%s}.%s() = %v, want = %v", env.name, test.name, got, env.want) } } } } func TestFinishError(t *testing.T) { var b Builder want := ErrNotStarted if _, got := b.Finish(); got != want { t.Errorf("got Builder.Finish() = %v, want = %v", got, want) } } func TestBuilder(t *testing.T) { msg := largeTestMsg() want, err := msg.Pack() if err != nil { t.Fatal("Message.Pack() =", err) } b := NewBuilder(nil, msg.Header) b.EnableCompression() if err := b.StartQuestions(); err != nil { t.Fatal("Builder.StartQuestions() =", err) } for _, q := range msg.Questions { if err := b.Question(q); err != nil { t.Fatalf("Builder.Question(%#v) = %v", q, err) } } if err := b.StartAnswers(); err != nil { t.Fatal("Builder.StartAnswers() =", err) } for _, a := range msg.Answers { switch a.Header.Type { case TypeA: if err := b.AResource(a.Header, *a.Body.(*AResource)); err != nil { t.Fatalf("Builder.AResource(%#v) = %v", a, err) } case TypeNS: if err := b.NSResource(a.Header, *a.Body.(*NSResource)); err != nil { t.Fatalf("Builder.NSResource(%#v) = %v", a, err) } case TypeCNAME: if err := b.CNAMEResource(a.Header, *a.Body.(*CNAMEResource)); err != nil { t.Fatalf("Builder.CNAMEResource(%#v) = %v", a, err) } case TypeSOA: if err := b.SOAResource(a.Header, *a.Body.(*SOAResource)); err != nil { t.Fatalf("Builder.SOAResource(%#v) = %v", a, err) } case TypePTR: if err := b.PTRResource(a.Header, *a.Body.(*PTRResource)); err != nil { t.Fatalf("Builder.PTRResource(%#v) = %v", a, err) } case TypeMX: if err := b.MXResource(a.Header, *a.Body.(*MXResource)); err != nil { t.Fatalf("Builder.MXResource(%#v) = %v", a, err) } case TypeTXT: if err := b.TXTResource(a.Header, *a.Body.(*TXTResource)); err != nil { t.Fatalf("Builder.TXTResource(%#v) = %v", a, err) } case TypeAAAA: if err := b.AAAAResource(a.Header, *a.Body.(*AAAAResource)); err != nil { t.Fatalf("Builder.AAAAResource(%#v) = %v", a, err) } case TypeSRV: if err := b.SRVResource(a.Header, *a.Body.(*SRVResource)); err != nil { t.Fatalf("Builder.SRVResource(%#v) = %v", a, err) } case privateUseType: if err := b.UnknownResource(a.Header, *a.Body.(*UnknownResource)); err != nil { t.Fatalf("Builder.UnknownResource(%#v) = %v", a, err) } } } if err := b.StartAuthorities(); err != nil { t.Fatal("Builder.StartAuthorities() =", err) } for _, a := range msg.Authorities { if err := b.NSResource(a.Header, *a.Body.(*NSResource)); err != nil { t.Fatalf("Builder.NSResource(%#v) = %v", a, err) } } if err := b.StartAdditionals(); err != nil { t.Fatal("Builder.StartAdditionals() =", err) } for _, a := range msg.Additionals { switch a.Body.(type) { case *TXTResource: if err := b.TXTResource(a.Header, *a.Body.(*TXTResource)); err != nil { t.Fatalf("Builder.TXTResource(%#v) = %v", a, err) } case *OPTResource: if err := b.OPTResource(a.Header, *a.Body.(*OPTResource)); err != nil { t.Fatalf("Builder.OPTResource(%#v) = %v", a, err) } } } got, err := b.Finish() if err != nil { t.Fatal("Builder.Finish() =", err) } if !bytes.Equal(got, want) { t.Fatalf("got from Builder.Finish() = %#v\nwant = %#v", got, want) } } func TestResourcePack(t *testing.T) { for _, tt := range []struct { m Message err error }{ { Message{ Questions: []Question{ { Name: MustNewName("."), Type: TypeAAAA, Class: ClassINET, }, }, Answers: []Resource{{ResourceHeader{}, nil}}, }, &nestedError{"packing Answer", errNilResouceBody}, }, { Message{ Questions: []Question{ { Name: MustNewName("."), Type: TypeAAAA, Class: ClassINET, }, }, Authorities: []Resource{{ResourceHeader{}, (*NSResource)(nil)}}, }, &nestedError{"packing Authority", &nestedError{"ResourceHeader", &nestedError{"Name", errNonCanonicalName}, }, }, }, { Message{ Questions: []Question{ { Name: MustNewName("."), Type: TypeA, Class: ClassINET, }, }, Additionals: []Resource{{ResourceHeader{}, nil}}, }, &nestedError{"packing Additional", errNilResouceBody}, }, } { _, err := tt.m.Pack() if !reflect.DeepEqual(err, tt.err) { t.Errorf("got Message{%v}.Pack() = %v, want %v", tt.m, err, tt.err) } } } func TestResourcePackLength(t *testing.T) { r := Resource{ ResourceHeader{ Name: MustNewName("."), Type: TypeA, Class: ClassINET, }, &AResource{[4]byte{127, 0, 0, 2}}, } hb, _, err := r.Header.pack(nil, nil, 0) if err != nil { t.Fatal("ResourceHeader.pack() =", err) } buf := make([]byte, 0, len(hb)) buf, err = r.pack(buf, nil, 0) if err != nil { t.Fatal("Resource.pack() =", err) } var hdr ResourceHeader if _, err := hdr.unpack(buf, 0); err != nil { t.Fatal("ResourceHeader.unpack() =", err) } if got, want := int(hdr.Length), len(buf)-len(hb); got != want { t.Errorf("got hdr.Length = %d, want = %d", got, want) } } func TestOptionPackUnpack(t *testing.T) { for _, tt := range []struct { name string w []byte // wire format of m.Additionals m Message dnssecOK bool extRCode RCode }{ { name: "without EDNS(0) options", w: []byte{ 0x00, 0x00, 0x29, 0x10, 0x00, 0xfe, 0x00, 0x80, 0x00, 0x00, 0x00, }, m: Message{ Header: Header{RCode: RCodeFormatError}, Questions: []Question{ { Name: MustNewName("."), Type: TypeA, Class: ClassINET, }, }, Additionals: []Resource{ { mustEDNS0ResourceHeader(4096, 0xfe0|RCodeFormatError, true), &OPTResource{}, }, }, }, dnssecOK: true, extRCode: 0xfe0 | RCodeFormatError, }, { name: "with EDNS(0) options", w: []byte{ 0x00, 0x00, 0x29, 0x10, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x12, 0x34, }, m: Message{ Header: Header{RCode: RCodeServerFailure}, Questions: []Question{ { Name: MustNewName("."), Type: TypeAAAA, Class: ClassINET, }, }, Additionals: []Resource{ { mustEDNS0ResourceHeader(4096, 0xff0|RCodeServerFailure, false), &OPTResource{ Options: []Option{ { Code: 12, // see RFC 7828 Data: []byte{0x00, 0x00}, }, { Code: 11, // see RFC 7830 Data: []byte{0x12, 0x34}, }, }, }, }, }, }, dnssecOK: false, extRCode: 0xff0 | RCodeServerFailure, }, { // Containing multiple OPT resources in a // message is invalid, but it's necessary for // protocol conformance testing. name: "with multiple OPT resources", w: []byte{ 0x00, 0x00, 0x29, 0x10, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x02, 0x12, 0x34, 0x00, 0x00, 0x29, 0x10, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, }, m: Message{ Header: Header{RCode: RCodeNameError}, Questions: []Question{ { Name: MustNewName("."), Type: TypeAAAA, Class: ClassINET, }, }, Additionals: []Resource{ { mustEDNS0ResourceHeader(4096, 0xff0|RCodeNameError, false), &OPTResource{ Options: []Option{ { Code: 11, // see RFC 7830 Data: []byte{0x12, 0x34}, }, }, }, }, { mustEDNS0ResourceHeader(4096, 0xff0|RCodeNameError, false), &OPTResource{ Options: []Option{ { Code: 12, // see RFC 7828 Data: []byte{0x00, 0x00}, }, }, }, }, }, }, }, } { w, err := tt.m.Pack() if err != nil { t.Errorf("Message.Pack() for %s = %v", tt.name, err) continue } if !bytes.Equal(w[len(w)-len(tt.w):], tt.w) { t.Errorf("got Message.Pack() for %s = %#v, want %#v", tt.name, w[len(w)-len(tt.w):], tt.w) continue } var m Message if err := m.Unpack(w); err != nil { t.Errorf("Message.Unpack() for %s = %v", tt.name, err) continue } if !reflect.DeepEqual(m.Additionals, tt.m.Additionals) { t.Errorf("got Message.Pack/Unpack() roundtrip for %s = %+v, want %+v", tt.name, m, tt.m) continue } } } func smallTestMsgWithUnknownResource() Message { return Message{ Questions: []Question{}, Answers: []Resource{ { Header: ResourceHeader{ Name: MustNewName("."), Type: privateUseType, Class: ClassINET, TTL: uint32(123), }, Body: &UnknownResource{ // The realType() method is called, when // packing, so Type must match the type // claimed by the Header above. Type: privateUseType, Data: []byte{42, 42, 42, 42}, }, }, }, } } func TestUnknownPackUnpack(t *testing.T) { msg := smallTestMsgWithUnknownResource() packed, err := msg.Pack() if err != nil { t.Fatalf("Failed to pack UnknownResource: %v", err) } var receivedMsg Message err = receivedMsg.Unpack(packed) if err != nil { t.Fatalf("Failed to unpack UnknownResource: %v", err) } if len(receivedMsg.Answers) != 1 { t.Fatalf("Got %d answers, wanted 1", len(receivedMsg.Answers)) } unknownResource, ok := receivedMsg.Answers[0].Body.(*UnknownResource) if !ok { t.Fatalf("Parsed a %T, wanted an UnknownResource", receivedMsg.Answers[0].Body) } wantBody := msg.Answers[0].Body if !reflect.DeepEqual(wantBody, unknownResource) { t.Fatalf("Unpacked resource does not match: %v vs %v", wantBody, unknownResource) } } func TestParseUnknownResource(t *testing.T) { msg := smallTestMsgWithUnknownResource() packed, err := msg.Pack() if err != nil { t.Fatalf("Failed to pack UnknownResource: %v", err) } var p Parser if _, err = p.Start(packed); err != nil { t.Fatalf("Parser failed to start: %s", err) } if _, err = p.AllQuestions(); err != nil { t.Fatalf("Failed to parse questions: %s", err) } parsedHeader, err := p.AnswerHeader() if err != nil { t.Fatalf("Error reading answer header: %s", err) } wantHeader := msg.Answers[0].Header if !reflect.DeepEqual(wantHeader, parsedHeader) { t.Fatalf("Parsed header does not match: %v vs %v", wantHeader, wantHeader) } parsedUnknownResource, err := p.UnknownResource() if err != nil { t.Fatalf("Failed to parse UnknownResource: %s", err) } wantBody := msg.Answers[0].Body if !reflect.DeepEqual(wantBody, &parsedUnknownResource) { t.Fatalf("Parsed resource does not match: %v vs %v", wantBody, &parsedUnknownResource) } // Finish parsing the rest of the message to ensure that // (*Parser).UnknownResource() leaves the parser in a consistent state. if _, err = p.AnswerHeader(); err != ErrSectionDone { t.Fatalf("Answer section should be fully parsed") } if _, err = p.AllAuthorities(); err != nil { t.Fatalf("Failed to parse authorities: %s", err) } if _, err = p.AllAdditionals(); err != nil { t.Fatalf("Failed to parse additionals: %s", err) } } // TestGoString tests that Message.GoString produces Go code that compiles to // reproduce the Message. // // This test was produced as follows: // 1. Run (*Message).GoString on largeTestMsg(). // 2. Remove "dnsmessage." from the output. // 3. Paste the result in the test to store it in msg. // 4. Also put the original output in the test to store in want. func TestGoString(t *testing.T) { msg := Message{Header: Header{ID: 0, Response: true, OpCode: 0, Authoritative: true, Truncated: false, RecursionDesired: false, RecursionAvailable: false, RCode: RCodeSuccess}, Questions: []Question{Question{Name: MustNewName("foo.bar.example.com."), Type: TypeA, Class: ClassINET}}, Answers: []Resource{Resource{Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeA, Class: ClassINET, TTL: 0, Length: 0}, Body: &AResource{A: [4]byte{127, 0, 0, 1}}}, Resource{Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeA, Class: ClassINET, TTL: 0, Length: 0}, Body: &AResource{A: [4]byte{127, 0, 0, 2}}}, Resource{Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeAAAA, Class: ClassINET, TTL: 0, Length: 0}, Body: &AAAAResource{AAAA: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}}, Resource{Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeCNAME, Class: ClassINET, TTL: 0, Length: 0}, Body: &CNAMEResource{CNAME: MustNewName("alias.example.com.")}}, Resource{Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeSOA, Class: ClassINET, TTL: 0, Length: 0}, Body: &SOAResource{NS: MustNewName("ns1.example.com."), MBox: MustNewName("mb.example.com."), Serial: 1, Refresh: 2, Retry: 3, Expire: 4, MinTTL: 5}}, Resource{Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypePTR, Class: ClassINET, TTL: 0, Length: 0}, Body: &PTRResource{PTR: MustNewName("ptr.example.com.")}}, Resource{Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeMX, Class: ClassINET, TTL: 0, Length: 0}, Body: &MXResource{Pref: 7, MX: MustNewName("mx.example.com.")}}, Resource{Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeSRV, Class: ClassINET, TTL: 0, Length: 0}, Body: &SRVResource{Priority: 8, Weight: 9, Port: 11, Target: MustNewName("srv.example.com.")}}, Resource{Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: 65362, Class: ClassINET, TTL: 0, Length: 0}, Body: &UnknownResource{Type: 65362, Data: []byte{42, 0, 43, 44}}}}, Authorities: []Resource{Resource{Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeNS, Class: ClassINET, TTL: 0, Length: 0}, Body: &NSResource{NS: MustNewName("ns1.example.com.")}}, Resource{Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeNS, Class: ClassINET, TTL: 0, Length: 0}, Body: &NSResource{NS: MustNewName("ns2.example.com.")}}}, Additionals: []Resource{Resource{Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeTXT, Class: ClassINET, TTL: 0, Length: 0}, Body: &TXTResource{TXT: []string{"So Long\x2c and Thanks for All the Fish"}}}, Resource{Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeTXT, Class: ClassINET, TTL: 0, Length: 0}, Body: &TXTResource{TXT: []string{"Hamster Huey and the Gooey Kablooie"}}}, Resource{Header: ResourceHeader{Name: MustNewName("."), Type: TypeOPT, Class: 4096, TTL: 4261412864, Length: 0}, Body: &OPTResource{Options: []Option{Option{Code: 10, Data: []byte{1, 35, 69, 103, 137, 171, 205, 239}}}}}}} if !reflect.DeepEqual(msg, largeTestMsg()) { t.Error("Message.GoString lost information or largeTestMsg changed: msg != largeTestMsg()") } got := msg.GoString() want := `dnsmessage.Message{Header: dnsmessage.Header{ID: 0, Response: true, OpCode: 0, Authoritative: true, Truncated: false, RecursionDesired: false, RecursionAvailable: false, RCode: dnsmessage.RCodeSuccess}, Questions: []dnsmessage.Question{dnsmessage.Question{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeA, Class: dnsmessage.ClassINET}}, Answers: []dnsmessage.Resource{dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeA, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.AResource{A: [4]byte{127, 0, 0, 1}}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeA, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.AResource{A: [4]byte{127, 0, 0, 2}}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeAAAA, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.AAAAResource{AAAA: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeCNAME, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.CNAMEResource{CNAME: dnsmessage.MustNewName("alias.example.com.")}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeSOA, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.SOAResource{NS: dnsmessage.MustNewName("ns1.example.com."), MBox: dnsmessage.MustNewName("mb.example.com."), Serial: 1, Refresh: 2, Retry: 3, Expire: 4, MinTTL: 5}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypePTR, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.PTRResource{PTR: dnsmessage.MustNewName("ptr.example.com.")}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeMX, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.MXResource{Pref: 7, MX: dnsmessage.MustNewName("mx.example.com.")}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeSRV, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.SRVResource{Priority: 8, Weight: 9, Port: 11, Target: dnsmessage.MustNewName("srv.example.com.")}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: 65362, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.UnknownResource{Type: 65362, Data: []byte{42, 0, 43, 44}}}}, Authorities: []dnsmessage.Resource{dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeNS, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.NSResource{NS: dnsmessage.MustNewName("ns1.example.com.")}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeNS, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.NSResource{NS: dnsmessage.MustNewName("ns2.example.com.")}}}, Additionals: []dnsmessage.Resource{dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeTXT, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.TXTResource{TXT: []string{"So Long\x2c and Thanks for All the Fish"}}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeTXT, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.TXTResource{TXT: []string{"Hamster Huey and the Gooey Kablooie"}}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("."), Type: dnsmessage.TypeOPT, Class: 4096, TTL: 4261412864, Length: 0}, Body: &dnsmessage.OPTResource{Options: []dnsmessage.Option{dnsmessage.Option{Code: 10, Data: []byte{1, 35, 69, 103, 137, 171, 205, 239}}}}}}}` if got != want { t.Errorf("got msg1.GoString() = %s\nwant = %s", got, want) } } func benchmarkParsingSetup() ([]byte, error) { name := MustNewName("foo.bar.example.com.") msg := Message{ Header: Header{Response: true, Authoritative: true}, Questions: []Question{ { Name: name, Type: TypeA, Class: ClassINET, }, }, Answers: []Resource{ { ResourceHeader{ Name: name, Class: ClassINET, }, &AResource{[4]byte{}}, }, { ResourceHeader{ Name: name, Class: ClassINET, }, &AAAAResource{[16]byte{}}, }, { ResourceHeader{ Name: name, Class: ClassINET, }, &CNAMEResource{name}, }, { ResourceHeader{ Name: name, Class: ClassINET, }, &NSResource{name}, }, }, } buf, err := msg.Pack() if err != nil { return nil, fmt.Errorf("Message.Pack() = %v", err) } return buf, nil } func benchmarkParsing(tb testing.TB, buf []byte) { var p Parser if _, err := p.Start(buf); err != nil { tb.Fatal("Parser.Start(non-nil) =", err) } for { _, err := p.Question() if err == ErrSectionDone { break } if err != nil { tb.Fatal("Parser.Question() =", err) } } for { h, err := p.AnswerHeader() if err == ErrSectionDone { break } if err != nil { tb.Fatal("Parser.AnswerHeader() =", err) } switch h.Type { case TypeA: if _, err := p.AResource(); err != nil { tb.Fatal("Parser.AResource() =", err) } case TypeAAAA: if _, err := p.AAAAResource(); err != nil { tb.Fatal("Parser.AAAAResource() =", err) } case TypeCNAME: if _, err := p.CNAMEResource(); err != nil { tb.Fatal("Parser.CNAMEResource() =", err) } case TypeNS: if _, err := p.NSResource(); err != nil { tb.Fatal("Parser.NSResource() =", err) } case TypeOPT: if _, err := p.OPTResource(); err != nil { tb.Fatal("Parser.OPTResource() =", err) } default: tb.Fatalf("got unknown type: %T", h) } } } func BenchmarkParsing(b *testing.B) { buf, err := benchmarkParsingSetup() if err != nil { b.Fatal(err) } b.ReportAllocs() for i := 0; i < b.N; i++ { benchmarkParsing(b, buf) } } func TestParsingAllocs(t *testing.T) { buf, err := benchmarkParsingSetup() if err != nil { t.Fatal(err) } if allocs := testing.AllocsPerRun(100, func() { benchmarkParsing(t, buf) }); allocs > 0.5 { t.Errorf("allocations during parsing: got = %f, want ~0", allocs) } } func benchmarkBuildingSetup() (Name, []byte) { name := MustNewName("foo.bar.example.com.") buf := make([]byte, 0, packStartingCap) return name, buf } func benchmarkBuilding(tb testing.TB, name Name, buf []byte) { bld := NewBuilder(buf, Header{Response: true, Authoritative: true}) if err := bld.StartQuestions(); err != nil { tb.Fatal("Builder.StartQuestions() =", err) } q := Question{ Name: name, Type: TypeA, Class: ClassINET, } if err := bld.Question(q); err != nil { tb.Fatalf("Builder.Question(%+v) = %v", q, err) } hdr := ResourceHeader{ Name: name, Class: ClassINET, } if err := bld.StartAnswers(); err != nil { tb.Fatal("Builder.StartQuestions() =", err) } ar := AResource{[4]byte{}} if err := bld.AResource(hdr, ar); err != nil { tb.Fatalf("Builder.AResource(%+v, %+v) = %v", hdr, ar, err) } aaar := AAAAResource{[16]byte{}} if err := bld.AAAAResource(hdr, aaar); err != nil { tb.Fatalf("Builder.AAAAResource(%+v, %+v) = %v", hdr, aaar, err) } cnr := CNAMEResource{name} if err := bld.CNAMEResource(hdr, cnr); err != nil { tb.Fatalf("Builder.CNAMEResource(%+v, %+v) = %v", hdr, cnr, err) } nsr := NSResource{name} if err := bld.NSResource(hdr, nsr); err != nil { tb.Fatalf("Builder.NSResource(%+v, %+v) = %v", hdr, nsr, err) } extrc := 0xfe0 | RCodeNotImplemented if err := (&hdr).SetEDNS0(4096, extrc, true); err != nil { tb.Fatalf("ResourceHeader.SetEDNS0(4096, %#x, true) = %v", extrc, err) } optr := OPTResource{} if err := bld.OPTResource(hdr, optr); err != nil { tb.Fatalf("Builder.OPTResource(%+v, %+v) = %v", hdr, optr, err) } if _, err := bld.Finish(); err != nil { tb.Fatal("Builder.Finish() =", err) } } func BenchmarkBuilding(b *testing.B) { name, buf := benchmarkBuildingSetup() b.ReportAllocs() for i := 0; i < b.N; i++ { benchmarkBuilding(b, name, buf) } } func TestBuildingAllocs(t *testing.T) { name, buf := benchmarkBuildingSetup() if allocs := testing.AllocsPerRun(100, func() { benchmarkBuilding(t, name, buf) }); allocs > 0.5 { t.Errorf("allocations during building: got = %f, want ~0", allocs) } } func smallTestMsg() Message { name := MustNewName("example.com.") return Message{ Header: Header{Response: true, Authoritative: true}, Questions: []Question{ { Name: name, Type: TypeA, Class: ClassINET, }, }, Answers: []Resource{ { ResourceHeader{ Name: name, Type: TypeA, Class: ClassINET, }, &AResource{[4]byte{127, 0, 0, 1}}, }, }, Authorities: []Resource{ { ResourceHeader{ Name: name, Type: TypeA, Class: ClassINET, }, &AResource{[4]byte{127, 0, 0, 1}}, }, }, Additionals: []Resource{ { ResourceHeader{ Name: name, Type: TypeA, Class: ClassINET, }, &AResource{[4]byte{127, 0, 0, 1}}, }, }, } } func BenchmarkPack(b *testing.B) { msg := largeTestMsg() b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := msg.Pack(); err != nil { b.Fatal("Message.Pack() =", err) } } } func BenchmarkAppendPack(b *testing.B) { msg := largeTestMsg() buf := make([]byte, 0, packStartingCap) b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := msg.AppendPack(buf[:0]); err != nil { b.Fatal("Message.AppendPack() = ", err) } } } func largeTestMsg() Message { name := MustNewName("foo.bar.example.com.") return Message{ Header: Header{Response: true, Authoritative: true}, Questions: []Question{ { Name: name, Type: TypeA, Class: ClassINET, }, }, Answers: []Resource{ { ResourceHeader{ Name: name, Type: TypeA, Class: ClassINET, }, &AResource{[4]byte{127, 0, 0, 1}}, }, { ResourceHeader{ Name: name, Type: TypeA, Class: ClassINET, }, &AResource{[4]byte{127, 0, 0, 2}}, }, { ResourceHeader{ Name: name, Type: TypeAAAA, Class: ClassINET, }, &AAAAResource{[16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}, }, { ResourceHeader{ Name: name, Type: TypeCNAME, Class: ClassINET, }, &CNAMEResource{MustNewName("alias.example.com.")}, }, { ResourceHeader{ Name: name, Type: TypeSOA, Class: ClassINET, }, &SOAResource{ NS: MustNewName("ns1.example.com."), MBox: MustNewName("mb.example.com."), Serial: 1, Refresh: 2, Retry: 3, Expire: 4, MinTTL: 5, }, }, { ResourceHeader{ Name: name, Type: TypePTR, Class: ClassINET, }, &PTRResource{MustNewName("ptr.example.com.")}, }, { ResourceHeader{ Name: name, Type: TypeMX, Class: ClassINET, }, &MXResource{ 7, MustNewName("mx.example.com."), }, }, { ResourceHeader{ Name: name, Type: TypeSRV, Class: ClassINET, }, &SRVResource{ 8, 9, 11, MustNewName("srv.example.com."), }, }, { ResourceHeader{ Name: name, Type: privateUseType, Class: ClassINET, }, &UnknownResource{ Type: privateUseType, Data: []byte{42, 0, 43, 44}, }, }, }, Authorities: []Resource{ { ResourceHeader{ Name: name, Type: TypeNS, Class: ClassINET, }, &NSResource{MustNewName("ns1.example.com.")}, }, { ResourceHeader{ Name: name, Type: TypeNS, Class: ClassINET, }, &NSResource{MustNewName("ns2.example.com.")}, }, }, Additionals: []Resource{ { ResourceHeader{ Name: name, Type: TypeTXT, Class: ClassINET, }, &TXTResource{[]string{"So Long, and Thanks for All the Fish"}}, }, { ResourceHeader{ Name: name, Type: TypeTXT, Class: ClassINET, }, &TXTResource{[]string{"Hamster Huey and the Gooey Kablooie"}}, }, { mustEDNS0ResourceHeader(4096, 0xfe0|RCodeSuccess, false), &OPTResource{ Options: []Option{ { Code: 10, // see RFC 7873 Data: []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, }, }, }, }, }, } } // This package is imported by the standard library net package // and therefore must not use fmt. We'll catch a mistake when vendored // into the standard library, but this test catches the mistake earlier. func TestNoFmt(t *testing.T) { files, err := filepath.Glob("*.go") if err != nil { t.Fatal(err) } for _, file := range files { if strings.HasSuffix(file, "_test.go") { continue } // Could use something complex like go/build or x/tools/go/packages, // but there's no reason for "fmt" to appear (in quotes) in the source // otherwise, so just use a simple substring search. data, err := ioutil.ReadFile(file) if err != nil { t.Fatal(err) } if bytes.Contains(data, []byte(`"fmt"`)) { t.Errorf(`%s: cannot import "fmt"`, file) } } } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/go.mod000066400000000000000000000002631415442132300215360ustar00rootroot00000000000000module golang.org/x/net go 1.17 require ( golang.org/x/sys v0.0.0-20210423082822-04245dca01da golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 golang.org/x/text v0.3.6 ) golang-golang-x-net-0.0+git20211209.491a49a+dfsg/go.sum000066400000000000000000000014211415442132300215600ustar00rootroot00000000000000golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/000077500000000000000000000000001415442132300213735ustar00rootroot00000000000000golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/atom/000077500000000000000000000000001415442132300223335ustar00rootroot00000000000000golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/atom/atom.go000066400000000000000000000044211415442132300236230ustar00rootroot00000000000000// Copyright 2012 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 atom provides integer codes (also known as atoms) for a fixed set of // frequently occurring HTML strings: tag names and attribute keys such as "p" // and "id". // // Sharing an atom's name between all elements with the same tag can result in // fewer string allocations when tokenizing and parsing HTML. Integer // comparisons are also generally faster than string comparisons. // // The value of an atom's particular code is not guaranteed to stay the same // between versions of this package. Neither is any ordering guaranteed: // whether atom.H1 < atom.H2 may also change. The codes are not guaranteed to // be dense. The only guarantees are that e.g. looking up "div" will yield // atom.Div, calling atom.Div.String will return "div", and atom.Div != 0. package atom // import "golang.org/x/net/html/atom" // Atom is an integer code for a string. The zero value maps to "". type Atom uint32 // String returns the atom's name. func (a Atom) String() string { start := uint32(a >> 8) n := uint32(a & 0xff) if start+n > uint32(len(atomText)) { return "" } return atomText[start : start+n] } func (a Atom) string() string { return atomText[a>>8 : a>>8+a&0xff] } // fnv computes the FNV hash with an arbitrary starting value h. func fnv(h uint32, s []byte) uint32 { for i := range s { h ^= uint32(s[i]) h *= 16777619 } return h } func match(s string, t []byte) bool { for i, c := range t { if s[i] != c { return false } } return true } // Lookup returns the atom whose name is s. It returns zero if there is no // such atom. The lookup is case sensitive. func Lookup(s []byte) Atom { if len(s) == 0 || len(s) > maxAtomLen { return 0 } h := fnv(hash0, s) if a := table[h&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) { return a } if a := table[(h>>16)&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) { return a } return 0 } // String returns a string whose contents are equal to s. In that sense, it is // equivalent to string(s) but may be more efficient. func String(s []byte) string { if a := Lookup(s); a != 0 { return a.String() } return string(s) } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/atom/atom_test.go000066400000000000000000000041621415442132300246640ustar00rootroot00000000000000// Copyright 2012 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 atom import ( "sort" "testing" ) func TestKnown(t *testing.T) { for _, s := range testAtomList { if atom := Lookup([]byte(s)); atom.String() != s { t.Errorf("Lookup(%q) = %#x (%q)", s, uint32(atom), atom.String()) } } } func TestHits(t *testing.T) { for _, a := range table { if a == 0 { continue } got := Lookup([]byte(a.String())) if got != a { t.Errorf("Lookup(%q) = %#x, want %#x", a.String(), uint32(got), uint32(a)) } } } func TestMisses(t *testing.T) { testCases := []string{ "", "\x00", "\xff", "A", "DIV", "Div", "dIV", "aa", "a\x00", "ab", "abb", "abbr0", "abbr ", " abbr", " a", "acceptcharset", "acceptCharset", "accept_charset", "h0", "h1h2", "h7", "onClick", "λ", // The following string has the same hash (0xa1d7fab7) as "onmouseover". "\x00\x00\x00\x00\x00\x50\x18\xae\x38\xd0\xb7", } for _, tc := range testCases { got := Lookup([]byte(tc)) if got != 0 { t.Errorf("Lookup(%q): got %d, want 0", tc, got) } } } func TestForeignObject(t *testing.T) { const ( afo = Foreignobject afO = ForeignObject sfo = "foreignobject" sfO = "foreignObject" ) if got := Lookup([]byte(sfo)); got != afo { t.Errorf("Lookup(%q): got %#v, want %#v", sfo, got, afo) } if got := Lookup([]byte(sfO)); got != afO { t.Errorf("Lookup(%q): got %#v, want %#v", sfO, got, afO) } if got := afo.String(); got != sfo { t.Errorf("Atom(%#v).String(): got %q, want %q", afo, got, sfo) } if got := afO.String(); got != sfO { t.Errorf("Atom(%#v).String(): got %q, want %q", afO, got, sfO) } } func BenchmarkLookup(b *testing.B) { sortedTable := make([]string, 0, len(table)) for _, a := range table { if a != 0 { sortedTable = append(sortedTable, a.String()) } } sort.Strings(sortedTable) x := make([][]byte, 1000) for i := range x { x[i] = []byte(sortedTable[i%len(sortedTable)]) } b.ResetTimer() for i := 0; i < b.N; i++ { for _, s := range x { Lookup(s) } } } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/atom/gen.go000066400000000000000000000275111415442132300234410ustar00rootroot00000000000000// Copyright 2012 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 ignore // +build ignore //go:generate go run gen.go //go:generate go run gen.go -test package main import ( "bytes" "flag" "fmt" "go/format" "io/ioutil" "math/rand" "os" "sort" "strings" ) // identifier converts s to a Go exported identifier. // It converts "div" to "Div" and "accept-charset" to "AcceptCharset". func identifier(s string) string { b := make([]byte, 0, len(s)) cap := true for _, c := range s { if c == '-' { cap = true continue } if cap && 'a' <= c && c <= 'z' { c -= 'a' - 'A' } cap = false b = append(b, byte(c)) } return string(b) } var test = flag.Bool("test", false, "generate table_test.go") func genFile(name string, buf *bytes.Buffer) { b, err := format.Source(buf.Bytes()) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } if err := ioutil.WriteFile(name, b, 0644); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func main() { flag.Parse() var all []string all = append(all, elements...) all = append(all, attributes...) all = append(all, eventHandlers...) all = append(all, extra...) sort.Strings(all) // uniq - lists have dups w := 0 for _, s := range all { if w == 0 || all[w-1] != s { all[w] = s w++ } } all = all[:w] if *test { var buf bytes.Buffer fmt.Fprintln(&buf, "// Code generated by go generate gen.go; DO NOT EDIT.\n") fmt.Fprintln(&buf, "//go:generate go run gen.go -test\n") fmt.Fprintln(&buf, "package atom\n") fmt.Fprintln(&buf, "var testAtomList = []string{") for _, s := range all { fmt.Fprintf(&buf, "\t%q,\n", s) } fmt.Fprintln(&buf, "}") genFile("table_test.go", &buf) return } // Find hash that minimizes table size. var best *table for i := 0; i < 1000000; i++ { if best != nil && 1<<(best.k-1) < len(all) { break } h := rand.Uint32() for k := uint(0); k <= 16; k++ { if best != nil && k >= best.k { break } var t table if t.init(h, k, all) { best = &t break } } } if best == nil { fmt.Fprintf(os.Stderr, "failed to construct string table\n") os.Exit(1) } // Lay out strings, using overlaps when possible. layout := append([]string{}, all...) // Remove strings that are substrings of other strings for changed := true; changed; { changed = false for i, s := range layout { if s == "" { continue } for j, t := range layout { if i != j && t != "" && strings.Contains(s, t) { changed = true layout[j] = "" } } } } // Join strings where one suffix matches another prefix. for { // Find best i, j, k such that layout[i][len-k:] == layout[j][:k], // maximizing overlap length k. besti := -1 bestj := -1 bestk := 0 for i, s := range layout { if s == "" { continue } for j, t := range layout { if i == j { continue } for k := bestk + 1; k <= len(s) && k <= len(t); k++ { if s[len(s)-k:] == t[:k] { besti = i bestj = j bestk = k } } } } if bestk > 0 { layout[besti] += layout[bestj][bestk:] layout[bestj] = "" continue } break } text := strings.Join(layout, "") atom := map[string]uint32{} for _, s := range all { off := strings.Index(text, s) if off < 0 { panic("lost string " + s) } atom[s] = uint32(off<<8 | len(s)) } var buf bytes.Buffer // Generate the Go code. fmt.Fprintln(&buf, "// Code generated by go generate gen.go; DO NOT EDIT.\n") fmt.Fprintln(&buf, "//go:generate go run gen.go\n") fmt.Fprintln(&buf, "package atom\n\nconst (") // compute max len maxLen := 0 for _, s := range all { if maxLen < len(s) { maxLen = len(s) } fmt.Fprintf(&buf, "\t%s Atom = %#x\n", identifier(s), atom[s]) } fmt.Fprintln(&buf, ")\n") fmt.Fprintf(&buf, "const hash0 = %#x\n\n", best.h0) fmt.Fprintf(&buf, "const maxAtomLen = %d\n\n", maxLen) fmt.Fprintf(&buf, "var table = [1<<%d]Atom{\n", best.k) for i, s := range best.tab { if s == "" { continue } fmt.Fprintf(&buf, "\t%#x: %#x, // %s\n", i, atom[s], s) } fmt.Fprintf(&buf, "}\n") datasize := (1 << best.k) * 4 fmt.Fprintln(&buf, "const atomText =") textsize := len(text) for len(text) > 60 { fmt.Fprintf(&buf, "\t%q +\n", text[:60]) text = text[60:] } fmt.Fprintf(&buf, "\t%q\n\n", text) genFile("table.go", &buf) fmt.Fprintf(os.Stdout, "%d atoms; %d string bytes + %d tables = %d total data\n", len(all), textsize, datasize, textsize+datasize) } type byLen []string func (x byLen) Less(i, j int) bool { return len(x[i]) > len(x[j]) } func (x byLen) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byLen) Len() int { return len(x) } // fnv computes the FNV hash with an arbitrary starting value h. func fnv(h uint32, s string) uint32 { for i := 0; i < len(s); i++ { h ^= uint32(s[i]) h *= 16777619 } return h } // A table represents an attempt at constructing the lookup table. // The lookup table uses cuckoo hashing, meaning that each string // can be found in one of two positions. type table struct { h0 uint32 k uint mask uint32 tab []string } // hash returns the two hashes for s. func (t *table) hash(s string) (h1, h2 uint32) { h := fnv(t.h0, s) h1 = h & t.mask h2 = (h >> 16) & t.mask return } // init initializes the table with the given parameters. // h0 is the initial hash value, // k is the number of bits of hash value to use, and // x is the list of strings to store in the table. // init returns false if the table cannot be constructed. func (t *table) init(h0 uint32, k uint, x []string) bool { t.h0 = h0 t.k = k t.tab = make([]string, 1< len(t.tab) { return false } s := t.tab[i] h1, h2 := t.hash(s) j := h1 + h2 - i if t.tab[j] != "" && !t.push(j, depth+1) { return false } t.tab[j] = s return true } // The lists of element names and attribute keys were taken from // https://html.spec.whatwg.org/multipage/indices.html#index // as of the "HTML Living Standard - Last Updated 16 April 2018" version. // "command", "keygen" and "menuitem" have been removed from the spec, // but are kept here for backwards compatibility. var elements = []string{ "a", "abbr", "address", "area", "article", "aside", "audio", "b", "base", "bdi", "bdo", "blockquote", "body", "br", "button", "canvas", "caption", "cite", "code", "col", "colgroup", "command", "data", "datalist", "dd", "del", "details", "dfn", "dialog", "div", "dl", "dt", "em", "embed", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "iframe", "img", "input", "ins", "kbd", "keygen", "label", "legend", "li", "link", "main", "map", "mark", "menu", "menuitem", "meta", "meter", "nav", "noscript", "object", "ol", "optgroup", "option", "output", "p", "param", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "script", "section", "select", "slot", "small", "source", "span", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "u", "ul", "var", "video", "wbr", } // https://html.spec.whatwg.org/multipage/indices.html#attributes-3 // // "challenge", "command", "contextmenu", "dropzone", "icon", "keytype", "mediagroup", // "radiogroup", "spellcheck", "scoped", "seamless", "sortable" and "sorted" have been removed from the spec, // but are kept here for backwards compatibility. var attributes = []string{ "abbr", "accept", "accept-charset", "accesskey", "action", "allowfullscreen", "allowpaymentrequest", "allowusermedia", "alt", "as", "async", "autocomplete", "autofocus", "autoplay", "challenge", "charset", "checked", "cite", "class", "color", "cols", "colspan", "command", "content", "contenteditable", "contextmenu", "controls", "coords", "crossorigin", "data", "datetime", "default", "defer", "dir", "dirname", "disabled", "download", "draggable", "dropzone", "enctype", "for", "form", "formaction", "formenctype", "formmethod", "formnovalidate", "formtarget", "headers", "height", "hidden", "high", "href", "hreflang", "http-equiv", "icon", "id", "inputmode", "integrity", "is", "ismap", "itemid", "itemprop", "itemref", "itemscope", "itemtype", "keytype", "kind", "label", "lang", "list", "loop", "low", "manifest", "max", "maxlength", "media", "mediagroup", "method", "min", "minlength", "multiple", "muted", "name", "nomodule", "nonce", "novalidate", "open", "optimum", "pattern", "ping", "placeholder", "playsinline", "poster", "preload", "radiogroup", "readonly", "referrerpolicy", "rel", "required", "reversed", "rows", "rowspan", "sandbox", "spellcheck", "scope", "scoped", "seamless", "selected", "shape", "size", "sizes", "sortable", "sorted", "slot", "span", "spellcheck", "src", "srcdoc", "srclang", "srcset", "start", "step", "style", "tabindex", "target", "title", "translate", "type", "typemustmatch", "updateviacache", "usemap", "value", "width", "workertype", "wrap", } // "onautocomplete", "onautocompleteerror", "onmousewheel", // "onshow" and "onsort" have been removed from the spec, // but are kept here for backwards compatibility. var eventHandlers = []string{ "onabort", "onautocomplete", "onautocompleteerror", "onauxclick", "onafterprint", "onbeforeprint", "onbeforeunload", "onblur", "oncancel", "oncanplay", "oncanplaythrough", "onchange", "onclick", "onclose", "oncontextmenu", "oncopy", "oncuechange", "oncut", "ondblclick", "ondrag", "ondragend", "ondragenter", "ondragexit", "ondragleave", "ondragover", "ondragstart", "ondrop", "ondurationchange", "onemptied", "onended", "onerror", "onfocus", "onhashchange", "oninput", "oninvalid", "onkeydown", "onkeypress", "onkeyup", "onlanguagechange", "onload", "onloadeddata", "onloadedmetadata", "onloadend", "onloadstart", "onmessage", "onmessageerror", "onmousedown", "onmouseenter", "onmouseleave", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onmousewheel", "onwheel", "onoffline", "ononline", "onpagehide", "onpageshow", "onpaste", "onpause", "onplay", "onplaying", "onpopstate", "onprogress", "onratechange", "onreset", "onresize", "onrejectionhandled", "onscroll", "onsecuritypolicyviolation", "onseeked", "onseeking", "onselect", "onshow", "onsort", "onstalled", "onstorage", "onsubmit", "onsuspend", "ontimeupdate", "ontoggle", "onunhandledrejection", "onunload", "onvolumechange", "onwaiting", } // extra are ad-hoc values not covered by any of the lists above. var extra = []string{ "acronym", "align", "annotation", "annotation-xml", "applet", "basefont", "bgsound", "big", "blink", "center", "color", "desc", "face", "font", "foreignObject", // HTML is case-insensitive, but SVG-embedded-in-HTML is case-sensitive. "foreignobject", "frame", "frameset", "image", "isindex", // "isindex" has been removed from the spec, but are kept here for backwards compatibility. "listing", "malignmark", "marquee", "math", "mglyph", "mi", "mn", "mo", "ms", "mtext", "nobr", "noembed", "noframes", "plaintext", "prompt", "public", "rb", "rtc", "spacer", "strike", "svg", "system", "tt", "xmp", } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/atom/table.go000066400000000000000000000662561415442132300237700ustar00rootroot00000000000000// Code generated by go generate gen.go; DO NOT EDIT. //go:generate go run gen.go package atom const ( A Atom = 0x1 Abbr Atom = 0x4 Accept Atom = 0x1a06 AcceptCharset Atom = 0x1a0e Accesskey Atom = 0x2c09 Acronym Atom = 0xaa07 Action Atom = 0x27206 Address Atom = 0x6f307 Align Atom = 0xb105 Allowfullscreen Atom = 0x2080f Allowpaymentrequest Atom = 0xc113 Allowusermedia Atom = 0xdd0e Alt Atom = 0xf303 Annotation Atom = 0x1c90a AnnotationXml Atom = 0x1c90e Applet Atom = 0x31906 Area Atom = 0x35604 Article Atom = 0x3fc07 As Atom = 0x3c02 Aside Atom = 0x10705 Async Atom = 0xff05 Audio Atom = 0x11505 Autocomplete Atom = 0x2780c Autofocus Atom = 0x12109 Autoplay Atom = 0x13c08 B Atom = 0x101 Base Atom = 0x3b04 Basefont Atom = 0x3b08 Bdi Atom = 0xba03 Bdo Atom = 0x14b03 Bgsound Atom = 0x15e07 Big Atom = 0x17003 Blink Atom = 0x17305 Blockquote Atom = 0x1870a Body Atom = 0x2804 Br Atom = 0x202 Button Atom = 0x19106 Canvas Atom = 0x10306 Caption Atom = 0x23107 Center Atom = 0x22006 Challenge Atom = 0x29b09 Charset Atom = 0x2107 Checked Atom = 0x47907 Cite Atom = 0x19c04 Class Atom = 0x56405 Code Atom = 0x5c504 Col Atom = 0x1ab03 Colgroup Atom = 0x1ab08 Color Atom = 0x1bf05 Cols Atom = 0x1c404 Colspan Atom = 0x1c407 Command Atom = 0x1d707 Content Atom = 0x58b07 Contenteditable Atom = 0x58b0f Contextmenu Atom = 0x3800b Controls Atom = 0x1de08 Coords Atom = 0x1ea06 Crossorigin Atom = 0x1fb0b Data Atom = 0x4a504 Datalist Atom = 0x4a508 Datetime Atom = 0x2b808 Dd Atom = 0x2d702 Default Atom = 0x10a07 Defer Atom = 0x5c705 Del Atom = 0x45203 Desc Atom = 0x56104 Details Atom = 0x7207 Dfn Atom = 0x8703 Dialog Atom = 0xbb06 Dir Atom = 0x9303 Dirname Atom = 0x9307 Disabled Atom = 0x16408 Div Atom = 0x16b03 Dl Atom = 0x5e602 Download Atom = 0x46308 Draggable Atom = 0x17a09 Dropzone Atom = 0x40508 Dt Atom = 0x64b02 Em Atom = 0x6e02 Embed Atom = 0x6e05 Enctype Atom = 0x28d07 Face Atom = 0x21e04 Fieldset Atom = 0x22608 Figcaption Atom = 0x22e0a Figure Atom = 0x24806 Font Atom = 0x3f04 Footer Atom = 0xf606 For Atom = 0x25403 ForeignObject Atom = 0x2540d Foreignobject Atom = 0x2610d Form Atom = 0x26e04 Formaction Atom = 0x26e0a Formenctype Atom = 0x2890b Formmethod Atom = 0x2a40a Formnovalidate Atom = 0x2ae0e Formtarget Atom = 0x2c00a Frame Atom = 0x8b05 Frameset Atom = 0x8b08 H1 Atom = 0x15c02 H2 Atom = 0x2de02 H3 Atom = 0x30d02 H4 Atom = 0x34502 H5 Atom = 0x34f02 H6 Atom = 0x64d02 Head Atom = 0x33104 Header Atom = 0x33106 Headers Atom = 0x33107 Height Atom = 0x5206 Hgroup Atom = 0x2ca06 Hidden Atom = 0x2d506 High Atom = 0x2db04 Hr Atom = 0x15702 Href Atom = 0x2e004 Hreflang Atom = 0x2e008 Html Atom = 0x5604 HttpEquiv Atom = 0x2e80a I Atom = 0x601 Icon Atom = 0x58a04 Id Atom = 0x10902 Iframe Atom = 0x2fc06 Image Atom = 0x30205 Img Atom = 0x30703 Input Atom = 0x44b05 Inputmode Atom = 0x44b09 Ins Atom = 0x20403 Integrity Atom = 0x23f09 Is Atom = 0x16502 Isindex Atom = 0x30f07 Ismap Atom = 0x31605 Itemid Atom = 0x38b06 Itemprop Atom = 0x19d08 Itemref Atom = 0x3cd07 Itemscope Atom = 0x67109 Itemtype Atom = 0x31f08 Kbd Atom = 0xb903 Keygen Atom = 0x3206 Keytype Atom = 0xd607 Kind Atom = 0x17704 Label Atom = 0x5905 Lang Atom = 0x2e404 Legend Atom = 0x18106 Li Atom = 0xb202 Link Atom = 0x17404 List Atom = 0x4a904 Listing Atom = 0x4a907 Loop Atom = 0x5d04 Low Atom = 0xc303 Main Atom = 0x1004 Malignmark Atom = 0xb00a Manifest Atom = 0x6d708 Map Atom = 0x31803 Mark Atom = 0xb604 Marquee Atom = 0x32707 Math Atom = 0x32e04 Max Atom = 0x33d03 Maxlength Atom = 0x33d09 Media Atom = 0xe605 Mediagroup Atom = 0xe60a Menu Atom = 0x38704 Menuitem Atom = 0x38708 Meta Atom = 0x4b804 Meter Atom = 0x9805 Method Atom = 0x2a806 Mglyph Atom = 0x30806 Mi Atom = 0x34702 Min Atom = 0x34703 Minlength Atom = 0x34709 Mn Atom = 0x2b102 Mo Atom = 0xa402 Ms Atom = 0x67402 Mtext Atom = 0x35105 Multiple Atom = 0x35f08 Muted Atom = 0x36705 Name Atom = 0x9604 Nav Atom = 0x1303 Nobr Atom = 0x3704 Noembed Atom = 0x6c07 Noframes Atom = 0x8908 Nomodule Atom = 0xa208 Nonce Atom = 0x1a605 Noscript Atom = 0x21608 Novalidate Atom = 0x2b20a Object Atom = 0x26806 Ol Atom = 0x13702 Onabort Atom = 0x19507 Onafterprint Atom = 0x2360c Onautocomplete Atom = 0x2760e Onautocompleteerror Atom = 0x27613 Onauxclick Atom = 0x61f0a Onbeforeprint Atom = 0x69e0d Onbeforeunload Atom = 0x6e70e Onblur Atom = 0x56d06 Oncancel Atom = 0x11908 Oncanplay Atom = 0x14d09 Oncanplaythrough Atom = 0x14d10 Onchange Atom = 0x41b08 Onclick Atom = 0x2f507 Onclose Atom = 0x36c07 Oncontextmenu Atom = 0x37e0d Oncopy Atom = 0x39106 Oncuechange Atom = 0x3970b Oncut Atom = 0x3a205 Ondblclick Atom = 0x3a70a Ondrag Atom = 0x3b106 Ondragend Atom = 0x3b109 Ondragenter Atom = 0x3ba0b Ondragexit Atom = 0x3c50a Ondragleave Atom = 0x3df0b Ondragover Atom = 0x3ea0a Ondragstart Atom = 0x3f40b Ondrop Atom = 0x40306 Ondurationchange Atom = 0x41310 Onemptied Atom = 0x40a09 Onended Atom = 0x42307 Onerror Atom = 0x42a07 Onfocus Atom = 0x43107 Onhashchange Atom = 0x43d0c Oninput Atom = 0x44907 Oninvalid Atom = 0x45509 Onkeydown Atom = 0x45e09 Onkeypress Atom = 0x46b0a Onkeyup Atom = 0x48007 Onlanguagechange Atom = 0x48d10 Onload Atom = 0x49d06 Onloadeddata Atom = 0x49d0c Onloadedmetadata Atom = 0x4b010 Onloadend Atom = 0x4c609 Onloadstart Atom = 0x4cf0b Onmessage Atom = 0x4da09 Onmessageerror Atom = 0x4da0e Onmousedown Atom = 0x4e80b Onmouseenter Atom = 0x4f30c Onmouseleave Atom = 0x4ff0c Onmousemove Atom = 0x50b0b Onmouseout Atom = 0x5160a Onmouseover Atom = 0x5230b Onmouseup Atom = 0x52e09 Onmousewheel Atom = 0x53c0c Onoffline Atom = 0x54809 Ononline Atom = 0x55108 Onpagehide Atom = 0x5590a Onpageshow Atom = 0x5730a Onpaste Atom = 0x57f07 Onpause Atom = 0x59a07 Onplay Atom = 0x5a406 Onplaying Atom = 0x5a409 Onpopstate Atom = 0x5ad0a Onprogress Atom = 0x5b70a Onratechange Atom = 0x5cc0c Onrejectionhandled Atom = 0x5d812 Onreset Atom = 0x5ea07 Onresize Atom = 0x5f108 Onscroll Atom = 0x60008 Onsecuritypolicyviolation Atom = 0x60819 Onseeked Atom = 0x62908 Onseeking Atom = 0x63109 Onselect Atom = 0x63a08 Onshow Atom = 0x64406 Onsort Atom = 0x64f06 Onstalled Atom = 0x65909 Onstorage Atom = 0x66209 Onsubmit Atom = 0x66b08 Onsuspend Atom = 0x67b09 Ontimeupdate Atom = 0x400c Ontoggle Atom = 0x68408 Onunhandledrejection Atom = 0x68c14 Onunload Atom = 0x6ab08 Onvolumechange Atom = 0x6b30e Onwaiting Atom = 0x6c109 Onwheel Atom = 0x6ca07 Open Atom = 0x1a304 Optgroup Atom = 0x5f08 Optimum Atom = 0x6d107 Option Atom = 0x6e306 Output Atom = 0x51d06 P Atom = 0xc01 Param Atom = 0xc05 Pattern Atom = 0x6607 Picture Atom = 0x7b07 Ping Atom = 0xef04 Placeholder Atom = 0x1310b Plaintext Atom = 0x1b209 Playsinline Atom = 0x1400b Poster Atom = 0x2cf06 Pre Atom = 0x47003 Preload Atom = 0x48607 Progress Atom = 0x5b908 Prompt Atom = 0x53606 Public Atom = 0x58606 Q Atom = 0xcf01 Radiogroup Atom = 0x30a Rb Atom = 0x3a02 Readonly Atom = 0x35708 Referrerpolicy Atom = 0x3d10e Rel Atom = 0x48703 Required Atom = 0x24c08 Reversed Atom = 0x8008 Rows Atom = 0x9c04 Rowspan Atom = 0x9c07 Rp Atom = 0x23c02 Rt Atom = 0x19a02 Rtc Atom = 0x19a03 Ruby Atom = 0xfb04 S Atom = 0x2501 Samp Atom = 0x7804 Sandbox Atom = 0x12907 Scope Atom = 0x67505 Scoped Atom = 0x67506 Script Atom = 0x21806 Seamless Atom = 0x37108 Section Atom = 0x56807 Select Atom = 0x63c06 Selected Atom = 0x63c08 Shape Atom = 0x1e505 Size Atom = 0x5f504 Sizes Atom = 0x5f505 Slot Atom = 0x1ef04 Small Atom = 0x20605 Sortable Atom = 0x65108 Sorted Atom = 0x33706 Source Atom = 0x37806 Spacer Atom = 0x43706 Span Atom = 0x9f04 Spellcheck Atom = 0x4740a Src Atom = 0x5c003 Srcdoc Atom = 0x5c006 Srclang Atom = 0x5f907 Srcset Atom = 0x6f906 Start Atom = 0x3fa05 Step Atom = 0x58304 Strike Atom = 0xd206 Strong Atom = 0x6dd06 Style Atom = 0x6ff05 Sub Atom = 0x66d03 Summary Atom = 0x70407 Sup Atom = 0x70b03 Svg Atom = 0x70e03 System Atom = 0x71106 Tabindex Atom = 0x4be08 Table Atom = 0x59505 Target Atom = 0x2c406 Tbody Atom = 0x2705 Td Atom = 0x9202 Template Atom = 0x71408 Textarea Atom = 0x35208 Tfoot Atom = 0xf505 Th Atom = 0x15602 Thead Atom = 0x33005 Time Atom = 0x4204 Title Atom = 0x11005 Tr Atom = 0xcc02 Track Atom = 0x1ba05 Translate Atom = 0x1f209 Tt Atom = 0x6802 Type Atom = 0xd904 Typemustmatch Atom = 0x2900d U Atom = 0xb01 Ul Atom = 0xa702 Updateviacache Atom = 0x460e Usemap Atom = 0x59e06 Value Atom = 0x1505 Var Atom = 0x16d03 Video Atom = 0x2f105 Wbr Atom = 0x57c03 Width Atom = 0x64905 Workertype Atom = 0x71c0a Wrap Atom = 0x72604 Xmp Atom = 0x12f03 ) const hash0 = 0x81cdf10e const maxAtomLen = 25 var table = [1 << 9]Atom{ 0x1: 0xe60a, // mediagroup 0x2: 0x2e404, // lang 0x4: 0x2c09, // accesskey 0x5: 0x8b08, // frameset 0x7: 0x63a08, // onselect 0x8: 0x71106, // system 0xa: 0x64905, // width 0xc: 0x2890b, // formenctype 0xd: 0x13702, // ol 0xe: 0x3970b, // oncuechange 0x10: 0x14b03, // bdo 0x11: 0x11505, // audio 0x12: 0x17a09, // draggable 0x14: 0x2f105, // video 0x15: 0x2b102, // mn 0x16: 0x38704, // menu 0x17: 0x2cf06, // poster 0x19: 0xf606, // footer 0x1a: 0x2a806, // method 0x1b: 0x2b808, // datetime 0x1c: 0x19507, // onabort 0x1d: 0x460e, // updateviacache 0x1e: 0xff05, // async 0x1f: 0x49d06, // onload 0x21: 0x11908, // oncancel 0x22: 0x62908, // onseeked 0x23: 0x30205, // image 0x24: 0x5d812, // onrejectionhandled 0x26: 0x17404, // link 0x27: 0x51d06, // output 0x28: 0x33104, // head 0x29: 0x4ff0c, // onmouseleave 0x2a: 0x57f07, // onpaste 0x2b: 0x5a409, // onplaying 0x2c: 0x1c407, // colspan 0x2f: 0x1bf05, // color 0x30: 0x5f504, // size 0x31: 0x2e80a, // http-equiv 0x33: 0x601, // i 0x34: 0x5590a, // onpagehide 0x35: 0x68c14, // onunhandledrejection 0x37: 0x42a07, // onerror 0x3a: 0x3b08, // basefont 0x3f: 0x1303, // nav 0x40: 0x17704, // kind 0x41: 0x35708, // readonly 0x42: 0x30806, // mglyph 0x44: 0xb202, // li 0x46: 0x2d506, // hidden 0x47: 0x70e03, // svg 0x48: 0x58304, // step 0x49: 0x23f09, // integrity 0x4a: 0x58606, // public 0x4c: 0x1ab03, // col 0x4d: 0x1870a, // blockquote 0x4e: 0x34f02, // h5 0x50: 0x5b908, // progress 0x51: 0x5f505, // sizes 0x52: 0x34502, // h4 0x56: 0x33005, // thead 0x57: 0xd607, // keytype 0x58: 0x5b70a, // onprogress 0x59: 0x44b09, // inputmode 0x5a: 0x3b109, // ondragend 0x5d: 0x3a205, // oncut 0x5e: 0x43706, // spacer 0x5f: 0x1ab08, // colgroup 0x62: 0x16502, // is 0x65: 0x3c02, // as 0x66: 0x54809, // onoffline 0x67: 0x33706, // sorted 0x69: 0x48d10, // onlanguagechange 0x6c: 0x43d0c, // onhashchange 0x6d: 0x9604, // name 0x6e: 0xf505, // tfoot 0x6f: 0x56104, // desc 0x70: 0x33d03, // max 0x72: 0x1ea06, // coords 0x73: 0x30d02, // h3 0x74: 0x6e70e, // onbeforeunload 0x75: 0x9c04, // rows 0x76: 0x63c06, // select 0x77: 0x9805, // meter 0x78: 0x38b06, // itemid 0x79: 0x53c0c, // onmousewheel 0x7a: 0x5c006, // srcdoc 0x7d: 0x1ba05, // track 0x7f: 0x31f08, // itemtype 0x82: 0xa402, // mo 0x83: 0x41b08, // onchange 0x84: 0x33107, // headers 0x85: 0x5cc0c, // onratechange 0x86: 0x60819, // onsecuritypolicyviolation 0x88: 0x4a508, // datalist 0x89: 0x4e80b, // onmousedown 0x8a: 0x1ef04, // slot 0x8b: 0x4b010, // onloadedmetadata 0x8c: 0x1a06, // accept 0x8d: 0x26806, // object 0x91: 0x6b30e, // onvolumechange 0x92: 0x2107, // charset 0x93: 0x27613, // onautocompleteerror 0x94: 0xc113, // allowpaymentrequest 0x95: 0x2804, // body 0x96: 0x10a07, // default 0x97: 0x63c08, // selected 0x98: 0x21e04, // face 0x99: 0x1e505, // shape 0x9b: 0x68408, // ontoggle 0x9e: 0x64b02, // dt 0x9f: 0xb604, // mark 0xa1: 0xb01, // u 0xa4: 0x6ab08, // onunload 0xa5: 0x5d04, // loop 0xa6: 0x16408, // disabled 0xaa: 0x42307, // onended 0xab: 0xb00a, // malignmark 0xad: 0x67b09, // onsuspend 0xae: 0x35105, // mtext 0xaf: 0x64f06, // onsort 0xb0: 0x19d08, // itemprop 0xb3: 0x67109, // itemscope 0xb4: 0x17305, // blink 0xb6: 0x3b106, // ondrag 0xb7: 0xa702, // ul 0xb8: 0x26e04, // form 0xb9: 0x12907, // sandbox 0xba: 0x8b05, // frame 0xbb: 0x1505, // value 0xbc: 0x66209, // onstorage 0xbf: 0xaa07, // acronym 0xc0: 0x19a02, // rt 0xc2: 0x202, // br 0xc3: 0x22608, // fieldset 0xc4: 0x2900d, // typemustmatch 0xc5: 0xa208, // nomodule 0xc6: 0x6c07, // noembed 0xc7: 0x69e0d, // onbeforeprint 0xc8: 0x19106, // button 0xc9: 0x2f507, // onclick 0xca: 0x70407, // summary 0xcd: 0xfb04, // ruby 0xce: 0x56405, // class 0xcf: 0x3f40b, // ondragstart 0xd0: 0x23107, // caption 0xd4: 0xdd0e, // allowusermedia 0xd5: 0x4cf0b, // onloadstart 0xd9: 0x16b03, // div 0xda: 0x4a904, // list 0xdb: 0x32e04, // math 0xdc: 0x44b05, // input 0xdf: 0x3ea0a, // ondragover 0xe0: 0x2de02, // h2 0xe2: 0x1b209, // plaintext 0xe4: 0x4f30c, // onmouseenter 0xe7: 0x47907, // checked 0xe8: 0x47003, // pre 0xea: 0x35f08, // multiple 0xeb: 0xba03, // bdi 0xec: 0x33d09, // maxlength 0xed: 0xcf01, // q 0xee: 0x61f0a, // onauxclick 0xf0: 0x57c03, // wbr 0xf2: 0x3b04, // base 0xf3: 0x6e306, // option 0xf5: 0x41310, // ondurationchange 0xf7: 0x8908, // noframes 0xf9: 0x40508, // dropzone 0xfb: 0x67505, // scope 0xfc: 0x8008, // reversed 0xfd: 0x3ba0b, // ondragenter 0xfe: 0x3fa05, // start 0xff: 0x12f03, // xmp 0x100: 0x5f907, // srclang 0x101: 0x30703, // img 0x104: 0x101, // b 0x105: 0x25403, // for 0x106: 0x10705, // aside 0x107: 0x44907, // oninput 0x108: 0x35604, // area 0x109: 0x2a40a, // formmethod 0x10a: 0x72604, // wrap 0x10c: 0x23c02, // rp 0x10d: 0x46b0a, // onkeypress 0x10e: 0x6802, // tt 0x110: 0x34702, // mi 0x111: 0x36705, // muted 0x112: 0xf303, // alt 0x113: 0x5c504, // code 0x114: 0x6e02, // em 0x115: 0x3c50a, // ondragexit 0x117: 0x9f04, // span 0x119: 0x6d708, // manifest 0x11a: 0x38708, // menuitem 0x11b: 0x58b07, // content 0x11d: 0x6c109, // onwaiting 0x11f: 0x4c609, // onloadend 0x121: 0x37e0d, // oncontextmenu 0x123: 0x56d06, // onblur 0x124: 0x3fc07, // article 0x125: 0x9303, // dir 0x126: 0xef04, // ping 0x127: 0x24c08, // required 0x128: 0x45509, // oninvalid 0x129: 0xb105, // align 0x12b: 0x58a04, // icon 0x12c: 0x64d02, // h6 0x12d: 0x1c404, // cols 0x12e: 0x22e0a, // figcaption 0x12f: 0x45e09, // onkeydown 0x130: 0x66b08, // onsubmit 0x131: 0x14d09, // oncanplay 0x132: 0x70b03, // sup 0x133: 0xc01, // p 0x135: 0x40a09, // onemptied 0x136: 0x39106, // oncopy 0x137: 0x19c04, // cite 0x138: 0x3a70a, // ondblclick 0x13a: 0x50b0b, // onmousemove 0x13c: 0x66d03, // sub 0x13d: 0x48703, // rel 0x13e: 0x5f08, // optgroup 0x142: 0x9c07, // rowspan 0x143: 0x37806, // source 0x144: 0x21608, // noscript 0x145: 0x1a304, // open 0x146: 0x20403, // ins 0x147: 0x2540d, // foreignObject 0x148: 0x5ad0a, // onpopstate 0x14a: 0x28d07, // enctype 0x14b: 0x2760e, // onautocomplete 0x14c: 0x35208, // textarea 0x14e: 0x2780c, // autocomplete 0x14f: 0x15702, // hr 0x150: 0x1de08, // controls 0x151: 0x10902, // id 0x153: 0x2360c, // onafterprint 0x155: 0x2610d, // foreignobject 0x156: 0x32707, // marquee 0x157: 0x59a07, // onpause 0x158: 0x5e602, // dl 0x159: 0x5206, // height 0x15a: 0x34703, // min 0x15b: 0x9307, // dirname 0x15c: 0x1f209, // translate 0x15d: 0x5604, // html 0x15e: 0x34709, // minlength 0x15f: 0x48607, // preload 0x160: 0x71408, // template 0x161: 0x3df0b, // ondragleave 0x162: 0x3a02, // rb 0x164: 0x5c003, // src 0x165: 0x6dd06, // strong 0x167: 0x7804, // samp 0x168: 0x6f307, // address 0x169: 0x55108, // ononline 0x16b: 0x1310b, // placeholder 0x16c: 0x2c406, // target 0x16d: 0x20605, // small 0x16e: 0x6ca07, // onwheel 0x16f: 0x1c90a, // annotation 0x170: 0x4740a, // spellcheck 0x171: 0x7207, // details 0x172: 0x10306, // canvas 0x173: 0x12109, // autofocus 0x174: 0xc05, // param 0x176: 0x46308, // download 0x177: 0x45203, // del 0x178: 0x36c07, // onclose 0x179: 0xb903, // kbd 0x17a: 0x31906, // applet 0x17b: 0x2e004, // href 0x17c: 0x5f108, // onresize 0x17e: 0x49d0c, // onloadeddata 0x180: 0xcc02, // tr 0x181: 0x2c00a, // formtarget 0x182: 0x11005, // title 0x183: 0x6ff05, // style 0x184: 0xd206, // strike 0x185: 0x59e06, // usemap 0x186: 0x2fc06, // iframe 0x187: 0x1004, // main 0x189: 0x7b07, // picture 0x18c: 0x31605, // ismap 0x18e: 0x4a504, // data 0x18f: 0x5905, // label 0x191: 0x3d10e, // referrerpolicy 0x192: 0x15602, // th 0x194: 0x53606, // prompt 0x195: 0x56807, // section 0x197: 0x6d107, // optimum 0x198: 0x2db04, // high 0x199: 0x15c02, // h1 0x19a: 0x65909, // onstalled 0x19b: 0x16d03, // var 0x19c: 0x4204, // time 0x19e: 0x67402, // ms 0x19f: 0x33106, // header 0x1a0: 0x4da09, // onmessage 0x1a1: 0x1a605, // nonce 0x1a2: 0x26e0a, // formaction 0x1a3: 0x22006, // center 0x1a4: 0x3704, // nobr 0x1a5: 0x59505, // table 0x1a6: 0x4a907, // listing 0x1a7: 0x18106, // legend 0x1a9: 0x29b09, // challenge 0x1aa: 0x24806, // figure 0x1ab: 0xe605, // media 0x1ae: 0xd904, // type 0x1af: 0x3f04, // font 0x1b0: 0x4da0e, // onmessageerror 0x1b1: 0x37108, // seamless 0x1b2: 0x8703, // dfn 0x1b3: 0x5c705, // defer 0x1b4: 0xc303, // low 0x1b5: 0x19a03, // rtc 0x1b6: 0x5230b, // onmouseover 0x1b7: 0x2b20a, // novalidate 0x1b8: 0x71c0a, // workertype 0x1ba: 0x3cd07, // itemref 0x1bd: 0x1, // a 0x1be: 0x31803, // map 0x1bf: 0x400c, // ontimeupdate 0x1c0: 0x15e07, // bgsound 0x1c1: 0x3206, // keygen 0x1c2: 0x2705, // tbody 0x1c5: 0x64406, // onshow 0x1c7: 0x2501, // s 0x1c8: 0x6607, // pattern 0x1cc: 0x14d10, // oncanplaythrough 0x1ce: 0x2d702, // dd 0x1cf: 0x6f906, // srcset 0x1d0: 0x17003, // big 0x1d2: 0x65108, // sortable 0x1d3: 0x48007, // onkeyup 0x1d5: 0x5a406, // onplay 0x1d7: 0x4b804, // meta 0x1d8: 0x40306, // ondrop 0x1da: 0x60008, // onscroll 0x1db: 0x1fb0b, // crossorigin 0x1dc: 0x5730a, // onpageshow 0x1dd: 0x4, // abbr 0x1de: 0x9202, // td 0x1df: 0x58b0f, // contenteditable 0x1e0: 0x27206, // action 0x1e1: 0x1400b, // playsinline 0x1e2: 0x43107, // onfocus 0x1e3: 0x2e008, // hreflang 0x1e5: 0x5160a, // onmouseout 0x1e6: 0x5ea07, // onreset 0x1e7: 0x13c08, // autoplay 0x1e8: 0x63109, // onseeking 0x1ea: 0x67506, // scoped 0x1ec: 0x30a, // radiogroup 0x1ee: 0x3800b, // contextmenu 0x1ef: 0x52e09, // onmouseup 0x1f1: 0x2ca06, // hgroup 0x1f2: 0x2080f, // allowfullscreen 0x1f3: 0x4be08, // tabindex 0x1f6: 0x30f07, // isindex 0x1f7: 0x1a0e, // accept-charset 0x1f8: 0x2ae0e, // formnovalidate 0x1fb: 0x1c90e, // annotation-xml 0x1fc: 0x6e05, // embed 0x1fd: 0x21806, // script 0x1fe: 0xbb06, // dialog 0x1ff: 0x1d707, // command } const atomText = "abbradiogrouparamainavalueaccept-charsetbodyaccesskeygenobrb" + "asefontimeupdateviacacheightmlabelooptgroupatternoembedetail" + "sampictureversedfnoframesetdirnameterowspanomoduleacronymali" + "gnmarkbdialogallowpaymentrequestrikeytypeallowusermediagroup" + "ingaltfooterubyasyncanvasidefaultitleaudioncancelautofocusan" + "dboxmplaceholderautoplaysinlinebdoncanplaythrough1bgsoundisa" + "bledivarbigblinkindraggablegendblockquotebuttonabortcitempro" + "penoncecolgrouplaintextrackcolorcolspannotation-xmlcommandco" + "ntrolshapecoordslotranslatecrossoriginsmallowfullscreenoscri" + "ptfacenterfieldsetfigcaptionafterprintegrityfigurequiredfore" + "ignObjectforeignobjectformactionautocompleteerrorformenctype" + "mustmatchallengeformmethodformnovalidatetimeformtargethgroup" + "osterhiddenhigh2hreflanghttp-equivideonclickiframeimageimgly" + "ph3isindexismappletitemtypemarqueematheadersortedmaxlength4m" + "inlength5mtextareadonlymultiplemutedoncloseamlessourceoncont" + "extmenuitemidoncopyoncuechangeoncutondblclickondragendondrag" + "enterondragexitemreferrerpolicyondragleaveondragoverondragst" + "articleondropzonemptiedondurationchangeonendedonerroronfocus" + "paceronhashchangeoninputmodeloninvalidonkeydownloadonkeypres" + "spellcheckedonkeyupreloadonlanguagechangeonloadeddatalisting" + "onloadedmetadatabindexonloadendonloadstartonmessageerroronmo" + "usedownonmouseenteronmouseleaveonmousemoveonmouseoutputonmou" + "seoveronmouseupromptonmousewheelonofflineononlineonpagehides" + "classectionbluronpageshowbronpastepublicontenteditableonpaus" + "emaponplayingonpopstateonprogressrcdocodeferonratechangeonre" + "jectionhandledonresetonresizesrclangonscrollonsecuritypolicy" + "violationauxclickonseekedonseekingonselectedonshowidth6onsor" + "tableonstalledonstorageonsubmitemscopedonsuspendontoggleonun" + "handledrejectionbeforeprintonunloadonvolumechangeonwaitingon" + "wheeloptimumanifestrongoptionbeforeunloaddressrcsetstylesumm" + "arysupsvgsystemplateworkertypewrap" golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/atom/table_test.go000066400000000000000000000106071415442132300250140ustar00rootroot00000000000000// Code generated by go generate gen.go; DO NOT EDIT. //go:generate go run gen.go -test package atom var testAtomList = []string{ "a", "abbr", "accept", "accept-charset", "accesskey", "acronym", "action", "address", "align", "allowfullscreen", "allowpaymentrequest", "allowusermedia", "alt", "annotation", "annotation-xml", "applet", "area", "article", "as", "aside", "async", "audio", "autocomplete", "autofocus", "autoplay", "b", "base", "basefont", "bdi", "bdo", "bgsound", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "challenge", "charset", "checked", "cite", "class", "code", "col", "colgroup", "color", "cols", "colspan", "command", "content", "contenteditable", "contextmenu", "controls", "coords", "crossorigin", "data", "datalist", "datetime", "dd", "default", "defer", "del", "desc", "details", "dfn", "dialog", "dir", "dirname", "disabled", "div", "dl", "download", "draggable", "dropzone", "dt", "em", "embed", "enctype", "face", "fieldset", "figcaption", "figure", "font", "footer", "for", "foreignObject", "foreignobject", "form", "formaction", "formenctype", "formmethod", "formnovalidate", "formtarget", "frame", "frameset", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "headers", "height", "hgroup", "hidden", "high", "hr", "href", "hreflang", "html", "http-equiv", "i", "icon", "id", "iframe", "image", "img", "input", "inputmode", "ins", "integrity", "is", "isindex", "ismap", "itemid", "itemprop", "itemref", "itemscope", "itemtype", "kbd", "keygen", "keytype", "kind", "label", "lang", "legend", "li", "link", "list", "listing", "loop", "low", "main", "malignmark", "manifest", "map", "mark", "marquee", "math", "max", "maxlength", "media", "mediagroup", "menu", "menuitem", "meta", "meter", "method", "mglyph", "mi", "min", "minlength", "mn", "mo", "ms", "mtext", "multiple", "muted", "name", "nav", "nobr", "noembed", "noframes", "nomodule", "nonce", "noscript", "novalidate", "object", "ol", "onabort", "onafterprint", "onautocomplete", "onautocompleteerror", "onauxclick", "onbeforeprint", "onbeforeunload", "onblur", "oncancel", "oncanplay", "oncanplaythrough", "onchange", "onclick", "onclose", "oncontextmenu", "oncopy", "oncuechange", "oncut", "ondblclick", "ondrag", "ondragend", "ondragenter", "ondragexit", "ondragleave", "ondragover", "ondragstart", "ondrop", "ondurationchange", "onemptied", "onended", "onerror", "onfocus", "onhashchange", "oninput", "oninvalid", "onkeydown", "onkeypress", "onkeyup", "onlanguagechange", "onload", "onloadeddata", "onloadedmetadata", "onloadend", "onloadstart", "onmessage", "onmessageerror", "onmousedown", "onmouseenter", "onmouseleave", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onmousewheel", "onoffline", "ononline", "onpagehide", "onpageshow", "onpaste", "onpause", "onplay", "onplaying", "onpopstate", "onprogress", "onratechange", "onrejectionhandled", "onreset", "onresize", "onscroll", "onsecuritypolicyviolation", "onseeked", "onseeking", "onselect", "onshow", "onsort", "onstalled", "onstorage", "onsubmit", "onsuspend", "ontimeupdate", "ontoggle", "onunhandledrejection", "onunload", "onvolumechange", "onwaiting", "onwheel", "open", "optgroup", "optimum", "option", "output", "p", "param", "pattern", "picture", "ping", "placeholder", "plaintext", "playsinline", "poster", "pre", "preload", "progress", "prompt", "public", "q", "radiogroup", "rb", "readonly", "referrerpolicy", "rel", "required", "reversed", "rows", "rowspan", "rp", "rt", "rtc", "ruby", "s", "samp", "sandbox", "scope", "scoped", "script", "seamless", "section", "select", "selected", "shape", "size", "sizes", "slot", "small", "sortable", "sorted", "source", "spacer", "span", "spellcheck", "src", "srcdoc", "srclang", "srcset", "start", "step", "strike", "strong", "style", "sub", "summary", "sup", "svg", "system", "tabindex", "table", "target", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "translate", "tt", "type", "typemustmatch", "u", "ul", "updateviacache", "usemap", "value", "var", "video", "wbr", "width", "workertype", "wrap", "xmp", } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/charset/000077500000000000000000000000001415442132300230245ustar00rootroot00000000000000golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/charset/charset.go000066400000000000000000000140731415442132300250110ustar00rootroot00000000000000// 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. // Package charset provides common text encodings for HTML documents. // // The mapping from encoding labels to encodings is defined at // https://encoding.spec.whatwg.org/. package charset // import "golang.org/x/net/html/charset" import ( "bytes" "fmt" "io" "mime" "strings" "unicode/utf8" "golang.org/x/net/html" "golang.org/x/text/encoding" "golang.org/x/text/encoding/charmap" "golang.org/x/text/encoding/htmlindex" "golang.org/x/text/transform" ) // Lookup returns the encoding with the specified label, and its canonical // name. It returns nil and the empty string if label is not one of the // standard encodings for HTML. Matching is case-insensitive and ignores // leading and trailing whitespace. Encoders will use HTML escape sequences for // runes that are not supported by the character set. func Lookup(label string) (e encoding.Encoding, name string) { e, err := htmlindex.Get(label) if err != nil { return nil, "" } name, _ = htmlindex.Name(e) return &htmlEncoding{e}, name } type htmlEncoding struct{ encoding.Encoding } func (h *htmlEncoding) NewEncoder() *encoding.Encoder { // HTML requires a non-terminating legacy encoder. We use HTML escapes to // substitute unsupported code points. return encoding.HTMLEscapeUnsupported(h.Encoding.NewEncoder()) } // DetermineEncoding determines the encoding of an HTML document by examining // up to the first 1024 bytes of content and the declared Content-Type. // // See http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#determining-the-character-encoding func DetermineEncoding(content []byte, contentType string) (e encoding.Encoding, name string, certain bool) { if len(content) > 1024 { content = content[:1024] } for _, b := range boms { if bytes.HasPrefix(content, b.bom) { e, name = Lookup(b.enc) return e, name, true } } if _, params, err := mime.ParseMediaType(contentType); err == nil { if cs, ok := params["charset"]; ok { if e, name = Lookup(cs); e != nil { return e, name, true } } } if len(content) > 0 { e, name = prescan(content) if e != nil { return e, name, false } } // Try to detect UTF-8. // First eliminate any partial rune at the end. for i := len(content) - 1; i >= 0 && i > len(content)-4; i-- { b := content[i] if b < 0x80 { break } if utf8.RuneStart(b) { content = content[:i] break } } hasHighBit := false for _, c := range content { if c >= 0x80 { hasHighBit = true break } } if hasHighBit && utf8.Valid(content) { return encoding.Nop, "utf-8", false } // TODO: change default depending on user's locale? return charmap.Windows1252, "windows-1252", false } // NewReader returns an io.Reader that converts the content of r to UTF-8. // It calls DetermineEncoding to find out what r's encoding is. func NewReader(r io.Reader, contentType string) (io.Reader, error) { preview := make([]byte, 1024) n, err := io.ReadFull(r, preview) switch { case err == io.ErrUnexpectedEOF: preview = preview[:n] r = bytes.NewReader(preview) case err != nil: return nil, err default: r = io.MultiReader(bytes.NewReader(preview), r) } if e, _, _ := DetermineEncoding(preview, contentType); e != encoding.Nop { r = transform.NewReader(r, e.NewDecoder()) } return r, nil } // NewReaderLabel returns a reader that converts from the specified charset to // UTF-8. It uses Lookup to find the encoding that corresponds to label, and // returns an error if Lookup returns nil. It is suitable for use as // encoding/xml.Decoder's CharsetReader function. func NewReaderLabel(label string, input io.Reader) (io.Reader, error) { e, _ := Lookup(label) if e == nil { return nil, fmt.Errorf("unsupported charset: %q", label) } return transform.NewReader(input, e.NewDecoder()), nil } func prescan(content []byte) (e encoding.Encoding, name string) { z := html.NewTokenizer(bytes.NewReader(content)) for { switch z.Next() { case html.ErrorToken: return nil, "" case html.StartTagToken, html.SelfClosingTagToken: tagName, hasAttr := z.TagName() if !bytes.Equal(tagName, []byte("meta")) { continue } attrList := make(map[string]bool) gotPragma := false const ( dontKnow = iota doNeedPragma doNotNeedPragma ) needPragma := dontKnow name = "" e = nil for hasAttr { var key, val []byte key, val, hasAttr = z.TagAttr() ks := string(key) if attrList[ks] { continue } attrList[ks] = true for i, c := range val { if 'A' <= c && c <= 'Z' { val[i] = c + 0x20 } } switch ks { case "http-equiv": if bytes.Equal(val, []byte("content-type")) { gotPragma = true } case "content": if e == nil { name = fromMetaElement(string(val)) if name != "" { e, name = Lookup(name) if e != nil { needPragma = doNeedPragma } } } case "charset": e, name = Lookup(string(val)) needPragma = doNotNeedPragma } } if needPragma == dontKnow || needPragma == doNeedPragma && !gotPragma { continue } if strings.HasPrefix(name, "utf-16") { name = "utf-8" e = encoding.Nop } if e != nil { return e, name } } } } func fromMetaElement(s string) string { for s != "" { csLoc := strings.Index(s, "charset") if csLoc == -1 { return "" } s = s[csLoc+len("charset"):] s = strings.TrimLeft(s, " \t\n\f\r") if !strings.HasPrefix(s, "=") { continue } s = s[1:] s = strings.TrimLeft(s, " \t\n\f\r") if s == "" { return "" } if q := s[0]; q == '"' || q == '\'' { s = s[1:] closeQuote := strings.IndexRune(s, rune(q)) if closeQuote == -1 { return "" } return s[:closeQuote] } end := strings.IndexAny(s, "; \t\n\f\r") if end == -1 { end = len(s) } return s[:end] } return "" } var boms = []struct { bom []byte enc string }{ {[]byte{0xfe, 0xff}, "utf-16be"}, {[]byte{0xff, 0xfe}, "utf-16le"}, {[]byte{0xef, 0xbb, 0xbf}, "utf-8"}, } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/charset/charset_test.go000066400000000000000000000177411415442132300260550ustar00rootroot00000000000000// 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. package charset import ( "bytes" "encoding/xml" "io/ioutil" "runtime" "strings" "testing" "golang.org/x/text/transform" ) func transformString(t transform.Transformer, s string) (string, error) { r := transform.NewReader(strings.NewReader(s), t) b, err := ioutil.ReadAll(r) return string(b), err } type testCase struct { utf8, other, otherEncoding string } // testCases for encoding and decoding. var testCases = []testCase{ {"Résumé", "Résumé", "utf8"}, {"Résumé", "R\xe9sum\xe9", "latin1"}, {"ã“ã‚Œã¯æ¼¢å­—ã§ã™ã€‚", "S0\x8c0o0\"oW[g0Y0\x020", "UTF-16LE"}, {"ã“ã‚Œã¯æ¼¢å­—ã§ã™ã€‚", "0S0\x8c0oo\"[W0g0Y0\x02", "UTF-16BE"}, {"Hello, world", "Hello, world", "ASCII"}, {"GdaÅ„sk", "Gda\xf1sk", "ISO-8859-2"}, {"Ââ ÄŒÄ ÄÄ‘ ÅŠÅ‹ Õõ Å Å¡ Žž Ã…Ã¥ Ää", "\xc2\xe2 \xc8\xe8 \xa9\xb9 \xaf\xbf \xd5\xf5 \xaa\xba \xac\xbc \xc5\xe5 \xc4\xe4", "ISO-8859-10"}, {"สำหรับ", "\xca\xd3\xcb\xc3\u047a", "ISO-8859-11"}, {"latvieÅ¡u", "latvie\xf0u", "ISO-8859-13"}, {"Seònaid", "Se\xf2naid", "ISO-8859-14"}, {"€1 is cheap", "\xa41 is cheap", "ISO-8859-15"}, {"româneÈ™te", "rom\xe2ne\xbate", "ISO-8859-16"}, {"nutraĵo", "nutra\xbco", "ISO-8859-3"}, {"Kalâdlit", "Kal\xe2dlit", "ISO-8859-4"}, {"руÑÑкий", "\xe0\xe3\xe1\xe1\xda\xd8\xd9", "ISO-8859-5"}, {"ελληνικά", "\xe5\xeb\xeb\xe7\xed\xe9\xea\xdc", "ISO-8859-7"}, {"KaÄŸan", "Ka\xf0an", "ISO-8859-9"}, {"Résumé", "R\x8esum\x8e", "macintosh"}, {"GdaÅ„sk", "Gda\xf1sk", "windows-1250"}, {"руÑÑкий", "\xf0\xf3\xf1\xf1\xea\xe8\xe9", "windows-1251"}, {"Résumé", "R\xe9sum\xe9", "windows-1252"}, {"ελληνικά", "\xe5\xeb\xeb\xe7\xed\xe9\xea\xdc", "windows-1253"}, {"KaÄŸan", "Ka\xf0an", "windows-1254"}, {"עִבְרִית", "\xf2\xc4\xe1\xc0\xf8\xc4\xe9\xfa", "windows-1255"}, {"العربية", "\xc7\xe1\xda\xd1\xc8\xed\xc9", "windows-1256"}, {"latvieÅ¡u", "latvie\xf0u", "windows-1257"}, {"Việt", "Vi\xea\xf2t", "windows-1258"}, {"สำหรับ", "\xca\xd3\xcb\xc3\u047a", "windows-874"}, {"руÑÑкий", "\xd2\xd5\xd3\xd3\xcb\xc9\xca", "KOI8-R"}, {"українÑька", "\xd5\xcb\xd2\xc1\xa7\xce\xd3\xd8\xcb\xc1", "KOI8-U"}, {"Hello 常用國字標準字體表", "Hello \xb1`\xa5\u03b0\xea\xa6r\xbc\u0437\u01e6r\xc5\xe9\xaa\xed", "big5"}, {"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gbk"}, {"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gb18030"}, {"עִבְרִית", "\x81\x30\xfb\x30\x81\x30\xf6\x34\x81\x30\xf9\x33\x81\x30\xf6\x30\x81\x30\xfb\x36\x81\x30\xf6\x34\x81\x30\xfa\x31\x81\x30\xfb\x38", "gb18030"}, {"㧯", "\x82\x31\x89\x38", "gb18030"}, {"ã“ã‚Œã¯æ¼¢å­—ã§ã™ã€‚", "\x82\xb1\x82\xea\x82\xcd\x8a\xbf\x8e\x9a\x82\xc5\x82\xb7\x81B", "SJIS"}, {"Hello, 世界!", "Hello, \x90\xa2\x8aE!", "SJIS"}, {"イウエオカ", "\xb2\xb3\xb4\xb5\xb6", "SJIS"}, {"ã“ã‚Œã¯æ¼¢å­—ã§ã™ã€‚", "\xa4\xb3\xa4\xec\xa4\u03f4\xc1\xbb\xfa\xa4\u01e4\xb9\xa1\xa3", "EUC-JP"}, {"Hello, 世界!", "Hello, \x1b$B@$3&\x1b(B!", "ISO-2022-JP"}, {"다ìŒê³¼ ê°™ì€ ì¡°ê±´ì„ ë”°ë¼ì•¼ 합니다: 저작ìží‘œì‹œ", "\xb4\xd9\xc0\xbd\xb0\xfa \xb0\xb0\xc0\xba \xc1\xb6\xb0\xc7\xc0\xbb \xb5\xfb\xb6\xf3\xbe\xdf \xc7Õ´Ï´\xd9: \xc0\xfa\xc0\xdb\xc0\xdaÇ¥\xbd\xc3", "EUC-KR"}, } func TestDecode(t *testing.T) { testCases := append(testCases, []testCase{ // Replace multi-byte maximum subpart of ill-formed subsequence with // single replacement character (WhatWG requirement). {"Rés\ufffdumé", "Rés\xe1\x80umé", "utf8"}, }...) for _, tc := range testCases { e, _ := Lookup(tc.otherEncoding) if e == nil { t.Errorf("%s: not found", tc.otherEncoding) continue } s, err := transformString(e.NewDecoder(), tc.other) if err != nil { t.Errorf("%s: decode %q: %v", tc.otherEncoding, tc.other, err) continue } if s != tc.utf8 { t.Errorf("%s: got %q, want %q", tc.otherEncoding, s, tc.utf8) } } } func TestEncode(t *testing.T) { testCases := append(testCases, []testCase{ // Use Go-style replacement. {"Rés\xe1\x80umé", "Rés\ufffd\ufffdumé", "utf8"}, // U+0144 LATIN SMALL LETTER N WITH ACUTE not supported by encoding. {"GdaÅ„sk", "Gdańsk", "ISO-8859-11"}, {"\ufffd", "�", "ISO-8859-11"}, {"a\xe1\x80b", "a��b", "ISO-8859-11"}, }...) for _, tc := range testCases { e, _ := Lookup(tc.otherEncoding) if e == nil { t.Errorf("%s: not found", tc.otherEncoding) continue } s, err := transformString(e.NewEncoder(), tc.utf8) if err != nil { t.Errorf("%s: encode %q: %s", tc.otherEncoding, tc.utf8, err) continue } if s != tc.other { t.Errorf("%s: got %q, want %q", tc.otherEncoding, s, tc.other) } } } var sniffTestCases = []struct { filename, declared, want string }{ {"HTTP-charset.html", "text/html; charset=iso-8859-15", "iso-8859-15"}, {"UTF-16LE-BOM.html", "", "utf-16le"}, {"UTF-16BE-BOM.html", "", "utf-16be"}, {"meta-content-attribute.html", "text/html", "iso-8859-15"}, {"meta-charset-attribute.html", "text/html", "iso-8859-15"}, {"No-encoding-declaration.html", "text/html", "utf-8"}, {"HTTP-vs-UTF-8-BOM.html", "text/html; charset=iso-8859-15", "utf-8"}, {"HTTP-vs-meta-content.html", "text/html; charset=iso-8859-15", "iso-8859-15"}, {"HTTP-vs-meta-charset.html", "text/html; charset=iso-8859-15", "iso-8859-15"}, {"UTF-8-BOM-vs-meta-content.html", "text/html", "utf-8"}, {"UTF-8-BOM-vs-meta-charset.html", "text/html", "utf-8"}, } func TestSniff(t *testing.T) { switch runtime.GOOS { case "nacl": // platforms that don't permit direct file system access t.Skipf("not supported on %q", runtime.GOOS) } for _, tc := range sniffTestCases { content, err := ioutil.ReadFile("testdata/" + tc.filename) if err != nil { t.Errorf("%s: error reading file: %v", tc.filename, err) continue } _, name, _ := DetermineEncoding(content, tc.declared) if name != tc.want { t.Errorf("%s: got %q, want %q", tc.filename, name, tc.want) continue } } } func TestReader(t *testing.T) { switch runtime.GOOS { case "nacl": // platforms that don't permit direct file system access t.Skipf("not supported on %q", runtime.GOOS) } for _, tc := range sniffTestCases { content, err := ioutil.ReadFile("testdata/" + tc.filename) if err != nil { t.Errorf("%s: error reading file: %v", tc.filename, err) continue } r, err := NewReader(bytes.NewReader(content), tc.declared) if err != nil { t.Errorf("%s: error creating reader: %v", tc.filename, err) continue } got, err := ioutil.ReadAll(r) if err != nil { t.Errorf("%s: error reading from charset.NewReader: %v", tc.filename, err) continue } e, _ := Lookup(tc.want) want, err := ioutil.ReadAll(transform.NewReader(bytes.NewReader(content), e.NewDecoder())) if err != nil { t.Errorf("%s: error decoding with hard-coded charset name: %v", tc.filename, err) continue } if !bytes.Equal(got, want) { t.Errorf("%s: got %q, want %q", tc.filename, got, want) continue } } } var metaTestCases = []struct { meta, want string }{ {"", ""}, {"text/html", ""}, {"text/html; charset utf-8", ""}, {"text/html; charset=latin-2", "latin-2"}, {"text/html; charset; charset = utf-8", "utf-8"}, {`charset="big5"`, "big5"}, {"charset='shift_jis'", "shift_jis"}, } func TestFromMeta(t *testing.T) { for _, tc := range metaTestCases { got := fromMetaElement(tc.meta) if got != tc.want { t.Errorf("%q: got %q, want %q", tc.meta, got, tc.want) } } } func TestXML(t *testing.T) { const s = "r\xe9sum\xe9" d := xml.NewDecoder(strings.NewReader(s)) d.CharsetReader = NewReaderLabel var a struct { Word string } if err := d.Decode(&a); err != nil { t.Fatalf("Decode: %v", err) } want := "résumé" if a.Word != want { t.Errorf("got %q, want %q", a.Word, want) } } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/charset/testdata/000077500000000000000000000000001415442132300246355ustar00rootroot00000000000000golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/charset/testdata/HTTP-charset.html000066400000000000000000000050711415442132300277340ustar00rootroot00000000000000 HTTP charset

HTTP charset

 

The character encoding of a page can be set using the HTTP header charset declaration.

The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ÜÀÚ. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.

The only character encoding declaration for this HTML file is in the HTTP header, which sets the encoding to ISO 8859-15.

HTML5

the-input-byte-stream-001
Result summary & related tests
Detailed results for this test
Link to spec

Assumptions:
  • The default encoding for the browser you are testing is not set to ISO 8859-15.
  • The test is read from a server that supports HTTP.
golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/charset/testdata/HTTP-vs-UTF-8-BOM.html000066400000000000000000000054061415442132300301510ustar00rootroot00000000000000 HTTP vs UTF-8 BOM

HTTP vs UTF-8 BOM

 

A character encoding set in the HTTP header has lower precedence than the UTF-8 signature.

The HTTP header attempts to set the character encoding to ISO 8859-15. The page starts with a UTF-8 signature.

The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ýäè. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.

If the test is unsuccessful, the characters  should appear at the top of the page. These represent the bytes that make up the UTF-8 signature when encountered in the ISO 8859-15 encoding.

HTML5

the-input-byte-stream-034
Result summary & related tests
Detailed results for this test
Link to spec

Assumptions:
  • The default encoding for the browser you are testing is not set to ISO 8859-15.
  • The test is read from a server that supports HTTP.
golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/charset/testdata/HTTP-vs-meta-charset.html000066400000000000000000000053501415442132300313060ustar00rootroot00000000000000 HTTP vs meta charset

HTTP vs meta charset

 

The HTTP header has a higher precedence than an encoding declaration in a meta charset attribute.

The HTTP header attempts to set the character encoding to ISO 8859-15. The page contains an encoding declaration in a meta charset attribute that attempts to set the character encoding to ISO 8859-1.

The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ÜÀÚ. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.

HTML5

the-input-byte-stream-018
Result summary & related tests
Detailed results for this test
Link to spec

Assumptions:
  • The default encoding for the browser you are testing is not set to ISO 8859-15.
  • The test is read from a server that supports HTTP.
golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/charset/testdata/HTTP-vs-meta-content.html000066400000000000000000000054241415442132300313310ustar00rootroot00000000000000 HTTP vs meta content

HTTP vs meta content

 

The HTTP header has a higher precedence than an encoding declaration in a meta content attribute.

The HTTP header attempts to set the character encoding to ISO 8859-15. The page contains an encoding declaration in a meta content attribute that attempts to set the character encoding to ISO 8859-1.

The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ÜÀÚ. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.

HTML5

the-input-byte-stream-016
Result summary & related tests
Detailed results for this test
Link to spec

Assumptions:
  • The default encoding for the browser you are testing is not set to ISO 8859-15.
  • The test is read from a server that supports HTTP.
golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/charset/testdata/No-encoding-declaration.html000066400000000000000000000046151415442132300321540ustar00rootroot00000000000000 No encoding declaration

No encoding declaration

 

A page with no encoding information in HTTP, BOM, XML declaration or meta element will be treated as UTF-8.

The test on this page contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ýäè. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.

HTML5

the-input-byte-stream-015
Result summary & related tests
Detailed results for this test
Link to spec

Assumptions:
  • The test is read from a server that supports HTTP.
golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/charset/testdata/README000066400000000000000000000006711415442132300255210ustar00rootroot00000000000000These test cases come from http://www.w3.org/International/tests/repository/html5/the-input-byte-stream/results-basics Distributed under both the W3C Test Suite License (http://www.w3.org/Consortium/Legal/2008/04-testsuite-license) and the W3C 3-clause BSD License (http://www.w3.org/Consortium/Legal/2008/03-bsd-license). To contribute to a W3C Test Suite, see the policies and contribution forms (http://www.w3.org/2004/10/27-testcases). golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/charset/testdata/UTF-16BE-BOM.html000066400000000000000000000051561415442132300272360ustar00rootroot00000000000000þÿ<!DOCTYPE html> <html lang="en" > <head> <title>UTF-16BE BOM</title> <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'> <link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'> <script src="http://w3c-test.org/resources/testharness.js"></script> <script src="http://w3c-test.org/resources/testharnessreport.js"></script> <meta name='flags' content='http'> <style type='text/css'> .test div { width: 50px; } </style> <link rel="stylesheet" type="text/css" href="encodingtests-15.css"> </head> <body> <div class='test'><div id='box' class='ÃSà ¬Ãa'>&#xA0;</div></div> <!-- Notes: No encoding information is declared in the HTTP header or inside the document, other than in the BOM. The text of a class name in the test contains the following sequence of bytes: 0xC3 0xc0 0x53 0xc1 0xC3 0xc0 0xAC 0xc20 0xC3 0xc0 0x61 0xc1. The external, UTF-8-encoded stylesheet contains a selector with a sequence of characters that will only match the class name in the HTML if the page is read as UTF-16BE. --> <script> test(function () { assert_equals(document.getElementById('box').offsetWidth, 100); }, 'A page with no encoding declarations, but with a UTF-16 little-endian BOM will be recognized as UTF-16.'); </script> <div id=log></div> </body> </html> golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/charset/testdata/UTF-16LE-BOM.html000066400000000000000000000051721415442132300272460ustar00rootroot00000000000000ÿþ<!DOCTYPE html> <html lang="en" > <head> <title>UTF-16LE BOM</title> <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'> <link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'> <script src="http://w3c-test.org/resources/testharness.js"></script> <script src="http://w3c-test.org/resources/testharnessreport.js"></script> <meta name='flags' content='http'> <style type='text/css'> .test div { width: 50px; } </style> <link rel="stylesheet" type="text/css" href="encodingtests-15.css"> </head> <body> <div class='test'><div id='box' class='ÃSì Ãa'>&#xA0;</div></div> <!-- Notes: No encoding information is declared in the HTTP header or inside the document, other than in the BOM. The text of a class name in the test contains the following sequence of bytes: 0xC3 0xc0 0x53 0xc1 0xC3 0xc0 0xAC 0xc20 0xC3 0xc0 0x61 0xc1. The external, UTF-8-encoded stylesheet contains a selector with a sequence of characters that will only match the class name in the HTML if the page is read as UTF-16BE. --> <script> test(function () { assert_equals(document.getElementById('box').offsetWidth, 100); }, 'A page with no encoding declarations, but with a UTF-16 little-endian BOM will be recognized as UTF-16.'); </script> <div id="log"></div> </body> </html> UTF-8-BOM-vs-meta-charset.html000066400000000000000000000053471415442132300316740ustar00rootroot00000000000000golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/charset/testdata UTF-8 BOM vs meta charset

UTF-8 BOM vs meta charset

 

A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta charset attribute declares a different encoding.

The page contains an encoding declaration in a meta charset attribute that attempts to set the character encoding to ISO 8859-15, but the file starts with a UTF-8 signature.

The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ýäè. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.

HTML5

the-input-byte-stream-038
Result summary & related tests
Detailed results for this test
Link to spec

Assumptions:
  • The default encoding for the browser you are testing is not set to ISO 8859-15.
  • The test is read from a server that supports HTTP.
UTF-8-BOM-vs-meta-content.html000066400000000000000000000053711415442132300317120ustar00rootroot00000000000000golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/charset/testdata UTF-8 BOM vs meta content

UTF-8 BOM vs meta content

 

A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta content attribute declares a different encoding.

The page contains an encoding declaration in a meta content attribute that attempts to set the character encoding to ISO 8859-15, but the file starts with a UTF-8 signature.

The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ýäè. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.

HTML5

the-input-byte-stream-037
Result summary & related tests
Detailed results for this test
Link to spec

Assumptions:
  • The default encoding for the browser you are testing is not set to ISO 8859-15.
  • The test is read from a server that supports HTTP.
golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/charset/testdata/meta-charset-attribute.html000066400000000000000000000052141415442132300321030ustar00rootroot00000000000000 meta charset attribute

meta charset attribute

 

The character encoding of the page can be set by a meta element with charset attribute.

The only character encoding declaration for this HTML file is in the charset attribute of the meta element, which declares the encoding to be ISO 8859-15.

The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ÜÀÚ. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.

HTML5

the-input-byte-stream-009
Result summary & related tests
Detailed results for this test
Link to spec

Assumptions:
  • The default encoding for the browser you are testing is not set to ISO 8859-15.
  • The test is read from a server that supports HTTP.
golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/charset/testdata/meta-content-attribute.html000066400000000000000000000053311415442132300321240ustar00rootroot00000000000000 meta content attribute

meta content attribute

 

The character encoding of the page can be set by a meta element with http-equiv and content attributes.

The only character encoding declaration for this HTML file is in the content attribute of the meta element, which declares the encoding to be ISO 8859-15.

The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector .test div.ÜÀÚ. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.

HTML5

the-input-byte-stream-007
Result summary & related tests
Detailed results for this test
Link to spec

Assumptions:
  • The default encoding for the browser you are testing is not set to ISO 8859-15.
  • The test is read from a server that supports HTTP.
golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/const.go000066400000000000000000000050541415442132300230540ustar00rootroot00000000000000// 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 html // Section 12.2.4.2 of the HTML5 specification says "The following elements // have varying levels of special parsing rules". // https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements var isSpecialElementMap = map[string]bool{ "address": true, "applet": true, "area": true, "article": true, "aside": true, "base": true, "basefont": true, "bgsound": true, "blockquote": true, "body": true, "br": true, "button": true, "caption": true, "center": true, "col": true, "colgroup": true, "dd": true, "details": true, "dir": true, "div": true, "dl": true, "dt": true, "embed": true, "fieldset": true, "figcaption": true, "figure": true, "footer": true, "form": true, "frame": true, "frameset": true, "h1": true, "h2": true, "h3": true, "h4": true, "h5": true, "h6": true, "head": true, "header": true, "hgroup": true, "hr": true, "html": true, "iframe": true, "img": true, "input": true, "keygen": true, // "keygen" has been removed from the spec, but are kept here for backwards compatibility. "li": true, "link": true, "listing": true, "main": true, "marquee": true, "menu": true, "meta": true, "nav": true, "noembed": true, "noframes": true, "noscript": true, "object": true, "ol": true, "p": true, "param": true, "plaintext": true, "pre": true, "script": true, "section": true, "select": true, "source": true, "style": true, "summary": true, "table": true, "tbody": true, "td": true, "template": true, "textarea": true, "tfoot": true, "th": true, "thead": true, "title": true, "tr": true, "track": true, "ul": true, "wbr": true, "xmp": true, } func isSpecialElement(element *Node) bool { switch element.Namespace { case "", "html": return isSpecialElementMap[element.Data] case "math": switch element.Data { case "mi", "mo", "mn", "ms", "mtext", "annotation-xml": return true } case "svg": switch element.Data { case "foreignObject", "desc", "title": return true } } return false } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/doc.go000066400000000000000000000065271415442132300225010ustar00rootroot00000000000000// Copyright 2010 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 html implements an HTML5-compliant tokenizer and parser. Tokenization is done by creating a Tokenizer for an io.Reader r. It is the caller's responsibility to ensure that r provides UTF-8 encoded HTML. z := html.NewTokenizer(r) Given a Tokenizer z, the HTML is tokenized by repeatedly calling z.Next(), which parses the next token and returns its type, or an error: for { tt := z.Next() if tt == html.ErrorToken { // ... return ... } // Process the current token. } There are two APIs for retrieving the current token. The high-level API is to call Token; the low-level API is to call Text or TagName / TagAttr. Both APIs allow optionally calling Raw after Next but before Token, Text, TagName, or TagAttr. In EBNF notation, the valid call sequence per token is: Next {Raw} [ Token | Text | TagName {TagAttr} ] Token returns an independent data structure that completely describes a token. Entities (such as "<") are unescaped, tag names and attribute keys are lower-cased, and attributes are collected into a []Attribute. For example: for { if z.Next() == html.ErrorToken { // Returning io.EOF indicates success. return z.Err() } emitToken(z.Token()) } The low-level API performs fewer allocations and copies, but the contents of the []byte values returned by Text, TagName and TagAttr may change on the next call to Next. For example, to extract an HTML page's anchor text: depth := 0 for { tt := z.Next() switch tt { case html.ErrorToken: return z.Err() case html.TextToken: if depth > 0 { // emitBytes should copy the []byte it receives, // if it doesn't process it immediately. emitBytes(z.Text()) } case html.StartTagToken, html.EndTagToken: tn, _ := z.TagName() if len(tn) == 1 && tn[0] == 'a' { if tt == html.StartTagToken { depth++ } else { depth-- } } } } Parsing is done by calling Parse with an io.Reader, which returns the root of the parse tree (the document element) as a *Node. It is the caller's responsibility to ensure that the Reader provides UTF-8 encoded HTML. For example, to process each anchor node in depth-first order: doc, err := html.Parse(r) if err != nil { // ... } var f func(*html.Node) f = func(n *html.Node) { if n.Type == html.ElementNode && n.Data == "a" { // Do something with n... } for c := n.FirstChild; c != nil; c = c.NextSibling { f(c) } } f(doc) The relevant specifications include: https://html.spec.whatwg.org/multipage/syntax.html and https://html.spec.whatwg.org/multipage/syntax.html#tokenization */ package html // import "golang.org/x/net/html" // The tokenization algorithm implemented by this package is not a line-by-line // transliteration of the relatively verbose state-machine in the WHATWG // specification. A more direct approach is used instead, where the program // counter implies the state, such as whether it is tokenizing a tag or a text // node. Specification compliance is verified by checking expected and actual // outputs over a test suite rather than aiming for algorithmic fidelity. // TODO(nigeltao): Does a DOM API belong in this package or a separate one? // TODO(nigeltao): How does parsing interact with a JavaScript engine? golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/doctype.go000066400000000000000000000114751415442132300234010ustar00rootroot00000000000000// 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 html import ( "strings" ) // parseDoctype parses the data from a DoctypeToken into a name, // public identifier, and system identifier. It returns a Node whose Type // is DoctypeNode, whose Data is the name, and which has attributes // named "system" and "public" for the two identifiers if they were present. // quirks is whether the document should be parsed in "quirks mode". func parseDoctype(s string) (n *Node, quirks bool) { n = &Node{Type: DoctypeNode} // Find the name. space := strings.IndexAny(s, whitespace) if space == -1 { space = len(s) } n.Data = s[:space] // The comparison to "html" is case-sensitive. if n.Data != "html" { quirks = true } n.Data = strings.ToLower(n.Data) s = strings.TrimLeft(s[space:], whitespace) if len(s) < 6 { // It can't start with "PUBLIC" or "SYSTEM". // Ignore the rest of the string. return n, quirks || s != "" } key := strings.ToLower(s[:6]) s = s[6:] for key == "public" || key == "system" { s = strings.TrimLeft(s, whitespace) if s == "" { break } quote := s[0] if quote != '"' && quote != '\'' { break } s = s[1:] q := strings.IndexRune(s, rune(quote)) var id string if q == -1 { id = s s = "" } else { id = s[:q] s = s[q+1:] } n.Attr = append(n.Attr, Attribute{Key: key, Val: id}) if key == "public" { key = "system" } else { key = "" } } if key != "" || s != "" { quirks = true } else if len(n.Attr) > 0 { if n.Attr[0].Key == "public" { public := strings.ToLower(n.Attr[0].Val) switch public { case "-//w3o//dtd w3 html strict 3.0//en//", "-/w3d/dtd html 4.0 transitional/en", "html": quirks = true default: for _, q := range quirkyIDs { if strings.HasPrefix(public, q) { quirks = true break } } } // The following two public IDs only cause quirks mode if there is no system ID. if len(n.Attr) == 1 && (strings.HasPrefix(public, "-//w3c//dtd html 4.01 frameset//") || strings.HasPrefix(public, "-//w3c//dtd html 4.01 transitional//")) { quirks = true } } if lastAttr := n.Attr[len(n.Attr)-1]; lastAttr.Key == "system" && strings.ToLower(lastAttr.Val) == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd" { quirks = true } } return n, quirks } // quirkyIDs is a list of public doctype identifiers that cause a document // to be interpreted in quirks mode. The identifiers should be in lower case. var quirkyIDs = []string{ "+//silmaril//dtd html pro v0r11 19970101//", "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", "-//as//dtd html 3.0 aswedit + extensions//", "-//ietf//dtd html 2.0 level 1//", "-//ietf//dtd html 2.0 level 2//", "-//ietf//dtd html 2.0 strict level 1//", "-//ietf//dtd html 2.0 strict level 2//", "-//ietf//dtd html 2.0 strict//", "-//ietf//dtd html 2.0//", "-//ietf//dtd html 2.1e//", "-//ietf//dtd html 3.0//", "-//ietf//dtd html 3.2 final//", "-//ietf//dtd html 3.2//", "-//ietf//dtd html 3//", "-//ietf//dtd html level 0//", "-//ietf//dtd html level 1//", "-//ietf//dtd html level 2//", "-//ietf//dtd html level 3//", "-//ietf//dtd html strict level 0//", "-//ietf//dtd html strict level 1//", "-//ietf//dtd html strict level 2//", "-//ietf//dtd html strict level 3//", "-//ietf//dtd html strict//", "-//ietf//dtd html//", "-//metrius//dtd metrius presentational//", "-//microsoft//dtd internet explorer 2.0 html strict//", "-//microsoft//dtd internet explorer 2.0 html//", "-//microsoft//dtd internet explorer 2.0 tables//", "-//microsoft//dtd internet explorer 3.0 html strict//", "-//microsoft//dtd internet explorer 3.0 html//", "-//microsoft//dtd internet explorer 3.0 tables//", "-//netscape comm. corp.//dtd html//", "-//netscape comm. corp.//dtd strict html//", "-//o'reilly and associates//dtd html 2.0//", "-//o'reilly and associates//dtd html extended 1.0//", "-//o'reilly and associates//dtd html extended relaxed 1.0//", "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", "-//spyglass//dtd html 2.0 extended//", "-//sq//dtd html 2.0 hotmetal + extensions//", "-//sun microsystems corp.//dtd hotjava html//", "-//sun microsystems corp.//dtd hotjava strict html//", "-//w3c//dtd html 3 1995-03-24//", "-//w3c//dtd html 3.2 draft//", "-//w3c//dtd html 3.2 final//", "-//w3c//dtd html 3.2//", "-//w3c//dtd html 3.2s draft//", "-//w3c//dtd html 4.0 frameset//", "-//w3c//dtd html 4.0 transitional//", "-//w3c//dtd html experimental 19960712//", "-//w3c//dtd html experimental 970421//", "-//w3c//dtd w3 html//", "-//w3o//dtd w3 html 3.0//", "-//webtechs//dtd mozilla html 2.0//", "-//webtechs//dtd mozilla html//", } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/entity.go000066400000000000000000003376421415442132300232550ustar00rootroot00000000000000// Copyright 2010 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 html // All entities that do not end with ';' are 6 or fewer bytes long. const longestEntityWithoutSemicolon = 6 // entity is a map from HTML entity names to their values. The semicolon matters: // https://html.spec.whatwg.org/multipage/syntax.html#named-character-references // lists both "amp" and "amp;" as two separate entries. // // Note that the HTML5 list is larger than the HTML4 list at // http://www.w3.org/TR/html4/sgml/entities.html var entity = map[string]rune{ "AElig;": '\U000000C6', "AMP;": '\U00000026', "Aacute;": '\U000000C1', "Abreve;": '\U00000102', "Acirc;": '\U000000C2', "Acy;": '\U00000410', "Afr;": '\U0001D504', "Agrave;": '\U000000C0', "Alpha;": '\U00000391', "Amacr;": '\U00000100', "And;": '\U00002A53', "Aogon;": '\U00000104', "Aopf;": '\U0001D538', "ApplyFunction;": '\U00002061', "Aring;": '\U000000C5', "Ascr;": '\U0001D49C', "Assign;": '\U00002254', "Atilde;": '\U000000C3', "Auml;": '\U000000C4', "Backslash;": '\U00002216', "Barv;": '\U00002AE7', "Barwed;": '\U00002306', "Bcy;": '\U00000411', "Because;": '\U00002235', "Bernoullis;": '\U0000212C', "Beta;": '\U00000392', "Bfr;": '\U0001D505', "Bopf;": '\U0001D539', "Breve;": '\U000002D8', "Bscr;": '\U0000212C', "Bumpeq;": '\U0000224E', "CHcy;": '\U00000427', "COPY;": '\U000000A9', "Cacute;": '\U00000106', "Cap;": '\U000022D2', "CapitalDifferentialD;": '\U00002145', "Cayleys;": '\U0000212D', "Ccaron;": '\U0000010C', "Ccedil;": '\U000000C7', "Ccirc;": '\U00000108', "Cconint;": '\U00002230', "Cdot;": '\U0000010A', "Cedilla;": '\U000000B8', "CenterDot;": '\U000000B7', "Cfr;": '\U0000212D', "Chi;": '\U000003A7', "CircleDot;": '\U00002299', "CircleMinus;": '\U00002296', "CirclePlus;": '\U00002295', "CircleTimes;": '\U00002297', "ClockwiseContourIntegral;": '\U00002232', "CloseCurlyDoubleQuote;": '\U0000201D', "CloseCurlyQuote;": '\U00002019', "Colon;": '\U00002237', "Colone;": '\U00002A74', "Congruent;": '\U00002261', "Conint;": '\U0000222F', "ContourIntegral;": '\U0000222E', "Copf;": '\U00002102', "Coproduct;": '\U00002210', "CounterClockwiseContourIntegral;": '\U00002233', "Cross;": '\U00002A2F', "Cscr;": '\U0001D49E', "Cup;": '\U000022D3', "CupCap;": '\U0000224D', "DD;": '\U00002145', "DDotrahd;": '\U00002911', "DJcy;": '\U00000402', "DScy;": '\U00000405', "DZcy;": '\U0000040F', "Dagger;": '\U00002021', "Darr;": '\U000021A1', "Dashv;": '\U00002AE4', "Dcaron;": '\U0000010E', "Dcy;": '\U00000414', "Del;": '\U00002207', "Delta;": '\U00000394', "Dfr;": '\U0001D507', "DiacriticalAcute;": '\U000000B4', "DiacriticalDot;": '\U000002D9', "DiacriticalDoubleAcute;": '\U000002DD', "DiacriticalGrave;": '\U00000060', "DiacriticalTilde;": '\U000002DC', "Diamond;": '\U000022C4', "DifferentialD;": '\U00002146', "Dopf;": '\U0001D53B', "Dot;": '\U000000A8', "DotDot;": '\U000020DC', "DotEqual;": '\U00002250', "DoubleContourIntegral;": '\U0000222F', "DoubleDot;": '\U000000A8', "DoubleDownArrow;": '\U000021D3', "DoubleLeftArrow;": '\U000021D0', "DoubleLeftRightArrow;": '\U000021D4', "DoubleLeftTee;": '\U00002AE4', "DoubleLongLeftArrow;": '\U000027F8', "DoubleLongLeftRightArrow;": '\U000027FA', "DoubleLongRightArrow;": '\U000027F9', "DoubleRightArrow;": '\U000021D2', "DoubleRightTee;": '\U000022A8', "DoubleUpArrow;": '\U000021D1', "DoubleUpDownArrow;": '\U000021D5', "DoubleVerticalBar;": '\U00002225', "DownArrow;": '\U00002193', "DownArrowBar;": '\U00002913', "DownArrowUpArrow;": '\U000021F5', "DownBreve;": '\U00000311', "DownLeftRightVector;": '\U00002950', "DownLeftTeeVector;": '\U0000295E', "DownLeftVector;": '\U000021BD', "DownLeftVectorBar;": '\U00002956', "DownRightTeeVector;": '\U0000295F', "DownRightVector;": '\U000021C1', "DownRightVectorBar;": '\U00002957', "DownTee;": '\U000022A4', "DownTeeArrow;": '\U000021A7', "Downarrow;": '\U000021D3', "Dscr;": '\U0001D49F', "Dstrok;": '\U00000110', "ENG;": '\U0000014A', "ETH;": '\U000000D0', "Eacute;": '\U000000C9', "Ecaron;": '\U0000011A', "Ecirc;": '\U000000CA', "Ecy;": '\U0000042D', "Edot;": '\U00000116', "Efr;": '\U0001D508', "Egrave;": '\U000000C8', "Element;": '\U00002208', "Emacr;": '\U00000112', "EmptySmallSquare;": '\U000025FB', "EmptyVerySmallSquare;": '\U000025AB', "Eogon;": '\U00000118', "Eopf;": '\U0001D53C', "Epsilon;": '\U00000395', "Equal;": '\U00002A75', "EqualTilde;": '\U00002242', "Equilibrium;": '\U000021CC', "Escr;": '\U00002130', "Esim;": '\U00002A73', "Eta;": '\U00000397', "Euml;": '\U000000CB', "Exists;": '\U00002203', "ExponentialE;": '\U00002147', "Fcy;": '\U00000424', "Ffr;": '\U0001D509', "FilledSmallSquare;": '\U000025FC', "FilledVerySmallSquare;": '\U000025AA', "Fopf;": '\U0001D53D', "ForAll;": '\U00002200', "Fouriertrf;": '\U00002131', "Fscr;": '\U00002131', "GJcy;": '\U00000403', "GT;": '\U0000003E', "Gamma;": '\U00000393', "Gammad;": '\U000003DC', "Gbreve;": '\U0000011E', "Gcedil;": '\U00000122', "Gcirc;": '\U0000011C', "Gcy;": '\U00000413', "Gdot;": '\U00000120', "Gfr;": '\U0001D50A', "Gg;": '\U000022D9', "Gopf;": '\U0001D53E', "GreaterEqual;": '\U00002265', "GreaterEqualLess;": '\U000022DB', "GreaterFullEqual;": '\U00002267', "GreaterGreater;": '\U00002AA2', "GreaterLess;": '\U00002277', "GreaterSlantEqual;": '\U00002A7E', "GreaterTilde;": '\U00002273', "Gscr;": '\U0001D4A2', "Gt;": '\U0000226B', "HARDcy;": '\U0000042A', "Hacek;": '\U000002C7', "Hat;": '\U0000005E', "Hcirc;": '\U00000124', "Hfr;": '\U0000210C', "HilbertSpace;": '\U0000210B', "Hopf;": '\U0000210D', "HorizontalLine;": '\U00002500', "Hscr;": '\U0000210B', "Hstrok;": '\U00000126', "HumpDownHump;": '\U0000224E', "HumpEqual;": '\U0000224F', "IEcy;": '\U00000415', "IJlig;": '\U00000132', "IOcy;": '\U00000401', "Iacute;": '\U000000CD', "Icirc;": '\U000000CE', "Icy;": '\U00000418', "Idot;": '\U00000130', "Ifr;": '\U00002111', "Igrave;": '\U000000CC', "Im;": '\U00002111', "Imacr;": '\U0000012A', "ImaginaryI;": '\U00002148', "Implies;": '\U000021D2', "Int;": '\U0000222C', "Integral;": '\U0000222B', "Intersection;": '\U000022C2', "InvisibleComma;": '\U00002063', "InvisibleTimes;": '\U00002062', "Iogon;": '\U0000012E', "Iopf;": '\U0001D540', "Iota;": '\U00000399', "Iscr;": '\U00002110', "Itilde;": '\U00000128', "Iukcy;": '\U00000406', "Iuml;": '\U000000CF', "Jcirc;": '\U00000134', "Jcy;": '\U00000419', "Jfr;": '\U0001D50D', "Jopf;": '\U0001D541', "Jscr;": '\U0001D4A5', "Jsercy;": '\U00000408', "Jukcy;": '\U00000404', "KHcy;": '\U00000425', "KJcy;": '\U0000040C', "Kappa;": '\U0000039A', "Kcedil;": '\U00000136', "Kcy;": '\U0000041A', "Kfr;": '\U0001D50E', "Kopf;": '\U0001D542', "Kscr;": '\U0001D4A6', "LJcy;": '\U00000409', "LT;": '\U0000003C', "Lacute;": '\U00000139', "Lambda;": '\U0000039B', "Lang;": '\U000027EA', "Laplacetrf;": '\U00002112', "Larr;": '\U0000219E', "Lcaron;": '\U0000013D', "Lcedil;": '\U0000013B', "Lcy;": '\U0000041B', "LeftAngleBracket;": '\U000027E8', "LeftArrow;": '\U00002190', "LeftArrowBar;": '\U000021E4', "LeftArrowRightArrow;": '\U000021C6', "LeftCeiling;": '\U00002308', "LeftDoubleBracket;": '\U000027E6', "LeftDownTeeVector;": '\U00002961', "LeftDownVector;": '\U000021C3', "LeftDownVectorBar;": '\U00002959', "LeftFloor;": '\U0000230A', "LeftRightArrow;": '\U00002194', "LeftRightVector;": '\U0000294E', "LeftTee;": '\U000022A3', "LeftTeeArrow;": '\U000021A4', "LeftTeeVector;": '\U0000295A', "LeftTriangle;": '\U000022B2', "LeftTriangleBar;": '\U000029CF', "LeftTriangleEqual;": '\U000022B4', "LeftUpDownVector;": '\U00002951', "LeftUpTeeVector;": '\U00002960', "LeftUpVector;": '\U000021BF', "LeftUpVectorBar;": '\U00002958', "LeftVector;": '\U000021BC', "LeftVectorBar;": '\U00002952', "Leftarrow;": '\U000021D0', "Leftrightarrow;": '\U000021D4', "LessEqualGreater;": '\U000022DA', "LessFullEqual;": '\U00002266', "LessGreater;": '\U00002276', "LessLess;": '\U00002AA1', "LessSlantEqual;": '\U00002A7D', "LessTilde;": '\U00002272', "Lfr;": '\U0001D50F', "Ll;": '\U000022D8', "Lleftarrow;": '\U000021DA', "Lmidot;": '\U0000013F', "LongLeftArrow;": '\U000027F5', "LongLeftRightArrow;": '\U000027F7', "LongRightArrow;": '\U000027F6', "Longleftarrow;": '\U000027F8', "Longleftrightarrow;": '\U000027FA', "Longrightarrow;": '\U000027F9', "Lopf;": '\U0001D543', "LowerLeftArrow;": '\U00002199', "LowerRightArrow;": '\U00002198', "Lscr;": '\U00002112', "Lsh;": '\U000021B0', "Lstrok;": '\U00000141', "Lt;": '\U0000226A', "Map;": '\U00002905', "Mcy;": '\U0000041C', "MediumSpace;": '\U0000205F', "Mellintrf;": '\U00002133', "Mfr;": '\U0001D510', "MinusPlus;": '\U00002213', "Mopf;": '\U0001D544', "Mscr;": '\U00002133', "Mu;": '\U0000039C', "NJcy;": '\U0000040A', "Nacute;": '\U00000143', "Ncaron;": '\U00000147', "Ncedil;": '\U00000145', "Ncy;": '\U0000041D', "NegativeMediumSpace;": '\U0000200B', "NegativeThickSpace;": '\U0000200B', "NegativeThinSpace;": '\U0000200B', "NegativeVeryThinSpace;": '\U0000200B', "NestedGreaterGreater;": '\U0000226B', "NestedLessLess;": '\U0000226A', "NewLine;": '\U0000000A', "Nfr;": '\U0001D511', "NoBreak;": '\U00002060', "NonBreakingSpace;": '\U000000A0', "Nopf;": '\U00002115', "Not;": '\U00002AEC', "NotCongruent;": '\U00002262', "NotCupCap;": '\U0000226D', "NotDoubleVerticalBar;": '\U00002226', "NotElement;": '\U00002209', "NotEqual;": '\U00002260', "NotExists;": '\U00002204', "NotGreater;": '\U0000226F', "NotGreaterEqual;": '\U00002271', "NotGreaterLess;": '\U00002279', "NotGreaterTilde;": '\U00002275', "NotLeftTriangle;": '\U000022EA', "NotLeftTriangleEqual;": '\U000022EC', "NotLess;": '\U0000226E', "NotLessEqual;": '\U00002270', "NotLessGreater;": '\U00002278', "NotLessTilde;": '\U00002274', "NotPrecedes;": '\U00002280', "NotPrecedesSlantEqual;": '\U000022E0', "NotReverseElement;": '\U0000220C', "NotRightTriangle;": '\U000022EB', "NotRightTriangleEqual;": '\U000022ED', "NotSquareSubsetEqual;": '\U000022E2', "NotSquareSupersetEqual;": '\U000022E3', "NotSubsetEqual;": '\U00002288', "NotSucceeds;": '\U00002281', "NotSucceedsSlantEqual;": '\U000022E1', "NotSupersetEqual;": '\U00002289', "NotTilde;": '\U00002241', "NotTildeEqual;": '\U00002244', "NotTildeFullEqual;": '\U00002247', "NotTildeTilde;": '\U00002249', "NotVerticalBar;": '\U00002224', "Nscr;": '\U0001D4A9', "Ntilde;": '\U000000D1', "Nu;": '\U0000039D', "OElig;": '\U00000152', "Oacute;": '\U000000D3', "Ocirc;": '\U000000D4', "Ocy;": '\U0000041E', "Odblac;": '\U00000150', "Ofr;": '\U0001D512', "Ograve;": '\U000000D2', "Omacr;": '\U0000014C', "Omega;": '\U000003A9', "Omicron;": '\U0000039F', "Oopf;": '\U0001D546', "OpenCurlyDoubleQuote;": '\U0000201C', "OpenCurlyQuote;": '\U00002018', "Or;": '\U00002A54', "Oscr;": '\U0001D4AA', "Oslash;": '\U000000D8', "Otilde;": '\U000000D5', "Otimes;": '\U00002A37', "Ouml;": '\U000000D6', "OverBar;": '\U0000203E', "OverBrace;": '\U000023DE', "OverBracket;": '\U000023B4', "OverParenthesis;": '\U000023DC', "PartialD;": '\U00002202', "Pcy;": '\U0000041F', "Pfr;": '\U0001D513', "Phi;": '\U000003A6', "Pi;": '\U000003A0', "PlusMinus;": '\U000000B1', "Poincareplane;": '\U0000210C', "Popf;": '\U00002119', "Pr;": '\U00002ABB', "Precedes;": '\U0000227A', "PrecedesEqual;": '\U00002AAF', "PrecedesSlantEqual;": '\U0000227C', "PrecedesTilde;": '\U0000227E', "Prime;": '\U00002033', "Product;": '\U0000220F', "Proportion;": '\U00002237', "Proportional;": '\U0000221D', "Pscr;": '\U0001D4AB', "Psi;": '\U000003A8', "QUOT;": '\U00000022', "Qfr;": '\U0001D514', "Qopf;": '\U0000211A', "Qscr;": '\U0001D4AC', "RBarr;": '\U00002910', "REG;": '\U000000AE', "Racute;": '\U00000154', "Rang;": '\U000027EB', "Rarr;": '\U000021A0', "Rarrtl;": '\U00002916', "Rcaron;": '\U00000158', "Rcedil;": '\U00000156', "Rcy;": '\U00000420', "Re;": '\U0000211C', "ReverseElement;": '\U0000220B', "ReverseEquilibrium;": '\U000021CB', "ReverseUpEquilibrium;": '\U0000296F', "Rfr;": '\U0000211C', "Rho;": '\U000003A1', "RightAngleBracket;": '\U000027E9', "RightArrow;": '\U00002192', "RightArrowBar;": '\U000021E5', "RightArrowLeftArrow;": '\U000021C4', "RightCeiling;": '\U00002309', "RightDoubleBracket;": '\U000027E7', "RightDownTeeVector;": '\U0000295D', "RightDownVector;": '\U000021C2', "RightDownVectorBar;": '\U00002955', "RightFloor;": '\U0000230B', "RightTee;": '\U000022A2', "RightTeeArrow;": '\U000021A6', "RightTeeVector;": '\U0000295B', "RightTriangle;": '\U000022B3', "RightTriangleBar;": '\U000029D0', "RightTriangleEqual;": '\U000022B5', "RightUpDownVector;": '\U0000294F', "RightUpTeeVector;": '\U0000295C', "RightUpVector;": '\U000021BE', "RightUpVectorBar;": '\U00002954', "RightVector;": '\U000021C0', "RightVectorBar;": '\U00002953', "Rightarrow;": '\U000021D2', "Ropf;": '\U0000211D', "RoundImplies;": '\U00002970', "Rrightarrow;": '\U000021DB', "Rscr;": '\U0000211B', "Rsh;": '\U000021B1', "RuleDelayed;": '\U000029F4', "SHCHcy;": '\U00000429', "SHcy;": '\U00000428', "SOFTcy;": '\U0000042C', "Sacute;": '\U0000015A', "Sc;": '\U00002ABC', "Scaron;": '\U00000160', "Scedil;": '\U0000015E', "Scirc;": '\U0000015C', "Scy;": '\U00000421', "Sfr;": '\U0001D516', "ShortDownArrow;": '\U00002193', "ShortLeftArrow;": '\U00002190', "ShortRightArrow;": '\U00002192', "ShortUpArrow;": '\U00002191', "Sigma;": '\U000003A3', "SmallCircle;": '\U00002218', "Sopf;": '\U0001D54A', "Sqrt;": '\U0000221A', "Square;": '\U000025A1', "SquareIntersection;": '\U00002293', "SquareSubset;": '\U0000228F', "SquareSubsetEqual;": '\U00002291', "SquareSuperset;": '\U00002290', "SquareSupersetEqual;": '\U00002292', "SquareUnion;": '\U00002294', "Sscr;": '\U0001D4AE', "Star;": '\U000022C6', "Sub;": '\U000022D0', "Subset;": '\U000022D0', "SubsetEqual;": '\U00002286', "Succeeds;": '\U0000227B', "SucceedsEqual;": '\U00002AB0', "SucceedsSlantEqual;": '\U0000227D', "SucceedsTilde;": '\U0000227F', "SuchThat;": '\U0000220B', "Sum;": '\U00002211', "Sup;": '\U000022D1', "Superset;": '\U00002283', "SupersetEqual;": '\U00002287', "Supset;": '\U000022D1', "THORN;": '\U000000DE', "TRADE;": '\U00002122', "TSHcy;": '\U0000040B', "TScy;": '\U00000426', "Tab;": '\U00000009', "Tau;": '\U000003A4', "Tcaron;": '\U00000164', "Tcedil;": '\U00000162', "Tcy;": '\U00000422', "Tfr;": '\U0001D517', "Therefore;": '\U00002234', "Theta;": '\U00000398', "ThinSpace;": '\U00002009', "Tilde;": '\U0000223C', "TildeEqual;": '\U00002243', "TildeFullEqual;": '\U00002245', "TildeTilde;": '\U00002248', "Topf;": '\U0001D54B', "TripleDot;": '\U000020DB', "Tscr;": '\U0001D4AF', "Tstrok;": '\U00000166', "Uacute;": '\U000000DA', "Uarr;": '\U0000219F', "Uarrocir;": '\U00002949', "Ubrcy;": '\U0000040E', "Ubreve;": '\U0000016C', "Ucirc;": '\U000000DB', "Ucy;": '\U00000423', "Udblac;": '\U00000170', "Ufr;": '\U0001D518', "Ugrave;": '\U000000D9', "Umacr;": '\U0000016A', "UnderBar;": '\U0000005F', "UnderBrace;": '\U000023DF', "UnderBracket;": '\U000023B5', "UnderParenthesis;": '\U000023DD', "Union;": '\U000022C3', "UnionPlus;": '\U0000228E', "Uogon;": '\U00000172', "Uopf;": '\U0001D54C', "UpArrow;": '\U00002191', "UpArrowBar;": '\U00002912', "UpArrowDownArrow;": '\U000021C5', "UpDownArrow;": '\U00002195', "UpEquilibrium;": '\U0000296E', "UpTee;": '\U000022A5', "UpTeeArrow;": '\U000021A5', "Uparrow;": '\U000021D1', "Updownarrow;": '\U000021D5', "UpperLeftArrow;": '\U00002196', "UpperRightArrow;": '\U00002197', "Upsi;": '\U000003D2', "Upsilon;": '\U000003A5', "Uring;": '\U0000016E', "Uscr;": '\U0001D4B0', "Utilde;": '\U00000168', "Uuml;": '\U000000DC', "VDash;": '\U000022AB', "Vbar;": '\U00002AEB', "Vcy;": '\U00000412', "Vdash;": '\U000022A9', "Vdashl;": '\U00002AE6', "Vee;": '\U000022C1', "Verbar;": '\U00002016', "Vert;": '\U00002016', "VerticalBar;": '\U00002223', "VerticalLine;": '\U0000007C', "VerticalSeparator;": '\U00002758', "VerticalTilde;": '\U00002240', "VeryThinSpace;": '\U0000200A', "Vfr;": '\U0001D519', "Vopf;": '\U0001D54D', "Vscr;": '\U0001D4B1', "Vvdash;": '\U000022AA', "Wcirc;": '\U00000174', "Wedge;": '\U000022C0', "Wfr;": '\U0001D51A', "Wopf;": '\U0001D54E', "Wscr;": '\U0001D4B2', "Xfr;": '\U0001D51B', "Xi;": '\U0000039E', "Xopf;": '\U0001D54F', "Xscr;": '\U0001D4B3', "YAcy;": '\U0000042F', "YIcy;": '\U00000407', "YUcy;": '\U0000042E', "Yacute;": '\U000000DD', "Ycirc;": '\U00000176', "Ycy;": '\U0000042B', "Yfr;": '\U0001D51C', "Yopf;": '\U0001D550', "Yscr;": '\U0001D4B4', "Yuml;": '\U00000178', "ZHcy;": '\U00000416', "Zacute;": '\U00000179', "Zcaron;": '\U0000017D', "Zcy;": '\U00000417', "Zdot;": '\U0000017B', "ZeroWidthSpace;": '\U0000200B', "Zeta;": '\U00000396', "Zfr;": '\U00002128', "Zopf;": '\U00002124', "Zscr;": '\U0001D4B5', "aacute;": '\U000000E1', "abreve;": '\U00000103', "ac;": '\U0000223E', "acd;": '\U0000223F', "acirc;": '\U000000E2', "acute;": '\U000000B4', "acy;": '\U00000430', "aelig;": '\U000000E6', "af;": '\U00002061', "afr;": '\U0001D51E', "agrave;": '\U000000E0', "alefsym;": '\U00002135', "aleph;": '\U00002135', "alpha;": '\U000003B1', "amacr;": '\U00000101', "amalg;": '\U00002A3F', "amp;": '\U00000026', "and;": '\U00002227', "andand;": '\U00002A55', "andd;": '\U00002A5C', "andslope;": '\U00002A58', "andv;": '\U00002A5A', "ang;": '\U00002220', "ange;": '\U000029A4', "angle;": '\U00002220', "angmsd;": '\U00002221', "angmsdaa;": '\U000029A8', "angmsdab;": '\U000029A9', "angmsdac;": '\U000029AA', "angmsdad;": '\U000029AB', "angmsdae;": '\U000029AC', "angmsdaf;": '\U000029AD', "angmsdag;": '\U000029AE', "angmsdah;": '\U000029AF', "angrt;": '\U0000221F', "angrtvb;": '\U000022BE', "angrtvbd;": '\U0000299D', "angsph;": '\U00002222', "angst;": '\U000000C5', "angzarr;": '\U0000237C', "aogon;": '\U00000105', "aopf;": '\U0001D552', "ap;": '\U00002248', "apE;": '\U00002A70', "apacir;": '\U00002A6F', "ape;": '\U0000224A', "apid;": '\U0000224B', "apos;": '\U00000027', "approx;": '\U00002248', "approxeq;": '\U0000224A', "aring;": '\U000000E5', "ascr;": '\U0001D4B6', "ast;": '\U0000002A', "asymp;": '\U00002248', "asympeq;": '\U0000224D', "atilde;": '\U000000E3', "auml;": '\U000000E4', "awconint;": '\U00002233', "awint;": '\U00002A11', "bNot;": '\U00002AED', "backcong;": '\U0000224C', "backepsilon;": '\U000003F6', "backprime;": '\U00002035', "backsim;": '\U0000223D', "backsimeq;": '\U000022CD', "barvee;": '\U000022BD', "barwed;": '\U00002305', "barwedge;": '\U00002305', "bbrk;": '\U000023B5', "bbrktbrk;": '\U000023B6', "bcong;": '\U0000224C', "bcy;": '\U00000431', "bdquo;": '\U0000201E', "becaus;": '\U00002235', "because;": '\U00002235', "bemptyv;": '\U000029B0', "bepsi;": '\U000003F6', "bernou;": '\U0000212C', "beta;": '\U000003B2', "beth;": '\U00002136', "between;": '\U0000226C', "bfr;": '\U0001D51F', "bigcap;": '\U000022C2', "bigcirc;": '\U000025EF', "bigcup;": '\U000022C3', "bigodot;": '\U00002A00', "bigoplus;": '\U00002A01', "bigotimes;": '\U00002A02', "bigsqcup;": '\U00002A06', "bigstar;": '\U00002605', "bigtriangledown;": '\U000025BD', "bigtriangleup;": '\U000025B3', "biguplus;": '\U00002A04', "bigvee;": '\U000022C1', "bigwedge;": '\U000022C0', "bkarow;": '\U0000290D', "blacklozenge;": '\U000029EB', "blacksquare;": '\U000025AA', "blacktriangle;": '\U000025B4', "blacktriangledown;": '\U000025BE', "blacktriangleleft;": '\U000025C2', "blacktriangleright;": '\U000025B8', "blank;": '\U00002423', "blk12;": '\U00002592', "blk14;": '\U00002591', "blk34;": '\U00002593', "block;": '\U00002588', "bnot;": '\U00002310', "bopf;": '\U0001D553', "bot;": '\U000022A5', "bottom;": '\U000022A5', "bowtie;": '\U000022C8', "boxDL;": '\U00002557', "boxDR;": '\U00002554', "boxDl;": '\U00002556', "boxDr;": '\U00002553', "boxH;": '\U00002550', "boxHD;": '\U00002566', "boxHU;": '\U00002569', "boxHd;": '\U00002564', "boxHu;": '\U00002567', "boxUL;": '\U0000255D', "boxUR;": '\U0000255A', "boxUl;": '\U0000255C', "boxUr;": '\U00002559', "boxV;": '\U00002551', "boxVH;": '\U0000256C', "boxVL;": '\U00002563', "boxVR;": '\U00002560', "boxVh;": '\U0000256B', "boxVl;": '\U00002562', "boxVr;": '\U0000255F', "boxbox;": '\U000029C9', "boxdL;": '\U00002555', "boxdR;": '\U00002552', "boxdl;": '\U00002510', "boxdr;": '\U0000250C', "boxh;": '\U00002500', "boxhD;": '\U00002565', "boxhU;": '\U00002568', "boxhd;": '\U0000252C', "boxhu;": '\U00002534', "boxminus;": '\U0000229F', "boxplus;": '\U0000229E', "boxtimes;": '\U000022A0', "boxuL;": '\U0000255B', "boxuR;": '\U00002558', "boxul;": '\U00002518', "boxur;": '\U00002514', "boxv;": '\U00002502', "boxvH;": '\U0000256A', "boxvL;": '\U00002561', "boxvR;": '\U0000255E', "boxvh;": '\U0000253C', "boxvl;": '\U00002524', "boxvr;": '\U0000251C', "bprime;": '\U00002035', "breve;": '\U000002D8', "brvbar;": '\U000000A6', "bscr;": '\U0001D4B7', "bsemi;": '\U0000204F', "bsim;": '\U0000223D', "bsime;": '\U000022CD', "bsol;": '\U0000005C', "bsolb;": '\U000029C5', "bsolhsub;": '\U000027C8', "bull;": '\U00002022', "bullet;": '\U00002022', "bump;": '\U0000224E', "bumpE;": '\U00002AAE', "bumpe;": '\U0000224F', "bumpeq;": '\U0000224F', "cacute;": '\U00000107', "cap;": '\U00002229', "capand;": '\U00002A44', "capbrcup;": '\U00002A49', "capcap;": '\U00002A4B', "capcup;": '\U00002A47', "capdot;": '\U00002A40', "caret;": '\U00002041', "caron;": '\U000002C7', "ccaps;": '\U00002A4D', "ccaron;": '\U0000010D', "ccedil;": '\U000000E7', "ccirc;": '\U00000109', "ccups;": '\U00002A4C', "ccupssm;": '\U00002A50', "cdot;": '\U0000010B', "cedil;": '\U000000B8', "cemptyv;": '\U000029B2', "cent;": '\U000000A2', "centerdot;": '\U000000B7', "cfr;": '\U0001D520', "chcy;": '\U00000447', "check;": '\U00002713', "checkmark;": '\U00002713', "chi;": '\U000003C7', "cir;": '\U000025CB', "cirE;": '\U000029C3', "circ;": '\U000002C6', "circeq;": '\U00002257', "circlearrowleft;": '\U000021BA', "circlearrowright;": '\U000021BB', "circledR;": '\U000000AE', "circledS;": '\U000024C8', "circledast;": '\U0000229B', "circledcirc;": '\U0000229A', "circleddash;": '\U0000229D', "cire;": '\U00002257', "cirfnint;": '\U00002A10', "cirmid;": '\U00002AEF', "cirscir;": '\U000029C2', "clubs;": '\U00002663', "clubsuit;": '\U00002663', "colon;": '\U0000003A', "colone;": '\U00002254', "coloneq;": '\U00002254', "comma;": '\U0000002C', "commat;": '\U00000040', "comp;": '\U00002201', "compfn;": '\U00002218', "complement;": '\U00002201', "complexes;": '\U00002102', "cong;": '\U00002245', "congdot;": '\U00002A6D', "conint;": '\U0000222E', "copf;": '\U0001D554', "coprod;": '\U00002210', "copy;": '\U000000A9', "copysr;": '\U00002117', "crarr;": '\U000021B5', "cross;": '\U00002717', "cscr;": '\U0001D4B8', "csub;": '\U00002ACF', "csube;": '\U00002AD1', "csup;": '\U00002AD0', "csupe;": '\U00002AD2', "ctdot;": '\U000022EF', "cudarrl;": '\U00002938', "cudarrr;": '\U00002935', "cuepr;": '\U000022DE', "cuesc;": '\U000022DF', "cularr;": '\U000021B6', "cularrp;": '\U0000293D', "cup;": '\U0000222A', "cupbrcap;": '\U00002A48', "cupcap;": '\U00002A46', "cupcup;": '\U00002A4A', "cupdot;": '\U0000228D', "cupor;": '\U00002A45', "curarr;": '\U000021B7', "curarrm;": '\U0000293C', "curlyeqprec;": '\U000022DE', "curlyeqsucc;": '\U000022DF', "curlyvee;": '\U000022CE', "curlywedge;": '\U000022CF', "curren;": '\U000000A4', "curvearrowleft;": '\U000021B6', "curvearrowright;": '\U000021B7', "cuvee;": '\U000022CE', "cuwed;": '\U000022CF', "cwconint;": '\U00002232', "cwint;": '\U00002231', "cylcty;": '\U0000232D', "dArr;": '\U000021D3', "dHar;": '\U00002965', "dagger;": '\U00002020', "daleth;": '\U00002138', "darr;": '\U00002193', "dash;": '\U00002010', "dashv;": '\U000022A3', "dbkarow;": '\U0000290F', "dblac;": '\U000002DD', "dcaron;": '\U0000010F', "dcy;": '\U00000434', "dd;": '\U00002146', "ddagger;": '\U00002021', "ddarr;": '\U000021CA', "ddotseq;": '\U00002A77', "deg;": '\U000000B0', "delta;": '\U000003B4', "demptyv;": '\U000029B1', "dfisht;": '\U0000297F', "dfr;": '\U0001D521', "dharl;": '\U000021C3', "dharr;": '\U000021C2', "diam;": '\U000022C4', "diamond;": '\U000022C4', "diamondsuit;": '\U00002666', "diams;": '\U00002666', "die;": '\U000000A8', "digamma;": '\U000003DD', "disin;": '\U000022F2', "div;": '\U000000F7', "divide;": '\U000000F7', "divideontimes;": '\U000022C7', "divonx;": '\U000022C7', "djcy;": '\U00000452', "dlcorn;": '\U0000231E', "dlcrop;": '\U0000230D', "dollar;": '\U00000024', "dopf;": '\U0001D555', "dot;": '\U000002D9', "doteq;": '\U00002250', "doteqdot;": '\U00002251', "dotminus;": '\U00002238', "dotplus;": '\U00002214', "dotsquare;": '\U000022A1', "doublebarwedge;": '\U00002306', "downarrow;": '\U00002193', "downdownarrows;": '\U000021CA', "downharpoonleft;": '\U000021C3', "downharpoonright;": '\U000021C2', "drbkarow;": '\U00002910', "drcorn;": '\U0000231F', "drcrop;": '\U0000230C', "dscr;": '\U0001D4B9', "dscy;": '\U00000455', "dsol;": '\U000029F6', "dstrok;": '\U00000111', "dtdot;": '\U000022F1', "dtri;": '\U000025BF', "dtrif;": '\U000025BE', "duarr;": '\U000021F5', "duhar;": '\U0000296F', "dwangle;": '\U000029A6', "dzcy;": '\U0000045F', "dzigrarr;": '\U000027FF', "eDDot;": '\U00002A77', "eDot;": '\U00002251', "eacute;": '\U000000E9', "easter;": '\U00002A6E', "ecaron;": '\U0000011B', "ecir;": '\U00002256', "ecirc;": '\U000000EA', "ecolon;": '\U00002255', "ecy;": '\U0000044D', "edot;": '\U00000117', "ee;": '\U00002147', "efDot;": '\U00002252', "efr;": '\U0001D522', "eg;": '\U00002A9A', "egrave;": '\U000000E8', "egs;": '\U00002A96', "egsdot;": '\U00002A98', "el;": '\U00002A99', "elinters;": '\U000023E7', "ell;": '\U00002113', "els;": '\U00002A95', "elsdot;": '\U00002A97', "emacr;": '\U00000113', "empty;": '\U00002205', "emptyset;": '\U00002205', "emptyv;": '\U00002205', "emsp;": '\U00002003', "emsp13;": '\U00002004', "emsp14;": '\U00002005', "eng;": '\U0000014B', "ensp;": '\U00002002', "eogon;": '\U00000119', "eopf;": '\U0001D556', "epar;": '\U000022D5', "eparsl;": '\U000029E3', "eplus;": '\U00002A71', "epsi;": '\U000003B5', "epsilon;": '\U000003B5', "epsiv;": '\U000003F5', "eqcirc;": '\U00002256', "eqcolon;": '\U00002255', "eqsim;": '\U00002242', "eqslantgtr;": '\U00002A96', "eqslantless;": '\U00002A95', "equals;": '\U0000003D', "equest;": '\U0000225F', "equiv;": '\U00002261', "equivDD;": '\U00002A78', "eqvparsl;": '\U000029E5', "erDot;": '\U00002253', "erarr;": '\U00002971', "escr;": '\U0000212F', "esdot;": '\U00002250', "esim;": '\U00002242', "eta;": '\U000003B7', "eth;": '\U000000F0', "euml;": '\U000000EB', "euro;": '\U000020AC', "excl;": '\U00000021', "exist;": '\U00002203', "expectation;": '\U00002130', "exponentiale;": '\U00002147', "fallingdotseq;": '\U00002252', "fcy;": '\U00000444', "female;": '\U00002640', "ffilig;": '\U0000FB03', "fflig;": '\U0000FB00', "ffllig;": '\U0000FB04', "ffr;": '\U0001D523', "filig;": '\U0000FB01', "flat;": '\U0000266D', "fllig;": '\U0000FB02', "fltns;": '\U000025B1', "fnof;": '\U00000192', "fopf;": '\U0001D557', "forall;": '\U00002200', "fork;": '\U000022D4', "forkv;": '\U00002AD9', "fpartint;": '\U00002A0D', "frac12;": '\U000000BD', "frac13;": '\U00002153', "frac14;": '\U000000BC', "frac15;": '\U00002155', "frac16;": '\U00002159', "frac18;": '\U0000215B', "frac23;": '\U00002154', "frac25;": '\U00002156', "frac34;": '\U000000BE', "frac35;": '\U00002157', "frac38;": '\U0000215C', "frac45;": '\U00002158', "frac56;": '\U0000215A', "frac58;": '\U0000215D', "frac78;": '\U0000215E', "frasl;": '\U00002044', "frown;": '\U00002322', "fscr;": '\U0001D4BB', "gE;": '\U00002267', "gEl;": '\U00002A8C', "gacute;": '\U000001F5', "gamma;": '\U000003B3', "gammad;": '\U000003DD', "gap;": '\U00002A86', "gbreve;": '\U0000011F', "gcirc;": '\U0000011D', "gcy;": '\U00000433', "gdot;": '\U00000121', "ge;": '\U00002265', "gel;": '\U000022DB', "geq;": '\U00002265', "geqq;": '\U00002267', "geqslant;": '\U00002A7E', "ges;": '\U00002A7E', "gescc;": '\U00002AA9', "gesdot;": '\U00002A80', "gesdoto;": '\U00002A82', "gesdotol;": '\U00002A84', "gesles;": '\U00002A94', "gfr;": '\U0001D524', "gg;": '\U0000226B', "ggg;": '\U000022D9', "gimel;": '\U00002137', "gjcy;": '\U00000453', "gl;": '\U00002277', "glE;": '\U00002A92', "gla;": '\U00002AA5', "glj;": '\U00002AA4', "gnE;": '\U00002269', "gnap;": '\U00002A8A', "gnapprox;": '\U00002A8A', "gne;": '\U00002A88', "gneq;": '\U00002A88', "gneqq;": '\U00002269', "gnsim;": '\U000022E7', "gopf;": '\U0001D558', "grave;": '\U00000060', "gscr;": '\U0000210A', "gsim;": '\U00002273', "gsime;": '\U00002A8E', "gsiml;": '\U00002A90', "gt;": '\U0000003E', "gtcc;": '\U00002AA7', "gtcir;": '\U00002A7A', "gtdot;": '\U000022D7', "gtlPar;": '\U00002995', "gtquest;": '\U00002A7C', "gtrapprox;": '\U00002A86', "gtrarr;": '\U00002978', "gtrdot;": '\U000022D7', "gtreqless;": '\U000022DB', "gtreqqless;": '\U00002A8C', "gtrless;": '\U00002277', "gtrsim;": '\U00002273', "hArr;": '\U000021D4', "hairsp;": '\U0000200A', "half;": '\U000000BD', "hamilt;": '\U0000210B', "hardcy;": '\U0000044A', "harr;": '\U00002194', "harrcir;": '\U00002948', "harrw;": '\U000021AD', "hbar;": '\U0000210F', "hcirc;": '\U00000125', "hearts;": '\U00002665', "heartsuit;": '\U00002665', "hellip;": '\U00002026', "hercon;": '\U000022B9', "hfr;": '\U0001D525', "hksearow;": '\U00002925', "hkswarow;": '\U00002926', "hoarr;": '\U000021FF', "homtht;": '\U0000223B', "hookleftarrow;": '\U000021A9', "hookrightarrow;": '\U000021AA', "hopf;": '\U0001D559', "horbar;": '\U00002015', "hscr;": '\U0001D4BD', "hslash;": '\U0000210F', "hstrok;": '\U00000127', "hybull;": '\U00002043', "hyphen;": '\U00002010', "iacute;": '\U000000ED', "ic;": '\U00002063', "icirc;": '\U000000EE', "icy;": '\U00000438', "iecy;": '\U00000435', "iexcl;": '\U000000A1', "iff;": '\U000021D4', "ifr;": '\U0001D526', "igrave;": '\U000000EC', "ii;": '\U00002148', "iiiint;": '\U00002A0C', "iiint;": '\U0000222D', "iinfin;": '\U000029DC', "iiota;": '\U00002129', "ijlig;": '\U00000133', "imacr;": '\U0000012B', "image;": '\U00002111', "imagline;": '\U00002110', "imagpart;": '\U00002111', "imath;": '\U00000131', "imof;": '\U000022B7', "imped;": '\U000001B5', "in;": '\U00002208', "incare;": '\U00002105', "infin;": '\U0000221E', "infintie;": '\U000029DD', "inodot;": '\U00000131', "int;": '\U0000222B', "intcal;": '\U000022BA', "integers;": '\U00002124', "intercal;": '\U000022BA', "intlarhk;": '\U00002A17', "intprod;": '\U00002A3C', "iocy;": '\U00000451', "iogon;": '\U0000012F', "iopf;": '\U0001D55A', "iota;": '\U000003B9', "iprod;": '\U00002A3C', "iquest;": '\U000000BF', "iscr;": '\U0001D4BE', "isin;": '\U00002208', "isinE;": '\U000022F9', "isindot;": '\U000022F5', "isins;": '\U000022F4', "isinsv;": '\U000022F3', "isinv;": '\U00002208', "it;": '\U00002062', "itilde;": '\U00000129', "iukcy;": '\U00000456', "iuml;": '\U000000EF', "jcirc;": '\U00000135', "jcy;": '\U00000439', "jfr;": '\U0001D527', "jmath;": '\U00000237', "jopf;": '\U0001D55B', "jscr;": '\U0001D4BF', "jsercy;": '\U00000458', "jukcy;": '\U00000454', "kappa;": '\U000003BA', "kappav;": '\U000003F0', "kcedil;": '\U00000137', "kcy;": '\U0000043A', "kfr;": '\U0001D528', "kgreen;": '\U00000138', "khcy;": '\U00000445', "kjcy;": '\U0000045C', "kopf;": '\U0001D55C', "kscr;": '\U0001D4C0', "lAarr;": '\U000021DA', "lArr;": '\U000021D0', "lAtail;": '\U0000291B', "lBarr;": '\U0000290E', "lE;": '\U00002266', "lEg;": '\U00002A8B', "lHar;": '\U00002962', "lacute;": '\U0000013A', "laemptyv;": '\U000029B4', "lagran;": '\U00002112', "lambda;": '\U000003BB', "lang;": '\U000027E8', "langd;": '\U00002991', "langle;": '\U000027E8', "lap;": '\U00002A85', "laquo;": '\U000000AB', "larr;": '\U00002190', "larrb;": '\U000021E4', "larrbfs;": '\U0000291F', "larrfs;": '\U0000291D', "larrhk;": '\U000021A9', "larrlp;": '\U000021AB', "larrpl;": '\U00002939', "larrsim;": '\U00002973', "larrtl;": '\U000021A2', "lat;": '\U00002AAB', "latail;": '\U00002919', "late;": '\U00002AAD', "lbarr;": '\U0000290C', "lbbrk;": '\U00002772', "lbrace;": '\U0000007B', "lbrack;": '\U0000005B', "lbrke;": '\U0000298B', "lbrksld;": '\U0000298F', "lbrkslu;": '\U0000298D', "lcaron;": '\U0000013E', "lcedil;": '\U0000013C', "lceil;": '\U00002308', "lcub;": '\U0000007B', "lcy;": '\U0000043B', "ldca;": '\U00002936', "ldquo;": '\U0000201C', "ldquor;": '\U0000201E', "ldrdhar;": '\U00002967', "ldrushar;": '\U0000294B', "ldsh;": '\U000021B2', "le;": '\U00002264', "leftarrow;": '\U00002190', "leftarrowtail;": '\U000021A2', "leftharpoondown;": '\U000021BD', "leftharpoonup;": '\U000021BC', "leftleftarrows;": '\U000021C7', "leftrightarrow;": '\U00002194', "leftrightarrows;": '\U000021C6', "leftrightharpoons;": '\U000021CB', "leftrightsquigarrow;": '\U000021AD', "leftthreetimes;": '\U000022CB', "leg;": '\U000022DA', "leq;": '\U00002264', "leqq;": '\U00002266', "leqslant;": '\U00002A7D', "les;": '\U00002A7D', "lescc;": '\U00002AA8', "lesdot;": '\U00002A7F', "lesdoto;": '\U00002A81', "lesdotor;": '\U00002A83', "lesges;": '\U00002A93', "lessapprox;": '\U00002A85', "lessdot;": '\U000022D6', "lesseqgtr;": '\U000022DA', "lesseqqgtr;": '\U00002A8B', "lessgtr;": '\U00002276', "lesssim;": '\U00002272', "lfisht;": '\U0000297C', "lfloor;": '\U0000230A', "lfr;": '\U0001D529', "lg;": '\U00002276', "lgE;": '\U00002A91', "lhard;": '\U000021BD', "lharu;": '\U000021BC', "lharul;": '\U0000296A', "lhblk;": '\U00002584', "ljcy;": '\U00000459', "ll;": '\U0000226A', "llarr;": '\U000021C7', "llcorner;": '\U0000231E', "llhard;": '\U0000296B', "lltri;": '\U000025FA', "lmidot;": '\U00000140', "lmoust;": '\U000023B0', "lmoustache;": '\U000023B0', "lnE;": '\U00002268', "lnap;": '\U00002A89', "lnapprox;": '\U00002A89', "lne;": '\U00002A87', "lneq;": '\U00002A87', "lneqq;": '\U00002268', "lnsim;": '\U000022E6', "loang;": '\U000027EC', "loarr;": '\U000021FD', "lobrk;": '\U000027E6', "longleftarrow;": '\U000027F5', "longleftrightarrow;": '\U000027F7', "longmapsto;": '\U000027FC', "longrightarrow;": '\U000027F6', "looparrowleft;": '\U000021AB', "looparrowright;": '\U000021AC', "lopar;": '\U00002985', "lopf;": '\U0001D55D', "loplus;": '\U00002A2D', "lotimes;": '\U00002A34', "lowast;": '\U00002217', "lowbar;": '\U0000005F', "loz;": '\U000025CA', "lozenge;": '\U000025CA', "lozf;": '\U000029EB', "lpar;": '\U00000028', "lparlt;": '\U00002993', "lrarr;": '\U000021C6', "lrcorner;": '\U0000231F', "lrhar;": '\U000021CB', "lrhard;": '\U0000296D', "lrm;": '\U0000200E', "lrtri;": '\U000022BF', "lsaquo;": '\U00002039', "lscr;": '\U0001D4C1', "lsh;": '\U000021B0', "lsim;": '\U00002272', "lsime;": '\U00002A8D', "lsimg;": '\U00002A8F', "lsqb;": '\U0000005B', "lsquo;": '\U00002018', "lsquor;": '\U0000201A', "lstrok;": '\U00000142', "lt;": '\U0000003C', "ltcc;": '\U00002AA6', "ltcir;": '\U00002A79', "ltdot;": '\U000022D6', "lthree;": '\U000022CB', "ltimes;": '\U000022C9', "ltlarr;": '\U00002976', "ltquest;": '\U00002A7B', "ltrPar;": '\U00002996', "ltri;": '\U000025C3', "ltrie;": '\U000022B4', "ltrif;": '\U000025C2', "lurdshar;": '\U0000294A', "luruhar;": '\U00002966', "mDDot;": '\U0000223A', "macr;": '\U000000AF', "male;": '\U00002642', "malt;": '\U00002720', "maltese;": '\U00002720', "map;": '\U000021A6', "mapsto;": '\U000021A6', "mapstodown;": '\U000021A7', "mapstoleft;": '\U000021A4', "mapstoup;": '\U000021A5', "marker;": '\U000025AE', "mcomma;": '\U00002A29', "mcy;": '\U0000043C', "mdash;": '\U00002014', "measuredangle;": '\U00002221', "mfr;": '\U0001D52A', "mho;": '\U00002127', "micro;": '\U000000B5', "mid;": '\U00002223', "midast;": '\U0000002A', "midcir;": '\U00002AF0', "middot;": '\U000000B7', "minus;": '\U00002212', "minusb;": '\U0000229F', "minusd;": '\U00002238', "minusdu;": '\U00002A2A', "mlcp;": '\U00002ADB', "mldr;": '\U00002026', "mnplus;": '\U00002213', "models;": '\U000022A7', "mopf;": '\U0001D55E', "mp;": '\U00002213', "mscr;": '\U0001D4C2', "mstpos;": '\U0000223E', "mu;": '\U000003BC', "multimap;": '\U000022B8', "mumap;": '\U000022B8', "nLeftarrow;": '\U000021CD', "nLeftrightarrow;": '\U000021CE', "nRightarrow;": '\U000021CF', "nVDash;": '\U000022AF', "nVdash;": '\U000022AE', "nabla;": '\U00002207', "nacute;": '\U00000144', "nap;": '\U00002249', "napos;": '\U00000149', "napprox;": '\U00002249', "natur;": '\U0000266E', "natural;": '\U0000266E', "naturals;": '\U00002115', "nbsp;": '\U000000A0', "ncap;": '\U00002A43', "ncaron;": '\U00000148', "ncedil;": '\U00000146', "ncong;": '\U00002247', "ncup;": '\U00002A42', "ncy;": '\U0000043D', "ndash;": '\U00002013', "ne;": '\U00002260', "neArr;": '\U000021D7', "nearhk;": '\U00002924', "nearr;": '\U00002197', "nearrow;": '\U00002197', "nequiv;": '\U00002262', "nesear;": '\U00002928', "nexist;": '\U00002204', "nexists;": '\U00002204', "nfr;": '\U0001D52B', "nge;": '\U00002271', "ngeq;": '\U00002271', "ngsim;": '\U00002275', "ngt;": '\U0000226F', "ngtr;": '\U0000226F', "nhArr;": '\U000021CE', "nharr;": '\U000021AE', "nhpar;": '\U00002AF2', "ni;": '\U0000220B', "nis;": '\U000022FC', "nisd;": '\U000022FA', "niv;": '\U0000220B', "njcy;": '\U0000045A', "nlArr;": '\U000021CD', "nlarr;": '\U0000219A', "nldr;": '\U00002025', "nle;": '\U00002270', "nleftarrow;": '\U0000219A', "nleftrightarrow;": '\U000021AE', "nleq;": '\U00002270', "nless;": '\U0000226E', "nlsim;": '\U00002274', "nlt;": '\U0000226E', "nltri;": '\U000022EA', "nltrie;": '\U000022EC', "nmid;": '\U00002224', "nopf;": '\U0001D55F', "not;": '\U000000AC', "notin;": '\U00002209', "notinva;": '\U00002209', "notinvb;": '\U000022F7', "notinvc;": '\U000022F6', "notni;": '\U0000220C', "notniva;": '\U0000220C', "notnivb;": '\U000022FE', "notnivc;": '\U000022FD', "npar;": '\U00002226', "nparallel;": '\U00002226', "npolint;": '\U00002A14', "npr;": '\U00002280', "nprcue;": '\U000022E0', "nprec;": '\U00002280', "nrArr;": '\U000021CF', "nrarr;": '\U0000219B', "nrightarrow;": '\U0000219B', "nrtri;": '\U000022EB', "nrtrie;": '\U000022ED', "nsc;": '\U00002281', "nsccue;": '\U000022E1', "nscr;": '\U0001D4C3', "nshortmid;": '\U00002224', "nshortparallel;": '\U00002226', "nsim;": '\U00002241', "nsime;": '\U00002244', "nsimeq;": '\U00002244', "nsmid;": '\U00002224', "nspar;": '\U00002226', "nsqsube;": '\U000022E2', "nsqsupe;": '\U000022E3', "nsub;": '\U00002284', "nsube;": '\U00002288', "nsubseteq;": '\U00002288', "nsucc;": '\U00002281', "nsup;": '\U00002285', "nsupe;": '\U00002289', "nsupseteq;": '\U00002289', "ntgl;": '\U00002279', "ntilde;": '\U000000F1', "ntlg;": '\U00002278', "ntriangleleft;": '\U000022EA', "ntrianglelefteq;": '\U000022EC', "ntriangleright;": '\U000022EB', "ntrianglerighteq;": '\U000022ED', "nu;": '\U000003BD', "num;": '\U00000023', "numero;": '\U00002116', "numsp;": '\U00002007', "nvDash;": '\U000022AD', "nvHarr;": '\U00002904', "nvdash;": '\U000022AC', "nvinfin;": '\U000029DE', "nvlArr;": '\U00002902', "nvrArr;": '\U00002903', "nwArr;": '\U000021D6', "nwarhk;": '\U00002923', "nwarr;": '\U00002196', "nwarrow;": '\U00002196', "nwnear;": '\U00002927', "oS;": '\U000024C8', "oacute;": '\U000000F3', "oast;": '\U0000229B', "ocir;": '\U0000229A', "ocirc;": '\U000000F4', "ocy;": '\U0000043E', "odash;": '\U0000229D', "odblac;": '\U00000151', "odiv;": '\U00002A38', "odot;": '\U00002299', "odsold;": '\U000029BC', "oelig;": '\U00000153', "ofcir;": '\U000029BF', "ofr;": '\U0001D52C', "ogon;": '\U000002DB', "ograve;": '\U000000F2', "ogt;": '\U000029C1', "ohbar;": '\U000029B5', "ohm;": '\U000003A9', "oint;": '\U0000222E', "olarr;": '\U000021BA', "olcir;": '\U000029BE', "olcross;": '\U000029BB', "oline;": '\U0000203E', "olt;": '\U000029C0', "omacr;": '\U0000014D', "omega;": '\U000003C9', "omicron;": '\U000003BF', "omid;": '\U000029B6', "ominus;": '\U00002296', "oopf;": '\U0001D560', "opar;": '\U000029B7', "operp;": '\U000029B9', "oplus;": '\U00002295', "or;": '\U00002228', "orarr;": '\U000021BB', "ord;": '\U00002A5D', "order;": '\U00002134', "orderof;": '\U00002134', "ordf;": '\U000000AA', "ordm;": '\U000000BA', "origof;": '\U000022B6', "oror;": '\U00002A56', "orslope;": '\U00002A57', "orv;": '\U00002A5B', "oscr;": '\U00002134', "oslash;": '\U000000F8', "osol;": '\U00002298', "otilde;": '\U000000F5', "otimes;": '\U00002297', "otimesas;": '\U00002A36', "ouml;": '\U000000F6', "ovbar;": '\U0000233D', "par;": '\U00002225', "para;": '\U000000B6', "parallel;": '\U00002225', "parsim;": '\U00002AF3', "parsl;": '\U00002AFD', "part;": '\U00002202', "pcy;": '\U0000043F', "percnt;": '\U00000025', "period;": '\U0000002E', "permil;": '\U00002030', "perp;": '\U000022A5', "pertenk;": '\U00002031', "pfr;": '\U0001D52D', "phi;": '\U000003C6', "phiv;": '\U000003D5', "phmmat;": '\U00002133', "phone;": '\U0000260E', "pi;": '\U000003C0', "pitchfork;": '\U000022D4', "piv;": '\U000003D6', "planck;": '\U0000210F', "planckh;": '\U0000210E', "plankv;": '\U0000210F', "plus;": '\U0000002B', "plusacir;": '\U00002A23', "plusb;": '\U0000229E', "pluscir;": '\U00002A22', "plusdo;": '\U00002214', "plusdu;": '\U00002A25', "pluse;": '\U00002A72', "plusmn;": '\U000000B1', "plussim;": '\U00002A26', "plustwo;": '\U00002A27', "pm;": '\U000000B1', "pointint;": '\U00002A15', "popf;": '\U0001D561', "pound;": '\U000000A3', "pr;": '\U0000227A', "prE;": '\U00002AB3', "prap;": '\U00002AB7', "prcue;": '\U0000227C', "pre;": '\U00002AAF', "prec;": '\U0000227A', "precapprox;": '\U00002AB7', "preccurlyeq;": '\U0000227C', "preceq;": '\U00002AAF', "precnapprox;": '\U00002AB9', "precneqq;": '\U00002AB5', "precnsim;": '\U000022E8', "precsim;": '\U0000227E', "prime;": '\U00002032', "primes;": '\U00002119', "prnE;": '\U00002AB5', "prnap;": '\U00002AB9', "prnsim;": '\U000022E8', "prod;": '\U0000220F', "profalar;": '\U0000232E', "profline;": '\U00002312', "profsurf;": '\U00002313', "prop;": '\U0000221D', "propto;": '\U0000221D', "prsim;": '\U0000227E', "prurel;": '\U000022B0', "pscr;": '\U0001D4C5', "psi;": '\U000003C8', "puncsp;": '\U00002008', "qfr;": '\U0001D52E', "qint;": '\U00002A0C', "qopf;": '\U0001D562', "qprime;": '\U00002057', "qscr;": '\U0001D4C6', "quaternions;": '\U0000210D', "quatint;": '\U00002A16', "quest;": '\U0000003F', "questeq;": '\U0000225F', "quot;": '\U00000022', "rAarr;": '\U000021DB', "rArr;": '\U000021D2', "rAtail;": '\U0000291C', "rBarr;": '\U0000290F', "rHar;": '\U00002964', "racute;": '\U00000155', "radic;": '\U0000221A', "raemptyv;": '\U000029B3', "rang;": '\U000027E9', "rangd;": '\U00002992', "range;": '\U000029A5', "rangle;": '\U000027E9', "raquo;": '\U000000BB', "rarr;": '\U00002192', "rarrap;": '\U00002975', "rarrb;": '\U000021E5', "rarrbfs;": '\U00002920', "rarrc;": '\U00002933', "rarrfs;": '\U0000291E', "rarrhk;": '\U000021AA', "rarrlp;": '\U000021AC', "rarrpl;": '\U00002945', "rarrsim;": '\U00002974', "rarrtl;": '\U000021A3', "rarrw;": '\U0000219D', "ratail;": '\U0000291A', "ratio;": '\U00002236', "rationals;": '\U0000211A', "rbarr;": '\U0000290D', "rbbrk;": '\U00002773', "rbrace;": '\U0000007D', "rbrack;": '\U0000005D', "rbrke;": '\U0000298C', "rbrksld;": '\U0000298E', "rbrkslu;": '\U00002990', "rcaron;": '\U00000159', "rcedil;": '\U00000157', "rceil;": '\U00002309', "rcub;": '\U0000007D', "rcy;": '\U00000440', "rdca;": '\U00002937', "rdldhar;": '\U00002969', "rdquo;": '\U0000201D', "rdquor;": '\U0000201D', "rdsh;": '\U000021B3', "real;": '\U0000211C', "realine;": '\U0000211B', "realpart;": '\U0000211C', "reals;": '\U0000211D', "rect;": '\U000025AD', "reg;": '\U000000AE', "rfisht;": '\U0000297D', "rfloor;": '\U0000230B', "rfr;": '\U0001D52F', "rhard;": '\U000021C1', "rharu;": '\U000021C0', "rharul;": '\U0000296C', "rho;": '\U000003C1', "rhov;": '\U000003F1', "rightarrow;": '\U00002192', "rightarrowtail;": '\U000021A3', "rightharpoondown;": '\U000021C1', "rightharpoonup;": '\U000021C0', "rightleftarrows;": '\U000021C4', "rightleftharpoons;": '\U000021CC', "rightrightarrows;": '\U000021C9', "rightsquigarrow;": '\U0000219D', "rightthreetimes;": '\U000022CC', "ring;": '\U000002DA', "risingdotseq;": '\U00002253', "rlarr;": '\U000021C4', "rlhar;": '\U000021CC', "rlm;": '\U0000200F', "rmoust;": '\U000023B1', "rmoustache;": '\U000023B1', "rnmid;": '\U00002AEE', "roang;": '\U000027ED', "roarr;": '\U000021FE', "robrk;": '\U000027E7', "ropar;": '\U00002986', "ropf;": '\U0001D563', "roplus;": '\U00002A2E', "rotimes;": '\U00002A35', "rpar;": '\U00000029', "rpargt;": '\U00002994', "rppolint;": '\U00002A12', "rrarr;": '\U000021C9', "rsaquo;": '\U0000203A', "rscr;": '\U0001D4C7', "rsh;": '\U000021B1', "rsqb;": '\U0000005D', "rsquo;": '\U00002019', "rsquor;": '\U00002019', "rthree;": '\U000022CC', "rtimes;": '\U000022CA', "rtri;": '\U000025B9', "rtrie;": '\U000022B5', "rtrif;": '\U000025B8', "rtriltri;": '\U000029CE', "ruluhar;": '\U00002968', "rx;": '\U0000211E', "sacute;": '\U0000015B', "sbquo;": '\U0000201A', "sc;": '\U0000227B', "scE;": '\U00002AB4', "scap;": '\U00002AB8', "scaron;": '\U00000161', "sccue;": '\U0000227D', "sce;": '\U00002AB0', "scedil;": '\U0000015F', "scirc;": '\U0000015D', "scnE;": '\U00002AB6', "scnap;": '\U00002ABA', "scnsim;": '\U000022E9', "scpolint;": '\U00002A13', "scsim;": '\U0000227F', "scy;": '\U00000441', "sdot;": '\U000022C5', "sdotb;": '\U000022A1', "sdote;": '\U00002A66', "seArr;": '\U000021D8', "searhk;": '\U00002925', "searr;": '\U00002198', "searrow;": '\U00002198', "sect;": '\U000000A7', "semi;": '\U0000003B', "seswar;": '\U00002929', "setminus;": '\U00002216', "setmn;": '\U00002216', "sext;": '\U00002736', "sfr;": '\U0001D530', "sfrown;": '\U00002322', "sharp;": '\U0000266F', "shchcy;": '\U00000449', "shcy;": '\U00000448', "shortmid;": '\U00002223', "shortparallel;": '\U00002225', "shy;": '\U000000AD', "sigma;": '\U000003C3', "sigmaf;": '\U000003C2', "sigmav;": '\U000003C2', "sim;": '\U0000223C', "simdot;": '\U00002A6A', "sime;": '\U00002243', "simeq;": '\U00002243', "simg;": '\U00002A9E', "simgE;": '\U00002AA0', "siml;": '\U00002A9D', "simlE;": '\U00002A9F', "simne;": '\U00002246', "simplus;": '\U00002A24', "simrarr;": '\U00002972', "slarr;": '\U00002190', "smallsetminus;": '\U00002216', "smashp;": '\U00002A33', "smeparsl;": '\U000029E4', "smid;": '\U00002223', "smile;": '\U00002323', "smt;": '\U00002AAA', "smte;": '\U00002AAC', "softcy;": '\U0000044C', "sol;": '\U0000002F', "solb;": '\U000029C4', "solbar;": '\U0000233F', "sopf;": '\U0001D564', "spades;": '\U00002660', "spadesuit;": '\U00002660', "spar;": '\U00002225', "sqcap;": '\U00002293', "sqcup;": '\U00002294', "sqsub;": '\U0000228F', "sqsube;": '\U00002291', "sqsubset;": '\U0000228F', "sqsubseteq;": '\U00002291', "sqsup;": '\U00002290', "sqsupe;": '\U00002292', "sqsupset;": '\U00002290', "sqsupseteq;": '\U00002292', "squ;": '\U000025A1', "square;": '\U000025A1', "squarf;": '\U000025AA', "squf;": '\U000025AA', "srarr;": '\U00002192', "sscr;": '\U0001D4C8', "ssetmn;": '\U00002216', "ssmile;": '\U00002323', "sstarf;": '\U000022C6', "star;": '\U00002606', "starf;": '\U00002605', "straightepsilon;": '\U000003F5', "straightphi;": '\U000003D5', "strns;": '\U000000AF', "sub;": '\U00002282', "subE;": '\U00002AC5', "subdot;": '\U00002ABD', "sube;": '\U00002286', "subedot;": '\U00002AC3', "submult;": '\U00002AC1', "subnE;": '\U00002ACB', "subne;": '\U0000228A', "subplus;": '\U00002ABF', "subrarr;": '\U00002979', "subset;": '\U00002282', "subseteq;": '\U00002286', "subseteqq;": '\U00002AC5', "subsetneq;": '\U0000228A', "subsetneqq;": '\U00002ACB', "subsim;": '\U00002AC7', "subsub;": '\U00002AD5', "subsup;": '\U00002AD3', "succ;": '\U0000227B', "succapprox;": '\U00002AB8', "succcurlyeq;": '\U0000227D', "succeq;": '\U00002AB0', "succnapprox;": '\U00002ABA', "succneqq;": '\U00002AB6', "succnsim;": '\U000022E9', "succsim;": '\U0000227F', "sum;": '\U00002211', "sung;": '\U0000266A', "sup;": '\U00002283', "sup1;": '\U000000B9', "sup2;": '\U000000B2', "sup3;": '\U000000B3', "supE;": '\U00002AC6', "supdot;": '\U00002ABE', "supdsub;": '\U00002AD8', "supe;": '\U00002287', "supedot;": '\U00002AC4', "suphsol;": '\U000027C9', "suphsub;": '\U00002AD7', "suplarr;": '\U0000297B', "supmult;": '\U00002AC2', "supnE;": '\U00002ACC', "supne;": '\U0000228B', "supplus;": '\U00002AC0', "supset;": '\U00002283', "supseteq;": '\U00002287', "supseteqq;": '\U00002AC6', "supsetneq;": '\U0000228B', "supsetneqq;": '\U00002ACC', "supsim;": '\U00002AC8', "supsub;": '\U00002AD4', "supsup;": '\U00002AD6', "swArr;": '\U000021D9', "swarhk;": '\U00002926', "swarr;": '\U00002199', "swarrow;": '\U00002199', "swnwar;": '\U0000292A', "szlig;": '\U000000DF', "target;": '\U00002316', "tau;": '\U000003C4', "tbrk;": '\U000023B4', "tcaron;": '\U00000165', "tcedil;": '\U00000163', "tcy;": '\U00000442', "tdot;": '\U000020DB', "telrec;": '\U00002315', "tfr;": '\U0001D531', "there4;": '\U00002234', "therefore;": '\U00002234', "theta;": '\U000003B8', "thetasym;": '\U000003D1', "thetav;": '\U000003D1', "thickapprox;": '\U00002248', "thicksim;": '\U0000223C', "thinsp;": '\U00002009', "thkap;": '\U00002248', "thksim;": '\U0000223C', "thorn;": '\U000000FE', "tilde;": '\U000002DC', "times;": '\U000000D7', "timesb;": '\U000022A0', "timesbar;": '\U00002A31', "timesd;": '\U00002A30', "tint;": '\U0000222D', "toea;": '\U00002928', "top;": '\U000022A4', "topbot;": '\U00002336', "topcir;": '\U00002AF1', "topf;": '\U0001D565', "topfork;": '\U00002ADA', "tosa;": '\U00002929', "tprime;": '\U00002034', "trade;": '\U00002122', "triangle;": '\U000025B5', "triangledown;": '\U000025BF', "triangleleft;": '\U000025C3', "trianglelefteq;": '\U000022B4', "triangleq;": '\U0000225C', "triangleright;": '\U000025B9', "trianglerighteq;": '\U000022B5', "tridot;": '\U000025EC', "trie;": '\U0000225C', "triminus;": '\U00002A3A', "triplus;": '\U00002A39', "trisb;": '\U000029CD', "tritime;": '\U00002A3B', "trpezium;": '\U000023E2', "tscr;": '\U0001D4C9', "tscy;": '\U00000446', "tshcy;": '\U0000045B', "tstrok;": '\U00000167', "twixt;": '\U0000226C', "twoheadleftarrow;": '\U0000219E', "twoheadrightarrow;": '\U000021A0', "uArr;": '\U000021D1', "uHar;": '\U00002963', "uacute;": '\U000000FA', "uarr;": '\U00002191', "ubrcy;": '\U0000045E', "ubreve;": '\U0000016D', "ucirc;": '\U000000FB', "ucy;": '\U00000443', "udarr;": '\U000021C5', "udblac;": '\U00000171', "udhar;": '\U0000296E', "ufisht;": '\U0000297E', "ufr;": '\U0001D532', "ugrave;": '\U000000F9', "uharl;": '\U000021BF', "uharr;": '\U000021BE', "uhblk;": '\U00002580', "ulcorn;": '\U0000231C', "ulcorner;": '\U0000231C', "ulcrop;": '\U0000230F', "ultri;": '\U000025F8', "umacr;": '\U0000016B', "uml;": '\U000000A8', "uogon;": '\U00000173', "uopf;": '\U0001D566', "uparrow;": '\U00002191', "updownarrow;": '\U00002195', "upharpoonleft;": '\U000021BF', "upharpoonright;": '\U000021BE', "uplus;": '\U0000228E', "upsi;": '\U000003C5', "upsih;": '\U000003D2', "upsilon;": '\U000003C5', "upuparrows;": '\U000021C8', "urcorn;": '\U0000231D', "urcorner;": '\U0000231D', "urcrop;": '\U0000230E', "uring;": '\U0000016F', "urtri;": '\U000025F9', "uscr;": '\U0001D4CA', "utdot;": '\U000022F0', "utilde;": '\U00000169', "utri;": '\U000025B5', "utrif;": '\U000025B4', "uuarr;": '\U000021C8', "uuml;": '\U000000FC', "uwangle;": '\U000029A7', "vArr;": '\U000021D5', "vBar;": '\U00002AE8', "vBarv;": '\U00002AE9', "vDash;": '\U000022A8', "vangrt;": '\U0000299C', "varepsilon;": '\U000003F5', "varkappa;": '\U000003F0', "varnothing;": '\U00002205', "varphi;": '\U000003D5', "varpi;": '\U000003D6', "varpropto;": '\U0000221D', "varr;": '\U00002195', "varrho;": '\U000003F1', "varsigma;": '\U000003C2', "vartheta;": '\U000003D1', "vartriangleleft;": '\U000022B2', "vartriangleright;": '\U000022B3', "vcy;": '\U00000432', "vdash;": '\U000022A2', "vee;": '\U00002228', "veebar;": '\U000022BB', "veeeq;": '\U0000225A', "vellip;": '\U000022EE', "verbar;": '\U0000007C', "vert;": '\U0000007C', "vfr;": '\U0001D533', "vltri;": '\U000022B2', "vopf;": '\U0001D567', "vprop;": '\U0000221D', "vrtri;": '\U000022B3', "vscr;": '\U0001D4CB', "vzigzag;": '\U0000299A', "wcirc;": '\U00000175', "wedbar;": '\U00002A5F', "wedge;": '\U00002227', "wedgeq;": '\U00002259', "weierp;": '\U00002118', "wfr;": '\U0001D534', "wopf;": '\U0001D568', "wp;": '\U00002118', "wr;": '\U00002240', "wreath;": '\U00002240', "wscr;": '\U0001D4CC', "xcap;": '\U000022C2', "xcirc;": '\U000025EF', "xcup;": '\U000022C3', "xdtri;": '\U000025BD', "xfr;": '\U0001D535', "xhArr;": '\U000027FA', "xharr;": '\U000027F7', "xi;": '\U000003BE', "xlArr;": '\U000027F8', "xlarr;": '\U000027F5', "xmap;": '\U000027FC', "xnis;": '\U000022FB', "xodot;": '\U00002A00', "xopf;": '\U0001D569', "xoplus;": '\U00002A01', "xotime;": '\U00002A02', "xrArr;": '\U000027F9', "xrarr;": '\U000027F6', "xscr;": '\U0001D4CD', "xsqcup;": '\U00002A06', "xuplus;": '\U00002A04', "xutri;": '\U000025B3', "xvee;": '\U000022C1', "xwedge;": '\U000022C0', "yacute;": '\U000000FD', "yacy;": '\U0000044F', "ycirc;": '\U00000177', "ycy;": '\U0000044B', "yen;": '\U000000A5', "yfr;": '\U0001D536', "yicy;": '\U00000457', "yopf;": '\U0001D56A', "yscr;": '\U0001D4CE', "yucy;": '\U0000044E', "yuml;": '\U000000FF', "zacute;": '\U0000017A', "zcaron;": '\U0000017E', "zcy;": '\U00000437', "zdot;": '\U0000017C', "zeetrf;": '\U00002128', "zeta;": '\U000003B6', "zfr;": '\U0001D537', "zhcy;": '\U00000436', "zigrarr;": '\U000021DD', "zopf;": '\U0001D56B', "zscr;": '\U0001D4CF', "zwj;": '\U0000200D', "zwnj;": '\U0000200C', "AElig": '\U000000C6', "AMP": '\U00000026', "Aacute": '\U000000C1', "Acirc": '\U000000C2', "Agrave": '\U000000C0', "Aring": '\U000000C5', "Atilde": '\U000000C3', "Auml": '\U000000C4', "COPY": '\U000000A9', "Ccedil": '\U000000C7', "ETH": '\U000000D0', "Eacute": '\U000000C9', "Ecirc": '\U000000CA', "Egrave": '\U000000C8', "Euml": '\U000000CB', "GT": '\U0000003E', "Iacute": '\U000000CD', "Icirc": '\U000000CE', "Igrave": '\U000000CC', "Iuml": '\U000000CF', "LT": '\U0000003C', "Ntilde": '\U000000D1', "Oacute": '\U000000D3', "Ocirc": '\U000000D4', "Ograve": '\U000000D2', "Oslash": '\U000000D8', "Otilde": '\U000000D5', "Ouml": '\U000000D6', "QUOT": '\U00000022', "REG": '\U000000AE', "THORN": '\U000000DE', "Uacute": '\U000000DA', "Ucirc": '\U000000DB', "Ugrave": '\U000000D9', "Uuml": '\U000000DC', "Yacute": '\U000000DD', "aacute": '\U000000E1', "acirc": '\U000000E2', "acute": '\U000000B4', "aelig": '\U000000E6', "agrave": '\U000000E0', "amp": '\U00000026', "aring": '\U000000E5', "atilde": '\U000000E3', "auml": '\U000000E4', "brvbar": '\U000000A6', "ccedil": '\U000000E7', "cedil": '\U000000B8', "cent": '\U000000A2', "copy": '\U000000A9', "curren": '\U000000A4', "deg": '\U000000B0', "divide": '\U000000F7', "eacute": '\U000000E9', "ecirc": '\U000000EA', "egrave": '\U000000E8', "eth": '\U000000F0', "euml": '\U000000EB', "frac12": '\U000000BD', "frac14": '\U000000BC', "frac34": '\U000000BE', "gt": '\U0000003E', "iacute": '\U000000ED', "icirc": '\U000000EE', "iexcl": '\U000000A1', "igrave": '\U000000EC', "iquest": '\U000000BF', "iuml": '\U000000EF', "laquo": '\U000000AB', "lt": '\U0000003C', "macr": '\U000000AF', "micro": '\U000000B5', "middot": '\U000000B7', "nbsp": '\U000000A0', "not": '\U000000AC', "ntilde": '\U000000F1', "oacute": '\U000000F3', "ocirc": '\U000000F4', "ograve": '\U000000F2', "ordf": '\U000000AA', "ordm": '\U000000BA', "oslash": '\U000000F8', "otilde": '\U000000F5', "ouml": '\U000000F6', "para": '\U000000B6', "plusmn": '\U000000B1', "pound": '\U000000A3', "quot": '\U00000022', "raquo": '\U000000BB', "reg": '\U000000AE', "sect": '\U000000A7', "shy": '\U000000AD', "sup1": '\U000000B9', "sup2": '\U000000B2', "sup3": '\U000000B3', "szlig": '\U000000DF', "thorn": '\U000000FE', "times": '\U000000D7', "uacute": '\U000000FA', "ucirc": '\U000000FB', "ugrave": '\U000000F9', "uml": '\U000000A8', "uuml": '\U000000FC', "yacute": '\U000000FD', "yen": '\U000000A5', "yuml": '\U000000FF', } // HTML entities that are two unicode codepoints. var entity2 = map[string][2]rune{ // TODO(nigeltao): Handle replacements that are wider than their names. // "nLt;": {'\u226A', '\u20D2'}, // "nGt;": {'\u226B', '\u20D2'}, "NotEqualTilde;": {'\u2242', '\u0338'}, "NotGreaterFullEqual;": {'\u2267', '\u0338'}, "NotGreaterGreater;": {'\u226B', '\u0338'}, "NotGreaterSlantEqual;": {'\u2A7E', '\u0338'}, "NotHumpDownHump;": {'\u224E', '\u0338'}, "NotHumpEqual;": {'\u224F', '\u0338'}, "NotLeftTriangleBar;": {'\u29CF', '\u0338'}, "NotLessLess;": {'\u226A', '\u0338'}, "NotLessSlantEqual;": {'\u2A7D', '\u0338'}, "NotNestedGreaterGreater;": {'\u2AA2', '\u0338'}, "NotNestedLessLess;": {'\u2AA1', '\u0338'}, "NotPrecedesEqual;": {'\u2AAF', '\u0338'}, "NotRightTriangleBar;": {'\u29D0', '\u0338'}, "NotSquareSubset;": {'\u228F', '\u0338'}, "NotSquareSuperset;": {'\u2290', '\u0338'}, "NotSubset;": {'\u2282', '\u20D2'}, "NotSucceedsEqual;": {'\u2AB0', '\u0338'}, "NotSucceedsTilde;": {'\u227F', '\u0338'}, "NotSuperset;": {'\u2283', '\u20D2'}, "ThickSpace;": {'\u205F', '\u200A'}, "acE;": {'\u223E', '\u0333'}, "bne;": {'\u003D', '\u20E5'}, "bnequiv;": {'\u2261', '\u20E5'}, "caps;": {'\u2229', '\uFE00'}, "cups;": {'\u222A', '\uFE00'}, "fjlig;": {'\u0066', '\u006A'}, "gesl;": {'\u22DB', '\uFE00'}, "gvertneqq;": {'\u2269', '\uFE00'}, "gvnE;": {'\u2269', '\uFE00'}, "lates;": {'\u2AAD', '\uFE00'}, "lesg;": {'\u22DA', '\uFE00'}, "lvertneqq;": {'\u2268', '\uFE00'}, "lvnE;": {'\u2268', '\uFE00'}, "nGg;": {'\u22D9', '\u0338'}, "nGtv;": {'\u226B', '\u0338'}, "nLl;": {'\u22D8', '\u0338'}, "nLtv;": {'\u226A', '\u0338'}, "nang;": {'\u2220', '\u20D2'}, "napE;": {'\u2A70', '\u0338'}, "napid;": {'\u224B', '\u0338'}, "nbump;": {'\u224E', '\u0338'}, "nbumpe;": {'\u224F', '\u0338'}, "ncongdot;": {'\u2A6D', '\u0338'}, "nedot;": {'\u2250', '\u0338'}, "nesim;": {'\u2242', '\u0338'}, "ngE;": {'\u2267', '\u0338'}, "ngeqq;": {'\u2267', '\u0338'}, "ngeqslant;": {'\u2A7E', '\u0338'}, "nges;": {'\u2A7E', '\u0338'}, "nlE;": {'\u2266', '\u0338'}, "nleqq;": {'\u2266', '\u0338'}, "nleqslant;": {'\u2A7D', '\u0338'}, "nles;": {'\u2A7D', '\u0338'}, "notinE;": {'\u22F9', '\u0338'}, "notindot;": {'\u22F5', '\u0338'}, "nparsl;": {'\u2AFD', '\u20E5'}, "npart;": {'\u2202', '\u0338'}, "npre;": {'\u2AAF', '\u0338'}, "npreceq;": {'\u2AAF', '\u0338'}, "nrarrc;": {'\u2933', '\u0338'}, "nrarrw;": {'\u219D', '\u0338'}, "nsce;": {'\u2AB0', '\u0338'}, "nsubE;": {'\u2AC5', '\u0338'}, "nsubset;": {'\u2282', '\u20D2'}, "nsubseteqq;": {'\u2AC5', '\u0338'}, "nsucceq;": {'\u2AB0', '\u0338'}, "nsupE;": {'\u2AC6', '\u0338'}, "nsupset;": {'\u2283', '\u20D2'}, "nsupseteqq;": {'\u2AC6', '\u0338'}, "nvap;": {'\u224D', '\u20D2'}, "nvge;": {'\u2265', '\u20D2'}, "nvgt;": {'\u003E', '\u20D2'}, "nvle;": {'\u2264', '\u20D2'}, "nvlt;": {'\u003C', '\u20D2'}, "nvltrie;": {'\u22B4', '\u20D2'}, "nvrtrie;": {'\u22B5', '\u20D2'}, "nvsim;": {'\u223C', '\u20D2'}, "race;": {'\u223D', '\u0331'}, "smtes;": {'\u2AAC', '\uFE00'}, "sqcaps;": {'\u2293', '\uFE00'}, "sqcups;": {'\u2294', '\uFE00'}, "varsubsetneq;": {'\u228A', '\uFE00'}, "varsubsetneqq;": {'\u2ACB', '\uFE00'}, "varsupsetneq;": {'\u228B', '\uFE00'}, "varsupsetneqq;": {'\u2ACC', '\uFE00'}, "vnsub;": {'\u2282', '\u20D2'}, "vnsup;": {'\u2283', '\u20D2'}, "vsubnE;": {'\u2ACB', '\uFE00'}, "vsubne;": {'\u228A', '\uFE00'}, "vsupnE;": {'\u2ACC', '\uFE00'}, "vsupne;": {'\u228B', '\uFE00'}, } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/entity_test.go000066400000000000000000000017761415442132300243100ustar00rootroot00000000000000// Copyright 2010 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 html import ( "testing" "unicode/utf8" ) func TestEntityLength(t *testing.T) { // We verify that the length of UTF-8 encoding of each value is <= 1 + len(key). // The +1 comes from the leading "&". This property implies that the length of // unescaped text is <= the length of escaped text. for k, v := range entity { if 1+len(k) < utf8.RuneLen(v) { t.Error("escaped entity &" + k + " is shorter than its UTF-8 encoding " + string(v)) } if len(k) > longestEntityWithoutSemicolon && k[len(k)-1] != ';' { t.Errorf("entity name %s is %d characters, but longestEntityWithoutSemicolon=%d", k, len(k), longestEntityWithoutSemicolon) } } for k, v := range entity2 { if 1+len(k) < utf8.RuneLen(v[0])+utf8.RuneLen(v[1]) { t.Error("escaped entity &" + k + " is shorter than its UTF-8 encoding " + string(v[0]) + string(v[1])) } } } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/escape.go000066400000000000000000000136451415442132300231730ustar00rootroot00000000000000// Copyright 2010 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 html import ( "bytes" "strings" "unicode/utf8" ) // These replacements permit compatibility with old numeric entities that // assumed Windows-1252 encoding. // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference var replacementTable = [...]rune{ '\u20AC', // First entry is what 0x80 should be replaced with. '\u0081', '\u201A', '\u0192', '\u201E', '\u2026', '\u2020', '\u2021', '\u02C6', '\u2030', '\u0160', '\u2039', '\u0152', '\u008D', '\u017D', '\u008F', '\u0090', '\u2018', '\u2019', '\u201C', '\u201D', '\u2022', '\u2013', '\u2014', '\u02DC', '\u2122', '\u0161', '\u203A', '\u0153', '\u009D', '\u017E', '\u0178', // Last entry is 0x9F. // 0x00->'\uFFFD' is handled programmatically. // 0x0D->'\u000D' is a no-op. } // unescapeEntity reads an entity like "<" from b[src:] and writes the // corresponding "<" to b[dst:], returning the incremented dst and src cursors. // Precondition: b[src] == '&' && dst <= src. // attribute should be true if parsing an attribute value. func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference // i starts at 1 because we already know that s[0] == '&'. i, s := 1, b[src:] if len(s) <= 1 { b[dst] = b[src] return dst + 1, src + 1 } if s[i] == '#' { if len(s) <= 3 { // We need to have at least "&#.". b[dst] = b[src] return dst + 1, src + 1 } i++ c := s[i] hex := false if c == 'x' || c == 'X' { hex = true i++ } x := '\x00' for i < len(s) { c = s[i] i++ if hex { if '0' <= c && c <= '9' { x = 16*x + rune(c) - '0' continue } else if 'a' <= c && c <= 'f' { x = 16*x + rune(c) - 'a' + 10 continue } else if 'A' <= c && c <= 'F' { x = 16*x + rune(c) - 'A' + 10 continue } } else if '0' <= c && c <= '9' { x = 10*x + rune(c) - '0' continue } if c != ';' { i-- } break } if i <= 3 { // No characters matched. b[dst] = b[src] return dst + 1, src + 1 } if 0x80 <= x && x <= 0x9F { // Replace characters from Windows-1252 with UTF-8 equivalents. x = replacementTable[x-0x80] } else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF { // Replace invalid characters with the replacement character. x = '\uFFFD' } return dst + utf8.EncodeRune(b[dst:], x), src + i } // Consume the maximum number of characters possible, with the // consumed characters matching one of the named references. for i < len(s) { c := s[i] i++ // Lower-cased characters are more common in entities, so we check for them first. if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' { continue } if c != ';' { i-- } break } entityName := string(s[1:i]) if entityName == "" { // No-op. } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' { // No-op. } else if x := entity[entityName]; x != 0 { return dst + utf8.EncodeRune(b[dst:], x), src + i } else if x := entity2[entityName]; x[0] != 0 { dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i } else if !attribute { maxLen := len(entityName) - 1 if maxLen > longestEntityWithoutSemicolon { maxLen = longestEntityWithoutSemicolon } for j := maxLen; j > 1; j-- { if x := entity[entityName[:j]]; x != 0 { return dst + utf8.EncodeRune(b[dst:], x), src + j + 1 } } } dst1, src1 = dst+i, src+i copy(b[dst:dst1], b[src:src1]) return dst1, src1 } // unescape unescapes b's entities in-place, so that "a<b" becomes "a': esc = ">" case '"': // """ is shorter than """. esc = """ case '\r': esc = " " default: panic("unrecognized escape character") } s = s[i+1:] if _, err := w.WriteString(esc); err != nil { return err } i = strings.IndexAny(s, escapedChars) } _, err := w.WriteString(s) return err } // EscapeString escapes special characters like "<" to become "<". It // escapes only five such characters: <, >, &, ' and ". // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't // always true. func EscapeString(s string) string { if strings.IndexAny(s, escapedChars) == -1 { return s } var buf bytes.Buffer escape(&buf, s) return buf.String() } // UnescapeString unescapes entities like "<" to become "<". It unescapes a // larger range of entities than EscapeString escapes. For example, "á" // unescapes to "á", as does "á" and "&xE1;". // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't // always true. func UnescapeString(s string) string { for _, c := range s { if c == '&' { return string(unescape([]byte(s), false)) } } return s } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/escape_test.go000066400000000000000000000035351415442132300242270ustar00rootroot00000000000000// 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. package html import "testing" type unescapeTest struct { // A short description of the test case. desc string // The HTML text. html string // The unescaped text. unescaped string } var unescapeTests = []unescapeTest{ // Handle no entities. { "copy", "A\ttext\nstring", "A\ttext\nstring", }, // Handle simple named entities. { "simple", "& > <", "& > <", }, // Handle hitting the end of the string. { "stringEnd", "& &", "& &", }, // Handle entities with two codepoints. { "multiCodepoint", "text ⋛︀ blah", "text \u22db\ufe00 blah", }, // Handle decimal numeric entities. { "decimalEntity", "Delta = Δ ", "Delta = Δ ", }, // Handle hexadecimal numeric entities. { "hexadecimalEntity", "Lambda = λ = λ ", "Lambda = λ = λ ", }, // Handle numeric early termination. { "numericEnds", "&# &#x €43 © = ©f = ©", "&# &#x €43 © = ©f = ©", }, // Handle numeric ISO-8859-1 entity replacements. { "numericReplacements", "Footnote‡", "Footnote‡", }, } func TestUnescape(t *testing.T) { for _, tt := range unescapeTests { unescaped := UnescapeString(tt.html) if unescaped != tt.unescaped { t.Errorf("TestUnescape %s: want %q, got %q", tt.desc, tt.unescaped, unescaped) } } } func TestUnescapeEscape(t *testing.T) { ss := []string{ ``, `abc def`, `a & b`, `a&b`, `a & b`, `"`, `"`, `"<&>"`, `"<&>"`, `3&5==1 && 0<1, "0<1", a+acute=á`, `The special characters are: <, >, &, ' and "`, } for _, s := range ss { if got := UnescapeString(EscapeString(s)); got != s { t.Errorf("got %q want %q", got, s) } } } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/example_test.go000066400000000000000000000015021415442132300244120ustar00rootroot00000000000000// Copyright 2012 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. // This example demonstrates parsing HTML data and walking the resulting tree. package html_test import ( "fmt" "log" "strings" "golang.org/x/net/html" ) func ExampleParse() { s := `

Links:

` doc, err := html.Parse(strings.NewReader(s)) if err != nil { log.Fatal(err) } var f func(*html.Node) f = func(n *html.Node) { if n.Type == html.ElementNode && n.Data == "a" { for _, a := range n.Attr { if a.Key == "href" { fmt.Println(a.Val) break } } } for c := n.FirstChild; c != nil; c = c.NextSibling { f(c) } } f(doc) // Output: // foo // /bar/baz } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/foreign.go000066400000000000000000000143321415442132300233560ustar00rootroot00000000000000// 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 html import ( "strings" ) func adjustAttributeNames(aa []Attribute, nameMap map[string]string) { for i := range aa { if newName, ok := nameMap[aa[i].Key]; ok { aa[i].Key = newName } } } func adjustForeignAttributes(aa []Attribute) { for i, a := range aa { if a.Key == "" || a.Key[0] != 'x' { continue } switch a.Key { case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show", "xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink": j := strings.Index(a.Key, ":") aa[i].Namespace = a.Key[:j] aa[i].Key = a.Key[j+1:] } } } func htmlIntegrationPoint(n *Node) bool { if n.Type != ElementNode { return false } switch n.Namespace { case "math": if n.Data == "annotation-xml" { for _, a := range n.Attr { if a.Key == "encoding" { val := strings.ToLower(a.Val) if val == "text/html" || val == "application/xhtml+xml" { return true } } } } case "svg": switch n.Data { case "desc", "foreignObject", "title": return true } } return false } func mathMLTextIntegrationPoint(n *Node) bool { if n.Namespace != "math" { return false } switch n.Data { case "mi", "mo", "mn", "ms", "mtext": return true } return false } // Section 12.2.6.5. var breakout = map[string]bool{ "b": true, "big": true, "blockquote": true, "body": true, "br": true, "center": true, "code": true, "dd": true, "div": true, "dl": true, "dt": true, "em": true, "embed": true, "h1": true, "h2": true, "h3": true, "h4": true, "h5": true, "h6": true, "head": true, "hr": true, "i": true, "img": true, "li": true, "listing": true, "menu": true, "meta": true, "nobr": true, "ol": true, "p": true, "pre": true, "ruby": true, "s": true, "small": true, "span": true, "strong": true, "strike": true, "sub": true, "sup": true, "table": true, "tt": true, "u": true, "ul": true, "var": true, } // Section 12.2.6.5. var svgTagNameAdjustments = map[string]string{ "altglyph": "altGlyph", "altglyphdef": "altGlyphDef", "altglyphitem": "altGlyphItem", "animatecolor": "animateColor", "animatemotion": "animateMotion", "animatetransform": "animateTransform", "clippath": "clipPath", "feblend": "feBlend", "fecolormatrix": "feColorMatrix", "fecomponenttransfer": "feComponentTransfer", "fecomposite": "feComposite", "feconvolvematrix": "feConvolveMatrix", "fediffuselighting": "feDiffuseLighting", "fedisplacementmap": "feDisplacementMap", "fedistantlight": "feDistantLight", "feflood": "feFlood", "fefunca": "feFuncA", "fefuncb": "feFuncB", "fefuncg": "feFuncG", "fefuncr": "feFuncR", "fegaussianblur": "feGaussianBlur", "feimage": "feImage", "femerge": "feMerge", "femergenode": "feMergeNode", "femorphology": "feMorphology", "feoffset": "feOffset", "fepointlight": "fePointLight", "fespecularlighting": "feSpecularLighting", "fespotlight": "feSpotLight", "fetile": "feTile", "feturbulence": "feTurbulence", "foreignobject": "foreignObject", "glyphref": "glyphRef", "lineargradient": "linearGradient", "radialgradient": "radialGradient", "textpath": "textPath", } // Section 12.2.6.1 var mathMLAttributeAdjustments = map[string]string{ "definitionurl": "definitionURL", } var svgAttributeAdjustments = map[string]string{ "attributename": "attributeName", "attributetype": "attributeType", "basefrequency": "baseFrequency", "baseprofile": "baseProfile", "calcmode": "calcMode", "clippathunits": "clipPathUnits", "diffuseconstant": "diffuseConstant", "edgemode": "edgeMode", "filterunits": "filterUnits", "glyphref": "glyphRef", "gradienttransform": "gradientTransform", "gradientunits": "gradientUnits", "kernelmatrix": "kernelMatrix", "kernelunitlength": "kernelUnitLength", "keypoints": "keyPoints", "keysplines": "keySplines", "keytimes": "keyTimes", "lengthadjust": "lengthAdjust", "limitingconeangle": "limitingConeAngle", "markerheight": "markerHeight", "markerunits": "markerUnits", "markerwidth": "markerWidth", "maskcontentunits": "maskContentUnits", "maskunits": "maskUnits", "numoctaves": "numOctaves", "pathlength": "pathLength", "patterncontentunits": "patternContentUnits", "patterntransform": "patternTransform", "patternunits": "patternUnits", "pointsatx": "pointsAtX", "pointsaty": "pointsAtY", "pointsatz": "pointsAtZ", "preservealpha": "preserveAlpha", "preserveaspectratio": "preserveAspectRatio", "primitiveunits": "primitiveUnits", "refx": "refX", "refy": "refY", "repeatcount": "repeatCount", "repeatdur": "repeatDur", "requiredextensions": "requiredExtensions", "requiredfeatures": "requiredFeatures", "specularconstant": "specularConstant", "specularexponent": "specularExponent", "spreadmethod": "spreadMethod", "startoffset": "startOffset", "stddeviation": "stdDeviation", "stitchtiles": "stitchTiles", "surfacescale": "surfaceScale", "systemlanguage": "systemLanguage", "tablevalues": "tableValues", "targetx": "targetX", "targety": "targetY", "textlength": "textLength", "viewbox": "viewBox", "viewtarget": "viewTarget", "xchannelselector": "xChannelSelector", "ychannelselector": "yChannelSelector", "zoomandpan": "zoomAndPan", } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/node.go000066400000000000000000000130211415442132300226440ustar00rootroot00000000000000// 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 html import ( "golang.org/x/net/html/atom" ) // A NodeType is the type of a Node. type NodeType uint32 const ( ErrorNode NodeType = iota TextNode DocumentNode ElementNode CommentNode DoctypeNode // RawNode nodes are not returned by the parser, but can be part of the // Node tree passed to func Render to insert raw HTML (without escaping). // If so, this package makes no guarantee that the rendered HTML is secure // (from e.g. Cross Site Scripting attacks) or well-formed. RawNode scopeMarkerNode ) // Section 12.2.4.3 says "The markers are inserted when entering applet, // object, marquee, template, td, th, and caption elements, and are used // to prevent formatting from "leaking" into applet, object, marquee, // template, td, th, and caption elements". var scopeMarker = Node{Type: scopeMarkerNode} // A Node consists of a NodeType and some Data (tag name for element nodes, // content for text) and are part of a tree of Nodes. Element nodes may also // have a Namespace and contain a slice of Attributes. Data is unescaped, so // that it looks like "a 0 { return (*s)[i-1] } return nil } // index returns the index of the top-most occurrence of n in the stack, or -1 // if n is not present. func (s *nodeStack) index(n *Node) int { for i := len(*s) - 1; i >= 0; i-- { if (*s)[i] == n { return i } } return -1 } // contains returns whether a is within s. func (s *nodeStack) contains(a atom.Atom) bool { for _, n := range *s { if n.DataAtom == a && n.Namespace == "" { return true } } return false } // insert inserts a node at the given index. func (s *nodeStack) insert(i int, n *Node) { (*s) = append(*s, nil) copy((*s)[i+1:], (*s)[i:]) (*s)[i] = n } // remove removes a node from the stack. It is a no-op if n is not present. func (s *nodeStack) remove(n *Node) { i := s.index(n) if i == -1 { return } copy((*s)[i:], (*s)[i+1:]) j := len(*s) - 1 (*s)[j] = nil *s = (*s)[:j] } type insertionModeStack []insertionMode func (s *insertionModeStack) pop() (im insertionMode) { i := len(*s) im = (*s)[i-1] *s = (*s)[:i-1] return im } func (s *insertionModeStack) top() insertionMode { if i := len(*s); i > 0 { return (*s)[i-1] } return nil } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/node_test.go000066400000000000000000000073051415442132300237130ustar00rootroot00000000000000// Copyright 2010 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 html import ( "fmt" ) // checkTreeConsistency checks that a node and its descendants are all // consistent in their parent/child/sibling relationships. func checkTreeConsistency(n *Node) error { return checkTreeConsistency1(n, 0) } func checkTreeConsistency1(n *Node, depth int) error { if depth == 1e4 { return fmt.Errorf("html: tree looks like it contains a cycle") } if err := checkNodeConsistency(n); err != nil { return err } for c := n.FirstChild; c != nil; c = c.NextSibling { if err := checkTreeConsistency1(c, depth+1); err != nil { return err } } return nil } // checkNodeConsistency checks that a node's parent/child/sibling relationships // are consistent. func checkNodeConsistency(n *Node) error { if n == nil { return nil } nParent := 0 for p := n.Parent; p != nil; p = p.Parent { nParent++ if nParent == 1e4 { return fmt.Errorf("html: parent list looks like an infinite loop") } } nForward := 0 for c := n.FirstChild; c != nil; c = c.NextSibling { nForward++ if nForward == 1e6 { return fmt.Errorf("html: forward list of children looks like an infinite loop") } if c.Parent != n { return fmt.Errorf("html: inconsistent child/parent relationship") } } nBackward := 0 for c := n.LastChild; c != nil; c = c.PrevSibling { nBackward++ if nBackward == 1e6 { return fmt.Errorf("html: backward list of children looks like an infinite loop") } if c.Parent != n { return fmt.Errorf("html: inconsistent child/parent relationship") } } if n.Parent != nil { if n.Parent == n { return fmt.Errorf("html: inconsistent parent relationship") } if n.Parent == n.FirstChild { return fmt.Errorf("html: inconsistent parent/first relationship") } if n.Parent == n.LastChild { return fmt.Errorf("html: inconsistent parent/last relationship") } if n.Parent == n.PrevSibling { return fmt.Errorf("html: inconsistent parent/prev relationship") } if n.Parent == n.NextSibling { return fmt.Errorf("html: inconsistent parent/next relationship") } parentHasNAsAChild := false for c := n.Parent.FirstChild; c != nil; c = c.NextSibling { if c == n { parentHasNAsAChild = true break } } if !parentHasNAsAChild { return fmt.Errorf("html: inconsistent parent/child relationship") } } if n.PrevSibling != nil && n.PrevSibling.NextSibling != n { return fmt.Errorf("html: inconsistent prev/next relationship") } if n.NextSibling != nil && n.NextSibling.PrevSibling != n { return fmt.Errorf("html: inconsistent next/prev relationship") } if (n.FirstChild == nil) != (n.LastChild == nil) { return fmt.Errorf("html: inconsistent first/last relationship") } if n.FirstChild != nil && n.FirstChild == n.LastChild { // We have a sole child. if n.FirstChild.PrevSibling != nil || n.FirstChild.NextSibling != nil { return fmt.Errorf("html: inconsistent sole child's sibling relationship") } } seen := map[*Node]bool{} var last *Node for c := n.FirstChild; c != nil; c = c.NextSibling { if seen[c] { return fmt.Errorf("html: inconsistent repeated child") } seen[c] = true last = c } if last != n.LastChild { return fmt.Errorf("html: inconsistent last relationship") } var first *Node for c := n.LastChild; c != nil; c = c.PrevSibling { if !seen[c] { return fmt.Errorf("html: inconsistent missing child") } delete(seen, c) first = c } if first != n.FirstChild { return fmt.Errorf("html: inconsistent first relationship") } if len(seen) != 0 { return fmt.Errorf("html: inconsistent forwards/backwards child list") } return nil } golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/parse.go000066400000000000000000001652361415442132300230510ustar00rootroot00000000000000// Copyright 2010 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 html import ( "errors" "fmt" "io" "strings" a "golang.org/x/net/html/atom" ) // A parser implements the HTML5 parsing algorithm: // https://html.spec.whatwg.org/multipage/syntax.html#tree-construction type parser struct { // tokenizer provides the tokens for the parser. tokenizer *Tokenizer // tok is the most recently read token. tok Token // Self-closing tags like
are treated as start tags, except that // hasSelfClosingToken is set while they are being processed. hasSelfClosingToken bool // doc is the document root element. doc *Node // The stack of open elements (section 12.2.4.2) and active formatting // elements (section 12.2.4.3). oe, afe nodeStack // Element pointers (section 12.2.4.4). head, form *Node // Other parsing state flags (section 12.2.4.5). scripting, framesetOK bool // The stack of template insertion modes templateStack insertionModeStack // im is the current insertion mode. im insertionMode // originalIM is the insertion mode to go back to after completing a text // or inTableText insertion mode. originalIM insertionMode // fosterParenting is whether new elements should be inserted according to // the foster parenting rules (section 12.2.6.1). fosterParenting bool // quirks is whether the parser is operating in "quirks mode." quirks bool // fragment is whether the parser is parsing an HTML fragment. fragment bool // context is the context element when parsing an HTML fragment // (section 12.4). context *Node } func (p *parser) top() *Node { if n := p.oe.top(); n != nil { return n } return p.doc } // Stop tags for use in popUntil. These come from section 12.2.4.2. var ( defaultScopeStopTags = map[string][]a.Atom{ "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template}, "math": {a.AnnotationXml, a.Mi, a.Mn, a.Mo, a.Ms, a.Mtext}, "svg": {a.Desc, a.ForeignObject, a.Title}, } ) type scope int const ( defaultScope scope = iota listItemScope buttonScope tableScope tableRowScope tableBodyScope selectScope ) // popUntil pops the stack of open elements at the highest element whose tag // is in matchTags, provided there is no higher element in the scope's stop // tags (as defined in section 12.2.4.2). It returns whether or not there was // such an element. If there was not, popUntil leaves the stack unchanged. // // For example, the set of stop tags for table scope is: "html", "table". If // the stack was: // ["html", "body", "font", "table", "b", "i", "u"] // then popUntil(tableScope, "font") would return false, but // popUntil(tableScope, "i") would return true and the stack would become: // ["html", "body", "font", "table", "b"] // // If an element's tag is in both the stop tags and matchTags, then the stack // will be popped and the function returns true (provided, of course, there was // no higher element in the stack that was also in the stop tags). For example, // popUntil(tableScope, "table") returns true and leaves: // ["html", "body", "font"] func (p *parser) popUntil(s scope, matchTags ...a.Atom) bool { if i := p.indexOfElementInScope(s, matchTags...); i != -1 { p.oe = p.oe[:i] return true } return false } // indexOfElementInScope returns the index in p.oe of the highest element whose // tag is in matchTags that is in scope. If no matching element is in scope, it // returns -1. func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int { for i := len(p.oe) - 1; i >= 0; i-- { tagAtom := p.oe[i].DataAtom if p.oe[i].Namespace == "" { for _, t := range matchTags { if t == tagAtom { return i } } switch s { case defaultScope: // No-op. case listItemScope: if tagAtom == a.Ol || tagAtom == a.Ul { return -1 } case buttonScope: if tagAtom == a.Button { return -1 } case tableScope: if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template { return -1 } case selectScope: if tagAtom != a.Optgroup && tagAtom != a.Option { return -1 } default: panic("unreachable") } } switch s { case defaultScope, listItemScope, buttonScope: for _, t := range defaultScopeStopTags[p.oe[i].Namespace] { if t == tagAtom { return -1 } } } } return -1 } // elementInScope is like popUntil, except that it doesn't modify the stack of // open elements. func (p *parser) elementInScope(s scope, matchTags ...a.Atom) bool { return p.indexOfElementInScope(s, matchTags...) != -1 } // clearStackToContext pops elements off the stack of open elements until a // scope-defined element is found. func (p *parser) clearStackToContext(s scope) { for i := len(p.oe) - 1; i >= 0; i-- { tagAtom := p.oe[i].DataAtom switch s { case tableScope: if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template { p.oe = p.oe[:i+1] return } case tableRowScope: if tagAtom == a.Html || tagAtom == a.Tr || tagAtom == a.Template { p.oe = p.oe[:i+1] return } case tableBodyScope: if tagAtom == a.Html || tagAtom == a.Tbody || tagAtom == a.Tfoot || tagAtom == a.Thead || tagAtom == a.Template { p.oe = p.oe[:i+1] return } default: panic("unreachable") } } } // parseGenericRawTextElements implements the generic raw text element parsing // algorithm defined in 12.2.6.2. // https://html.spec.whatwg.org/multipage/parsing.html#parsing-elements-that-contain-only-text // TODO: Since both RAWTEXT and RCDATA states are treated as tokenizer's part // officially, need to make tokenizer consider both states. func (p *parser) parseGenericRawTextElement() { p.addElement() p.originalIM = p.im p.im = textIM } // generateImpliedEndTags pops nodes off the stack of open elements as long as // the top node has a tag name of dd, dt, li, optgroup, option, p, rb, rp, rt or rtc. // If exceptions are specified, nodes with that name will not be popped off. func (p *parser) generateImpliedEndTags(exceptions ...string) { var i int loop: for i = len(p.oe) - 1; i >= 0; i-- { n := p.oe[i] if n.Type != ElementNode { break } switch n.DataAtom { case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc: for _, except := range exceptions { if n.Data == except { break loop } } continue } break } p.oe = p.oe[:i+1] } // addChild adds a child node n to the top element, and pushes n onto the stack // of open elements if it is an element node. func (p *parser) addChild(n *Node) { if p.shouldFosterParent() { p.fosterParent(n) } else { p.top().AppendChild(n) } if n.Type == ElementNode { p.oe = append(p.oe, n) } } // shouldFosterParent returns whether the next node to be added should be // foster parented. func (p *parser) shouldFosterParent() bool { if p.fosterParenting { switch p.top().DataAtom { case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: return true } } return false } // fosterParent adds a child node according to the foster parenting rules. // Section 12.2.6.1, "foster parenting". func (p *parser) fosterParent(n *Node) { var table, parent, prev, template *Node var i int for i = len(p.oe) - 1; i >= 0; i-- { if p.oe[i].DataAtom == a.Table { table = p.oe[i] break } } var j int for j = len(p.oe) - 1; j >= 0; j-- { if p.oe[j].DataAtom == a.Template { template = p.oe[j] break } } if template != nil && (table == nil || j > i) { template.AppendChild(n) return } if table == nil { // The foster parent is the html element. parent = p.oe[0] } else { parent = table.Parent } if parent == nil { parent = p.oe[i-1] } if table != nil { prev = table.PrevSibling } else { prev = parent.LastChild } if prev != nil && prev.Type == TextNode && n.Type == TextNode { prev.Data += n.Data return } parent.InsertBefore(n, table) } // addText adds text to the preceding node if it is a text node, or else it // calls addChild with a new text node. func (p *parser) addText(text string) { if text == "" { return } if p.shouldFosterParent() { p.fosterParent(&Node{ Type: TextNode, Data: text, }) return } t := p.top() if n := t.LastChild; n != nil && n.Type == TextNode { n.Data += text return } p.addChild(&Node{ Type: TextNode, Data: text, }) } // addElement adds a child element based on the current token. func (p *parser) addElement() { p.addChild(&Node{ Type: ElementNode, DataAtom: p.tok.DataAtom, Data: p.tok.Data, Attr: p.tok.Attr, }) } // Section 12.2.4.3. func (p *parser) addFormattingElement() { tagAtom, attr := p.tok.DataAtom, p.tok.Attr p.addElement() // Implement the Noah's Ark clause, but with three per family instead of two. identicalElements := 0 findIdenticalElements: for i := len(p.afe) - 1; i >= 0; i-- { n := p.afe[i] if n.Type == scopeMarkerNode { break } if n.Type != ElementNode { continue } if n.Namespace != "" { continue } if n.DataAtom != tagAtom { continue } if len(n.Attr) != len(attr) { continue } compareAttributes: for _, t0 := range n.Attr { for _, t1 := range attr { if t0.Key == t1.Key && t0.Namespace == t1.Namespace && t0.Val == t1.Val { // Found a match for this attribute, continue with the next attribute. continue compareAttributes } } // If we get here, there is no attribute that matches a. // Therefore the element is not identical to the new one. continue findIdenticalElements } identicalElements++ if identicalElements >= 3 { p.afe.remove(n) } } p.afe = append(p.afe, p.top()) } // Section 12.2.4.3. func (p *parser) clearActiveFormattingElements() { for { if n := p.afe.pop(); len(p.afe) == 0 || n.Type == scopeMarkerNode { return } } } // Section 12.2.4.3. func (p *parser) reconstructActiveFormattingElements() { n := p.afe.top() if n == nil { return } if n.Type == scopeMarkerNode || p.oe.index(n) != -1 { return } i := len(p.afe) - 1 for n.Type != scopeMarkerNode && p.oe.index(n) == -1 { if i == 0 { i = -1 break } i-- n = p.afe[i] } for { i++ clone := p.afe[i].clone() p.addChild(clone) p.afe[i] = clone if i == len(p.afe)-1 { break } } } // Section 12.2.5. func (p *parser) acknowledgeSelfClosingTag() { p.hasSelfClosingToken = false } // An insertion mode (section 12.2.4.1) is the state transition function from // a particular state in the HTML5 parser's state machine. It updates the // parser's fields depending on parser.tok (where ErrorToken means EOF). // It returns whether the token was consumed. type insertionMode func(*parser) bool // setOriginalIM sets the insertion mode to return to after completing a text or // inTableText insertion mode. // Section 12.2.4.1, "using the rules for". func (p *parser) setOriginalIM() { if p.originalIM != nil { panic("html: bad parser state: originalIM was set twice") } p.originalIM = p.im } // Section 12.2.4.1, "reset the insertion mode". func (p *parser) resetInsertionMode() { for i := len(p.oe) - 1; i >= 0; i-- { n := p.oe[i] last := i == 0 if last && p.context != nil { n = p.context } switch n.DataAtom { case a.Select: if !last { for ancestor, first := n, p.oe[0]; ancestor != first; { ancestor = p.oe[p.oe.index(ancestor)-1] switch ancestor.DataAtom { case a.Template: p.im = inSelectIM return case a.Table: p.im = inSelectInTableIM return } } } p.im = inSelectIM case a.Td, a.Th: // TODO: remove this divergence from the HTML5 spec. // // See https://bugs.chromium.org/p/chromium/issues/detail?id=829668 p.im = inCellIM case a.Tr: p.im = inRowIM case a.Tbody, a.Thead, a.Tfoot: p.im = inTableBodyIM case a.Caption: p.im = inCaptionIM case a.Colgroup: p.im = inColumnGroupIM case a.Table: p.im = inTableIM case a.Template: // TODO: remove this divergence from the HTML5 spec. if n.Namespace != "" { continue } p.im = p.templateStack.top() case a.Head: // TODO: remove this divergence from the HTML5 spec. // // See https://bugs.chromium.org/p/chromium/issues/detail?id=829668 p.im = inHeadIM case a.Body: p.im = inBodyIM case a.Frameset: p.im = inFramesetIM case a.Html: if p.head == nil { p.im = beforeHeadIM } else { p.im = afterHeadIM } default: if last { p.im = inBodyIM return } continue } return } } const whitespace = " \t\r\n\f" // Section 12.2.6.4.1. func initialIM(p *parser) bool { switch p.tok.Type { case TextToken: p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace) if len(p.tok.Data) == 0 { // It was all whitespace, so ignore it. return true } case CommentToken: p.doc.AppendChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true case DoctypeToken: n, quirks := parseDoctype(p.tok.Data) p.doc.AppendChild(n) p.quirks = quirks p.im = beforeHTMLIM return true } p.quirks = true p.im = beforeHTMLIM return false } // Section 12.2.6.4.2. func beforeHTMLIM(p *parser) bool { switch p.tok.Type { case DoctypeToken: // Ignore the token. return true case TextToken: p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace) if len(p.tok.Data) == 0 { // It was all whitespace, so ignore it. return true } case StartTagToken: if p.tok.DataAtom == a.Html { p.addElement() p.im = beforeHeadIM return true } case EndTagToken: switch p.tok.DataAtom { case a.Head, a.Body, a.Html, a.Br: p.parseImpliedToken(StartTagToken, a.Html, a.Html.String()) return false default: // Ignore the token. return true } case CommentToken: p.doc.AppendChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true } p.parseImpliedToken(StartTagToken, a.Html, a.Html.String()) return false } // Section 12.2.6.4.3. func beforeHeadIM(p *parser) bool { switch p.tok.Type { case TextToken: p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace) if len(p.tok.Data) == 0 { // It was all whitespace, so ignore it. return true } case StartTagToken: switch p.tok.DataAtom { case a.Head: p.addElement() p.head = p.top() p.im = inHeadIM return true case a.Html: return inBodyIM(p) } case EndTagToken: switch p.tok.DataAtom { case a.Head, a.Body, a.Html, a.Br: p.parseImpliedToken(StartTagToken, a.Head, a.Head.String()) return false default: // Ignore the token. return true } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true case DoctypeToken: // Ignore the token. return true } p.parseImpliedToken(StartTagToken, a.Head, a.Head.String()) return false } // Section 12.2.6.4.4. func inHeadIM(p *parser) bool { switch p.tok.Type { case TextToken: s := strings.TrimLeft(p.tok.Data, whitespace) if len(s) < len(p.tok.Data) { // Add the initial whitespace to the current node. p.addText(p.tok.Data[:len(p.tok.Data)-len(s)]) if s == "" { return true } p.tok.Data = s } case StartTagToken: switch p.tok.DataAtom { case a.Html: return inBodyIM(p) case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta: p.addElement() p.oe.pop() p.acknowledgeSelfClosingTag() return true case a.Noscript: if p.scripting { p.parseGenericRawTextElement() return true } p.addElement() p.im = inHeadNoscriptIM // Don't let the tokenizer go into raw text mode when scripting is disabled. p.tokenizer.NextIsNotRawText() return true case a.Script, a.Title: p.addElement() p.setOriginalIM() p.im = textIM return true case a.Noframes, a.Style: p.parseGenericRawTextElement() return true case a.Head: // Ignore the token. return true case a.Template: // TODO: remove this divergence from the HTML5 spec. // // We don't handle all of the corner cases when mixing foreign // content (i.e. or ) with tag. case a.Template: return inHeadIM(p) default: // Ignore the token. return true } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true case DoctypeToken: // Ignore the token. return true } p.parseImpliedToken(StartTagToken, a.Body, a.Body.String()) p.framesetOK = true return false } // copyAttributes copies attributes of src not found on dst to dst. func copyAttributes(dst *Node, src Token) { if len(src.Attr) == 0 { return } attr := map[string]string{} for _, t := range dst.Attr { attr[t.Key] = t.Val } for _, t := range src.Attr { if _, ok := attr[t.Key]; !ok { dst.Attr = append(dst.Attr, t) attr[t.Key] = t.Val } } } // Section 12.2.6.4.7. func inBodyIM(p *parser) bool { switch p.tok.Type { case TextToken: d := p.tok.Data switch n := p.oe.top(); n.DataAtom { case a.Pre, a.Listing: if n.FirstChild == nil { // Ignore a newline at the start of a
 block.
				if d != "" && d[0] == '\r' {
					d = d[1:]
				}
				if d != "" && d[0] == '\n' {
					d = d[1:]
				}
			}
		}
		d = strings.Replace(d, "\x00", "", -1)
		if d == "" {
			return true
		}
		p.reconstructActiveFormattingElements()
		p.addText(d)
		if p.framesetOK && strings.TrimLeft(d, whitespace) != "" {
			// There were non-whitespace characters inserted.
			p.framesetOK = false
		}
	case StartTagToken:
		switch p.tok.DataAtom {
		case a.Html:
			if p.oe.contains(a.Template) {
				return true
			}
			copyAttributes(p.oe[0], p.tok)
		case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
			return inHeadIM(p)
		case a.Body:
			if p.oe.contains(a.Template) {
				return true
			}
			if len(p.oe) >= 2 {
				body := p.oe[1]
				if body.Type == ElementNode && body.DataAtom == a.Body {
					p.framesetOK = false
					copyAttributes(body, p.tok)
				}
			}
		case a.Frameset:
			if !p.framesetOK || len(p.oe) < 2 || p.oe[1].DataAtom != a.Body {
				// Ignore the token.
				return true
			}
			body := p.oe[1]
			if body.Parent != nil {
				body.Parent.RemoveChild(body)
			}
			p.oe = p.oe[:1]
			p.addElement()
			p.im = inFramesetIM
			return true
		case a.Address, a.Article, a.Aside, a.Blockquote, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Main, a.Menu, a.Nav, a.Ol, a.P, a.Section, a.Summary, a.Ul:
			p.popUntil(buttonScope, a.P)
			p.addElement()
		case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
			p.popUntil(buttonScope, a.P)
			switch n := p.top(); n.DataAtom {
			case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
				p.oe.pop()
			}
			p.addElement()
		case a.Pre, a.Listing:
			p.popUntil(buttonScope, a.P)
			p.addElement()
			// The newline, if any, will be dealt with by the TextToken case.
			p.framesetOK = false
		case a.Form:
			if p.form != nil && !p.oe.contains(a.Template) {
				// Ignore the token
				return true
			}
			p.popUntil(buttonScope, a.P)
			p.addElement()
			if !p.oe.contains(a.Template) {
				p.form = p.top()
			}
		case a.Li:
			p.framesetOK = false
			for i := len(p.oe) - 1; i >= 0; i-- {
				node := p.oe[i]
				switch node.DataAtom {
				case a.Li:
					p.oe = p.oe[:i]
				case a.Address, a.Div, a.P:
					continue
				default:
					if !isSpecialElement(node) {
						continue
					}
				}
				break
			}
			p.popUntil(buttonScope, a.P)
			p.addElement()
		case a.Dd, a.Dt:
			p.framesetOK = false
			for i := len(p.oe) - 1; i >= 0; i-- {
				node := p.oe[i]
				switch node.DataAtom {
				case a.Dd, a.Dt:
					p.oe = p.oe[:i]
				case a.Address, a.Div, a.P:
					continue
				default:
					if !isSpecialElement(node) {
						continue
					}
				}
				break
			}
			p.popUntil(buttonScope, a.P)
			p.addElement()
		case a.Plaintext:
			p.popUntil(buttonScope, a.P)
			p.addElement()
		case a.Button:
			p.popUntil(defaultScope, a.Button)
			p.reconstructActiveFormattingElements()
			p.addElement()
			p.framesetOK = false
		case a.A:
			for i := len(p.afe) - 1; i >= 0 && p.afe[i].Type != scopeMarkerNode; i-- {
				if n := p.afe[i]; n.Type == ElementNode && n.DataAtom == a.A {
					p.inBodyEndTagFormatting(a.A, "a")
					p.oe.remove(n)
					p.afe.remove(n)
					break
				}
			}
			p.reconstructActiveFormattingElements()
			p.addFormattingElement()
		case a.B, a.Big, a.Code, a.Em, a.Font, a.I, a.S, a.Small, a.Strike, a.Strong, a.Tt, a.U:
			p.reconstructActiveFormattingElements()
			p.addFormattingElement()
		case a.Nobr:
			p.reconstructActiveFormattingElements()
			if p.elementInScope(defaultScope, a.Nobr) {
				p.inBodyEndTagFormatting(a.Nobr, "nobr")
				p.reconstructActiveFormattingElements()
			}
			p.addFormattingElement()
		case a.Applet, a.Marquee, a.Object:
			p.reconstructActiveFormattingElements()
			p.addElement()
			p.afe = append(p.afe, &scopeMarker)
			p.framesetOK = false
		case a.Table:
			if !p.quirks {
				p.popUntil(buttonScope, a.P)
			}
			p.addElement()
			p.framesetOK = false
			p.im = inTableIM
			return true
		case a.Area, a.Br, a.Embed, a.Img, a.Input, a.Keygen, a.Wbr:
			p.reconstructActiveFormattingElements()
			p.addElement()
			p.oe.pop()
			p.acknowledgeSelfClosingTag()
			if p.tok.DataAtom == a.Input {
				for _, t := range p.tok.Attr {
					if t.Key == "type" {
						if strings.ToLower(t.Val) == "hidden" {
							// Skip setting framesetOK = false
							return true
						}
					}
				}
			}
			p.framesetOK = false
		case a.Param, a.Source, a.Track:
			p.addElement()
			p.oe.pop()
			p.acknowledgeSelfClosingTag()
		case a.Hr:
			p.popUntil(buttonScope, a.P)
			p.addElement()
			p.oe.pop()
			p.acknowledgeSelfClosingTag()
			p.framesetOK = false
		case a.Image:
			p.tok.DataAtom = a.Img
			p.tok.Data = a.Img.String()
			return false
		case a.Textarea:
			p.addElement()
			p.setOriginalIM()
			p.framesetOK = false
			p.im = textIM
		case a.Xmp:
			p.popUntil(buttonScope, a.P)
			p.reconstructActiveFormattingElements()
			p.framesetOK = false
			p.parseGenericRawTextElement()
		case a.Iframe:
			p.framesetOK = false
			p.parseGenericRawTextElement()
		case a.Noembed:
			p.parseGenericRawTextElement()
		case a.Noscript:
			if p.scripting {
				p.parseGenericRawTextElement()
				return true
			}
			p.reconstructActiveFormattingElements()
			p.addElement()
			// Don't let the tokenizer go into raw text mode when scripting is disabled.
			p.tokenizer.NextIsNotRawText()
		case a.Select:
			p.reconstructActiveFormattingElements()
			p.addElement()
			p.framesetOK = false
			p.im = inSelectIM
			return true
		case a.Optgroup, a.Option:
			if p.top().DataAtom == a.Option {
				p.oe.pop()
			}
			p.reconstructActiveFormattingElements()
			p.addElement()
		case a.Rb, a.Rtc:
			if p.elementInScope(defaultScope, a.Ruby) {
				p.generateImpliedEndTags()
			}
			p.addElement()
		case a.Rp, a.Rt:
			if p.elementInScope(defaultScope, a.Ruby) {
				p.generateImpliedEndTags("rtc")
			}
			p.addElement()
		case a.Math, a.Svg:
			p.reconstructActiveFormattingElements()
			if p.tok.DataAtom == a.Math {
				adjustAttributeNames(p.tok.Attr, mathMLAttributeAdjustments)
			} else {
				adjustAttributeNames(p.tok.Attr, svgAttributeAdjustments)
			}
			adjustForeignAttributes(p.tok.Attr)
			p.addElement()
			p.top().Namespace = p.tok.Data
			if p.hasSelfClosingToken {
				p.oe.pop()
				p.acknowledgeSelfClosingTag()
			}
			return true
		case a.Caption, a.Col, a.Colgroup, a.Frame, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
			// Ignore the token.
		default:
			p.reconstructActiveFormattingElements()
			p.addElement()
		}
	case EndTagToken:
		switch p.tok.DataAtom {
		case a.Body:
			if p.elementInScope(defaultScope, a.Body) {
				p.im = afterBodyIM
			}
		case a.Html:
			if p.elementInScope(defaultScope, a.Body) {
				p.parseImpliedToken(EndTagToken, a.Body, a.Body.String())
				return false
			}
			return true
		case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Section, a.Summary, a.Ul:
			p.popUntil(defaultScope, p.tok.DataAtom)
		case a.Form:
			if p.oe.contains(a.Template) {
				i := p.indexOfElementInScope(defaultScope, a.Form)
				if i == -1 {
					// Ignore the token.
					return true
				}
				p.generateImpliedEndTags()
				if p.oe[i].DataAtom != a.Form {
					// Ignore the token.
					return true
				}
				p.popUntil(defaultScope, a.Form)
			} else {
				node := p.form
				p.form = nil
				i := p.indexOfElementInScope(defaultScope, a.Form)
				if node == nil || i == -1 || p.oe[i] != node {
					// Ignore the token.
					return true
				}
				p.generateImpliedEndTags()
				p.oe.remove(node)
			}
		case a.P:
			if !p.elementInScope(buttonScope, a.P) {
				p.parseImpliedToken(StartTagToken, a.P, a.P.String())
			}
			p.popUntil(buttonScope, a.P)
		case a.Li:
			p.popUntil(listItemScope, a.Li)
		case a.Dd, a.Dt:
			p.popUntil(defaultScope, p.tok.DataAtom)
		case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
			p.popUntil(defaultScope, a.H1, a.H2, a.H3, a.H4, a.H5, a.H6)
		case a.A, a.B, a.Big, a.Code, a.Em, a.Font, a.I, a.Nobr, a.S, a.Small, a.Strike, a.Strong, a.Tt, a.U:
			p.inBodyEndTagFormatting(p.tok.DataAtom, p.tok.Data)
		case a.Applet, a.Marquee, a.Object:
			if p.popUntil(defaultScope, p.tok.DataAtom) {
				p.clearActiveFormattingElements()
			}
		case a.Br:
			p.tok.Type = StartTagToken
			return false
		case a.Template:
			return inHeadIM(p)
		default:
			p.inBodyEndTagOther(p.tok.DataAtom, p.tok.Data)
		}
	case CommentToken:
		p.addChild(&Node{
			Type: CommentNode,
			Data: p.tok.Data,
		})
	case ErrorToken:
		// TODO: remove this divergence from the HTML5 spec.
		if len(p.templateStack) > 0 {
			p.im = inTemplateIM
			return false
		}
		for _, e := range p.oe {
			switch e.DataAtom {
			case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc, a.Tbody, a.Td, a.Tfoot, a.Th,
				a.Thead, a.Tr, a.Body, a.Html:
			default:
				return true
			}
		}
	}

	return true
}

func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom, tagName string) {
	// This is the "adoption agency" algorithm, described at
	// https://html.spec.whatwg.org/multipage/syntax.html#adoptionAgency

	// TODO: this is a fairly literal line-by-line translation of that algorithm.
	// Once the code successfully parses the comprehensive test suite, we should
	// refactor this code to be more idiomatic.

	// Steps 1-2
	if current := p.oe.top(); current.Data == tagName && p.afe.index(current) == -1 {
		p.oe.pop()
		return
	}

	// Steps 3-5. The outer loop.
	for i := 0; i < 8; i++ {
		// Step 6. Find the formatting element.
		var formattingElement *Node
		for j := len(p.afe) - 1; j >= 0; j-- {
			if p.afe[j].Type == scopeMarkerNode {
				break
			}
			if p.afe[j].DataAtom == tagAtom {
				formattingElement = p.afe[j]
				break
			}
		}
		if formattingElement == nil {
			p.inBodyEndTagOther(tagAtom, tagName)
			return
		}

		// Step 7. Ignore the tag if formatting element is not in the stack of open elements.
		feIndex := p.oe.index(formattingElement)
		if feIndex == -1 {
			p.afe.remove(formattingElement)
			return
		}
		// Step 8. Ignore the tag if formatting element is not in the scope.
		if !p.elementInScope(defaultScope, tagAtom) {
			// Ignore the tag.
			return
		}

		// Step 9. This step is omitted because it's just a parse error but no need to return.

		// Steps 10-11. Find the furthest block.
		var furthestBlock *Node
		for _, e := range p.oe[feIndex:] {
			if isSpecialElement(e) {
				furthestBlock = e
				break
			}
		}
		if furthestBlock == nil {
			e := p.oe.pop()
			for e != formattingElement {
				e = p.oe.pop()
			}
			p.afe.remove(e)
			return
		}

		// Steps 12-13. Find the common ancestor and bookmark node.
		commonAncestor := p.oe[feIndex-1]
		bookmark := p.afe.index(formattingElement)

		// Step 14. The inner loop. Find the lastNode to reparent.
		lastNode := furthestBlock
		node := furthestBlock
		x := p.oe.index(node)
		// Step 14.1.
		j := 0
		for {
			// Step 14.2.
			j++
			// Step. 14.3.
			x--
			node = p.oe[x]
			// Step 14.4. Go to the next step if node is formatting element.
			if node == formattingElement {
				break
			}
			// Step 14.5. Remove node from the list of active formatting elements if
			// inner loop counter is greater than three and node is in the list of
			// active formatting elements.
			if ni := p.afe.index(node); j > 3 && ni > -1 {
				p.afe.remove(node)
				// If any element of the list of active formatting elements is removed,
				// we need to take care whether bookmark should be decremented or not.
				// This is because the value of bookmark may exceed the size of the
				// list by removing elements from the list.
				if ni <= bookmark {
					bookmark--
				}
				continue
			}
			// Step 14.6. Continue the next inner loop if node is not in the list of
			// active formatting elements.
			if p.afe.index(node) == -1 {
				p.oe.remove(node)
				continue
			}
			// Step 14.7.
			clone := node.clone()
			p.afe[p.afe.index(node)] = clone
			p.oe[p.oe.index(node)] = clone
			node = clone
			// Step 14.8.
			if lastNode == furthestBlock {
				bookmark = p.afe.index(node) + 1
			}
			// Step 14.9.
			if lastNode.Parent != nil {
				lastNode.Parent.RemoveChild(lastNode)
			}
			node.AppendChild(lastNode)
			// Step 14.10.
			lastNode = node
		}

		// Step 15. Reparent lastNode to the common ancestor,
		// or for misnested table nodes, to the foster parent.
		if lastNode.Parent != nil {
			lastNode.Parent.RemoveChild(lastNode)
		}
		switch commonAncestor.DataAtom {
		case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
			p.fosterParent(lastNode)
		default:
			commonAncestor.AppendChild(lastNode)
		}

		// Steps 16-18. Reparent nodes from the furthest block's children
		// to a clone of the formatting element.
		clone := formattingElement.clone()
		reparentChildren(clone, furthestBlock)
		furthestBlock.AppendChild(clone)

		// Step 19. Fix up the list of active formatting elements.
		if oldLoc := p.afe.index(formattingElement); oldLoc != -1 && oldLoc < bookmark {
			// Move the bookmark with the rest of the list.
			bookmark--
		}
		p.afe.remove(formattingElement)
		p.afe.insert(bookmark, clone)

		// Step 20. Fix up the stack of open elements.
		p.oe.remove(formattingElement)
		p.oe.insert(p.oe.index(furthestBlock)+1, clone)
	}
}

// inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM.
// "Any other end tag" handling from 12.2.6.5 The rules for parsing tokens in foreign content
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign
func (p *parser) inBodyEndTagOther(tagAtom a.Atom, tagName string) {
	for i := len(p.oe) - 1; i >= 0; i-- {
		// Two element nodes have the same tag if they have the same Data (a
		// string-typed field). As an optimization, for common HTML tags, each
		// Data string is assigned a unique, non-zero DataAtom (a uint32-typed
		// field), since integer comparison is faster than string comparison.
		// Uncommon (custom) tags get a zero DataAtom.
		//
		// The if condition here is equivalent to (p.oe[i].Data == tagName).
		if (p.oe[i].DataAtom == tagAtom) &&
			((tagAtom != 0) || (p.oe[i].Data == tagName)) {
			p.oe = p.oe[:i]
			break
		}
		if isSpecialElement(p.oe[i]) {
			break
		}
	}
}

// Section 12.2.6.4.8.
func textIM(p *parser) bool {
	switch p.tok.Type {
	case ErrorToken:
		p.oe.pop()
	case TextToken:
		d := p.tok.Data
		if n := p.oe.top(); n.DataAtom == a.Textarea && n.FirstChild == nil {
			// Ignore a newline at the start of a -->
#errors
(1,10): expected-doctype-but-got-start-tag
(1,39): unexpected-end-tag
#document
| 
|   
|   
|     -->
#errors
(1,10): expected-doctype-but-got-start-tag
#document
| 
|   
|   
|     
#errors
(1,10): expected-doctype-but-got-start-tag
#document
| 
|   
|   
|     
#errors
(1,9): expected-doctype-but-got-end-tag
(1,9): unexpected-end-tag-before-html
(1,13): unexpected-end-tag-before-html
(1,18): unexpected-end-tag-before-html
(1,22): unexpected-end-tag-before-html
(1,26): unexpected-end-tag-before-html
(1,35): unexpected-end-tag-before-html
(1,39): unexpected-end-tag-before-html
(1,47): unexpected-end-tag-before-html
(1,52): unexpected-end-tag-before-html
(1,58): unexpected-end-tag-before-html
(1,64): unexpected-end-tag-before-html
(1,72): unexpected-end-tag-before-html
(1,79): unexpected-end-tag-before-html
(1,88): unexpected-end-tag-before-html
(1,93): unexpected-end-tag-before-html
(1,98): unexpected-end-tag-before-html
(1,103): unexpected-end-tag-before-html
(1,108): unexpected-end-tag-before-html
(1,113): unexpected-end-tag-before-html
(1,118): unexpected-end-tag-before-html
(1,130): unexpected-end-tag-after-body
(1,130): unexpected-end-tag-treated-as
(1,134): unexpected-end-tag
(1,140): unexpected-end-tag
(1,148): unexpected-end-tag
(1,155): unexpected-end-tag
(1,163): unexpected-end-tag
(1,172): unexpected-end-tag
(1,180): unexpected-end-tag
(1,185): unexpected-end-tag
(1,190): unexpected-end-tag
(1,195): unexpected-end-tag
(1,203): unexpected-end-tag
(1,210): unexpected-end-tag
(1,217): unexpected-end-tag
(1,225): unexpected-end-tag
(1,230): unexpected-end-tag
(1,238): unexpected-end-tag
(1,244): unexpected-end-tag
(1,251): unexpected-end-tag
(1,258): unexpected-end-tag
(1,269): unexpected-end-tag
(1,279): unexpected-end-tag
(1,287): unexpected-end-tag
(1,296): unexpected-end-tag
(1,300): unexpected-end-tag
(1,305): unexpected-end-tag
(1,310): unexpected-end-tag
(1,320): unexpected-end-tag
(1,331): unexpected-end-tag
(1,339): unexpected-end-tag
(1,347): unexpected-end-tag
(1,355): unexpected-end-tag
(1,365): end-tag-too-early
(1,378): end-tag-too-early
(1,387): end-tag-too-early
(1,393): end-tag-too-early
(1,399): end-tag-too-early
(1,404): end-tag-too-early
(1,415): end-tag-too-early
(1,425): end-tag-too-early
(1,432): end-tag-too-early
(1,437): end-tag-too-early
(1,442): end-tag-too-early
(1,447): unexpected-end-tag
(1,454): unexpected-end-tag
(1,460): unexpected-end-tag
(1,467): unexpected-end-tag
(1,476): end-tag-too-early
(1,486): end-tag-too-early
(1,495): end-tag-too-early
(1,513): expected-eof-but-got-end-tag
(1,513): unexpected-end-tag
(1,520): unexpected-end-tag
(1,529): unexpected-end-tag
(1,537): unexpected-end-tag
(1,547): unexpected-end-tag
(1,557): unexpected-end-tag
(1,568): unexpected-end-tag
(1,579): unexpected-end-tag
(1,590): unexpected-end-tag
(1,599): unexpected-end-tag
(1,611): unexpected-end-tag
(1,622): unexpected-end-tag
#document
| 
|   
|   
|     
|

#data

#errors (1,7): expected-doctype-but-got-start-tag (1,20): unexpected-end-tag-implies-table-voodoo (1,20): unexpected-end-tag (1,24): unexpected-end-tag-implies-table-voodoo (1,24): unexpected-end-tag (1,29): unexpected-end-tag-implies-table-voodoo (1,29): unexpected-end-tag (1,33): unexpected-end-tag-implies-table-voodoo (1,33): unexpected-end-tag (1,37): unexpected-end-tag-implies-table-voodoo (1,37): unexpected-end-tag (1,46): unexpected-end-tag-implies-table-voodoo (1,46): unexpected-end-tag (1,50): unexpected-end-tag-implies-table-voodoo (1,50): unexpected-end-tag (1,58): unexpected-end-tag-implies-table-voodoo (1,58): unexpected-end-tag (1,63): unexpected-end-tag-implies-table-voodoo (1,63): unexpected-end-tag (1,69): unexpected-end-tag-implies-table-voodoo (1,69): end-tag-too-early (1,75): unexpected-end-tag-implies-table-voodoo (1,75): unexpected-end-tag (1,83): unexpected-end-tag-implies-table-voodoo (1,83): unexpected-end-tag (1,90): unexpected-end-tag-implies-table-voodoo (1,90): unexpected-end-tag (1,99): unexpected-end-tag-implies-table-voodoo (1,99): unexpected-end-tag (1,104): unexpected-end-tag-implies-table-voodoo (1,104): end-tag-too-early (1,109): unexpected-end-tag-implies-table-voodoo (1,109): end-tag-too-early (1,114): unexpected-end-tag-implies-table-voodoo (1,114): end-tag-too-early (1,119): unexpected-end-tag-implies-table-voodoo (1,119): end-tag-too-early (1,124): unexpected-end-tag-implies-table-voodoo (1,124): end-tag-too-early (1,129): unexpected-end-tag-implies-table-voodoo (1,129): end-tag-too-early (1,136): unexpected-end-tag-in-table-row (1,141): unexpected-end-tag-implies-table-voodoo (1,141): unexpected-end-tag-treated-as (1,145): unexpected-end-tag-implies-table-voodoo (1,145): unexpected-end-tag (1,151): unexpected-end-tag-implies-table-voodoo (1,151): unexpected-end-tag (1,159): unexpected-end-tag-implies-table-voodoo (1,159): unexpected-end-tag (1,166): unexpected-end-tag-implies-table-voodoo (1,166): unexpected-end-tag (1,174): unexpected-end-tag-implies-table-voodoo (1,174): unexpected-end-tag (1,183): unexpected-end-tag-implies-table-voodoo (1,183): unexpected-end-tag (1,196): unexpected-end-tag (1,201): unexpected-end-tag (1,206): unexpected-end-tag (1,214): unexpected-end-tag (1,221): unexpected-end-tag (1,228): unexpected-end-tag (1,236): unexpected-end-tag (1,241): unexpected-end-tag (1,249): unexpected-end-tag (1,255): unexpected-end-tag (1,262): unexpected-end-tag (1,269): unexpected-end-tag (1,280): unexpected-end-tag (1,290): unexpected-end-tag (1,298): unexpected-end-tag (1,307): unexpected-end-tag (1,311): unexpected-end-tag (1,316): unexpected-end-tag (1,321): unexpected-end-tag (1,331): unexpected-end-tag (1,342): unexpected-end-tag (1,350): unexpected-end-tag (1,358): unexpected-end-tag (1,366): unexpected-end-tag (1,376): end-tag-too-early (1,389): end-tag-too-early (1,398): end-tag-too-early (1,404): end-tag-too-early (1,410): end-tag-too-early (1,415): end-tag-too-early (1,426): end-tag-too-early (1,436): end-tag-too-early (1,443): end-tag-too-early (1,448): end-tag-too-early (1,453): end-tag-too-early (1,458): unexpected-end-tag (1,465): unexpected-end-tag (1,471): unexpected-end-tag (1,478): unexpected-end-tag (1,487): end-tag-too-early (1,497): end-tag-too-early (1,506): end-tag-too-early (1,524): expected-eof-but-got-end-tag (1,524): unexpected-end-tag (1,531): unexpected-end-tag (1,540): unexpected-end-tag (1,548): unexpected-end-tag (1,558): unexpected-end-tag (1,568): unexpected-end-tag (1,579): unexpected-end-tag (1,590): unexpected-end-tag (1,601): unexpected-end-tag (1,610): unexpected-end-tag (1,622): unexpected-end-tag (1,633): unexpected-end-tag #document | | | |
| | | |

#data #errors (1,10): expected-doctype-but-got-start-tag (1,10): eof-in-frameset #document | | | golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/testdata/webkit/tests10.dat000066400000000000000000000374701415442132300265010ustar00rootroot00000000000000#data #errors #document | | | | | #data a #errors (1,28) expected-dashes-or-doctype #new-errors (1:35) cdata-in-html-content #document | | | | | | #data #errors #document | | | | | #data #errors (1,34) unexpected-start-tag-in-select (1,40) unexpected-end-tag-in-select #document | | | | | #errors (1,42) unexpected-start-tag-in-select (1,48) unexpected-end-tag-in-select #document | | | | |

#errors (1,33) foster-parenting-start-tag #document | | | | | | #data
foo
#errors (1,33) foster-parenting-start-tag #document | | | | | | | "foo" | #data
foobar
#errors (1,33) foster-parenting-start-tag #document | | | | | | | "foo" | | "bar" | #data
foobar
#errors (1,40) foster-parenting-start-tag #document | | | | | | | "foo" | | "bar" | | #data
foobar
#errors (1,44) foster-parenting-start-tag #document | | | | | | | "foo" | | "bar" | | | #data
foobar
#errors #document | | | | | | | |
| | | "foo" | | "bar" #data
foobar

baz

#errors #document | | | | | | | |
| | | "foo" | | "bar" |

| "baz" #data
foobar

baz

#errors #document | | | | | |
| | | "foo" | | "bar" |

| "baz" #data
foobar

baz

quux #errors (1,65) unexpected-html-element-in-foreign-content #document | | | | | |
| | | "foo" | | "bar" |

| "baz" |

| "quux" #data
foobarbaz

quux #errors (1,73) unexpected-end-tag (1,73) expected-one-end-tag-but-got-another #document | | | | | |
| | | "foo" | | "bar" | "baz" |

| "quux" #data foobar

baz

quux #errors (1,43) foster-parenting-start-tag svg (1,66) unexpected HTML-like start tag token in foreign content (1,66) foster-parenting-start-tag (1,67) foster-parenting-character (1,68) foster-parenting-character (1,69) foster-parenting-character #document | | | | | | | "foo" | | "bar" |

| "baz" | | |

| "quux" #data

quux #errors (1,49) unexpected-start-tag-in-select (1,52) unexpected-start-tag-in-select (1,59) unexpected-end-tag-in-select (1,62) unexpected-start-tag-in-select (1,69) unexpected-end-tag-in-select (1,72) unexpected-start-tag-in-select (1,83) unexpected-table-element-end-tag-in-select-in-table #document | | | | | | | |
|

quux #errors (1,36) unexpected-start-tag-implies-table-voodoo (1,41) unexpected-start-tag-in-select (1,44) unexpected-start-tag-in-select (1,51) unexpected-end-tag-in-select (1,54) unexpected-start-tag-in-select (1,61) unexpected-end-tag-in-select (1,64) unexpected-start-tag-in-select (1,75) unexpected-table-element-end-tag-in-select-in-table #document | | | | | |

| "quux" #data foobar

baz #errors (1,40) expected-eof-but-got-start-tag (1,63) unexpected-html-element-in-foreign-content #document | | | | | | | "foo" | | "bar" |

| "baz" #data foobar

baz #errors (1,33) unexpected-start-tag-after-body (1,56) unexpected-html-element-in-foreign-content #document | | | | | | | "foo" | | "bar" |

| "baz" #data

#errors (1,30) unexpected-start-tag-in-frameset (1,33) unexpected-start-tag-in-frameset (1,37) unexpected-end-tag-in-frameset (1,40) unexpected-start-tag-in-frameset (1,44) unexpected-end-tag-in-frameset (1,47) unexpected-start-tag-in-frameset (1,53) unexpected-start-tag-in-frameset (1,53) eof-in-frameset #document | | | | #data

#errors (1,41) unexpected-start-tag-after-frameset (1,44) unexpected-start-tag-after-frameset (1,48) unexpected-end-tag-after-frameset (1,51) unexpected-start-tag-after-frameset (1,55) unexpected-end-tag-after-frameset (1,58) unexpected-start-tag-after-frameset (1,64) unexpected-start-tag-after-frameset #document | | | | #data #errors #document | | | | | xlink:href="foo" | | xlink href="foo" #data #errors #document | | | | | xlink:href="foo" | xml:lang="en" | | | xlink href="foo" | xml lang="en" #data #errors #document | | | | | xlink:href="foo" | xml:lang="en" | | | xlink href="foo" | xml lang="en" #data bar #errors #document | | | | | xlink:href="foo" | xml:lang="en" | | | xlink href="foo" | xml lang="en" | "bar" #data #errors (1,5) expected-doctype-but-got-start-tag (1,12) unexpected-end-tag (1,12) unexpected-end-tag (1,12) expected-closing-tag-but-got-eof #document | | | | #data

a #errors (1,5) expected-doctype-but-got-start-tag (1,16) unexpected-end-tag (1,16) end-tag-too-early #document | | | |
| | "a" #data
a #errors (1,5) expected-doctype-but-got-start-tag (1,22) unexpected-end-tag (1,22) end-tag-too-early #document | | | |
| | | "a" #data
#errors (1,5) expected-doctype-but-got-start-tag (1,22) unexpected-end-tag (1,28) expected-closing-tag-but-got-eof #document | | | |
| | | #data
a #errors (1,5) expected-doctype-but-got-start-tag (1,43) unexpected-end-tag (1,43) end-tag-too-early (1,44) expected-closing-tag-but-got-eof #document | | | |
| | | | | "a" #data

a #errors (1,5) expected-doctype-but-got-start-tag (1,40) end-tag-too-early (1,41) expected-closing-tag-but-got-eof #document | | | |

| | | |

| "a" #data
    a #errors (1,40) unexpected-html-element-in-foreign-content (1,41) expected-closing-tag-but-got-eof #document | | | | | | |
    | |
      | "a" #data
        a #errors (1,35) unexpected-html-element-in-foreign-content (1,36) expected-closing-tag-but-got-eof #document | | | | | | | |
          | "a" #data

          #errors (1,32) expected-closing-tag-but-got-eof #document | | | | |

          | | |

          #data

          #errors (1,33) expected-closing-tag-but-got-eof #document | | | | |

          | | |

          #data

          #errors (1,5) expected-doctype-but-got-start-tag (1,50) unexpected-end-tag (1,53) expected-closing-tag-but-got-eof #document | | | |

          | | | |

          |

          #data
          #errors (1,6) expected-doctype-but-got-start-tag (1,71) expected-closing-tag-but-got-eof #document | | | | | |
          | |
          | | #data
          #errors (1,6) expected-doctype-but-got-start-tag (1,83) expected-closing-tag-but-got-eof #document | | | | | | | |
          |
          | #data #errors (1,5) expected-doctype-but-got-start-tag (1,28) expected-closing-tag-but-got-eof #document | | | | | | #data

#errors (1,7) expected-doctype-but-got-start-tag (1,12) unexpected-start-tag-implies-table-voodoo (1,22) eof-in-table #document | | | | |
| | #data #errors (1,6) expected-doctype-but-got-start-tag (1,18) expected-closing-tag-but-got-eof #document | | | | | | #data #errors (1,6) expected-doctype-but-got-start-tag (1,22) expected-closing-tag-but-got-eof #document | | | | | | #data #errors (1,6) expected-doctype-but-got-start-tag (1,18) expected-closing-tag-but-got-eof #document | | | | | | #data #errors (1,6) expected-doctype-but-got-start-tag (1,22) expected-closing-tag-but-got-eof #document | | | | | | #data #errors (1,6) expected-doctype-but-got-start-tag (1,18) expected-closing-tag-but-got-eof #document | | | | | | #data #errors (1,6) expected-doctype-but-got-start-tag (1,22) expected-closing-tag-but-got-eof #document | | | | | | #data #errors (1,6) expected-doctype-but-got-start-tag (1,18) expected-closing-tag-but-got-eof #document | | | | | | #data #errors (1,6) expected-doctype-but-got-start-tag (1,22) expected-closing-tag-but-got-eof #document | | | | | | #data #errors (1,6) expected-doctype-but-got-start-tag (1,21) expected-closing-tag-but-got-eof #document | | | | | | #data #errors (1,6) expected-doctype-but-got-start-tag (1,25) expected-closing-tag-but-got-eof #document | | | | | | #data #errors (1,6) expected-doctype-but-got-start-tag (1,54) expected-closing-tag-but-got-eof #document | | | | | | | #data
#errors (1,6) expected-doctype-but-got-start-tag (1,144) expected-closing-tag-but-got-eof #document | | | | | | | |
| | | | | #data #errors (1,6) expected-doctype-but-got-start-tag (1,153) expected-closing-tag-but-got-eof #document | | | | | | | | | | | | | | ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/testdata/webkit/tests11.dat�������������������0000664�0000000�0000000�00000042417�14154421323�0026477�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data #errors #document | | | | | | attributeName="" | attributeType="" | baseFrequency="" | baseProfile="" | calcMode="" | clipPathUnits="" | diffuseConstant="" | edgeMode="" | filterUnits="" | glyphRef="" | gradientTransform="" | gradientUnits="" | kernelMatrix="" | kernelUnitLength="" | keyPoints="" | keySplines="" | keyTimes="" | lengthAdjust="" | limitingConeAngle="" | markerHeight="" | markerUnits="" | markerWidth="" | maskContentUnits="" | maskUnits="" | numOctaves="" | pathLength="" | patternContentUnits="" | patternTransform="" | patternUnits="" | pointsAtX="" | pointsAtY="" | pointsAtZ="" | preserveAlpha="" | preserveAspectRatio="" | primitiveUnits="" | refX="" | refY="" | repeatCount="" | repeatDur="" | requiredExtensions="" | requiredFeatures="" | specularConstant="" | specularExponent="" | spreadMethod="" | startOffset="" | stdDeviation="" | stitchTiles="" | surfaceScale="" | systemLanguage="" | tableValues="" | targetX="" | targetY="" | textLength="" | viewBox="" | viewTarget="" | xChannelSelector="" | yChannelSelector="" | zoomAndPan="" #data #errors #document | | | | | | attributeName="" | attributeType="" | baseFrequency="" | baseProfile="" | calcMode="" | clipPathUnits="" | diffuseConstant="" | edgeMode="" | filterUnits="" | glyphRef="" | gradientTransform="" | gradientUnits="" | kernelMatrix="" | kernelUnitLength="" | keyPoints="" | keySplines="" | keyTimes="" | lengthAdjust="" | limitingConeAngle="" | markerHeight="" | markerUnits="" | markerWidth="" | maskContentUnits="" | maskUnits="" | numOctaves="" | pathLength="" | patternContentUnits="" | patternTransform="" | patternUnits="" | pointsAtX="" | pointsAtY="" | pointsAtZ="" | preserveAlpha="" | preserveAspectRatio="" | primitiveUnits="" | refX="" | refY="" | repeatCount="" | repeatDur="" | requiredExtensions="" | requiredFeatures="" | specularConstant="" | specularExponent="" | spreadMethod="" | startOffset="" | stdDeviation="" | stitchTiles="" | surfaceScale="" | systemLanguage="" | tableValues="" | targetX="" | targetY="" | textLength="" | viewBox="" | viewTarget="" | xChannelSelector="" | yChannelSelector="" | zoomAndPan="" #data #errors #document | | | | | | attributeName="" | attributeType="" | baseFrequency="" | baseProfile="" | calcMode="" | clipPathUnits="" | diffuseConstant="" | edgeMode="" | filterUnits="" | filterres="" | glyphRef="" | gradientTransform="" | gradientUnits="" | kernelMatrix="" | kernelUnitLength="" | keyPoints="" | keySplines="" | keyTimes="" | lengthAdjust="" | limitingConeAngle="" | markerHeight="" | markerUnits="" | markerWidth="" | maskContentUnits="" | maskUnits="" | numOctaves="" | pathLength="" | patternContentUnits="" | patternTransform="" | patternUnits="" | pointsAtX="" | pointsAtY="" | pointsAtZ="" | preserveAlpha="" | preserveAspectRatio="" | primitiveUnits="" | refX="" | refY="" | repeatCount="" | repeatDur="" | requiredExtensions="" | requiredFeatures="" | specularConstant="" | specularExponent="" | spreadMethod="" | startOffset="" | stdDeviation="" | stitchTiles="" | surfaceScale="" | systemLanguage="" | tableValues="" | targetX="" | targetY="" | textLength="" | viewBox="" | viewTarget="" | xChannelSelector="" | yChannelSelector="" | zoomAndPan="" #data #errors #document | | | | | | attributename="" | attributetype="" | basefrequency="" | baseprofile="" | calcmode="" | clippathunits="" | diffuseconstant="" | edgemode="" | filterunits="" | glyphref="" | gradienttransform="" | gradientunits="" | kernelmatrix="" | kernelunitlength="" | keypoints="" | keysplines="" | keytimes="" | lengthadjust="" | limitingconeangle="" | markerheight="" | markerunits="" | markerwidth="" | maskcontentunits="" | maskunits="" | numoctaves="" | pathlength="" | patterncontentunits="" | patterntransform="" | patternunits="" | pointsatx="" | pointsaty="" | pointsatz="" | preservealpha="" | preserveaspectratio="" | primitiveunits="" | refx="" | refy="" | repeatcount="" | repeatdur="" | requiredextensions="" | requiredfeatures="" | specularconstant="" | specularexponent="" | spreadmethod="" | startoffset="" | stddeviation="" | stitchtiles="" | surfacescale="" | systemlanguage="" | tablevalues="" | targetx="" | targety="" | textlength="" | viewbox="" | viewtarget="" | xchannelselector="" | ychannelselector="" | zoomandpan="" #data #errors #document | | | | | | contentscripttype="" | contentstyletype="" | externalresourcesrequired="" | filterres="" #data #errors #document | | | | | | contentscripttype="" | contentstyletype="" | externalresourcesrequired="" | filterres="" #data #errors #document | | | | | | contentscripttype="" | contentstyletype="" | externalresourcesrequired="" | filterres="" #data #errors #document | | | | | | contentscripttype="" | contentstyletype="" | externalresourcesrequired="" | filterres="" #data #errors #document | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | #data #errors #document | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | #data #errors #document | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | #data #errors #document | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | #data #errors #document | | | | | | �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/testdata/webkit/tests12.dat�������������������0000664�0000000�0000000�00000003114�14154421323�0026467�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data

foobazeggs

spam

quuxbar #errors #document | | | | |

| "foo" | | | | "baz" | | | | | "eggs" | | |

| "spam" | | | |
| | | "quux" | "bar" #data foobazeggs

spam
quuxbar #errors #document | | | | | "foo" | | | | "baz" | | | | | "eggs" | | |

| "spam" | | | |
| | | "quux" | "bar" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/testdata/webkit/tests14.dat�������������������0000664�0000000�0000000�00000002053�14154421323�0026472�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data #errors #document | | | | | #data #errors #document | | | | | | #data #errors (1,38): non-html-root #document | | | abc:def="gh" | | | #data #errors (1,53): non-html-root #document | | | xml:lang="bar" | | #data #errors #document | | | 123="456" | | #data #errors (1,43): non-html-root #document | | | 123="456" | 789="012" | | #data #errors #document | | | | | 789="012" golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/testdata/webkit/tests15.dat000066400000000000000000000071011415442132300264720ustar00rootroot00000000000000#data

X #errors (1,31): unexpected-end-tag (1,36): expected-closing-tag-but-got-eof #document | | | | |

| | | | | | | " " |

| "X" #data

X #errors (1,3): expected-doctype-but-got-start-tag (1,16): unexpected-end-tag (2,4): expected-closing-tag-but-got-eof #document | | | |

| | | | | | | " " |

| "X" #data #errors (1,29): expected-eof-but-got-start-tag (1,29): unexpected-start-tag-ignored #document | | | | | " " #data #errors (1,28): unexpected-start-tag-after-body #document | | | | | #data #errors (1,6): expected-doctype-but-got-start-tag #document | | | | #data X #errors (1,29): unexpected-start-tag-after-body #document | | | | | | "X" #data <!doctype html><table> X<meta></table> #errors (1,23): foster-parenting-character (1,24): foster-parenting-character (1,30): foster-parenting-start-character #document | <!DOCTYPE html> | <html> | <head> | <body> | " X" | <meta> | <table> #data <!doctype html><table> x</table> #errors (1,23): foster-parenting-character (1,24): foster-parenting-character #document | <!DOCTYPE html> | <html> | <head> | <body> | " x" | <table> #data <!doctype html><table> x </table> #errors (1,23): foster-parenting-character (1,24): foster-parenting-character (1,25): foster-parenting-character #document | <!DOCTYPE html> | <html> | <head> | <body> | " x " | <table> #data <!doctype html><table><tr> x</table> #errors (1,27): foster-parenting-character (1,28): foster-parenting-character #document | <!DOCTYPE html> | <html> | <head> | <body> | " x" | <table> | <tbody> | <tr> #data <!doctype html><table>X<style> <tr>x </style> </table> #errors (1,23): foster-parenting-character #document | <!DOCTYPE html> | <html> | <head> | <body> | "X" | <table> | <style> | " <tr>x " | " " #data <!doctype html><div><table><a>foo</a> <tr><td>bar</td> </tr></table></div> #errors (1,30): foster-parenting-start-tag (1,31): foster-parenting-character (1,32): foster-parenting-character (1,33): foster-parenting-character (1,37): foster-parenting-end-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <div> | <a> | "foo" | <table> | " " | <tbody> | <tr> | <td> | "bar" | " " #data <frame></frame></frame><frameset><frame><frameset><frame></frameset><noframes></frameset><noframes> #errors (1,7): expected-doctype-but-got-start-tag (1,7): unexpected-start-tag-ignored (1,15): unexpected-end-tag (1,23): unexpected-end-tag (1,33): unexpected-start-tag (1,99): expected-named-closing-tag-but-got-eof (1,99): eof-in-frameset #document | <html> | <head> | <frameset> | <frame> | <frameset> | <frame> | <noframes> | "</frameset><noframes>" #data <!DOCTYPE html><object></html> #errors (1,30): expected-body-in-scope (1,30): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <object> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/testdata/webkit/tests16.dat�������������������0000664�0000000�0000000�00000132724�14154421323�0026505�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!doctype html><script> #errors (1,23): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | <body> #data <!doctype html><script>a #errors (1,24): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "a" | <body> #data <!doctype html><script>< #errors (1,24): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "<" | <body> #data <!doctype html><script></ #errors (1,25): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "</" | <body> #data <!doctype html><script></S #errors (1,26): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "</S" | <body> #data <!doctype html><script></SC #errors (1,27): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "</SC" | <body> #data <!doctype html><script></SCR #errors (1,28): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "</SCR" | <body> #data <!doctype html><script></SCRI #errors (1,29): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "</SCRI" | <body> #data <!doctype html><script></SCRIP #errors (1,30): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "</SCRIP" | <body> #data <!doctype html><script></SCRIPT #errors (1,31): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "</SCRIPT" | <body> #data <!doctype html><script></SCRIPT #errors (1,32): expected-attribute-name-but-got-eof (1,32): expected-named-closing-tag-but-got-eof #new-errors (1:33) eof-in-tag #document | <!DOCTYPE html> | <html> | <head> | <script> | <body> #data <!doctype html><script></s #errors (1,26): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "</s" | <body> #data <!doctype html><script></sc #errors (1,27): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "</sc" | <body> #data <!doctype html><script></scr #errors (1,28): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "</scr" | <body> #data <!doctype html><script></scri #errors (1,29): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "</scri" | <body> #data <!doctype html><script></scrip #errors (1,30): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "</scrip" | <body> #data <!doctype html><script></script #errors (1,31): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "</script" | <body> #data <!doctype html><script></script #errors (1,32): expected-attribute-name-but-got-eof (1,32): expected-named-closing-tag-but-got-eof #new-errors (1:33) eof-in-tag #document | <!DOCTYPE html> | <html> | <head> | <script> | <body> #data <!doctype html><script><! #errors (1,25): expected-script-data-but-got-eof (1,25): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!" | <body> #data <!doctype html><script><!a #errors (1,26): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!a" | <body> #data <!doctype html><script><!- #errors (1,26): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!-" | <body> #data <!doctype html><script><!-a #errors (1,27): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!-a" | <body> #data <!doctype html><script><!-- #errors (1,27): expected-named-closing-tag-but-got-eof (1,27): unexpected-eof-in-text-mode #new-errors (1:28) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--" | <body> #data <!doctype html><script><!--a #errors (1,28): expected-named-closing-tag-but-got-eof (1,28): unexpected-eof-in-text-mode #new-errors (1:29) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--a" | <body> #data <!doctype html><script><!--< #errors (1,28): expected-named-closing-tag-but-got-eof (1,28): unexpected-eof-in-text-mode #new-errors (1:29) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<" | <body> #data <!doctype html><script><!--<a #errors (1,29): expected-named-closing-tag-but-got-eof (1,29): unexpected-eof-in-text-mode #new-errors (1:30) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<a" | <body> #data <!doctype html><script><!--</ #errors (1,29): expected-named-closing-tag-but-got-eof (1,29): unexpected-eof-in-text-mode #new-errors (1:30) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--</" | <body> #data <!doctype html><script><!--</script #errors (1,35): expected-named-closing-tag-but-got-eof (1,35): unexpected-eof-in-text-mode #new-errors (1:36) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--</script" | <body> #data <!doctype html><script><!--</script #errors (1,36): expected-attribute-name-but-got-eof (1,36): expected-named-closing-tag-but-got-eof #new-errors (1:37) eof-in-tag #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--" | <body> #data <!doctype html><script><!--<s #errors (1,29): expected-named-closing-tag-but-got-eof (1,29): unexpected-eof-in-text-mode #new-errors (1:30) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<s" | <body> #data <!doctype html><script><!--<script #errors (1,34): expected-named-closing-tag-but-got-eof (1,34): unexpected-eof-in-text-mode #new-errors (1:35) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script" | <body> #data <!doctype html><script><!--<script #errors (1,35): eof-in-script-in-script (1,35): expected-named-closing-tag-but-got-eof #new-errors (1:36) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script " | <body> #data <!doctype html><script><!--<script < #errors (1,36): eof-in-script-in-script (1,36): expected-named-closing-tag-but-got-eof #new-errors (1:37) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script <" | <body> #data <!doctype html><script><!--<script <a #errors (1,37): eof-in-script-in-script (1,37): expected-named-closing-tag-but-got-eof #new-errors (1:38) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script <a" | <body> #data <!doctype html><script><!--<script </ #errors (1,37): eof-in-script-in-script (1,37): expected-named-closing-tag-but-got-eof #new-errors (1:38) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </" | <body> #data <!doctype html><script><!--<script </s #errors (1,38): eof-in-script-in-script (1,38): expected-named-closing-tag-but-got-eof #new-errors (1:39) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </s" | <body> #data <!doctype html><script><!--<script </script #errors (1,43): eof-in-script-in-script (1,43): expected-named-closing-tag-but-got-eof #new-errors (1:44) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script" | <body> #data <!doctype html><script><!--<script </scripta #errors (1,44): eof-in-script-in-script (1,44): expected-named-closing-tag-but-got-eof #new-errors (1:45) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </scripta" | <body> #data <!doctype html><script><!--<script </script #errors (1,44): expected-named-closing-tag-but-got-eof (1,44): unexpected-eof-in-text-mode #new-errors (1:45) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script " | <body> #data <!doctype html><script><!--<script </script> #errors (1,44): expected-named-closing-tag-but-got-eof (1,44): unexpected-eof-in-text-mode #new-errors (1:45) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script>" | <body> #data <!doctype html><script><!--<script </script/ #errors (1,44): expected-named-closing-tag-but-got-eof (1,44): unexpected-eof-in-text-mode #new-errors (1:45) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script/" | <body> #data <!doctype html><script><!--<script </script < #errors (1,45): expected-named-closing-tag-but-got-eof (1,45): unexpected-eof-in-text-mode #new-errors (1:46) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script <" | <body> #data <!doctype html><script><!--<script </script <a #errors (1,46): expected-named-closing-tag-but-got-eof (1,46): unexpected-eof-in-text-mode #new-errors (1:47) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script <a" | <body> #data <!doctype html><script><!--<script </script </ #errors (1,46): expected-named-closing-tag-but-got-eof (1,46): unexpected-eof-in-text-mode #new-errors (1:47) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script </" | <body> #data <!doctype html><script><!--<script </script </script #errors (1,52): expected-named-closing-tag-but-got-eof (1,52): unexpected-eof-in-text-mode #new-errors (1:53) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script </script" | <body> #data <!doctype html><script><!--<script </script </script #errors (1,53): expected-attribute-name-but-got-eof (1,53): expected-named-closing-tag-but-got-eof #new-errors (1:54) eof-in-tag #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script " | <body> #data <!doctype html><script><!--<script </script </script/ #errors (1,53): unexpected-EOF-after-solidus-in-tag (1,53): expected-named-closing-tag-but-got-eof #new-errors (1:54) eof-in-tag #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script " | <body> #data <!doctype html><script><!--<script </script </script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script " | <body> #data <!doctype html><script><!--<script - #errors (1,36): eof-in-script-in-script (1,36): expected-named-closing-tag-but-got-eof #new-errors (1:37) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script -" | <body> #data <!doctype html><script><!--<script -a #errors (1,37): eof-in-script-in-script (1,37): expected-named-closing-tag-but-got-eof #new-errors (1:38) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script -a" | <body> #data <!doctype html><script><!--<script -< #errors (1,37): eof-in-script-in-script (1,37): expected-named-closing-tag-but-got-eof #new-errors (1:38) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script -<" | <body> #data <!doctype html><script><!--<script -- #errors (1,37): eof-in-script-in-script (1,37): expected-named-closing-tag-but-got-eof #new-errors (1:38) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script --" | <body> #data <!doctype html><script><!--<script --a #errors (1,38): eof-in-script-in-script (1,38): expected-named-closing-tag-but-got-eof #new-errors (1:39) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script --a" | <body> #data <!doctype html><script><!--<script --< #errors (1,38): eof-in-script-in-script (1,38): expected-named-closing-tag-but-got-eof #new-errors (1:39) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script --<" | <body> #data <!doctype html><script><!--<script --> #errors (1,38): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script -->" | <body> #data <!doctype html><script><!--<script -->< #errors (1,39): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script --><" | <body> #data <!doctype html><script><!--<script --></ #errors (1,40): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script --></" | <body> #data <!doctype html><script><!--<script --></script #errors (1,46): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script --></script" | <body> #data <!doctype html><script><!--<script --></script #errors (1,47): expected-attribute-name-but-got-eof (1,47): expected-named-closing-tag-but-got-eof #new-errors (1:48) eof-in-tag #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script -->" | <body> #data <!doctype html><script><!--<script --></script/ #errors (1,47): unexpected-EOF-after-solidus-in-tag (1,47): expected-named-closing-tag-but-got-eof #new-errors (1:48) eof-in-tag #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script -->" | <body> #data <!doctype html><script><!--<script --></script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script -->" | <body> #data <!doctype html><script><!--<script><\/script>--></script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script><\/script>-->" | <body> #data <!doctype html><script><!--<script></scr'+'ipt>--></script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script></scr'+'ipt>-->" | <body> #data <!doctype html><script><!--<script></script><script></script></script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script></script><script></script>" | <body> #data <!doctype html><script><!--<script></script><script></script>--><!--</script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script></script><script></script>--><!--" | <body> #data <!doctype html><script><!--<script></script><script></script>-- ></script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script></script><script></script>-- >" | <body> #data <!doctype html><script><!--<script></script><script></script>- -></script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script></script><script></script>- ->" | <body> #data <!doctype html><script><!--<script></script><script></script>- - ></script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script></script><script></script>- - >" | <body> #data <!doctype html><script><!--<script></script><script></script>-></script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script></script><script></script>->" | <body> #data <!doctype html><script><!--<script>--!></script>X #errors (1,49): expected-named-closing-tag-but-got-eof (1,49): unexpected-EOF-in-text-mode #new-errors (1:50) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script>--!></script>X" | <body> #data <!doctype html><script><!--<scr'+'ipt></script>--></script> #errors (1,59): unexpected-end-tag #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<scr'+'ipt>" | <body> | "-->" #data <!doctype html><script><!--<script></scr'+'ipt></script>X #errors (1,57): expected-named-closing-tag-but-got-eof (1,57): unexpected-eof-in-text-mode #new-errors (1:58) eof-in-script-html-comment-like-text #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script></scr'+'ipt></script>X" | <body> #data <!doctype html><style><!--<style></style>--></style> #errors (1,52): unexpected-end-tag #document | <!DOCTYPE html> | <html> | <head> | <style> | "<!--<style>" | <body> | "-->" #data <!doctype html><style><!--</style>X #errors #document | <!DOCTYPE html> | <html> | <head> | <style> | "<!--" | <body> | "X" #data <!doctype html><style><!--...</style>...--></style> #errors (1,51): unexpected-end-tag #document | <!DOCTYPE html> | <html> | <head> | <style> | "<!--..." | <body> | "...-->" #data <!doctype html><style><!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style></style>X #errors #document | <!DOCTYPE html> | <html> | <head> | <style> | "<!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style>" | <body> | "X" #data <!doctype html><style><!--...<style><!--...--!></style>--></style> #errors (1,66): unexpected-end-tag #document | <!DOCTYPE html> | <html> | <head> | <style> | "<!--...<style><!--...--!>" | <body> | "-->" #data <!doctype html><style><!--...</style><!-- --><style>@import ...</style> #errors #document | <!DOCTYPE html> | <html> | <head> | <style> | "<!--..." | <!-- --> | <style> | "@import ..." | <body> #data <!doctype html><style>...<style><!--...</style><!-- --></style> #errors (1,63): unexpected-end-tag #document | <!DOCTYPE html> | <html> | <head> | <style> | "...<style><!--..." | <!-- --> | <body> #data <!doctype html><style>...<!--[if IE]><style>...</style>X #errors #document | <!DOCTYPE html> | <html> | <head> | <style> | "...<!--[if IE]><style>..." | <body> | "X" #data <!doctype html><title><!--<title>--> #errors (1,52): unexpected-end-tag #document | | | | | "<!--<title>" | <body> | "-->" #data <!doctype html><title></title> #errors #document | | | | | "" | #data foo/title><link></head><body>X #errors (1,52): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <title> | "foo/title><link></head><body>X" | <body> #data <!doctype html><noscript><!--<noscript></noscript>--></noscript> #errors (1,64): unexpected-end-tag #script-on #document | <!DOCTYPE html> | <html> | <head> | <noscript> | "<!--<noscript>" | <body> | "-->" #data <!doctype html><noscript><!--<noscript></noscript>--></noscript> #errors #script-off #document | <!DOCTYPE html> | <html> | <head> | <noscript> | <!-- <noscript></noscript> --> | <body> #data <!doctype html><noscript><!--</noscript>X<noscript>--></noscript> #errors #script-on #document | <!DOCTYPE html> | <html> | <head> | <noscript> | "<!--" | <body> | "X" | <noscript> | "-->" #data <!doctype html><noscript><!--</noscript>X<noscript>--></noscript> #errors #script-off #document | <!DOCTYPE html> | <html> | <head> | <noscript> | <!-- </noscript>X<noscript> --> | <body> #data <!doctype html><noscript><iframe></noscript>X #errors #script-on #document | <!DOCTYPE html> | <html> | <head> | <noscript> | "<iframe>" | <body> | "X" #data <!doctype html><noscript><iframe></noscript>X #errors * (1,34) unexpected token in head noscript * (1,46) unexpected EOF #script-off #document | <!DOCTYPE html> | <html> | <head> | <noscript> | <body> | <iframe> | "</noscript>X" #data <!doctype html><noframes><!--<noframes></noframes>--></noframes> #errors (1,64): unexpected-end-tag #document | <!DOCTYPE html> | <html> | <head> | <noframes> | "<!--<noframes>" | <body> | "-->" #data <!doctype html><noframes><body><script><!--...</script></body></noframes></html> #errors #document | <!DOCTYPE html> | <html> | <head> | <noframes> | "<body><script><!--...</script></body>" | <body> #data <!doctype html><textarea><!--<textarea></textarea>--></textarea> #errors (1,64): unexpected-end-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <textarea> | "<!--<textarea>" | "-->" #data <!doctype html><textarea></textarea></textarea> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <textarea> | "</textarea>" #data <!doctype html><textarea><</textarea> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <textarea> | "<" #data <!doctype html><textarea>a<b</textarea> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <textarea> | "a<b" #data <!doctype html><iframe><!--<iframe></iframe>--></iframe> #errors (1,56): unexpected-end-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <iframe> | "<!--<iframe>" | "-->" #data <!doctype html><iframe>...<!--X->...<!--/X->...</iframe> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <iframe> | "...<!--X->...<!--/X->..." #data <!doctype html><xmp><!--<xmp></xmp>--></xmp> #errors (1,44): unexpected-end-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <xmp> | "<!--<xmp>" | "-->" #data <!doctype html><noembed><!--<noembed></noembed>--></noembed> #errors (1,60): unexpected-end-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <noembed> | "<!--<noembed>" | "-->" #data <script> #errors (1,8): expected-doctype-but-got-start-tag (1,8): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | <body> #data <script>a #errors (1,8): expected-doctype-but-got-start-tag (1,9): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "a" | <body> #data <script>< #errors (1,8): expected-doctype-but-got-start-tag (1,9): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "<" | <body> #data <script></ #errors (1,8): expected-doctype-but-got-start-tag (1,10): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "</" | <body> #data <script></S #errors (1,8): expected-doctype-but-got-start-tag (1,11): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "</S" | <body> #data <script></SC #errors (1,8): expected-doctype-but-got-start-tag (1,12): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "</SC" | <body> #data <script></SCR #errors (1,8): expected-doctype-but-got-start-tag (1,13): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "</SCR" | <body> #data <script></SCRI #errors (1,8): expected-doctype-but-got-start-tag (1,14): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "</SCRI" | <body> #data <script></SCRIP #errors (1,8): expected-doctype-but-got-start-tag (1,15): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "</SCRIP" | <body> #data <script></SCRIPT #errors (1,8): expected-doctype-but-got-start-tag (1,16): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "</SCRIPT" | <body> #data <script></SCRIPT #errors (1,8): expected-doctype-but-got-start-tag (1,17): expected-attribute-name-but-got-eof (1,17): expected-named-closing-tag-but-got-eof #new-errors (1:18) eof-in-tag #document | <html> | <head> | <script> | <body> #data <script></s #errors (1,8): expected-doctype-but-got-start-tag (1,11): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "</s" | <body> #data <script></sc #errors (1,8): expected-doctype-but-got-start-tag (1,12): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "</sc" | <body> #data <script></scr #errors (1,8): expected-doctype-but-got-start-tag (1,13): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "</scr" | <body> #data <script></scri #errors (1,8): expected-doctype-but-got-start-tag (1,14): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "</scri" | <body> #data <script></scrip #errors (1,8): expected-doctype-but-got-start-tag (1,15): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "</scrip" | <body> #data <script></script #errors (1,8): expected-doctype-but-got-start-tag (1,16): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "</script" | <body> #data <script></script #errors (1,8): expected-doctype-but-got-start-tag (1,17): expected-attribute-name-but-got-eof (1,17): expected-named-closing-tag-but-got-eof #new-errors (1:18) eof-in-tag #document | <html> | <head> | <script> | <body> #data <script><! #errors (1,8): expected-doctype-but-got-start-tag (1,10): expected-script-data-but-got-eof (1,10): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "<!" | <body> #data <script><!a #errors (1,8): expected-doctype-but-got-start-tag (1,11): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "<!a" | <body> #data <script><!- #errors (1,8): expected-doctype-but-got-start-tag (1,11): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "<!-" | <body> #data <script><!-a #errors (1,8): expected-doctype-but-got-start-tag (1,12): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "<!-a" | <body> #data <script><!-- #errors (1,8): expected-doctype-but-got-start-tag (1,12): expected-named-closing-tag-but-got-eof (1,12): unexpected-eof-in-text-mode #new-errors (1:13) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--" | <body> #data <script><!--a #errors (1,8): expected-doctype-but-got-start-tag (1,13): expected-named-closing-tag-but-got-eof (1,13): unexpected-eof-in-text-mode #new-errors (1:14) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--a" | <body> #data <script><!--< #errors (1,8): expected-doctype-but-got-start-tag (1,13): expected-named-closing-tag-but-got-eof (1,13): unexpected-eof-in-text-mode #new-errors (1:14) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<" | <body> #data <script><!--<a #errors (1,8): expected-doctype-but-got-start-tag (1,14): expected-named-closing-tag-but-got-eof (1,14): unexpected-eof-in-text-mode #new-errors (1:15) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<a" | <body> #data <script><!--</ #errors (1,8): expected-doctype-but-got-start-tag (1,14): expected-named-closing-tag-but-got-eof (1,14): unexpected-eof-in-text-mode #new-errors (1:15) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--</" | <body> #data <script><!--</script #errors (1,8): expected-doctype-but-got-start-tag (1,20): expected-named-closing-tag-but-got-eof (1,20): unexpected-eof-in-text-mode #new-errors (1:21) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--</script" | <body> #data <script><!--</script #errors (1,8): expected-doctype-but-got-start-tag (1,21): expected-attribute-name-but-got-eof (1,21): expected-named-closing-tag-but-got-eof #new-errors (1:22) eof-in-tag #document | <html> | <head> | <script> | "<!--" | <body> #data <script><!--<s #errors (1,8): expected-doctype-but-got-start-tag (1,14): expected-named-closing-tag-but-got-eof (1,14): unexpected-eof-in-text-mode #new-errors (1:15) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<s" | <body> #data <script><!--<script #errors (1,8): expected-doctype-but-got-start-tag (1,19): expected-named-closing-tag-but-got-eof (1,19): unexpected-eof-in-text-mode #new-errors (1:20) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script" | <body> #data <script><!--<script #errors (1,8): expected-doctype-but-got-start-tag (1,20): eof-in-script-in-script (1,20): expected-named-closing-tag-but-got-eof #new-errors (1:21) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script " | <body> #data <script><!--<script < #errors (1,8): expected-doctype-but-got-start-tag (1,21): eof-in-script-in-script (1,21): expected-named-closing-tag-but-got-eof #new-errors (1:22) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script <" | <body> #data <script><!--<script <a #errors (1,8): expected-doctype-but-got-start-tag (1,22): eof-in-script-in-script (1,22): expected-named-closing-tag-but-got-eof #new-errors (1:23) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script <a" | <body> #data <script><!--<script </ #errors (1,8): expected-doctype-but-got-start-tag (1,22): eof-in-script-in-script (1,22): expected-named-closing-tag-but-got-eof #new-errors (1:23) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script </" | <body> #data <script><!--<script </s #errors (1,8): expected-doctype-but-got-start-tag (1,23): eof-in-script-in-script (1,23): expected-named-closing-tag-but-got-eof #new-errors (1:24) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script </s" | <body> #data <script><!--<script </script #errors (1,8): expected-doctype-but-got-start-tag (1,28): eof-in-script-in-script (1,28): expected-named-closing-tag-but-got-eof #new-errors (1:29) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script </script" | <body> #data <script><!--<script </scripta #errors (1,8): expected-doctype-but-got-start-tag (1,29): eof-in-script-in-script (1,29): expected-named-closing-tag-but-got-eof #new-errors (1:30) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script </scripta" | <body> #data <script><!--<script </script #errors (1,8): expected-doctype-but-got-start-tag (1,29): expected-named-closing-tag-but-got-eof (1,29): unexpected-eof-in-text-mode #new-errors (1:30) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script </script " | <body> #data <script><!--<script </script> #errors (1,8): expected-doctype-but-got-start-tag (1,29): expected-named-closing-tag-but-got-eof (1,29): unexpected-eof-in-text-mode #new-errors (1:30) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script </script>" | <body> #data <script><!--<script </script/ #errors (1,8): expected-doctype-but-got-start-tag (1,29): expected-named-closing-tag-but-got-eof (1,29): unexpected-eof-in-text-mode #new-errors (1:30) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script </script/" | <body> #data <script><!--<script </script < #errors (1,8): expected-doctype-but-got-start-tag (1,30): expected-named-closing-tag-but-got-eof (1,30): unexpected-eof-in-text-mode #new-errors (1:31) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script </script <" | <body> #data <script><!--<script </script <a #errors (1,8): expected-doctype-but-got-start-tag (1,31): expected-named-closing-tag-but-got-eof (1,31): unexpected-eof-in-text-mode #new-errors (1:32) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script </script <a" | <body> #data <script><!--<script </script </ #errors (1,8): expected-doctype-but-got-start-tag (1,31): expected-named-closing-tag-but-got-eof (1,31): unexpected-eof-in-text-mode #new-errors (1:32) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script </script </" | <body> #data <script><!--<script </script </script #errors (1,8): expected-doctype-but-got-start-tag (1,37): expected-named-closing-tag-but-got-eof (1,37): unexpected-eof-in-text-mode #new-errors (1:38) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script </script </script" | <body> #data <script><!--<script </script </script #errors (1,8): expected-doctype-but-got-start-tag (1,38): expected-attribute-name-but-got-eof (1,38): expected-named-closing-tag-but-got-eof #new-errors (1:39) eof-in-tag #document | <html> | <head> | <script> | "<!--<script </script " | <body> #data <script><!--<script </script </script/ #errors (1,8): expected-doctype-but-got-start-tag (1,38): unexpected-EOF-after-solidus-in-tag (1,38): expected-named-closing-tag-but-got-eof #new-errors (1:39) eof-in-tag #document | <html> | <head> | <script> | "<!--<script </script " | <body> #data <script><!--<script </script </script> #errors (1,8): expected-doctype-but-got-start-tag #document | <html> | <head> | <script> | "<!--<script </script " | <body> #data <script><!--<script - #errors (1,8): expected-doctype-but-got-start-tag (1,21): eof-in-script-in-script (1,21): expected-named-closing-tag-but-got-eof #new-errors (1:22) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script -" | <body> #data <script><!--<script -a #errors (1,8): expected-doctype-but-got-start-tag (1,22): eof-in-script-in-script (1,22): expected-named-closing-tag-but-got-eof #new-errors (1:23) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script -a" | <body> #data <script><!--<script -- #errors (1,8): expected-doctype-but-got-start-tag (1,22): eof-in-script-in-script (1,22): expected-named-closing-tag-but-got-eof #new-errors (1:23) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script --" | <body> #data <script><!--<script --a #errors (1,8): expected-doctype-but-got-start-tag (1,23): eof-in-script-in-script (1,23): expected-named-closing-tag-but-got-eof #new-errors (1:24) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script --a" | <body> #data <script><!--<script --> #errors (1,8): expected-doctype-but-got-start-tag (1,23): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "<!--<script -->" | <body> #data <script><!--<script -->< #errors (1,8): expected-doctype-but-got-start-tag (1,24): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "<!--<script --><" | <body> #data <script><!--<script --></ #errors (1,8): expected-doctype-but-got-start-tag (1,25): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "<!--<script --></" | <body> #data <script><!--<script --></script #errors (1,8): expected-doctype-but-got-start-tag (1,31): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "<!--<script --></script" | <body> #data <script><!--<script --></script #errors (1,8): expected-doctype-but-got-start-tag (1,32): expected-attribute-name-but-got-eof (1,32): expected-named-closing-tag-but-got-eof #new-errors (1:33) eof-in-tag #document | <html> | <head> | <script> | "<!--<script -->" | <body> #data <script><!--<script --></script/ #errors (1,8): expected-doctype-but-got-start-tag (1,32): unexpected-EOF-after-solidus-in-tag (1,32): expected-named-closing-tag-but-got-eof #new-errors (1:33) eof-in-tag #document | <html> | <head> | <script> | "<!--<script -->" | <body> #data <script><!--<script --></script> #errors (1,8): expected-doctype-but-got-start-tag #document | <html> | <head> | <script> | "<!--<script -->" | <body> #data <script><!--<script><\/script>--></script> #errors (1,8): expected-doctype-but-got-start-tag #document | <html> | <head> | <script> | "<!--<script><\/script>-->" | <body> #data <script><!--<script></scr'+'ipt>--></script> #errors (1,8): expected-doctype-but-got-start-tag #document | <html> | <head> | <script> | "<!--<script></scr'+'ipt>-->" | <body> #data <script><!--<script></script><script></script></script> #errors (1,8): expected-doctype-but-got-start-tag #document | <html> | <head> | <script> | "<!--<script></script><script></script>" | <body> #data <script><!--<script></script><script></script>--><!--</script> #errors (1,8): expected-doctype-but-got-start-tag #document | <html> | <head> | <script> | "<!--<script></script><script></script>--><!--" | <body> #data <script><!--<script></script><script></script>-- ></script> #errors (1,8): expected-doctype-but-got-start-tag #document | <html> | <head> | <script> | "<!--<script></script><script></script>-- >" | <body> #data <script><!--<script></script><script></script>- -></script> #errors (1,8): expected-doctype-but-got-start-tag #document | <html> | <head> | <script> | "<!--<script></script><script></script>- ->" | <body> #data <script><!--<script></script><script></script>- - ></script> #errors (1,8): expected-doctype-but-got-start-tag #document | <html> | <head> | <script> | "<!--<script></script><script></script>- - >" | <body> #data <script><!--<script></script><script></script>-></script> #errors (1,8): expected-doctype-but-got-start-tag #document | <html> | <head> | <script> | "<!--<script></script><script></script>->" | <body> #data <script><!--<script>--!></script>X #errors (1,8): expected-doctype-but-got-start-tag (1,34): expected-named-closing-tag-but-got-eof (1,34): unexpected-eof-in-text-mode #new-errors (1:35) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script>--!></script>X" | <body> #data <script><!--<scr'+'ipt></script>--></script> #errors (1,8): expected-doctype-but-got-start-tag (1,44): unexpected-end-tag #document | <html> | <head> | <script> | "<!--<scr'+'ipt>" | <body> | "-->" #data <script><!--<script></scr'+'ipt></script>X #errors (1,8): expected-doctype-but-got-start-tag (1,42): expected-named-closing-tag-but-got-eof (1,42): unexpected-eof-in-text-mode #new-errors (1:43) eof-in-script-html-comment-like-text #document | <html> | <head> | <script> | "<!--<script></scr'+'ipt></script>X" | <body> #data <style><!--<style></style>--></style> #errors (1,7): expected-doctype-but-got-start-tag (1,37): unexpected-end-tag #document | <html> | <head> | <style> | "<!--<style>" | <body> | "-->" #data <style><!--</style>X #errors (1,7): expected-doctype-but-got-start-tag #document | <html> | <head> | <style> | "<!--" | <body> | "X" #data <style><!--...</style>...--></style> #errors (1,7): expected-doctype-but-got-start-tag (1,36): unexpected-end-tag #document | <html> | <head> | <style> | "<!--..." | <body> | "...-->" #data <style><!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style></style>X #errors (1,7): expected-doctype-but-got-start-tag #document | <html> | <head> | <style> | "<!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style>" | <body> | "X" #data <style><!--...<style><!--...--!></style>--></style> #errors (1,7): expected-doctype-but-got-start-tag (1,51): unexpected-end-tag #document | <html> | <head> | <style> | "<!--...<style><!--...--!>" | <body> | "-->" #data <style><!--...</style><!-- --><style>@import ...</style> #errors (1,7): expected-doctype-but-got-start-tag #document | <html> | <head> | <style> | "<!--..." | <!-- --> | <style> | "@import ..." | <body> #data <style>...<style><!--...</style><!-- --></style> #errors (1,7): expected-doctype-but-got-start-tag (1,48): unexpected-end-tag #document | <html> | <head> | <style> | "...<style><!--..." | <!-- --> | <body> #data <style>...<!--[if IE]><style>...</style>X #errors (1,7): expected-doctype-but-got-start-tag #document | <html> | <head> | <style> | "...<!--[if IE]><style>..." | <body> | "X" #data <title><!--<title>--> #errors (1,7): expected-doctype-but-got-start-tag (1,37): unexpected-end-tag #document | | | | "<!--<title>" | <body> | "-->" #data <title></title> #errors (1,7): expected-doctype-but-got-start-tag #document | | | | "" | #data foo/title><link></head><body>X #errors (1,7): expected-doctype-but-got-start-tag (1,37): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <title> | "foo/title><link></head><body>X" | <body> #data <noscript><!--<noscript></noscript>--></noscript> #errors (1,10): expected-doctype-but-got-start-tag (1,49): unexpected-end-tag #script-on #document | <html> | <head> | <noscript> | "<!--<noscript>" | <body> | "-->" #data <noscript><!--<noscript></noscript>--></noscript> #errors * (1,11) missing DOCTYPE #script-off #document | <html> | <head> | <noscript> | <!-- <noscript></noscript> --> | <body> #data <noscript><!--</noscript>X<noscript>--></noscript> #errors (1,10): expected-doctype-but-got-start-tag #script-on #document | <html> | <head> | <noscript> | "<!--" | <body> | "X" | <noscript> | "-->" #data <noscript><!--</noscript>X<noscript>--></noscript> #errors (1,10): expected-doctype-but-got-start-tag #script-off #document | <html> | <head> | <noscript> | <!-- </noscript>X<noscript> --> | <body> #data <noscript><iframe></noscript>X #errors (1,10): expected-doctype-but-got-start-tag #script-on #document | <html> | <head> | <noscript> | "<iframe>" | <body> | "X" #data <noscript><iframe></noscript>X #errors * (1,11) missing DOCTYPE * (1,19) unexpected token in head noscript * (1,31) unexpected EOF #script-off #document | <html> | <head> | <noscript> | <body> | <iframe> | "</noscript>X" #data <noframes><!--<noframes></noframes>--></noframes> #errors (1,10): expected-doctype-but-got-start-tag (1,49): unexpected-end-tag #document | <html> | <head> | <noframes> | "<!--<noframes>" | <body> | "-->" #data <noframes><body><script><!--...</script></body></noframes></html> #errors (1,10): expected-doctype-but-got-start-tag #document | <html> | <head> | <noframes> | "<body><script><!--...</script></body>" | <body> #data <textarea><!--<textarea></textarea>--></textarea> #errors (1,10): expected-doctype-but-got-start-tag (1,49): unexpected-end-tag #document | <html> | <head> | <body> | <textarea> | "<!--<textarea>" | "-->" #data <textarea></textarea></textarea> #errors (1,10): expected-doctype-but-got-start-tag #document | <html> | <head> | <body> | <textarea> | "</textarea>" #data <iframe><!--<iframe></iframe>--></iframe> #errors (1,8): expected-doctype-but-got-start-tag (1,41): unexpected-end-tag #document | <html> | <head> | <body> | <iframe> | "<!--<iframe>" | "-->" #data <iframe>...<!--X->...<!--/X->...</iframe> #errors (1,8): expected-doctype-but-got-start-tag #document | <html> | <head> | <body> | <iframe> | "...<!--X->...<!--/X->..." #data <xmp><!--<xmp></xmp>--></xmp> #errors (1,5): expected-doctype-but-got-start-tag (1,29): unexpected-end-tag #document | <html> | <head> | <body> | <xmp> | "<!--<xmp>" | "-->" #data <noembed><!--<noembed></noembed>--></noembed> #errors (1,9): expected-doctype-but-got-start-tag (1,45): unexpected-end-tag #document | <html> | <head> | <body> | <noembed> | "<!--<noembed>" | "-->" #data <!doctype html><table> #errors (2,0): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | " " #data <!doctype html><table><td><span><font></span><span> #errors (1,26): unexpected-cell-in-table-body (1,45): unexpected-end-tag (1,51): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <span> | <font> | <font> | <span> #data <!doctype html><form><table></form><form></table></form> #errors (1,35): unexpected-end-tag-implies-table-voodoo (1,35): unexpected-end-tag (1,41): unexpected-form-in-table (1,56): unexpected-end-tag (1,56): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <form> | <table> | <form> ��������������������������������������������golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/testdata/webkit/tests17.dat�������������������0000664�0000000�0000000�00000005673�14154421323�0026510�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!doctype html><table><tbody><select><tr> #errors (1,37): unexpected-start-tag-implies-table-voodoo (1,41): unexpected-table-element-start-tag-in-select-in-table (1,41): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <table> | <tbody> | <tr> #data <!doctype html><table><tr><select><td> #errors (1,34): unexpected-start-tag-implies-table-voodoo (1,38): unexpected-table-element-start-tag-in-select-in-table (1,38): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <table> | <tbody> | <tr> | <td> #data <!doctype html><table><tr><td><select><td> #errors (1,42): unexpected-table-element-start-tag-in-select-in-table (1,42): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <select> | <td> #data <!doctype html><table><tr><th><select><td> #errors (1,42): unexpected-table-element-start-tag-in-select-in-table (1,42): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <th> | <select> | <td> #data <!doctype html><table><caption><select><tr> #errors (1,43): unexpected-table-element-start-tag-in-select-in-table (1,43): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <caption> | <select> | <tbody> | <tr> #data <!doctype html><select><tr> #errors (1,27): unexpected-start-tag-in-select (1,27): eof-in-select #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!doctype html><select><td> #errors (1,27): unexpected-start-tag-in-select (1,27): eof-in-select #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!doctype html><select><th> #errors (1,27): unexpected-start-tag-in-select (1,27): eof-in-select #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!doctype html><select><tbody> #errors (1,30): unexpected-start-tag-in-select (1,30): eof-in-select #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!doctype html><select><thead> #errors (1,30): unexpected-start-tag-in-select (1,30): eof-in-select #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!doctype html><select><tfoot> #errors (1,30): unexpected-start-tag-in-select (1,30): eof-in-select #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!doctype html><select><caption> #errors (1,32): unexpected-start-tag-in-select (1,32): eof-in-select #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!doctype html><table><tr></table>a #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | "a" ���������������������������������������������������������������������golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/testdata/webkit/tests18.dat�������������������0000664�0000000�0000000�00000023721�14154421323�0026503�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <plaintext></plaintext> #errors 11: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>â€. 23: End of file seen and there were open elements. 11: Unclosed element “plaintextâ€. #document | <html> | <head> | <body> | <plaintext> | "</plaintext>" #data <!doctype html><plaintext></plaintext> #errors (1,38): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <plaintext> | "</plaintext>" #data <!doctype html><html><plaintext></plaintext> #errors 44: End of file seen and there were open elements. 32: Unclosed element “plaintextâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <plaintext> | "</plaintext>" #data <!doctype html><head><plaintext></plaintext> #errors 44: End of file seen and there were open elements. 32: Unclosed element “plaintextâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <plaintext> | "</plaintext>" #data <!doctype html><html><noscript><plaintext></plaintext> #errors 42: Bad start tag in “plaintext†in “headâ€. 54: End of file seen and there were open elements. 42: Unclosed element “plaintextâ€. #script-off #document | <!DOCTYPE html> | <html> | <head> | <noscript> | <body> | <plaintext> | "</plaintext>" #data <!doctype html></head><plaintext></plaintext> #errors 45: End of file seen and there were open elements. 33: Unclosed element “plaintextâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <plaintext> | "</plaintext>" #data <!doctype html><body><plaintext></plaintext> #errors 44: End of file seen and there were open elements. 32: Unclosed element “plaintextâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <plaintext> | "</plaintext>" #data <!doctype html><table><plaintext></plaintext> #errors (1,33): foster-parenting-start-tag (1,45): foster-parenting-character (1,45): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | <plaintext> | "</plaintext>" | <table> #data <!doctype html><table><tbody><plaintext></plaintext> #errors (1,40): foster-parenting-start-tag (1,41): foster-parenting-character (1,52): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | <plaintext> | "</plaintext>" | <table> | <tbody> #data <!doctype html><table><tbody><tr><plaintext></plaintext> #errors (1,44): foster-parenting-start-tag (1,56): foster-parenting-character (1,56): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | <plaintext> | "</plaintext>" | <table> | <tbody> | <tr> #data <!doctype html><table><td><plaintext></plaintext> #errors (1,26): unexpected-cell-in-table-body (1,49): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <plaintext> | "</plaintext>" #data <!doctype html><table><caption><plaintext></plaintext> #errors (1,54): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <caption> | <plaintext> | "</plaintext>" #data <!doctype html><table><colgroup><plaintext></plaintext> #errors 43: Start tag “plaintext†seen in “tableâ€. 55: Misplaced non-space characters inside a table. 55: End of file seen and there were open elements. 43: Unclosed element “plaintextâ€. 22: Unclosed element “tableâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <plaintext> | "</plaintext>" | <table> | <colgroup> #data <!doctype html><select><plaintext></plaintext>X #errors 34: Stray start tag “plaintextâ€. 46: Stray end tag “plaintextâ€. 47: End of file seen and there were open elements. 23: Unclosed element “selectâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | "X" #data <!doctype html><table><select><plaintext>a<caption>b #errors 30: Start tag “select†seen in “tableâ€. 41: Stray start tag “plaintextâ€. 51: “caption†start tag with “select†open. 52: End of file seen and there were open elements. 51: Unclosed element “captionâ€. 22: Unclosed element “tableâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | "a" | <table> | <caption> | "b" #data <!doctype html><template><plaintext>a</template>b #errors 49: End of file seen and there were open elements. 36: Unclosed element “plaintextâ€. 25: Unclosed element “templateâ€. #document | <!DOCTYPE html> | <html> | <head> | <template> | content | <plaintext> | "a</template>b" | <body> #data <!doctype html><body></body><plaintext></plaintext> #errors 39: Stray start tag “plaintextâ€. 51: End of file seen and there were open elements. 39: Unclosed element “plaintextâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <plaintext> | "</plaintext>" #data <!doctype html><frameset><plaintext></plaintext> #errors 36: Stray start tag “plaintextâ€. 48: Stray end tag “plaintextâ€. 48: End of file seen and there were open elements. 25: Unclosed element “framesetâ€. #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!doctype html><frameset></frameset><plaintext></plaintext> #errors 47: Stray start tag “plaintextâ€. 59: Stray end tag “plaintextâ€. #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!doctype html><body></body></html><plaintext></plaintext> #errors 46: Stray start tag “plaintextâ€. 58: End of file seen and there were open elements. 46: Unclosed element “plaintextâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <plaintext> | "</plaintext>" #data <!doctype html><frameset></frameset></html><plaintext></plaintext> #errors 54: Stray start tag “plaintextâ€. 66: Stray end tag “plaintextâ€. #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!doctype html><svg><plaintext>a</plaintext>b #errors 45: End of file seen and there were open elements. 20: Unclosed element “svgâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg plaintext> | "a" | "b" #data <!doctype html><svg><title><plaintext>a</plaintext>b #errors 52: End of file seen and there were open elements. 38: Unclosed element “plaintextâ€. 27: Unclosed element “titleâ€. 20: Unclosed element “svgâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg title> | <plaintext> | "a</plaintext>b" #data <!doctype html><table><tr><style></script></style>abc #errors (1,51): foster-parenting-character (1,52): foster-parenting-character (1,53): foster-parenting-character (1,53): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | "abc" | <table> | <tbody> | <tr> | <style> | "</script>" #data <!doctype html><table><tr><script></style></script>abc #errors (1,52): foster-parenting-character (1,53): foster-parenting-character (1,54): foster-parenting-character (1,54): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | "abc" | <table> | <tbody> | <tr> | <script> | "</style>" #data <!doctype html><table><caption><style></script></style>abc #errors (1,58): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <caption> | <style> | "</script>" | "abc" #data <!doctype html><table><td><style></script></style>abc #errors (1,26): unexpected-cell-in-table-body (1,53): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <style> | "</script>" | "abc" #data <!doctype html><select><script></style></script>abc #errors (1,51): eof-in-select #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <script> | "</style>" | "abc" #data <!doctype html><table><select><script></style></script>abc #errors (1,30): unexpected-start-tag-implies-table-voodoo (1,58): eof-in-select #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <script> | "</style>" | "abc" | <table> #data <!doctype html><table><tr><select><script></style></script>abc #errors (1,34): unexpected-start-tag-implies-table-voodoo (1,62): eof-in-select #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <script> | "</style>" | "abc" | <table> | <tbody> | <tr> #data <!doctype html><frameset></frameset><noframes>abc #errors (1,49): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <noframes> | "abc" #data <!doctype html><frameset></frameset><noframes>abc</noframes><!--abc--> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <noframes> | "abc" | <!-- abc --> #data <!doctype html><frameset></frameset></html><noframes>abc #errors (1,56): expected-named-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <noframes> | "abc" #data <!doctype html><frameset></frameset></html><noframes>abc</noframes><!--abc--> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <noframes> | "abc" | <!-- abc --> #data <!doctype html><table><tr></tbody><tfoot> #errors (1,41): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <tfoot> #data <!doctype html><table><td><svg></svg>abc<td> #errors (1,26): unexpected-cell-in-table-body (1,44): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <svg svg> | "abc" | <td> �����������������������������������������������golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/testdata/webkit/tests19.dat�������������������0000664�0000000�0000000�00000057061�14154421323�0026510�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!doctype html><math><mn DefinitionUrl="foo"> #errors (1,45): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math mn> | definitionURL="foo" #data <!doctype html><html></p><!--foo--> #errors (1,25): end-tag-after-implied-root #document | <!DOCTYPE html> | <html> | <!-- foo --> | <head> | <body> #data <!doctype html><head></head></p><!--foo--> #errors (1,32): unexpected-end-tag #document | <!DOCTYPE html> | <html> | <head> | <!-- foo --> | <body> #data <!doctype html><body><p><pre> #errors (1,29): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <pre> #data <!doctype html><body><p><listing> #errors (1,33): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <listing> #data <!doctype html><p><plaintext> #errors (1,29): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <plaintext> #data <!doctype html><p><h1> #errors (1,22): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <h1> #data <!doctype html><isindex type="hidden"> #errors (1,38): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <isindex> | type="hidden" #data <!doctype html><ruby><p><rp> #errors (1,28): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <ruby> | <p> | <rp> #data <!doctype html><ruby><div><span><rp> #errors (1,36): XXX-undefined-error (1,36): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <ruby> | <div> | <span> | <rp> #data <!doctype html><ruby><div><p><rp> #errors (1,33): XXX-undefined-error (1,33): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <ruby> | <div> | <p> | <rp> #data <!doctype html><ruby><p><rt> #errors (1,28): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <ruby> | <p> | <rt> #data <!doctype html><ruby><div><span><rt> #errors (1,36): XXX-undefined-error (1,36): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <ruby> | <div> | <span> | <rt> #data <!doctype html><ruby><div><p><rt> #errors (1,33): XXX-undefined-error (1,33): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <ruby> | <div> | <p> | <rt> #data <html><ruby>a<rb>b<rt></ruby></html> #errors (1,6): expected-doctype-but-got-start-tag #document | <html> | <head> | <body> | <ruby> | "a" | <rb> | "b" | <rt> #data <html><ruby>a<rp>b<rt></ruby></html> #errors (1,6): expected-doctype-but-got-start-tag #document | <html> | <head> | <body> | <ruby> | "a" | <rp> | "b" | <rt> #data <html><ruby>a<rt>b<rt></ruby></html> #errors (1,6): expected-doctype-but-got-start-tag #document | <html> | <head> | <body> | <ruby> | "a" | <rt> | "b" | <rt> #data <html><ruby>a<rtc>b<rt>c<rb>d</ruby></html> #errors (1,6): expected-doctype-but-got-start-tag #document | <html> | <head> | <body> | <ruby> | "a" | <rtc> | "b" | <rt> | "c" | <rb> | "d" #data <!doctype html><math/><foo> #errors (1,27): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <foo> #data <!doctype html><svg/><foo> #errors (1,26): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <foo> #data <!doctype html><div></body><!--foo--> #errors (1,27): expected-one-end-tag-but-got-another #document | <!DOCTYPE html> | <html> | <head> | <body> | <div> | <!-- foo --> #data <!doctype html><h1><div><h3><span></h1>foo #errors (1,39): end-tag-too-early (1,42): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <h1> | <div> | <h3> | <span> | "foo" #data <!doctype html><p></h3>foo #errors (1,23): end-tag-too-early #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | "foo" #data <!doctype html><h3><li>abc</h2>foo #errors (1,31): end-tag-too-early #document | <!DOCTYPE html> | <html> | <head> | <body> | <h3> | <li> | "abc" | "foo" #data <!doctype html><table>abc<!--foo--> #errors (1,23): foster-parenting-character (1,24): foster-parenting-character (1,25): foster-parenting-character (1,35): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | "abc" | <table> | <!-- foo --> #data <!doctype html><table> <!--foo--> #errors (1,34): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | " " | <!-- foo --> #data <!doctype html><table> b <!--foo--> #errors (1,23): foster-parenting-character (1,24): foster-parenting-character (1,25): foster-parenting-character (1,35): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | " b " | <table> | <!-- foo --> #data <!doctype html><select><option><option> #errors (1,39): eof-in-select #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <option> | <option> #data <!doctype html><select><option></optgroup> #errors (1,42): unexpected-end-tag-in-select (1,42): eof-in-select #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <option> #data <!doctype html><select><option></optgroup> #errors (1,42): unexpected-end-tag-in-select (1,42): eof-in-select #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <option> #data <!doctype html><dd><optgroup><dd> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <dd> | <optgroup> | <dd> #data <!doctype html><p><math><mi><p><h1> #errors (1,35): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <math math> | <math mi> | <p> | <h1> #data <!doctype html><p><math><mo><p><h1> #errors (1,35): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <math math> | <math mo> | <p> | <h1> #data <!doctype html><p><math><mn><p><h1> #errors (1,35): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <math math> | <math mn> | <p> | <h1> #data <!doctype html><p><math><ms><p><h1> #errors (1,35): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <math math> | <math ms> | <p> | <h1> #data <!doctype html><p><math><mtext><p><h1> #errors (1,38): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <math math> | <math mtext> | <p> | <h1> #data <!doctype html><frameset></noframes> #errors (1,36): unexpected-end-tag-in-frameset (1,36): eof-in-frameset #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!doctype html><html c=d><body></html><html a=b> #errors (1,48): non-html-root #document | <!DOCTYPE html> | <html> | a="b" | c="d" | <head> | <body> #data <!doctype html><html c=d><frameset></frameset></html><html a=b> #errors (1,63): non-html-root #document | <!DOCTYPE html> | <html> | a="b" | c="d" | <head> | <frameset> #data <!doctype html><html><frameset></frameset></html><!--foo--> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <!-- foo --> #data <!doctype html><html><frameset></frameset></html> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> | " " #data <!doctype html><html><frameset></frameset></html>abc #errors (1,50): expected-eof-but-got-char (1,51): expected-eof-but-got-char (1,52): expected-eof-but-got-char #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!doctype html><html><frameset></frameset></html><p> #errors (1,52): expected-eof-but-got-start-tag #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!doctype html><html><frameset></frameset></html></p> #errors (1,53): expected-eof-but-got-end-tag #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <html><frameset></frameset></html><!doctype html> #errors (1,6): expected-doctype-but-got-start-tag (1,49): unexpected-doctype #document | <html> | <head> | <frameset> #data <!doctype html><body><frameset> #errors (1,31): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> #data <!doctype html><p><frameset><frame> #errors (1,28): unexpected-start-tag (1,35): eof-in-frameset #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <frame> #data <!doctype html><p>a<frameset> #errors (1,29): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | "a" #data <!doctype html><p> <frameset><frame> #errors (1,29): unexpected-start-tag (1,36): eof-in-frameset #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <frame> #data <!doctype html><pre><frameset> #errors (1,30): unexpected-start-tag (1,30): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <pre> #data <!doctype html><listing><frameset> #errors (1,34): unexpected-start-tag (1,34): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <listing> #data <!doctype html><li><frameset> #errors (1,29): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <li> #data <!doctype html><dd><frameset> #errors (1,29): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <dd> #data <!doctype html><dt><frameset> #errors (1,29): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <dt> #data <!doctype html><button><frameset> #errors (1,33): unexpected-start-tag (1,33): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <button> #data <!doctype html><applet><frameset> #errors (1,33): unexpected-start-tag (1,33): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <applet> #data <!doctype html><marquee><frameset> #errors (1,34): unexpected-start-tag (1,34): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <marquee> #data <!doctype html><object><frameset> #errors (1,33): unexpected-start-tag (1,33): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <object> #data <!doctype html><table><frameset> #errors (1,32): unexpected-start-tag-implies-table-voodoo (1,32): unexpected-start-tag (1,32): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> #data <!doctype html><area><frameset> #errors (1,31): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <area> #data <!doctype html><basefont><frameset> #errors (1,35): eof-in-frameset #document | <!DOCTYPE html> | <html> | <head> | <basefont> | <frameset> #data <!doctype html><bgsound><frameset> #errors (1,34): eof-in-frameset #document | <!DOCTYPE html> | <html> | <head> | <bgsound> | <frameset> #data <!doctype html><br><frameset> #errors (1,29): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <br> #data <!doctype html><embed><frameset> #errors (1,32): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <embed> #data <!doctype html><img><frameset> #errors (1,30): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <img> #data <!doctype html><input><frameset> #errors (1,32): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <input> #data <!doctype html><keygen><frameset> #errors (1,33): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <keygen> #data <!doctype html><wbr><frameset> #errors (1,30): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <wbr> #data <!doctype html><hr><frameset> #errors (1,29): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <hr> #data <!doctype html><textarea></textarea><frameset> #errors (1,46): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <textarea> #data <!doctype html><xmp></xmp><frameset> #errors (1,36): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <xmp> #data <!doctype html><iframe></iframe><frameset> #errors (1,42): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <iframe> #data <!doctype html><select></select><frameset> #errors (1,42): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!doctype html><svg></svg><frameset><frame> #errors (1,36): unexpected-start-tag (1,43): eof-in-frameset #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <frame> #data <!doctype html><math></math><frameset><frame> #errors (1,38): unexpected-start-tag (1,45): eof-in-frameset #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <frame> #data <!doctype html><svg><foreignObject><div> <frameset><frame> #errors (1,51): unexpected-start-tag (1,58): eof-in-frameset #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <frame> #data <!doctype html><svg>a</svg><frameset><frame> #errors (1,37): unexpected-start-tag (1,44): unexpected-start-tag-ignored #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | "a" #data <!doctype html><svg> </svg><frameset><frame> #errors (1,37): unexpected-start-tag (1,44): eof-in-frameset #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <frame> #data <html>aaa<frameset></frameset> #errors (1,6): expected-doctype-but-got-start-tag (1,19): unexpected-start-tag (1,30): unexpected-end-tag #document | <html> | <head> | <body> | "aaa" #data <html> a <frameset></frameset> #errors (1,6): expected-doctype-but-got-start-tag (1,19): unexpected-start-tag (1,30): unexpected-end-tag #document | <html> | <head> | <body> | "a " #data <!doctype html><div><frameset> #errors (1,30): unexpected-start-tag (1,30): eof-in-frameset #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!doctype html><div><body><frameset> #errors (1,26): unexpected-start-tag (1,36): unexpected-start-tag (1,36): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <div> #data <!doctype html><p><math></p>a #errors (1,28): unexpected-end-tag (1,28): unexpected-end-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <math math> | "a" #data <!doctype html><p><math><mn><span></p>a #errors (1,38): unexpected-end-tag (1,39): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <math math> | <math mn> | <span> | <p> | "a" #data <!doctype html><math></html> #errors (1,28): unexpected-end-tag (1,28): expected-one-end-tag-but-got-another (1,28): unexpected-end-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> #data <!doctype html><meta charset="ascii"> #errors #document | <!DOCTYPE html> | <html> | <head> | <meta> | charset="ascii" | <body> #data <!doctype html><meta http-equiv="content-type" content="text/html;charset=ascii"> #errors #document | <!DOCTYPE html> | <html> | <head> | <meta> | content="text/html;charset=ascii" | http-equiv="content-type" | <body> #data <!doctype html><head><!--aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--><meta charset="utf8"> #errors #document | <!DOCTYPE html> | <html> | <head> | <!-- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa --> | <meta> | charset="utf8" | <body> #data <!doctype html><html a=b><head></head><html c=d> #errors (1,48): non-html-root #document | <!DOCTYPE html> | <html> | a="b" | c="d" | <head> | <body> #data <!doctype html><image/> #errors (1,23): image-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | <img> #data <!doctype html>a<i>b<table>c<b>d</i>e</b>f #errors (1,28): foster-parenting-character (1,31): foster-parenting-start-tag (1,32): foster-parenting-character (1,36): foster-parenting-end-tag (1,36): adoption-agency-1.3 (1,37): foster-parenting-character (1,41): foster-parenting-end-tag (1,42): foster-parenting-character (1,42): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | "a" | <i> | "bc" | <b> | "de" | "f" | <table> #data <!doctype html><table><i>a<b>b<div>c<a>d</i>e</b>f #errors (1,25): foster-parenting-start-tag (1,26): foster-parenting-character (1,29): foster-parenting-start-tag (1,30): foster-parenting-character (1,35): foster-parenting-start-tag (1,36): foster-parenting-character (1,39): foster-parenting-start-tag (1,40): foster-parenting-character (1,44): foster-parenting-end-tag (1,44): adoption-agency-1.3 (1,44): adoption-agency-1.3 (1,45): foster-parenting-character (1,49): foster-parenting-end-tag (1,49): adoption-agency-1.3 (1,49): adoption-agency-1.3 (1,50): foster-parenting-character (1,50): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | <i> | "a" | <b> | "b" | <b> | <div> | <b> | <i> | "c" | <a> | "d" | <a> | "e" | <a> | "f" | <table> #data <!doctype html><i>a<b>b<div>c<a>d</i>e</b>f #errors (1,37): adoption-agency-1.3 (1,37): adoption-agency-1.3 (1,42): adoption-agency-1.3 (1,42): adoption-agency-1.3 (1,43): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <i> | "a" | <b> | "b" | <b> | <div> | <b> | <i> | "c" | <a> | "d" | <a> | "e" | <a> | "f" #data <!doctype html><table><i>a<b>b<div>c</i> #errors (1,25): foster-parenting-start-tag (1,26): foster-parenting-character (1,29): foster-parenting-start-tag (1,30): foster-parenting-character (1,35): foster-parenting-start-tag (1,36): foster-parenting-character (1,40): foster-parenting-end-tag (1,40): adoption-agency-1.3 (1,40): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | <i> | "a" | <b> | "b" | <b> | <div> | <i> | "c" | <table> #data <!doctype html><table><i>a<b>b<div>c<a>d</i>e</b>f #errors (1,25): foster-parenting-start-tag (1,26): foster-parenting-character (1,29): foster-parenting-start-tag (1,30): foster-parenting-character (1,35): foster-parenting-start-tag (1,36): foster-parenting-character (1,39): foster-parenting-start-tag (1,40): foster-parenting-character (1,44): foster-parenting-end-tag (1,44): adoption-agency-1.3 (1,44): adoption-agency-1.3 (1,45): foster-parenting-character (1,49): foster-parenting-end-tag (1,44): adoption-agency-1.3 (1,44): adoption-agency-1.3 (1,50): foster-parenting-character (1,50): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | <i> | "a" | <b> | "b" | <b> | <div> | <b> | <i> | "c" | <a> | "d" | <a> | "e" | <a> | "f" | <table> #data <!doctype html><table><i>a<div>b<tr>c<b>d</i>e #errors (1,25): foster-parenting-start-tag (1,26): foster-parenting-character (1,31): foster-parenting-start-tag (1,32): foster-parenting-character (1,37): foster-parenting-character (1,40): foster-parenting-start-tag (1,41): foster-parenting-character (1,45): foster-parenting-end-tag (1,45): adoption-agency-1.3 (1,46): foster-parenting-character (1,46): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | <i> | "a" | <div> | "b" | <i> | "c" | <b> | "d" | <b> | "e" | <table> | <tbody> | <tr> #data <!doctype html><table><td><table><i>a<div>b<b>c</i>d #errors (1,26): unexpected-cell-in-table-body (1,36): foster-parenting-start-tag (1,37): foster-parenting-character (1,42): foster-parenting-start-tag (1,43): foster-parenting-character (1,46): foster-parenting-start-tag (1,47): foster-parenting-character (1,51): foster-parenting-end-tag (1,51): adoption-agency-1.3 (1,51): adoption-agency-1.3 (1,52): foster-parenting-character (1,52): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <i> | "a" | <div> | <i> | "b" | <b> | "c" | <b> | "d" | <table> #data <!doctype html><body><bgsound> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <bgsound> #data <!doctype html><body><basefont> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <basefont> #data <!doctype html><a><b></a><basefont> #errors (1,25): adoption-agency-1.3 #document | <!DOCTYPE html> | <html> | <head> | <body> | <a> | <b> | <basefont> #data <!doctype html><a><b></a><bgsound> #errors (1,25): adoption-agency-1.3 #document | <!DOCTYPE html> | <html> | <head> | <body> | <a> | <b> | <bgsound> #data <!doctype html><figcaption><article></figcaption>a #errors (1,49): end-tag-too-early #document | <!DOCTYPE html> | <html> | <head> | <body> | <figcaption> | <article> | "a" #data <!doctype html><summary><article></summary>a #errors (1,43): end-tag-too-early #document | <!DOCTYPE html> | <html> | <head> | <body> | <summary> | <article> | "a" #data <!doctype html><p><a><plaintext>b #errors (1,32): unexpected-end-tag (1,33): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <a> | <plaintext> | <a> | "b" #data <!DOCTYPE html><div>a<a></div>b<p>c</p>d #errors (1,30): end-tag-too-early (1,40): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <div> | "a" | <a> | <a> | "b" | <p> | "c" | "d" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-golang-x-net-0.0+git20211209.491a49a+dfsg/html/testdata/webkit/tests2.dat��������������������0000664�0000000�0000000�00000031261�14154421323�0026412�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!DOCTYPE html>Test #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "Test" #data <textarea>test</div>test #errors (1,10): expected-doctype-but-got-start-tag (1,24): expected-closing-tag-but-got-eof #document | <html> | <head> | <body> | <textarea> | "test</div>test" #data <table><td> #errors (1,7): expected-doctype-but-got-start-tag (1,11): unexpected-cell-in-table-body (1,11): expected-closing-tag-but-got-eof #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> #data <table><td>test</tbody></table> #errors (1,7): expected-doctype-but-got-start-tag (1,11): unexpected-cell-in-table-body #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | "test" #data <frame>test #errors (1,7): expected-doctype-but-got-start-tag (1,7): unexpected-start-tag-ignored #document | <html> | <head> | <body> | "test" #data <!DOCTYPE html><frameset>test #errors (1,29): unexpected-char-in-frameset (1,29): unexpected-char-in-frameset (1,29): unexpected-char-in-frameset (1,29): unexpected-char-in-frameset (1,29): eof-in-frameset #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!DOCTYPE html><frameset> te st #errors (1,29): unexpected-char-in-frameset (1,29): unexpected-char-in-frameset (1,29): unexpected-char-in-frameset (1,29): unexpected-char-in-frameset (1,29): eof-in-frameset #document | <!DOCTYPE html> | <html> | <head> | <frameset> | " " #data <!DOCTYPE html><frameset></frameset> te st #errors (1,29): unexpected-char-after-frameset (1,29): unexpected-char-after-frameset (1,29): unexpected-char-after-frameset (1,29): unexpected-char-after-frameset #document | <!DOCTYPE html> | <html> | <head> | <frameset> | " " #data <!DOCTYPE html><frameset><!DOCTYPE html> #errors (1,40): unexpected-doctype (1,40): eof-in-frameset #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!DOCTYPE html><font><p><b>test</font> #errors (1,38): adoption-agency-1.3 (1,38): adoption-agency-1.3 #document | <!DOCTYPE html> | <html> | <head> | <body> | <font> | <p> | <font> | <b> | "test" #data <!DOCTYPE html><dt><div><dd> #errors (1,28): end-tag-too-early #document | <!DOCTYPE html> | <html> | <head> | <body> | <dt> | <div> | <dd> #data <script></x #errors (1,8): expected-doctype-but-got-start-tag (1,11): expected-named-closing-tag-but-got-eof #document | <html> | <head> | <script> | "</x" | <body> #data <table><plaintext><td> #errors (1,7): expected-doctype-but-got-start-tag (1,18): unexpected-start-tag-implies-table-voodoo (1,22): foster-parenting-character-in-table (1,22): foster-parenting-character-in-table (1,22): foster-parenting-character-in-table (1,22): foster-parenting-character-in-table (1,22): eof-in-table #document | <html> | <head> | <body> | <plaintext> | "<td>" | <table> #data <plaintext></plaintext> #errors (1,11): expected-doctype-but-got-start-tag (1,23): expected-closing-tag-but-got-eof #document | <html> | <head> | <body> | <plaintext> | "</plaintext>" #data <!DOCTYPE html><table><tr>TEST #errors (1,30): foster-parenting-character-in-table (1,30): foster-parenting-character-in-table (1,30): foster-parenting-character-in-table (1,30): foster-parenting-character-in-table (1,30): eof-in-table #document | <!DOCTYPE html> | <html> | <head> | <body> | "TEST" | <table> | <tbody> | <tr> #data <!DOCTYPE html><body t1=1><body t2=2><body t3=3 t4=4> #errors (1,37): unexpected-start-tag (1,53): unexpected-start-tag #document | <!DOCTYPE html> | <html> | <head> | <body> | t1="1" | t2="2" | t3="3" | t4="4" #data </b test #errors (1,8): eof-in-attribute-name (1,8): expected-doctype-but-got-eof #new-errors (1:9) eof-in-tag #document | <html> | <head> | <body> #data <!DOCTYPE html></b test<b &=&>X #errors (1,24): invalid-character-in-attribute-name (1,32): named-entity-without-semicolon (1,33): attributes-in-end-tag (1,33): unexpected-end-tag-before-html #new-errors (1:24) unexpected-character-in-attribute-name (1:33) missing-semicolon-after-character-reference (1:33) end-tag-with-attributes #document | <!DOCTYPE html> | <html> | <head> | <body> | "X" #data <!doctypehtml><scrIPt type=text/x-foobar;baz>X</SCRipt #errors (1,9): need-space-after-doctype (1,54): expected-named-closing-tag-but-got-eof #new-errors (1:10) missing-whitespace-before-doctype-name #document | <!DOCTYPE html> | <html> | <head> | <script> | type="text/x-foobar;baz" | "X</SCRipt" | <body> #data & #errors (1,1): expected-doctype-but-got-chars #document | <html> | <head> | <body> | "&" #data &# #errors (1,2): expected-numeric-entity (1,2): expected-doctype-but-got-chars #new-errors (1:3) absence-of-digits-in-numeric-character-reference #document | <html> | <head> | <body> | "&#" #data &#X #errors (1,3): expected-numeric-entity (1,3): expected-doctype-but-got-chars #new-errors (1:4) absence-of-digits-in-numeric-character-reference #document | <html> | <head> | <body> | "&#X" #data &#x #errors (1,3): expected-numeric-entity (1,3): expected-doctype-but-got-chars #new-errors (1:4) absence-of-digits-in-numeric-character-reference #document | <html> | <head> | <body> | "&#x" #data - #errors (1,4): numeric-entity-without-semicolon (1,4): expected-doctype-but-got-chars #new-errors (1:5) missing-semicolon-after-character-reference #document | <html> | <head> | <body> | "-" #data &x-test #errors (1,2): expected-doctype-but-got-chars #document | <html> | <head> | <body> | "&x-test" #data <!doctypehtml><p><li> #errors (1,9): need-space-after-doctype #new-errors (1:10) missing-whitespace-before-doctype-name #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <li> #data <!doctypehtml><p><dt> #errors (1,9): need-space-after-doctype #new-errors (1:10) missing-whitespace-before-doctype-name #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <dt> #data <!doctypehtml><p><dd> #errors (1,9): need-space-after-doctype #new-errors (1:10) missing-whitespace-before-doctype-name #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <dd> #data <!doctypehtml><p><form> #errors (1,9): need-space-after-doctype (1,23): expected-closing-tag-but-got-eof #new-errors (1:10) missing-whitespace-before-doctype-name #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <form> #data <!DOCTYPE html><p></P>X #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | "X" #data & #errors (1,4): named-entity-without-semicolon (1,4): expected-doctype-but-got-chars #new-errors (1:5) missing-semicolon-after-character-reference #document | <html> | <head> | <body> | "&" #data &AMp; #errors (1,3): expected-named-entity (1,3): expected-doctype-but-got-chars #new-errors (1:5) unknown-named-character-reference #document | <html> | <head> | <body> | "&AMp;" #data <!DOCTYPE html><html><head></head><body><thisISasillyTESTelementNameToMakeSureCrazyTagNamesArePARSEDcorrectLY> #errors (1,110): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <thisisasillytestelementnametomakesurecrazytagnamesareparsedcorrectly> #data <!DOCTYPE html>X</body>X #errors (1,24): unexpected-char-after-body #document | <!DOCTYPE html> | <html> | <head> | <body> | "XX" #data <!DOCTYPE html><!-- X #errors (1,21): eof-in-comment #new-errors (1:22) eof-in-comment #document | <!DOCTYPE html> | <!-- X --> | <html> | <head> | <body> #data <!DOCTYPE html><table><caption>test TEST</caption><td>test #errors (1,54): unexpected-cell-in-table-body (1,58): expected-closing-tag-but-got-eof #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <caption> | "test TEST" | <tbody> | <tr> | <td> | "test" #data <!DOCTYPE html><select><option><optgroup> #errors (1,41): eof-in-select #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <option> | <optgroup> #data <!DOCTYPE html><select><optgroup><option></optgroup><option><select><option> #errors (1,68): unexpected-select-in-select #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <optgroup> | <option> | <option> | <option> #data <!DOCTYPE html><select><optgroup><option><optgroup> #errors (1,51): eof-in-select #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <optgroup> | <option> | <optgroup> #data <!DOCTYPE html><datalist><option>foo</datalist>bar #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <datalist> | <option> | "foo" | "bar" #data <!DOCTYPE html><font><input><input></font> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <font> | <input> | <input> #data <!DOCTYPE html><!-- XXX - XXX --> #errors #document | <!DOCTYPE html> | <!-- XXX - XXX --> | <html> | <head> | <body> #data <!DOCTYPE html><!-- XXX - XXX #errors (1,29): eof-in-comment #new-errors (1:30) eof-in-comment #document | <!DOCTYPE html> | <!-- XXX - XXX --> | <html> | <head> | <body> #data <!DOCTYPE html><!-- XXX - XXX - XXX --> #errors #document | <!DOCTYPE html> | <!-- XXX - XXX - XXX --> | <html> | <head> | <body> #data test test #errors (2,4): expected-doctype-but-got-chars #document | <html> | <head> | <body> | "test test" #data <!DOCTYPE html><body><title>test</body> #errors #document | | | | | | "test</body>" #data <!DOCTYPE html><body><title>X #errors #document | | | | | | "X" | <meta> | name="z" | <link> | rel="foo" | <style> | " x { content:"</style" } " #data <!DOCTYPE html><select><optgroup></optgroup></select> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <optgroup> #data #errors (2,1): expected-doctype-but-got-eof #document | <html> | <head> | <body> #data <!DOCTYPE html> <html> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> #data <!DOCTYPE html><script> </script> <title>x #errors #document | | | | #errors (1,6): expected-doctype-but-got-start-tag (1,21): unexpected-start-tag-out-of-my-head #document | | | #errors (1,6): expected-doctype-but-got-start-tag (1,28): unexpected-start-tag-out-of-my-head (1,52): unexpected-start-tag-out-of-my-head #document | | | #errors (1,6): expected-doctype-but-got-start-tag #document | | | | | "x" | x #errors (1,7): expected-doctype-but-got-start-tag #document | | | --> x #errors (1,7): expected-doctype-but-got-start-tag (1,34): unexpected-end-tag #document | | | x #errors (1,7): expected-doctype-but-got-start-tag #document | | | x #errors (1,7): expected-doctype-but-got-start-tag #document | | | x #errors (1,7): expected-doctype-but-got-start-tag #document | | |

#errors #document | | | | | | ddd #errors (1,6): expected-doctype-but-got-start-tag (1,21): unexpected-start-tag-out-of-my-head #document | | | #errors (1,3): expected-doctype-but-got-start-tag (1,41): adoption-agency-1.3 #document | | | | |
  • | | ", "