parent
b209531959
commit
33ad554800
@ -0,0 +1,6 @@ |
||||
module github.com/xanzy/ssh-agent |
||||
|
||||
require ( |
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2 |
||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0 // indirect |
||||
) |
@ -0,0 +1,4 @@ |
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2 h1:NwxKRvbkH5MsNkvOtPZi3/3kmI8CAzs3mtv+GLQMkNo= |
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= |
||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0 h1:bzeyCHgoAyjZjAhvTpks+qM7sdlh4cCSitmXeCEO3B4= |
||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
@ -0,0 +1,73 @@ |
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.13
|
||||
|
||||
// Package ed25519 implements the Ed25519 signature algorithm. See
|
||||
// https://ed25519.cr.yp.to/.
|
||||
//
|
||||
// These functions are also compatible with the “Ed25519” function defined in
|
||||
// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
|
||||
// representation includes a public key suffix to make multiple signing
|
||||
// operations with the same key more efficient. This package refers to the RFC
|
||||
// 8032 private key as the “seed”.
|
||||
//
|
||||
// Beginning with Go 1.13, the functionality of this package was moved to the
|
||||
// standard library as crypto/ed25519. This package only acts as a compatibility
|
||||
// wrapper.
|
||||
package ed25519 |
||||
|
||||
import ( |
||||
"crypto/ed25519" |
||||
"io" |
||||
) |
||||
|
||||
const ( |
||||
// PublicKeySize is the size, in bytes, of public keys as used in this package.
|
||||
PublicKeySize = 32 |
||||
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
|
||||
PrivateKeySize = 64 |
||||
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
||||
SignatureSize = 64 |
||||
// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
|
||||
SeedSize = 32 |
||||
) |
||||
|
||||
// PublicKey is the type of Ed25519 public keys.
|
||||
//
|
||||
// This type is an alias for crypto/ed25519's PublicKey type.
|
||||
// See the crypto/ed25519 package for the methods on this type.
|
||||
type PublicKey = ed25519.PublicKey |
||||
|
||||
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
|
||||
//
|
||||
// This type is an alias for crypto/ed25519's PrivateKey type.
|
||||
// See the crypto/ed25519 package for the methods on this type.
|
||||
type PrivateKey = ed25519.PrivateKey |
||||
|
||||
// GenerateKey generates a public/private key pair using entropy from rand.
|
||||
// If rand is nil, crypto/rand.Reader will be used.
|
||||
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) { |
||||
return ed25519.GenerateKey(rand) |
||||
} |
||||
|
||||
// NewKeyFromSeed calculates a private key from a seed. It will panic if
|
||||
// len(seed) is not SeedSize. This function is provided for interoperability
|
||||
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
|
||||
// package.
|
||||
func NewKeyFromSeed(seed []byte) PrivateKey { |
||||
return ed25519.NewKeyFromSeed(seed) |
||||
} |
||||
|
||||
// Sign signs the message with privateKey and returns a signature. It will
|
||||
// panic if len(privateKey) is not PrivateKeySize.
|
||||
func Sign(privateKey PrivateKey, message []byte) []byte { |
||||
return ed25519.Sign(privateKey, message) |
||||
} |
||||
|
||||
// Verify reports whether sig is a valid signature of message by publicKey. It
|
||||
// will panic if len(publicKey) is not PublicKeySize.
|
||||
func Verify(publicKey PublicKey, message, sig []byte) bool { |
||||
return ed25519.Verify(publicKey, message, sig) |
||||
} |
@ -0,0 +1,668 @@ |
||||
// Copyright 2019 The Go Authors. All rights reserved. |
||||
// Use of this source code is governed by a BSD-style |
||||
// license that can be found in the LICENSE file. |
||||
|
||||
// Based on CRYPTOGAMS code with the following comment: |
||||
// # ==================================================================== |
||||
// # Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
|
||||
// # project. The module is, however, dual licensed under OpenSSL and |
||||
// # CRYPTOGAMS licenses depending on where you obtain it. For further |
||||
// # details see http://www.openssl.org/~appro/cryptogams/. |
||||
// # ==================================================================== |
||||
|
||||
// Original code can be found at the link below: |
||||
// https://github.com/dot-asm/cryptogams/commit/a60f5b50ed908e91e5c39ca79126a4a876d5d8ff |
||||
|
||||
// There are some differences between CRYPTOGAMS code and this one. The round |
||||
// loop for "_int" isn't the same as the original. Some adjustments were |
||||
// necessary because there are less vector registers available. For example, some |
||||
// X variables (r12, r13, r14, and r15) share the same register used by the |
||||
// counter. The original code uses ctr to name the counter. Here we use CNT |
||||
// because golang uses CTR as the counter register name. |
||||
|
||||
// +build ppc64le,!gccgo,!appengine |
||||
|
||||
#include "textflag.h" |
||||
|
||||
#define OUT R3 |
||||
#define INP R4 |
||||
#define LEN R5 |
||||
#define KEY R6 |
||||
#define CNT R7 |
||||
|
||||
#define TEMP R8 |
||||
|
||||
#define X0 R11 |
||||
#define X1 R12 |
||||
#define X2 R14 |
||||
#define X3 R15 |
||||
#define X4 R16 |
||||
#define X5 R17 |
||||
#define X6 R18 |
||||
#define X7 R19 |
||||
#define X8 R20 |
||||
#define X9 R21 |
||||
#define X10 R22 |
||||
#define X11 R23 |
||||
#define X12 R24 |
||||
#define X13 R25 |
||||
#define X14 R26 |
||||
#define X15 R27 |
||||
|
||||
#define CON0 X0 |
||||
#define CON1 X1 |
||||
#define CON2 X2 |
||||
#define CON3 X3 |
||||
|
||||
#define KEY0 X4 |
||||
#define KEY1 X5 |
||||
#define KEY2 X6 |
||||
#define KEY3 X7 |
||||
#define KEY4 X8 |
||||
#define KEY5 X9 |
||||
#define KEY6 X10 |
||||
#define KEY7 X11 |
||||
|
||||
#define CNT0 X12 |
||||
#define CNT1 X13 |
||||
#define CNT2 X14 |
||||
#define CNT3 X15 |
||||
|
||||
#define TMP0 R9 |
||||
#define TMP1 R10 |
||||
#define TMP2 R28 |
||||
#define TMP3 R29 |
||||
|
||||
#define CONSTS R8 |
||||
|
||||
#define A0 V0 |
||||
#define B0 V1 |
||||
#define C0 V2 |
||||
#define D0 V3 |
||||
#define A1 V4 |
||||
#define B1 V5 |
||||
#define C1 V6 |
||||
#define D1 V7 |
||||
#define A2 V8 |
||||
#define B2 V9 |
||||
#define C2 V10 |
||||
#define D2 V11 |
||||
#define T0 V12 |
||||
#define T1 V13 |
||||
#define T2 V14 |
||||
|
||||
#define K0 V15 |
||||
#define K1 V16 |
||||
#define K2 V17 |
||||
#define K3 V18 |
||||
#define K4 V19 |
||||
#define K5 V20 |
||||
|
||||
#define FOUR V21 |
||||
#define SIXTEEN V22 |
||||
#define TWENTY4 V23 |
||||
#define TWENTY V24 |
||||
#define TWELVE V25 |
||||
#define TWENTY5 V26 |
||||
#define SEVEN V27 |
||||
|
||||
#define INPPERM V28 |
||||
#define OUTPERM V29 |
||||
#define OUTMASK V30 |
||||
|
||||
#define DD0 V31 |
||||
#define DD1 SEVEN |
||||
#define DD2 T0 |
||||
#define DD3 T1 |
||||
#define DD4 T2 |
||||
|
||||
DATA ·consts+0x00(SB)/8, $0x3320646e61707865 |
||||
DATA ·consts+0x08(SB)/8, $0x6b20657479622d32 |
||||
DATA ·consts+0x10(SB)/8, $0x0000000000000001 |
||||
DATA ·consts+0x18(SB)/8, $0x0000000000000000 |
||||
DATA ·consts+0x20(SB)/8, $0x0000000000000004 |
||||
DATA ·consts+0x28(SB)/8, $0x0000000000000000 |
||||
DATA ·consts+0x30(SB)/8, $0x0a0b08090e0f0c0d |
||||
DATA ·consts+0x38(SB)/8, $0x0203000106070405 |
||||
DATA ·consts+0x40(SB)/8, $0x090a0b080d0e0f0c |
||||
DATA ·consts+0x48(SB)/8, $0x0102030005060704 |
||||
GLOBL ·consts(SB), RODATA, $80 |
||||
|
||||
//func chaCha20_ctr32_vmx(out, inp *byte, len int, key *[32]byte, counter *[16]byte) |
||||
TEXT ·chaCha20_ctr32_vmx(SB),NOSPLIT|NOFRAME,$0 |
||||
// Load the arguments inside the registers |
||||
MOVD out+0(FP), OUT |
||||
MOVD inp+8(FP), INP |
||||
MOVD len+16(FP), LEN |
||||
MOVD key+24(FP), KEY |
||||
MOVD counter+32(FP), CNT |
||||
|
||||
MOVD $·consts(SB), CONSTS // point to consts addr |
||||
|
||||
MOVD $16, X0 |
||||
MOVD $32, X1 |
||||
MOVD $48, X2 |
||||
MOVD $64, X3 |
||||
MOVD $31, X4 |
||||
MOVD $15, X5 |
||||
|
||||
// Load key |
||||
LVX (KEY)(R0), K1 |
||||
LVSR (KEY)(R0), T0 |
||||
LVX (KEY)(X0), K2 |
||||
LVX (KEY)(X4), DD0 |
||||
|
||||
// Load counter |
||||
LVX (CNT)(R0), K3 |
||||
LVSR (CNT)(R0), T1 |
||||
LVX (CNT)(X5), DD1 |
||||
|
||||
// Load constants |
||||
LVX (CONSTS)(R0), K0 |
||||
LVX (CONSTS)(X0), K5 |
||||
LVX (CONSTS)(X1), FOUR |
||||
LVX (CONSTS)(X2), SIXTEEN |
||||
LVX (CONSTS)(X3), TWENTY4 |
||||
|
||||
// Align key and counter |
||||
VPERM K2, K1, T0, K1 |
||||
VPERM DD0, K2, T0, K2 |
||||
VPERM DD1, K3, T1, K3 |
||||
|
||||
// Load counter to GPR |
||||
MOVWZ 0(CNT), CNT0 |
||||
MOVWZ 4(CNT), CNT1 |
||||
MOVWZ 8(CNT), CNT2 |
||||
MOVWZ 12(CNT), CNT3 |
||||
|
||||
// Adjust vectors for the initial state |
||||
VADDUWM K3, K5, K3 |
||||
VADDUWM K3, K5, K4 |
||||
VADDUWM K4, K5, K5 |
||||
|
||||
// Synthesized constants |
||||
VSPLTISW $-12, TWENTY |
||||
VSPLTISW $12, TWELVE |
||||
VSPLTISW $-7, TWENTY5 |
||||
|
||||
VXOR T0, T0, T0 |
||||
VSPLTISW $-1, OUTMASK |
||||
LVSR (INP)(R0), INPPERM |
||||
LVSL (OUT)(R0), OUTPERM |
||||
VPERM OUTMASK, T0, OUTPERM, OUTMASK |
||||
|
||||
loop_outer_vmx: |
||||
// Load constant |
||||
MOVD $0x61707865, CON0 |
||||
MOVD $0x3320646e, CON1 |
||||
MOVD $0x79622d32, CON2 |
||||
MOVD $0x6b206574, CON3 |
||||
|
||||
VOR K0, K0, A0 |
||||
VOR K0, K0, A1 |
||||
VOR K0, K0, A2 |
||||
VOR K1, K1, B0 |
||||
|
||||
MOVD $10, TEMP |
||||
|
||||
// Load key to GPR |
||||
MOVWZ 0(KEY), X4 |
||||
MOVWZ 4(KEY), X5 |
||||
MOVWZ 8(KEY), X6 |
||||
MOVWZ 12(KEY), X7 |
||||
VOR K1, K1, B1 |
||||
VOR K1, K1, B2 |
||||
MOVWZ 16(KEY), X8 |
||||
MOVWZ 0(CNT), X12 |
||||
MOVWZ 20(KEY), X9 |
||||
MOVWZ 4(CNT), X13 |
||||
VOR K2, K2, C0 |
||||
VOR K2, K2, C1 |
||||
MOVWZ 24(KEY), X10 |
||||
MOVWZ 8(CNT), X14 |
||||
VOR K2, K2, C2 |
||||
VOR K3, K3, D0 |
||||
MOVWZ 28(KEY), X11 |
||||
MOVWZ 12(CNT), X15 |
||||
VOR K4, K4, D1 |
||||
VOR K5, K5, D2 |
||||
|
||||
MOVD X4, TMP0 |
||||
MOVD X5, TMP1 |
||||
MOVD X6, TMP2 |
||||
MOVD X7, TMP3 |
||||
VSPLTISW $7, SEVEN |
||||
|
||||
MOVD TEMP, CTR |
||||
|
||||
loop_vmx: |
||||
// CRYPTOGAMS uses a macro to create a loop using perl. This isn't possible |
||||
// using assembly macros. Therefore, the macro expansion result was used |
||||
// in order to maintain the algorithm efficiency. |
||||
// This loop generates three keystream blocks using VMX instructions and, |
||||
// in parallel, one keystream block using scalar instructions. |
||||
ADD X4, X0, X0 |
||||
ADD X5, X1, X1 |
||||
VADDUWM A0, B0, A0 |
||||
VADDUWM A1, B1, A1 |
||||
ADD X6, X2, X2 |
||||
ADD X7, X3, X3 |
||||
VADDUWM A2, B2, A2 |
||||
VXOR D0, A0, D0 |
||||
XOR X0, X12, X12 |
||||
XOR X1, X13, X13 |
||||
VXOR D1, A1, D1 |
||||
VXOR D2, A2, D2 |
||||
XOR X2, X14, X14 |
||||
XOR X3, X15, X15 |
||||
VPERM D0, D0, SIXTEEN, D0 |
||||
VPERM D1, D1, SIXTEEN, D1 |
||||
ROTLW $16, X12, X12 |
||||
ROTLW $16, X13, X13 |
||||
VPERM D2, D2, SIXTEEN, D2 |
||||
VADDUWM C0, D0, C0 |
||||
ROTLW $16, X14, X14 |
||||
ROTLW $16, X15, X15 |
||||
VADDUWM C1, D1, C1 |
||||
VADDUWM C2, D2, C2 |
||||
ADD X12, X8, X8 |
||||
ADD X13, X9, X9 |
||||
VXOR B0, C0, T0 |
||||
VXOR B1, C1, T1 |
||||
ADD X14, X10, X10 |
||||
ADD X15, X11, X11 |
||||
VXOR B2, C2, T2 |
||||
VRLW T0, TWELVE, B0 |
||||
XOR X8, X4, X4 |
||||
XOR X9, X5, X5 |
||||
VRLW T1, TWELVE, B1 |
||||
VRLW T2, TWELVE, B2 |
||||
XOR X10, X6, X6 |
||||
XOR X11, X7, X7 |
||||
VADDUWM A0, B0, A0 |
||||
VADDUWM A1, B1, A1 |
||||
ROTLW $12, X4, X4 |
||||
ROTLW $12, X5, X5 |
||||
VADDUWM A2, B2, A2 |
||||
VXOR D0, A0, D0 |
||||
ROTLW $12, X6, X6 |
||||
ROTLW $12, X7, X7 |
||||
VXOR D1, A1, D1 |
||||
VXOR D2, A2, D2 |
||||
ADD X4, X0, X0 |
||||
ADD X5, X1, X1 |
||||
VPERM D0, D0, TWENTY4, D0 |
||||
VPERM D1, D1, TWENTY4, D1 |
||||
ADD X6, X2, X2 |
||||
ADD X7, X3, X3 |
||||
VPERM D2, D2, TWENTY4, D2 |
||||
VADDUWM C0, D0, C0 |
||||
XOR X0, X12, X12 |
||||
XOR X1, X13, X13 |
||||
VADDUWM C1, D1, C1 |
||||
VADDUWM C2, D2, C2 |
||||
XOR X2, X14, X14 |
||||
XOR X3, X15, X15 |
||||
VXOR B0, C0, T0 |
||||
VXOR B1, C1, T1 |
||||
ROTLW $8, X12, X12 |
||||
ROTLW $8, X13, X13 |
||||
VXOR B2, C2, T2 |
||||
VRLW T0, SEVEN, B0 |
||||
ROTLW $8, X14, X14 |
||||
ROTLW $8, X15, X15 |
||||
VRLW T1, SEVEN, B1 |
||||
VRLW T2, SEVEN, B2 |
||||
ADD X12, X8, X8 |
||||
ADD X13, X9, X9 |
||||
VSLDOI $8, C0, C0, C0 |
||||
VSLDOI $8, C1, C1, C1 |
||||
ADD X14, X10, X10 |
||||
ADD X15, X11, X11 |
||||
VSLDOI $8, C2, C2, C2 |
||||
VSLDOI $12, B0, B0, B0 |
||||
XOR X8, X4, X4 |
||||
XOR X9, X5, X5 |
||||
VSLDOI $12, B1, B1, B1 |
||||
VSLDOI $12, B2, B2, B2 |
||||
XOR X10, X6, X6 |
||||
XOR X11, X7, X7 |
||||
VSLDOI $4, D0, D0, D0 |
||||
VSLDOI $4, D1, D1, D1 |
||||
ROTLW $7, X4, X4 |
||||
ROTLW $7, X5, X5 |
||||
VSLDOI $4, D2, D2, D2 |
||||
VADDUWM A0, B0, A0 |
||||
ROTLW $7, X6, X6 |
||||
ROTLW $7, X7, X7 |
||||
VADDUWM A1, B1, A1 |
||||
VADDUWM A2, B2, A2 |
||||
ADD X5, X0, X0 |
||||
ADD X6, X1, X1 |
||||
VXOR D0, A0, D0 |
||||
VXOR D1, A1, D1 |
||||
ADD X7, X2, X2 |
||||
ADD X4, X3, X3 |
||||
VXOR D2, A2, D2 |
||||
VPERM D0, D0, SIXTEEN, D0 |
||||
XOR X0, X15, X15 |
||||
XOR X1, X12, X12 |
||||
VPERM D1, D1, SIXTEEN, D1 |
||||
VPERM D2, D2, SIXTEEN, D2 |
||||
XOR X2, X13, X13 |
||||
XOR X3, X14, X14 |
||||
VADDUWM C0, D0, C0 |
||||
VADDUWM C1, D1, C1 |
||||
ROTLW $16, X15, X15 |
||||
ROTLW $16, X12, X12 |
||||
VADDUWM C2, D2, C2 |
||||
VXOR B0, C0, T0 |
||||
ROTLW $16, X13, X13 |
||||
ROTLW $16, X14, X14 |
||||
VXOR B1, C1, T1 |
||||
VXOR B2, C2, T2 |
||||
ADD X15, X10, X10 |
||||
ADD X12, X11, X11 |
||||
VRLW T0, TWELVE, B0 |
||||
VRLW T1, TWELVE, B1 |
||||
ADD X13, X8, X8 |
||||
ADD X14, X9, X9 |
||||
VRLW T2, TWELVE, B2 |
||||
VADDUWM A0, B0, A0 |
||||
XOR X10, X5, X5 |
||||
XOR X11, X6, X6 |
||||
VADDUWM A1, B1, A1 |
||||
VADDUWM A2, B2, A2 |
||||
XOR X8, X7, X7 |
||||
XOR X9, X4, X4 |
||||
VXOR D0, A0, D0 |
||||
VXOR D1, A1, D1 |
||||
ROTLW $12, X5, X5 |
||||
ROTLW $12, X6, X6 |
||||
VXOR D2, A2, D2 |
||||
VPERM D0, D0, TWENTY4, D0 |
||||
ROTLW $12, X7, X7 |
||||
ROTLW $12, X4, X4 |
||||
VPERM D1, D1, TWENTY4, D1 |
||||
VPERM D2, D2, TWENTY4, D2 |
||||
ADD X5, X0, X0 |
||||
ADD X6, X1, X1 |
||||
VADDUWM C0, D0, C0 |
||||
VADDUWM C1, D1, C1 |
||||
ADD X7, X2, X2 |
||||
ADD X4, X3, X3 |
||||
VADDUWM C2, D2, C2 |
||||
VXOR B0, C0, T0 |
||||
XOR X0, X15, X15 |
||||
XOR X1, X12, X12 |
||||
VXOR B1, C1, T1 |
||||
VXOR B2, C2, T2 |
||||
XOR X2, X13, X13 |
||||
XOR X3, X14, X14 |
||||
VRLW T0, SEVEN, B0 |
||||
VRLW T1, SEVEN, B1 |
||||
ROTLW $8, X15, X15 |
||||
ROTLW $8, X12, X12 |
||||
VRLW T2, SEVEN, B2 |
||||
VSLDOI $8, C0, C0, C0 |
||||
ROTLW $8, X13, X13 |
||||
ROTLW $8, X14, X14 |
||||
VSLDOI $8, C1, C1, C1 |
||||
VSLDOI $8, C2, C2, C2 |
||||
ADD X15, X10, X10 |
||||
ADD X12, X11, X11 |
||||
VSLDOI $4, B0, B0, B0 |
||||
VSLDOI $4, B1, B1, B1 |
||||
ADD X13, X8, X8 |
||||
ADD X14, X9, X9 |
||||
VSLDOI $4, B2, B2, B2 |
||||
VSLDOI $12, D0, D0, D0 |
||||
XOR X10, X5, X5 |
||||
XOR X11, X6, X6 |
||||
VSLDOI $12, D1, D1, D1 |
||||
VSLDOI $12, D2, D2, D2 |
||||
XOR X8, X7, X7 |
||||
XOR X9, X4, X4 |
||||
ROTLW $7, X5, X5 |
||||
ROTLW $7, X6, X6 |
||||
ROTLW $7, X7, X7 |
||||
ROTLW $7, X4, X4 |
||||
BC 0x10, 0, loop_vmx |
||||
|
||||
SUB $256, LEN, LEN |
||||
|
||||
// Accumulate key block |
||||
ADD $0x61707865, X0, X0 |
||||
ADD $0x3320646e, X1, X1 |
||||
ADD $0x79622d32, X2, X2 |
||||
ADD $0x6b206574, X3, X3 |
||||
ADD TMP0, X4, X4 |
||||
ADD TMP1, X5, X5 |
||||
ADD TMP2, X6, X6 |
||||
ADD TMP3, X7, X7 |
||||
MOVWZ 16(KEY), TMP0 |
||||
MOVWZ 20(KEY), TMP1 |
||||
MOVWZ 24(KEY), TMP2 |
||||
MOVWZ 28(KEY), TMP3 |
||||
ADD TMP0, X8, X8 |
||||
ADD TMP1, X9, X9 |
||||
ADD TMP2, X10, X10 |
||||
ADD TMP3, X11, X11 |
||||
|
||||
MOVWZ 12(CNT), TMP0 |
||||
MOVWZ 8(CNT), TMP1 |
||||
MOVWZ 4(CNT), TMP2 |
||||
MOVWZ 0(CNT), TEMP |
||||
ADD TMP0, X15, X15 |
||||
ADD TMP1, X14, X14 |
||||
ADD TMP2, X13, X13 |
||||
ADD TEMP, X12, X12 |
||||
|
||||
// Accumulate key block |
||||
VADDUWM A0, K0, A0 |
||||
VADDUWM A1, K0, A1 |
||||
VADDUWM A2, K0, A2 |
||||
VADDUWM B0, K1, B0 |
||||
VADDUWM B1, K1, B1 |
||||
VADDUWM B2, K1, B2 |
||||
VADDUWM C0, K2, C0 |
||||
VADDUWM C1, K2, C1 |
||||
VADDUWM C2, K2, C2 |
||||
VADDUWM D0, K3, D0 |
||||
VADDUWM D1, K4, D1 |
||||
VADDUWM D2, K5, D2 |
||||
|
||||
// Increment counter |
||||
ADD $4, TEMP, TEMP |
||||
MOVW TEMP, 0(CNT) |
||||
|
||||
VADDUWM K3, FOUR, K3 |
||||
VADDUWM K4, FOUR, K4 |
||||
VADDUWM K5, FOUR, K5 |
||||
|
||||
// XOR the input slice (INP) with the keystream, which is stored in GPRs (X0-X3). |
||||
|
||||
// Load input (aligned or not) |
||||
MOVWZ 0(INP), TMP0 |
||||
MOVWZ 4(INP), TMP1 |
||||
MOVWZ 8(INP), TMP2 |
||||
MOVWZ 12(INP), TMP3 |
||||
|
||||
// XOR with input |
||||
XOR TMP0, X0, X0 |
||||
XOR TMP1, X1, X1 |
||||
XOR TMP2, X2, X2 |
||||
XOR TMP3, X3, X3 |
||||
MOVWZ 16(INP), TMP0 |
||||
MOVWZ 20(INP), TMP1 |
||||
MOVWZ 24(INP), TMP2 |
||||
MOVWZ 28(INP), TMP3 |
||||
XOR TMP0, X4, X4 |
||||
XOR TMP1, X5, X5 |
||||
XOR TMP2, X6, X6 |
||||
XOR TMP3, X7, X7 |
||||
MOVWZ 32(INP), TMP0 |
||||
MOVWZ 36(INP), TMP1 |
||||
MOVWZ 40(INP), TMP2 |
||||
MOVWZ 44(INP), TMP3 |
||||
XOR TMP0, X8, X8 |
||||
XOR TMP1, X9, X9 |
||||
XOR TMP2, X10, X10 |
||||
XOR TMP3, X11, X11 |
||||
MOVWZ 48(INP), TMP0 |
||||
MOVWZ 52(INP), TMP1 |
||||
MOVWZ 56(INP), TMP2 |
||||
MOVWZ 60(INP), TMP3 |
||||
XOR TMP0, X12, X12 |
||||
XOR TMP1, X13, X13 |
||||
XOR TMP2, X14, X14 |
||||
XOR TMP3, X15, X15 |
||||
|
||||
// Store output (aligned or not) |
||||
MOVW X0, 0(OUT) |
||||
MOVW X1, 4(OUT) |
||||
MOVW X2, 8(OUT) |
||||
MOVW X3, 12(OUT) |
||||
|
||||
ADD $64, INP, INP // INP points to the end of the slice for the alignment code below |
||||
|
||||
MOVW X4, 16(OUT) |
||||
MOVD $16, TMP0 |
||||
MOVW X5, 20(OUT) |
||||
MOVD $32, TMP1 |
||||
MOVW X6, 24(OUT) |
||||
MOVD $48, TMP2 |
||||
MOVW X7, 28(OUT) |
||||
MOVD $64, TMP3 |
||||
MOVW X8, 32(OUT) |
||||
MOVW X9, 36(OUT) |
||||
MOVW X10, 40(OUT) |
||||
MOVW X11, 44(OUT) |
||||
MOVW X12, 48(OUT) |
||||
MOVW X13, 52(OUT) |
||||
MOVW X14, 56(OUT) |
||||
MOVW X15, 60(OUT) |
||||
ADD $64, OUT, OUT |
||||
|
||||
// Load input |
||||
LVX (INP)(R0), DD0 |
||||
LVX (INP)(TMP0), DD1 |
||||
LVX (INP)(TMP1), DD2 |
||||
LVX (INP)(TMP2), DD3 |
||||
LVX (INP)(TMP3), DD4 |
||||
ADD $64, INP, INP |
||||
|
||||
VPERM DD1, DD0, INPPERM, DD0 // Align input |
||||
VPERM DD2, DD1, INPPERM, DD1 |
||||
VPERM DD3, DD2, INPPERM, DD2 |
||||
VPERM DD4, DD3, INPPERM, DD3 |
||||
VXOR A0, DD0, A0 // XOR with input |
||||
VXOR B0, DD1, B0 |
||||
LVX (INP)(TMP0), DD1 // Keep loading input |
||||
VXOR C0, DD2, C0 |
||||
LVX (INP)(TMP1), DD2 |
||||
VXOR D0, DD3, D0 |
||||
LVX (INP)(TMP2), DD3 |
||||
LVX (INP)(TMP3), DD0 |
||||
ADD $64, INP, INP |
||||
MOVD $63, TMP3 // 63 is not a typo |
||||
VPERM A0, A0, OUTPERM, A0 |
||||
VPERM B0, B0, OUTPERM, B0 |
||||
VPERM C0, C0, OUTPERM, C0 |
||||
VPERM D0, D0, OUTPERM, D0 |
||||
|
||||
VPERM DD1, DD4, INPPERM, DD4 // Align input |
||||
VPERM DD2, DD1, INPPERM, DD1 |
||||
VPERM DD3, DD2, INPPERM, DD2 |
||||
VPERM DD0, DD3, INPPERM, DD3 |
||||
VXOR A1, DD4, A1 |
||||
VXOR B1, DD1, B1 |
||||
LVX (INP)(TMP0), DD1 // Keep loading |
||||
VXOR C1, DD2, C1 |
||||
LVX (INP)(TMP1), DD2 |
||||
VXOR D1, DD3, D1 |
||||
LVX (INP)(TMP2), DD3 |
||||
|
||||
// Note that the LVX address is always rounded down to the nearest 16-byte |
||||
// boundary, and that it always points to at most 15 bytes beyond the end of |
||||
// the slice, so we cannot cross a page boundary. |
||||
LVX (INP)(TMP3), DD4 // Redundant in aligned case. |
||||
ADD $64, INP, INP |
||||
VPERM A1, A1, OUTPERM, A1 // Pre-misalign output |
||||
VPERM B1, B1, OUTPERM, B1 |
||||
VPERM C1, C1, OUTPERM, C1 |
||||
VPERM D1, D1, OUTPERM, D1 |
||||
|
||||
VPERM DD1, DD0, INPPERM, DD0 // Align Input |
||||
VPERM DD2, DD1, INPPERM, DD1 |
||||
VPERM DD3, DD2, INPPERM, DD2 |
||||
VPERM DD4, DD3, INPPERM, DD3 |
||||
VXOR A2, DD0, A2 |
||||
VXOR B2, DD1, B2 |
||||
VXOR C2, DD2, C2 |
||||
VXOR D2, DD3, D2 |
||||
VPERM A2, A2, OUTPERM, A2 |
||||
VPERM B2, B2, OUTPERM, B2 |
||||
VPERM C2, C2, OUTPERM, C2 |
||||
VPERM D2, D2, OUTPERM, D2 |
||||
|
||||
ANDCC $15, OUT, X1 // Is out aligned? |
||||
MOVD OUT, X0 |
||||
|
||||
VSEL A0, B0, OUTMASK, DD0 // Collect pre-misaligned output |
||||
VSEL B0, C0, OUTMASK, DD1 |
||||
VSEL C0, D0, OUTMASK, DD2 |
||||
VSEL D0, A1, OUTMASK, DD3 |
||||
VSEL A1, B1, OUTMASK, B0 |
||||
VSEL B1, C1, OUTMASK, C0 |
||||
VSEL C1, D1, OUTMASK, D0 |
||||
VSEL D1, A2, OUTMASK, A1 |
||||
VSEL A2, B2, OUTMASK, B1 |
||||
VSEL B2, C2, OUTMASK, C1 |
||||
VSEL C2, D2, OUTMASK, D1 |
||||
|
||||
STVX DD0, (OUT+TMP0) |
||||
STVX DD1, (OUT+TMP1) |
||||
STVX DD2, (OUT+TMP2) |
||||
ADD $64, OUT, OUT |
||||
STVX DD3, (OUT+R0) |
||||
STVX B0, (OUT+TMP0) |
||||
STVX C0, (OUT+TMP1) |
||||
STVX D0, (OUT+TMP2) |
||||
ADD $64, OUT, OUT |
||||
STVX A1, (OUT+R0) |
||||
STVX B1, (OUT+TMP0) |
||||
STVX C1, (OUT+TMP1) |
||||
STVX D1, (OUT+TMP2) |
||||
ADD $64, OUT, OUT |
||||
|
||||
BEQ aligned_vmx |
||||
|
||||
SUB X1, OUT, X2 // in misaligned case edges |
||||
MOVD $0, X3 // are written byte-by-byte |
||||
|
||||
unaligned_tail_vmx: |
||||
STVEBX D2, (X2+X3) |
||||
ADD $1, X3, X3 |
||||
CMPW X3, X1 |
||||
BNE unaligned_tail_vmx |
||||
SUB X1, X0, X2 |
||||
|
||||
unaligned_head_vmx: |
||||
STVEBX A0, (X2+X1) |
||||
CMPW X1, $15 |
||||
ADD $1, X1, X1 |
||||
BNE unaligned_head_vmx |
||||
|
||||
CMPU LEN, $255 // done with 256-byte block yet? |
||||
BGT loop_outer_vmx |
||||
|
||||
JMP done_vmx |
||||
|
||||
aligned_vmx: |
||||
STVX A0, (X0+R0) |
||||
CMPU LEN, $255 // done with 256-byte block yet? |
||||
BGT loop_outer_vmx |
||||
|
||||
done_vmx: |
||||
RET |
@ -0,0 +1,52 @@ |
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ppc64le,!gccgo,!appengine
|
||||
|
||||
package chacha20 |
||||
|
||||
import "encoding/binary" |
||||
|
||||
const ( |
||||
bufSize = 256 |
||||
haveAsm = true |
||||
) |
||||
|
||||
//go:noescape
|
||||
func chaCha20_ctr32_vmx(out, inp *byte, len int, key *[8]uint32, counter *uint32) |
||||
|
||||
func (c *Cipher) xorKeyStreamAsm(dst, src []byte) { |
||||
if len(src) >= bufSize { |
||||
chaCha20_ctr32_vmx(&dst[0], &src[0], len(src)-len(src)%bufSize, &c.key, &c.counter) |
||||
} |
||||
if len(src)%bufSize != 0 { |
||||
chaCha20_ctr32_vmx(&c.buf[0], &c.buf[0], bufSize, &c.key, &c.counter) |
||||
start := len(src) - len(src)%bufSize |
||||
ts, td, tb := src[start:], dst[start:], c.buf[:] |
||||
// Unroll loop to XOR 32 bytes per iteration.
|
||||
for i := 0; i < len(ts)-32; i += 32 { |
||||
td, tb = td[:len(ts)], tb[:len(ts)] // bounds check elimination
|
||||
s0 := binary.LittleEndian.Uint64(ts[0:8]) |
||||
s1 := binary.LittleEndian.Uint64(ts[8:16]) |
||||
s2 := binary.LittleEndian.Uint64(ts[16:24]) |
||||
s3 := binary.LittleEndian.Uint64(ts[24:32]) |
||||
b0 := binary.LittleEndian.Uint64(tb[0:8]) |
||||
b1 := binary.LittleEndian.Uint64(tb[8:16]) |
||||
b2 := binary.LittleEndian.Uint64(tb[16:24]) |
||||
b3 := binary.LittleEndian.Uint64(tb[24:32]) |
||||
binary.LittleEndian.PutUint64(td[0:8], s0^b0) |
||||
binary.LittleEndian.PutUint64(td[8:16], s1^b1) |
||||
binary.LittleEndian.PutUint64(td[16:24], s2^b2) |
||||
binary.LittleEndian.PutUint64(td[24:32], s3^b3) |
||||
ts, td, tb = ts[32:], td[32:], tb[32:] |
||||
} |
||||
td, tb = td[:len(ts)], tb[:len(ts)] // bounds check elimination
|
||||
for i, v := range ts { |
||||
td[i] = tb[i] ^ v |
||||
} |
||||
c.len = bufSize - (len(src) % bufSize) |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,68 @@ |
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ppc64le,!gccgo,!appengine
|
||||
|
||||
package poly1305 |
||||
|
||||
//go:noescape
|
||||
func initialize(state *[7]uint64, key *[32]byte) |
||||
|
||||
//go:noescape
|
||||
func update(state *[7]uint64, msg []byte) |
||||
|
||||
//go:noescape
|
||||
func finalize(tag *[TagSize]byte, state *[7]uint64) |
||||
|
||||
// Sum generates an authenticator for m using a one-time key and puts the
|
||||
// 16-byte result into out. Authenticating two different messages with the same
|
||||
// key allows an attacker to forge messages at will.
|
||||
func Sum(out *[16]byte, m []byte, key *[32]byte) { |
||||
h := newMAC(key) |
||||
h.Write(m) |
||||
h.Sum(out) |
||||
} |
||||
|
||||
func newMAC(key *[32]byte) (h mac) { |
||||
initialize(&h.state, key) |
||||
return |
||||
} |
||||
|
||||
type mac struct { |
||||
state [7]uint64 // := uint64{ h0, h1, h2, r0, r1, pad0, pad1 }
|
||||
|
||||
buffer [TagSize]byte |
||||
offset int |
||||
} |
||||
|
||||
func (h *mac) Write(p []byte) (n int, err error) { |
||||
n = len(p) |
||||
if h.offset > 0 { |
||||
remaining := TagSize - h.offset |
||||
if n < remaining { |
||||
h.offset += copy(h.buffer[h.offset:], p) |
||||
return n, nil |
||||
} |
||||
copy(h.buffer[h.offset:], p[:remaining]) |
||||
p = p[remaining:] |
||||
h.offset = 0 |
||||
update(&h.state, h.buffer[:]) |
||||
} |
||||
if nn := len(p) - (len(p) % TagSize); nn > 0 { |
||||
update(&h.state, p[:nn]) |
||||
p = p[nn:] |
||||
} |
||||
if len(p) > 0 { |
||||
h.offset += copy(h.buffer[h.offset:], p) |
||||
} |
||||
return n, nil |
||||
} |
||||
|
||||
func (h *mac) Sum(out *[16]byte) { |
||||
state := h.state |
||||
if h.offset > 0 { |
||||
update(&state, h.buffer[:h.offset]) |
||||
} |
||||
finalize(out, &state) |
||||
} |
@ -0,0 +1,247 @@ |
||||
// Copyright 2019 The Go Authors. All rights reserved. |
||||
// Use of this source code is governed by a BSD-style |
||||
// license that can be found in the LICENSE file. |
||||
|
||||
// +build ppc64le,!gccgo,!appengine |
||||
|
||||
#include "textflag.h" |
||||
|
||||
// This was ported from the amd64 implementation. |
||||
|
||||
#define POLY1305_ADD(msg, h0, h1, h2, t0, t1, t2) \ |
||||
MOVD (msg), t0; \
|
||||
MOVD 8(msg), t1; \
|
||||
MOVD $1, t2; \
|
||||
ADDC t0, h0, h0; \
|
||||
ADDE t1, h1, h1; \
|
||||
ADDE t2, h2; \
|
||||
ADD $16, msg |
||||
|
||||
#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3, t4, t5) \ |
||||
MULLD r0, h0, t0; \
|
||||
MULLD r0, h1, t4; \
|
||||
MULHDU r0, h0, t1; \
|
||||
MULHDU r0, h1, t5; \
|
||||
ADDC t4, t1, t1; \
|
||||
MULLD r0, h2, t2; \
|
||||
ADDZE t5; \
|
||||
MULHDU r1, h0, t4; \
|
||||
MULLD r1, h0, h0; \
|
||||
ADD t5, t2, t2; \
|
||||
ADDC h0, t1, t1; \
|
||||
MULLD h2, r1, t3; \
|
||||
ADDZE t4, h0; \
|
||||
MULHDU r1, h1, t5; \
|
||||
MULLD r1, h1, t4; \
|
||||
ADDC t4, t2, t2; \
|
||||
ADDE t5, t3, t3; \
|
||||
ADDC h0, t2, t2; \
|
||||
MOVD $-4, t4; \
|
||||
MOVD t0, h0; \
|
||||
MOVD t1, h1; \
|
||||
ADDZE t3; \
|
||||
ANDCC $3, t2, h2; \
|
||||
AND t2, t4, t0; \
|
||||
ADDC t0, h0, h0; \
|
||||
ADDE t3, h1, h1; \
|
||||
SLD $62, t3, t4; \
|
||||
SRD $2, t2; \
|
||||
ADDZE h2; \
|
||||
OR t4, t2, t2; \
|
||||
SRD $2, t3; \
|
||||
ADDC t2, h0, h0; \
|
||||
ADDE t3, h1, h1; \
|
||||
ADDZE h2 |
||||
|
||||
DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF |
||||
DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC |
||||
GLOBL ·poly1305Mask<>(SB), RODATA, $16 |
||||
|
||||
// func update(state *[7]uint64, msg []byte) |
||||
|
||||
TEXT ·update(SB), $0-32 |
||||
MOVD state+0(FP), R3 |
||||
MOVD msg_base+8(FP), R4 |
||||
MOVD msg_len+16(FP), R5 |
||||
|
||||
MOVD 0(R3), R8 // h0 |
||||
MOVD 8(R3), R9 // h1 |
||||
MOVD 16(R3), R10 // h2 |
||||
MOVD 24(R3), R11 // r0 |
||||
MOVD 32(R3), R12 // r1 |
||||
|
||||
CMP R5, $16 |
||||
BLT bytes_between_0_and_15 |
||||
|
||||
loop: |
||||
POLY1305_ADD(R4, R8, R9, R10, R20, R21, R22) |
||||
|
||||
multiply: |
||||
POLY1305_MUL(R8, R9, R10, R11, R12, R16, R17, R18, R14, R20, R21) |
||||
ADD $-16, R5 |
||||
CMP R5, $16 |
||||
BGE loop |
||||
|
||||
bytes_between_0_and_15: |
||||
CMP $0, R5 |
||||
BEQ done |
||||
MOVD $0, R16 // h0 |
||||
MOVD $0, R17 // h1 |
||||
|
||||
flush_buffer: |
||||
CMP R5, $8 |
||||
BLE just1 |
||||
|
||||
MOVD $8, R21 |
||||
SUB R21, R5, R21 |
||||
|
||||
// Greater than 8 -- load the rightmost remaining bytes in msg |
||||
// and put into R17 (h1) |
||||
MOVD (R4)(R21), R17 |
||||
MOVD $16, R22 |
||||
|
||||
// Find the offset to those bytes |
||||
SUB R5, R22, R22 |
||||
SLD $3, R22 |
||||
|
||||
// Shift to get only the bytes in msg |
||||
SRD R22, R17, R17 |
||||
|
||||
// Put 1 at high end |
||||
MOVD $1, R23 |
||||
SLD $3, R21 |
||||
SLD R21, R23, R23 |
||||
OR R23, R17, R17 |
||||
|
||||
// Remainder is 8 |
||||
MOVD $8, R5 |
||||
|
||||
just1: |
||||
CMP R5, $8 |
||||
BLT less8 |
||||
|
||||
// Exactly 8 |
||||
MOVD (R4), R16 |
||||
|
||||
CMP $0, R17 |
||||
|
||||
// Check if we've already set R17; if not
|
||||
// set 1 to indicate end of msg. |
||||
BNE carry |
||||
MOVD $1, R17 |
||||
BR carry |
||||
|
||||
less8: |
||||
MOVD $0, R16 // h0 |
||||
MOVD $0, R22 // shift count |
||||
CMP R5, $4 |
||||
BLT less4 |
||||
MOVWZ (R4), R16 |
||||
ADD $4, R4 |
||||
ADD $-4, R5 |
||||
MOVD $32, R22 |
||||
|
||||
less4: |
||||
CMP R5, $2 |
||||
BLT less2 |
||||
MOVHZ (R4), R21 |
||||
SLD R22, R21, R21 |
||||
OR R16, R21, R16 |
||||
ADD $16, R22 |
||||
ADD $-2, R5 |
||||
ADD $2, R4 |
||||
|
||||
less2: |
||||
CMP $0, R5 |
||||
BEQ insert1 |
||||
MOVBZ (R4), R21 |
||||
SLD R22, R21, R21 |
||||
OR R16, R21, R16 |
||||
ADD $8, R22 |
||||
|
||||
insert1: |
||||
// Insert 1 at end of msg |
||||
MOVD $1, R21 |
||||
SLD R22, R21, R21 |
||||
OR R16, R21, R16 |
||||
|
||||
carry: |
||||
// Add new values to h0, h1, h2 |
||||
ADDC R16, R8 |
||||
ADDE R17, R9 |
||||
ADDE $0, R10 |
||||
MOVD $16, R5 |
||||
ADD R5, R4 |
||||
BR multiply |
||||
|
||||
done: |
||||
// Save h0, h1, h2 in state |
||||
MOVD R8, 0(R3) |
||||
MOVD R9, 8(R3) |
||||
MOVD R10, 16(R3) |
||||
RET |
||||
|
||||
// func initialize(state *[7]uint64, key *[32]byte) |
||||
TEXT ·initialize(SB), $0-16 |
||||
MOVD state+0(FP), R3 |
||||
MOVD key+8(FP), R4 |
||||
|
||||
// state[0...7] is initialized with zero |
||||
// Load key |
||||
MOVD 0(R4), R5 |
||||
MOVD 8(R4), R6 |
||||
MOVD 16(R4), R7 |
||||
MOVD 24(R4), R8 |
||||
|
||||
// Address of key mask |
||||
MOVD $·poly1305Mask<>(SB), R9 |
||||
|
||||
// Save original key in state |
||||
MOVD R7, 40(R3) |
||||
MOVD R8, 48(R3) |
||||
|
||||
// Get mask |
||||
MOVD (R9), R7 |
||||
MOVD 8(R9), R8 |
||||
|
||||
// And with key |
||||
AND R5, R7, R5 |
||||
AND R6, R8, R6 |
||||
|
||||
// Save masked key in state |
||||
MOVD R5, 24(R3) |
||||
MOVD R6, 32(R3) |
||||
RET |
||||
|
||||
// func finalize(tag *[TagSize]byte, state *[7]uint64) |
||||
TEXT ·finalize(SB), $0-16 |
||||
MOVD tag+0(FP), R3 |
||||
MOVD state+8(FP), R4 |
||||
|
||||
// Get h0, h1, h2 from state |
||||
MOVD 0(R4), R5 |
||||
MOVD 8(R4), R6 |
||||
MOVD 16(R4), R7 |
||||
|
||||
// Save h0, h1 |
||||
MOVD R5, R8 |
||||
MOVD R6, R9 |
||||
MOVD $3, R20 |
||||
MOVD $-1, R21 |
||||
SUBC $-5, R5 |
||||
SUBE R21, R6 |
||||
SUBE R20, R7 |
||||
MOVD $0, R21 |
||||
SUBZE R21 |
||||
|
||||
// Check for carry |
||||
CMP $0, R21 |
||||
ISEL $2, R5, R8, R5 |
||||
ISEL $2, R6, R9, R6 |
||||
MOVD 40(R4), R8 |
||||
MOVD 48(R4), R9 |
||||
ADDC R8, R5 |
||||
ADDE R9, R6 |
||||
MOVD R5, 0(R3) |
||||
MOVD R6, 8(R3) |
||||
RET |
@ -0,0 +1,139 @@ |
||||
// 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 ssh |
||||
|
||||
import ( |
||||
"encoding/asn1" |
||||
"errors" |
||||
) |
||||
|
||||
var krb5OID []byte |
||||
|
||||
func init() { |
||||
krb5OID, _ = asn1.Marshal(krb5Mesh) |
||||
} |
||||
|
||||
// GSSAPIClient provides the API to plug-in GSSAPI authentication for client logins.
|
||||
type GSSAPIClient interface { |
||||
// InitSecContext initiates the establishment of a security context for GSS-API between the
|
||||
// ssh client and ssh server. Initially the token parameter should be specified as nil.
|
||||
// The routine may return a outputToken which should be transferred to
|
||||
// the ssh server, where the ssh server will present it to
|
||||
// AcceptSecContext. If no token need be sent, InitSecContext will indicate this by setting
|
||||
// needContinue to false. To complete the context
|
||||
// establishment, one or more reply tokens may be required from the ssh
|
||||
// server;if so, InitSecContext will return a needContinue which is true.
|
||||
// In this case, InitSecContext should be called again when the
|
||||
// reply token is received from the ssh server, passing the reply
|
||||
// token to InitSecContext via the token parameters.
|
||||
// See RFC 2743 section 2.2.1 and RFC 4462 section 3.4.
|
||||
InitSecContext(target string, token []byte, isGSSDelegCreds bool) (outputToken []byte, needContinue bool, err error) |
||||
// GetMIC generates a cryptographic MIC for the SSH2 message, and places
|
||||
// the MIC in a token for transfer to the ssh server.
|
||||
// The contents of the MIC field are obtained by calling GSS_GetMIC()
|
||||
// over the following, using the GSS-API context that was just
|
||||
// established:
|
||||
// string session identifier
|
||||
// byte SSH_MSG_USERAUTH_REQUEST
|
||||
// string user name
|
||||
// string service
|
||||
// string "gssapi-with-mic"
|
||||
// See RFC 2743 section 2.3.1 and RFC 4462 3.5.
|
||||
GetMIC(micFiled []byte) ([]byte, error) |
||||
// Whenever possible, it should be possible for
|
||||
// DeleteSecContext() calls to be successfully processed even
|
||||
// if other calls cannot succeed, thereby enabling context-related
|
||||
// resources to be released.
|
||||
// In addition to deleting established security contexts,
|
||||
// gss_delete_sec_context must also be able to delete "half-built"
|
||||
// security contexts resulting from an incomplete sequence of
|
||||
// InitSecContext()/AcceptSecContext() calls.
|
||||
// See RFC 2743 section 2.2.3.
|
||||
DeleteSecContext() error |
||||
} |
||||
|
||||
// GSSAPIServer provides the API to plug in GSSAPI authentication for server logins.
|
||||
type GSSAPIServer interface { |
||||
// AcceptSecContext allows a remotely initiated security context between the application
|
||||
// and a remote peer to be established by the ssh client. The routine may return a
|
||||
// outputToken which should be transferred to the ssh client,
|
||||
// where the ssh client will present it to InitSecContext.
|
||||
// If no token need be sent, AcceptSecContext will indicate this
|
||||
// by setting the needContinue to false. To
|
||||
// complete the context establishment, one or more reply tokens may be
|
||||
// required from the ssh client. if so, AcceptSecContext
|
||||
// will return a needContinue which is true, in which case it
|
||||
// should be called again when the reply token is received from the ssh
|
||||
// client, passing the token to AcceptSecContext via the
|
||||
// token parameters.
|
||||
// The srcName return value is the authenticated username.
|
||||
// See RFC 2743 section 2.2.2 and RFC 4462 section 3.4.
|
||||
AcceptSecContext(token []byte) (outputToken []byte, srcName string, needContinue bool, err error) |
||||
// VerifyMIC verifies that a cryptographic MIC, contained in the token parameter,
|
||||
// fits the supplied message is received from the ssh client.
|
||||
// See RFC 2743 section 2.3.2.
|
||||
VerifyMIC(micField []byte, micToken []byte) error |
||||
// Whenever possible, it should be possible for
|
||||
// DeleteSecContext() calls to be successfully processed even
|
||||
// if other calls cannot succeed, thereby enabling context-related
|
||||
// resources to be released.
|
||||
// In addition to deleting established security contexts,
|
||||
// gss_delete_sec_context must also be able to delete "half-built"
|
||||
// security contexts resulting from an incomplete sequence of
|
||||
// InitSecContext()/AcceptSecContext() calls.
|
||||
// See RFC 2743 section 2.2.3.
|
||||
DeleteSecContext() error |
||||
} |
||||
|
||||
var ( |
||||
// OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication,
|
||||
// so we also support the krb5 mechanism only.
|
||||
// See RFC 1964 section 1.
|
||||
krb5Mesh = asn1.ObjectIdentifier{1, 2, 840, 113554, 1, 2, 2} |
||||
) |
||||
|
||||
// The GSS-API authentication method is initiated when the client sends an SSH_MSG_USERAUTH_REQUEST
|
||||
// See RFC 4462 section 3.2.
|
||||
type userAuthRequestGSSAPI struct { |
||||
N uint32 |
||||
OIDS []asn1.ObjectIdentifier |
||||
} |
||||
|
||||
func parseGSSAPIPayload(payload []byte) (*userAuthRequestGSSAPI, error) { |
||||
n, rest, ok := parseUint32(payload) |
||||
if !ok { |
||||
return nil, errors.New("parse uint32 failed") |
||||
} |
||||
s := &userAuthRequestGSSAPI{ |
||||
N: n, |
||||
OIDS: make([]asn1.ObjectIdentifier, n), |
||||
} |
||||
for i := 0; i < int(n); i++ { |
||||
var ( |
||||
desiredMech []byte |
||||
err error |
||||
) |
||||
desiredMech, rest, ok = parseString(rest) |
||||
if !ok { |
||||
return nil, errors.New("parse string failed") |
||||
} |
||||
if rest, err = asn1.Unmarshal(desiredMech, &s.OIDS[i]); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
} |
||||
return s, nil |
||||
} |
||||
|
||||
// See RFC 4462 section 3.6.
|
||||
func buildMIC(sessionID string, username string, service string, authMethod string) []byte { |
||||
out := make([]byte, 0, 0) |
||||
out = appendString(out, sessionID) |
||||
out = append(out, msgUserAuthRequest) |
||||
out = appendString(out, username) |
||||
out = appendString(out, service) |
||||
out = appendString(out, authMethod) |
||||
return out |
||||
} |
@ -1,147 +0,0 @@ |
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.7
|
||||
|
||||
package ctxhttp // import "golang.org/x/net/context/ctxhttp"
|
||||
|
||||
import ( |
||||
"io" |
||||
"net/http" |
||||
"net/url" |
||||
"strings" |
||||
|
||||
"golang.org/x/net/context" |
||||
) |
||||
|
||||
func nop() {} |
||||
|
||||
var ( |
||||
testHookContextDoneBeforeHeaders = nop |
||||
testHookDoReturned = nop |
||||
testHookDidBodyClose = nop |
||||
) |
||||
|
||||
// Do sends an HTTP request with the provided http.Client and returns an HTTP response.
|
||||
// If the client is nil, http.DefaultClient is used.
|
||||
// If the context 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 |
||||
} |
||||
|
||||
// TODO(djd): Respect any existing value of req.Cancel.
|
||||
cancel := make(chan struct{}) |
||||
req.Cancel = cancel |
||||
|
||||
type responseAndError struct { |
||||
resp *http.Response |
||||
err error |
||||
} |
||||
result := make(chan responseAndError, 1) |
||||
|
||||
// Make local copies of test hooks closed over by goroutines below.
|
||||
// Prevents data races in tests.
|
||||
testHookDoReturned := testHookDoReturned |
||||
testHookDidBodyClose := testHookDidBodyClose |
||||
|
||||
go func() { |
||||
resp, err := client.Do(req) |
||||
testHookDoReturned() |
||||
result <- responseAndError{resp, err} |
||||
}() |
||||
|
||||
var resp *http.Response |
||||
|
||||
select { |
||||
case <-ctx.Done(): |
||||
testHookContextDoneBeforeHeaders() |
||||
close(cancel) |
||||
// Clean up after the goroutine calling client.Do:
|
||||
go func() { |
||||
if r := <-result; r.resp != nil { |
||||
testHookDidBodyClose() |
||||
r.resp.Body.Close() |
||||
} |
||||
}() |
||||
return nil, ctx.Err() |
||||
case r := <-result: |
||||
var err error |
||||
resp, err = r.resp, r.err |
||||
if err != nil { |
||||
return resp, err |
||||
} |
||||
} |
||||
|
||||
c := make(chan struct{}) |
||||
go func() { |
||||
select { |
||||
case <-ctx.Done(): |
||||
close(cancel) |
||||
case <-c: |
||||
// The response's Body is closed.
|
||||
} |
||||
}() |
||||
resp.Body = ¬ifyingReader{resp.Body, c} |
||||
|
||||
return resp, nil |
||||
} |
||||
|
||||
// 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())) |
||||
} |
||||
|
||||
// notifyingReader is an io.ReadCloser that closes the notify channel after
|
||||
// Close is called or a Read fails on the underlying ReadCloser.
|
||||
type notifyingReader struct { |
||||
io.ReadCloser |
||||
notify chan<- struct{} |
||||
} |
||||
|
||||
func (r *notifyingReader) Read(p []byte) (int, error) { |
||||
n, err := r.ReadCloser.Read(p) |
||||
if err != nil && r.notify != nil { |
||||
close(r.notify) |
||||
r.notify = nil |
||||
} |
||||
return n, err |
||||
} |
||||
|
||||
func (r *notifyingReader) Close() error { |
||||
err := r.ReadCloser.Close() |
||||
if r.notify != nil { |
||||
close(r.notify) |
||||
r.notify = nil |
||||
} |
||||
return err |
||||
} |
@ -0,0 +1,734 @@ |
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.10
|
||||
|
||||
// Package idna implements IDNA2008 using the compatibility processing
|
||||
// defined by UTS (Unicode Technical Standard) #46, which defines a standard to
|
||||
// deal with the transition from IDNA2003.
|
||||
//
|
||||
// IDNA2008 (Internationalized Domain Names for Applications), is defined in RFC
|
||||
// 5890, RFC 5891, RFC 5892, RFC 5893 and RFC 5894.
|
||||
// UTS #46 is defined in https://www.unicode.org/reports/tr46.
|
||||
// See https://unicode.org/cldr/utility/idna.jsp for a visualization of the
|
||||
// differences between these two standards.
|
||||
package idna // import "golang.org/x/net/idna"
|
||||
|
||||
import ( |
||||
"fmt" |
||||
"strings" |
||||
"unicode/utf8" |
||||
|
||||
"golang.org/x/text/secure/bidirule" |
||||
"golang.org/x/text/unicode/bidi" |
||||
"golang.org/x/text/unicode/norm" |
||||
) |
||||
|
||||
// NOTE: Unlike common practice in Go APIs, the functions will return a
|
||||
// sanitized domain name in case of errors. Browsers sometimes use a partially
|
||||
// evaluated string as lookup.
|
||||
// TODO: the current error handling is, in my opinion, the least opinionated.
|
||||
// Other strategies are also viable, though:
|
||||
// Option 1) Return an empty string in case of error, but allow the user to
|
||||
// specify explicitly which errors to ignore.
|
||||
// Option 2) Return the partially evaluated string if it is itself a valid
|
||||
// string, otherwise return the empty string in case of error.
|
||||
// Option 3) Option 1 and 2.
|
||||
// Option 4) Always return an empty string for now and implement Option 1 as
|
||||
// needed, and document that the return string may not be empty in case of
|
||||
// error in the future.
|
||||
// I think Option 1 is best, but it is quite opinionated.
|
||||
|
||||
// ToASCII is a wrapper for Punycode.ToASCII.
|
||||
func ToASCII(s string) (string, error) { |
||||
return Punycode.process(s, true) |
||||
} |
||||
|
||||
// ToUnicode is a wrapper for Punycode.ToUnicode.
|
||||
func ToUnicode(s string) (string, error) { |
||||
return Punycode.process(s, false) |
||||
} |
||||
|
||||
// An Option configures a Profile at creation time.
|
||||
type Option func(*options) |
||||
|
||||
// Transitional sets a Profile to use the Transitional mapping as defined in UTS
|
||||
// #46. This will cause, for example, "ß" to be mapped to "ss". Using the
|
||||
// transitional mapping provides a compromise between IDNA2003 and IDNA2008
|
||||
// compatibility. It is used by most browsers when resolving domain names. This
|
||||
// option is only meaningful if combined with MapForLookup.
|
||||
func Transitional(transitional bool) Option { |
||||
return func(o *options) { o.transitional = true } |
||||
} |
||||
|
||||
// VerifyDNSLength sets whether a Profile should fail if any of the IDN parts
|
||||
// are longer than allowed by the RFC.
|
||||
func VerifyDNSLength(verify bool) Option { |
||||
return func(o *options) { o.verifyDNSLength = verify } |
||||
} |
||||
|
||||
// RemoveLeadingDots removes leading label separators. Leading runes that map to
|
||||
// dots, such as U+3002 IDEOGRAPHIC FULL STOP, are removed as well.
|
||||
//
|
||||
// This is the behavior suggested by the UTS #46 and is adopted by some
|
||||
// browsers.
|
||||
func RemoveLeadingDots(remove bool) Option { |
||||
return func(o *options) { o.removeLeadingDots = remove } |
||||
} |
||||
|
||||
// ValidateLabels sets whether to check the mandatory label validation criteria
|
||||
// as defined in Section 5.4 of RFC 5891. This includes testing for correct use
|
||||
// of hyphens ('-'), normalization, validity of runes, and the context rules.
|
||||
func ValidateLabels(enable bool) Option { |
||||
return func(o *options) { |
||||
// Don't override existing mappings, but set one that at least checks
|
||||
// normalization if it is not set.
|
||||
if o.mapping == nil && enable { |
||||
o.mapping = normalize |
||||
} |
||||
o.trie = trie |
||||
o.validateLabels = enable |
||||
o.fromPuny = validateFromPunycode |
||||
} |
||||
} |
||||
|
||||
// StrictDomainName limits the set of permissible ASCII characters to those
|
||||
// allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the
|
||||
// hyphen). This is set by default for MapForLookup and ValidateForRegistration.
|
||||
//
|
||||
// This option is useful, for instance, for browsers that allow characters
|
||||
// outside this range, for example a '_' (U+005F LOW LINE). See
|
||||
// http://www.rfc-editor.org/std/std3.txt for more details This option
|
||||
// corresponds to the UseSTD3ASCIIRules option in UTS #46.
|
||||
func StrictDomainName(use bool) Option { |
||||
return func(o *options) { |
||||
o.trie = trie |
||||
o.useSTD3Rules = use |
||||
o.fromPuny = validateFromPunycode |
||||
} |
||||
} |
||||
|
||||
// NOTE: the following options pull in tables. The tables should not be linked
|
||||
// in as long as the options are not used.
|
||||
|
||||
// BidiRule enables the Bidi rule as defined in RFC 5893. Any application
|
||||
// that relies on proper validation of labels should include this rule.
|
||||
func BidiRule() Option { |
||||
return func(o *options) { o.bidirule = bidirule.ValidString } |
||||
} |
||||
|
||||
// ValidateForRegistration sets validation options to verify that a given IDN is
|
||||
// properly formatted for registration as defined by Section 4 of RFC 5891.
|
||||
func ValidateForRegistration() Option { |
||||
return func(o *options) { |
||||
o.mapping = validateRegistration |
||||
StrictDomainName(true)(o) |
||||
ValidateLabels(true)(o) |
||||
VerifyDNSLength(true)(o) |
||||
BidiRule()(o) |
||||
} |
||||
} |
||||
|
||||
// MapForLookup sets validation and mapping options such that a given IDN is
|
||||
// transformed for domain name lookup according to the requirements set out in
|
||||
// Section 5 of RFC 5891. The mappings follow the recommendations of RFC 5894,
|
||||
// RFC 5895 and UTS 46. It does not add the Bidi Rule. Use the BidiRule option
|
||||
// to add this check.
|
||||
//
|
||||
// The mappings include normalization and mapping case, width and other
|
||||
// compatibility mappings.
|
||||
func MapForLookup() Option { |
||||
return func(o *options) { |
||||
o.mapping = validateAndMap |
||||
StrictDomainName(true)(o) |
||||
ValidateLabels(true)(o) |
||||
} |
||||
} |
||||
|
||||
type options struct { |
||||
transitional bool |
||||
useSTD3Rules bool |
||||
validateLabels bool |
||||
verifyDNSLength bool |
||||
removeLeadingDots bool |
||||
|
||||
trie *idnaTrie |
||||
|
||||
// fromPuny calls validation rules when converting A-labels to U-labels.
|
||||
fromPuny func(p *Profile, s string) error |
||||
|
||||
// mapping implements a validation and mapping step as defined in RFC 5895
|
||||
// or UTS 46, tailored to, for example, domain registration or lookup.
|
||||
mapping func(p *Profile, s string) (mapped string, isBidi bool, err error) |
||||
|
||||
// bidirule, if specified, checks whether s conforms to the Bidi Rule
|
||||
// defined in RFC 5893.
|
||||
bidirule func(s string) bool |
||||
} |
||||
|
||||
// A Profile defines the configuration of an IDNA mapper.
|
||||
type Profile struct { |
||||
options |
||||
} |
||||
|
||||
func apply(o *options, opts []Option) { |
||||
for _, f := range opts { |
||||
f(o) |
||||
} |
||||
} |
||||
|
||||
// New creates a new Profile.
|
||||
//
|
||||
// With no options, the returned Profile is the most permissive and equals the
|
||||
// Punycode Profile. Options can be passed to further restrict the Profile. The
|
||||
// MapForLookup and ValidateForRegistration options set a collection of options,
|
||||
// for lookup and registration purposes respectively, which can be tailored by
|
||||
// adding more fine-grained options, where later options override earlier
|
||||
// options.
|
||||
func New(o ...Option) *Profile { |
||||
p := &Profile{} |
||||
apply(&p.options, o) |
||||
return p |
||||
} |
||||
|
||||
// ToASCII converts a domain or domain label to its ASCII form. For example,
|
||||
// ToASCII("bücher.example.com") is "xn--bcher-kva.example.com", and
|
||||
// ToASCII("golang") is "golang". If an error is encountered it will return
|
||||
// an error and a (partially) processed result.
|
||||
func (p *Profile) ToASCII(s string) (string, error) { |
||||
return p.process(s, true) |
||||
} |
||||
|
||||
// ToUnicode converts a domain or domain label to its Unicode form. For example,
|
||||
// ToUnicode("xn--bcher-kva.example.com") is "bücher.example.com", and
|
||||
// ToUnicode("golang") is "golang". If an error is encountered it will return
|
||||
// an error and a (partially) processed result.
|
||||
func (p *Profile) ToUnicode(s string) (string, error) { |
||||
pp := *p |
||||
pp.transitional = false |
||||
return pp.process(s, false) |
||||
} |
||||
|
||||
// String reports a string with a description of the profile for debugging
|
||||
// purposes. The string format may change with different versions.
|
||||
func (p *Profile) String() string { |
||||
s := "" |
||||
if p.transitional { |
||||
s = "Transitional" |
||||
} else { |
||||
s = "NonTransitional" |
||||
} |
||||
if p.useSTD3Rules { |
||||
s += ":UseSTD3Rules" |
||||
} |
||||
if p.validateLabels { |
||||
s += ":ValidateLabels" |
||||
} |
||||
if p.verifyDNSLength { |
||||
s += ":VerifyDNSLength" |
||||
} |
||||
return s |
||||
} |
||||
|
||||
var ( |
||||
// Punycode is a Profile that does raw punycode processing with a minimum
|
||||
// of validation.
|
||||
Punycode *Profile = punycode |
||||
|
||||
// Lookup is the recommended profile for looking up domain names, according
|
||||
// to Section 5 of RFC 5891. The exact configuration of this profile may
|
||||
// change over time.
|
||||
Lookup *Profile = lookup |
||||
|
||||
// Display is the recommended profile for displaying domain names.
|
||||
// The configuration of this profile may change over time.
|
||||
Display *Profile = display |
||||
|
||||
// Registration is the recommended profile for checking whether a given
|
||||
// IDN is valid for registration, according to Section 4 of RFC 5891.
|
||||
Registration *Profile = registration |
||||
|
||||
punycode = &Profile{} |
||||
lookup = &Profile{options{ |
||||
transitional: true, |
||||
useSTD3Rules: true, |
||||
validateLabels: true, |
||||
trie: trie, |
||||
fromPuny: validateFromPunycode, |
||||
mapping: validateAndMap, |
||||
bidirule: bidirule.ValidString, |
||||
}} |
||||
display = &Profile{options{ |
||||
useSTD3Rules: true, |
||||
validateLabels: true, |
||||
trie: trie, |
||||
fromPuny: validateFromPunycode, |
||||
mapping: validateAndMap, |
||||
bidirule: bidirule.ValidString, |
||||
}} |
||||
registration = &Profile{options{ |
||||
useSTD3Rules: true, |
||||
validateLabels: true, |
||||
verifyDNSLength: true, |
||||
trie: trie, |
||||
fromPuny: validateFromPunycode, |
||||
mapping: validateRegistration, |
||||
bidirule: bidirule.ValidString, |
||||
}} |
||||
|
||||
// TODO: profiles
|
||||
// Register: recommended for approving domain names: don't do any mappings
|
||||
// but rather reject on invalid input. Bundle or block deviation characters.
|
||||
) |
||||
|
||||
type labelError struct{ label, code_ string } |
||||
|
||||
func (e labelError) code() string { return e.code_ } |
||||
func (e labelError) Error() string { |
||||
return fmt.Sprintf("idna: invalid label %q", e.label) |
||||
} |
||||
|
||||
type runeError rune |
||||
|
||||
func (e runeError) code() string { return "P1" } |
||||
func (e runeError) Error() string { |
||||
return fmt.Sprintf("idna: disallowed rune %U", e) |
||||
} |
||||
|
||||
// process implements the algorithm described in section 4 of UTS #46,
|
||||
// see https://www.unicode.org/reports/tr46.
|
||||
func (p *Profile) process(s string, toASCII bool) (string, error) { |
||||
var err error |
||||
var isBidi bool |
||||
if p.mapping != nil { |
||||
s, isBidi, err = p.mapping(p, s) |
||||
} |
||||
// Remove leading empty labels.
|
||||
if p.removeLeadingDots { |
||||
for ; len(s) > 0 && s[0] == '.'; s = s[1:] { |
||||
} |
||||
} |
||||
// TODO: allow for a quick check of the tables data.
|
||||
// It seems like we should only create this error on ToASCII, but the
|
||||
// UTS 46 conformance tests suggests we should always check this.
|
||||
if err == nil && p.verifyDNSLength && s == "" { |
||||
err = &labelError{s, "A4"} |
||||
} |
||||
labels := labelIter{orig: s} |
||||
for ; !labels.done(); labels.next() { |
||||
label := labels.label() |
||||
if label == "" { |
||||
// Empty labels are not okay. The label iterator skips the last
|
||||
// label if it is empty.
|
||||
if err == nil && p.verifyDNSLength { |
||||
err = &labelError{s, "A4"} |
||||
} |
||||
continue |
||||
} |
||||
if strings.HasPrefix(label, acePrefix) { |
||||
u, err2 := decode(label[len(acePrefix):]) |
||||
if err2 != nil { |
||||
if err == nil { |
||||
err = err2 |
||||
} |
||||
// Spec says keep the old label.
|
||||
continue |
||||
} |
||||
isBidi = isBidi || bidirule.DirectionString(u) != bidi.LeftToRight |
||||
labels.set(u) |
||||
if err == nil && p.validateLabels { |
||||
err = p.fromPuny(p, u) |
||||
} |
||||
if err == nil { |
||||
// This should be called on NonTransitional, according to the
|
||||
// spec, but that currently does not have any effect. Use the
|
||||
// original profile to preserve options.
|
||||
err = p.validateLabel(u) |
||||
} |
||||
} else if err == nil { |
||||
err = p.validateLabel(label) |
||||
} |
||||
} |
||||
if isBidi && p.bidirule != nil && err == nil { |
||||
for labels.reset(); !labels.done(); labels.next() { |
||||
if !p.bidirule(labels.label()) { |
||||
err = &labelError{s, "B"} |
||||
break |
||||
} |
||||
} |
||||
} |
||||
if toASCII { |
||||
for labels.reset(); !labels.done(); labels.next() { |
||||
label := labels.label() |
||||
if !ascii(label) { |
||||
a, err2 := encode(acePrefix, label) |
||||
if err == nil { |
||||
err = err2 |
||||
} |
||||
label = a |
||||
labels.set(a) |
||||
} |
||||
n := len(label) |
||||
if p.verifyDNSLength && err == nil && (n == 0 || n > 63) { |
||||
err = &labelError{label, "A4"} |
||||
} |
||||
} |
||||
} |
||||
s = labels.result() |
||||
if toASCII && p.verifyDNSLength && err == nil { |
||||
// Compute the length of the domain name minus the root label and its dot.
|
||||
n := len(s) |
||||
if n > 0 && s[n-1] == '.' { |
||||
n-- |
||||
} |
||||
if len(s) < 1 || n > 253 { |
||||
err = &labelError{s, "A4"} |
||||
} |
||||
} |
||||
return s, err |
||||
} |
||||
|
||||
func normalize(p *Profile, s string) (mapped string, isBidi bool, err error) { |
||||
// TODO: consider first doing a quick check to see if any of these checks
|
||||
// need to be done. This will make it slower in the general case, but
|
||||
// faster in the common case.
|
||||
mapped = norm.NFC.String(s) |
||||
isBidi = bidirule.DirectionString(mapped) == bidi.RightToLeft |
||||
return mapped, isBidi, nil |
||||
} |
||||
|
||||
func validateRegistration(p *Profile, s string) (idem string, bidi bool, err error) { |
||||
// TODO: filter need for normalization in loop below.
|
||||
if !norm.NFC.IsNormalString(s) { |
||||
return s, false, &labelError{s, "V1"} |
||||
} |
||||
for i := 0; i < len(s); { |
||||
v, sz := trie.lookupString(s[i:]) |
||||
if sz == 0 { |
||||
return s, bidi, runeError(utf8.RuneError) |
||||
} |
||||
bidi = bidi || info(v).isBidi(s[i:]) |
||||
// Copy bytes not copied so far.
|
||||
switch p.simplify(info(v).category()) { |
||||
// TODO: handle the NV8 defined in the Unicode idna data set to allow
|
||||
// for strict conformance to IDNA2008.
|
||||
case valid, deviation: |
||||
case disallowed, mapped, unknown, ignored: |
||||
r, _ := utf8.DecodeRuneInString(s[i:]) |
||||
return s, bidi, runeError(r) |
||||
} |
||||
i += sz |
||||
} |
||||
return s, bidi, nil |
||||
} |
||||
|
||||
func (c info) isBidi(s string) bool { |
||||
if !c.isMapped() { |
||||
return c&attributesMask == rtl |
||||
} |
||||
// TODO: also store bidi info for mapped data. This is possible, but a bit
|
||||
// cumbersome and not for the common case.
|
||||
p, _ := bidi.LookupString(s) |
||||
switch p.Class() { |
||||
case bidi.R, bidi.AL, bidi.AN: |
||||
return true |
||||
} |
||||
return false |
||||
} |
||||
|
||||
func validateAndMap(p *Profile, s string) (vm string, bidi bool, err error) { |
||||
var ( |
||||
b []byte |
||||
k int |
||||
) |
||||
// combinedInfoBits contains the or-ed bits of all runes. We use this
|
||||
// to derive the mayNeedNorm bit later. This may trigger normalization
|
||||
// overeagerly, but it will not do so in the common case. The end result
|
||||
// is another 10% saving on BenchmarkProfile for the common case.
|
||||
var combinedInfoBits info |
||||
for i := 0; i < len(s); { |
||||
v, sz := trie.lookupString(s[i:]) |
||||
if sz == 0 { |
||||
b = append(b, s[k:i]...) |
||||
b = append(b, "\ufffd"...) |
||||
k = len(s) |
||||
if err == nil { |
||||
err = runeError(utf8.RuneError) |
||||
} |
||||
break |
||||
} |
||||
combinedInfoBits |= info(v) |
||||
bidi = bidi || info(v).isBidi(s[i:]) |
||||
start := i |
||||
i += sz |
||||
// Copy bytes not copied so far.
|
||||
switch p.simplify(info(v).category()) { |
||||
case valid: |
||||
continue |
||||
case disallowed: |
||||
if err == nil { |
||||
r, _ := utf8.DecodeRuneInString(s[start:]) |
||||
err = runeError(r) |
||||
} |
||||
continue |
||||
case mapped, deviation: |
||||
b = append(b, s[k:start]...) |
||||
b = info(v).appendMapping(b, s[start:i]) |
||||
case ignored: |
||||
b = append(b, s[k:start]...) |
||||
// drop the rune
|
||||
case unknown: |
||||
b = append(b, s[k:start]...) |
||||
b = append(b, "\ufffd"...) |
||||
} |
||||
k = i |
||||
} |
||||
if k == 0 { |
||||
// No changes so far.
|
||||
if combinedInfoBits&mayNeedNorm != 0 { |
||||
s = norm.NFC.String(s) |
||||
} |
||||
} else { |
||||
b = append(b, s[k:]...) |
||||
if norm.NFC.QuickSpan(b) != len(b) { |
||||
b = norm.NFC.Bytes(b) |
||||
} |
||||
// TODO: the punycode converters require strings as input.
|
||||
s = string(b) |
||||
} |
||||
return s, bidi, err |
||||
} |
||||
|
||||
// A labelIter allows iterating over domain name labels.
|
||||
type labelIter struct { |
||||
orig string |
||||
slice []string |
||||
curStart int |
||||
curEnd int |
||||
i int |
||||
} |
||||
|
||||
func (l *labelIter) reset() { |
||||
l.curStart = 0 |
||||
l.curEnd = 0 |
||||
l.i = 0 |
||||
} |
||||
|
||||
func (l *labelIter) done() bool { |
||||
return l.curStart >= len(l.orig) |
||||
} |
||||
|
||||
func (l *labelIter) result() string { |
||||
if l.slice != nil { |
||||
return strings.Join(l.slice, ".") |
||||
} |
||||
return l.orig |
||||
} |
||||
|
||||
func (l *labelIter) label() string { |
||||
if l.slice != nil { |
||||
return l.slice[l.i] |
||||
} |
||||
p := strings.IndexByte(l.orig[l.curStart:], '.') |
||||
l.curEnd = l.curStart + p |
||||
if p == -1 { |
||||
l.curEnd = len(l.orig) |
||||
} |
||||
return l.orig[l.curStart:l.curEnd] |
||||
} |
||||
|
||||
// next sets the value to the next label. It skips the last label if it is empty.
|
||||
func (l *labelIter) next() { |
||||
l.i++ |
||||
if l.slice != nil { |
||||
if l.i >= len(l.slice) || l.i == len(l.slice)-1 && l.slice[l.i] == "" { |
||||
l.curStart = len(l.orig) |
||||
} |
||||
} else { |
||||
l.curStart = l.curEnd + 1 |
||||
if l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' { |
||||
l.curStart = len(l.orig) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func (l *labelIter) set(s string) { |
||||
if l.slice == nil { |
||||
l.slice = strings.Split(l.orig, ".") |
||||
} |
||||
l.slice[l.i] = s |
||||
} |
||||
|
||||
// acePrefix is the ASCII Compatible Encoding prefix.
|
||||
const acePrefix = "xn--" |
||||
|
||||
func (p *Profile) simplify(cat category) category { |
||||
switch cat { |
||||
case disallowedSTD3Mapped: |
||||
if p.useSTD3Rules { |
||||
cat = disallowed |
||||
} else { |
||||
cat = mapped |
||||
} |
||||
case disallowedSTD3Valid: |
||||
if p.useSTD3Rules { |
||||
cat = disallowed |
||||
} else { |
||||
cat = valid |
||||
} |
||||
case deviation: |
||||
if !p.transitional { |
||||
cat = valid |
||||
} |
||||
case validNV8, validXV8: |
||||
// TODO: handle V2008
|
||||
cat = valid |
||||
} |
||||
return cat |
||||
} |
||||
|
||||
func validateFromPunycode(p *Profile, s string) error { |
||||
if !norm.NFC.IsNormalString(s) { |
||||
return &labelError{s, "V1"} |
||||
} |
||||
// TODO: detect whether string may have to be normalized in the following
|
||||
// loop.
|
||||
for i := 0; i < len(s); { |
||||
v, sz := trie.lookupString(s[i:]) |
||||
if sz == 0 { |
||||
return runeError(utf8.RuneError) |
||||
} |
||||
if c := p.simplify(info(v).category()); c != valid && c != deviation { |
||||
return &labelError{s, "V6"} |
||||
} |
||||
i += sz |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
const ( |
||||
zwnj = "\u200c" |
||||
zwj = "\u200d" |
||||
) |
||||
|
||||
type joinState int8 |
||||
|
||||
const ( |
||||
stateStart joinState = iota |
||||
stateVirama |
||||
stateBefore |
||||
stateBeforeVirama |
||||
stateAfter |
||||
stateFAIL |
||||
) |
||||
|
||||
var joinStates = [][numJoinTypes]joinState{ |
||||
stateStart: { |
||||
joiningL: stateBefore, |
||||
joiningD: stateBefore, |
||||
joinZWNJ: stateFAIL, |
||||
joinZWJ: stateFAIL, |
||||
joinVirama: stateVirama, |
||||
}, |
||||
stateVirama: { |
||||
joiningL: stateBefore, |
||||
joiningD: stateBefore, |
||||
}, |
||||
stateBefore: { |
||||
joiningL: stateBefore, |
||||
joiningD: stateBefore, |
||||
joiningT: stateBefore, |
||||
joinZWNJ: stateAfter, |
||||
joinZWJ: stateFAIL, |
||||
joinVirama: stateBeforeVirama, |
||||
}, |
||||
stateBeforeVirama: { |
||||
joiningL: stateBefore, |
||||
joiningD: stateBefore, |
||||
joiningT: stateBefore, |
||||
}, |
||||
stateAfter: { |
||||
joiningL: stateFAIL, |
||||
joiningD: stateBefore, |
||||
joiningT: stateAfter, |
||||
joiningR: stateStart, |
||||
joinZWNJ: stateFAIL, |
||||
joinZWJ: stateFAIL, |
||||
joinVirama: stateAfter, // no-op as we can't accept joiners here
|
||||
}, |
||||
stateFAIL: { |
||||
0: stateFAIL, |
||||
joiningL: stateFAIL, |
||||
joiningD: stateFAIL, |
||||
joiningT: stateFAIL, |
||||
joiningR: stateFAIL, |
||||
joinZWNJ: stateFAIL, |
||||
joinZWJ: stateFAIL, |
||||
joinVirama: stateFAIL, |
||||
}, |
||||
} |
||||
|
||||
// validateLabel validates the criteria from Section 4.1. Item 1, 4, and 6 are
|
||||
// already implicitly satisfied by the overall implementation.
|
||||
func (p *Profile) validateLabel(s string) (err error) { |
||||
if s == "" { |
||||
if p.verifyDNSLength { |
||||
return &labelError{s, "A4"} |
||||
} |
||||
return nil |
||||
} |
||||
if !p.validateLabels { |
||||
return nil |
||||
} |
||||
trie := p.trie // p.validateLabels is only set if trie is set.
|
||||
if len(s) > 4 && s[2] == '-' && s[3] == '-' { |
||||
return &labelError{s, "V2"} |
||||
} |
||||
if s[0] == '-' || s[len(s)-1] == '-' { |
||||
return &labelError{s, "V3"} |
||||
} |
||||
// TODO: merge the use of this in the trie.
|
||||
v, sz := trie.lookupString(s) |
||||
x := info(v) |
||||
if x.isModifier() { |
||||
return &labelError{s, "V5"} |
||||
} |
||||
// Quickly return in the absence of zero-width (non) joiners.
|
||||
if strings.Index(s, zwj) == -1 && strings.Index(s, zwnj) == -1 { |
||||
return nil |
||||
} |
||||
st := stateStart |
||||
for i := 0; ; { |
||||
jt := x.joinType() |
||||
if s[i:i+sz] == zwj { |
||||
jt = joinZWJ |
||||
} else if s[i:i+sz] == zwnj { |
||||
jt = joinZWNJ |
||||
} |
||||
st = joinStates[st][jt] |
||||
if x.isViramaModifier() { |
||||
st = joinStates[st][joinVirama] |
||||
} |
||||
if i += sz; i == len(s) { |
||||
break |
||||
} |
||||
v, sz = trie.lookupString(s[i:]) |
||||
x = info(v) |
||||
} |
||||
if st == stateFAIL || st == stateAfter { |
||||
return &labelError{s, "C"} |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func ascii(s string) bool { |
||||
for i := 0; i < len(s); i++ { |
||||
if s[i] >= utf8.RuneSelf { |
||||
return false |
||||
} |
||||
} |
||||
return true |
||||
} |
@ -0,0 +1,682 @@ |
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.10
|
||||
|
||||
// Package idna implements IDNA2008 using the compatibility processing
|
||||
// defined by UTS (Unicode Technical Standard) #46, which defines a standard to
|
||||
// deal with the transition from IDNA2003.
|
||||
//
|
||||
// IDNA2008 (Internationalized Domain Names for Applications), is defined in RFC
|
||||
// 5890, RFC 5891, RFC 5892, RFC 5893 and RFC 5894.
|
||||
// UTS #46 is defined in https://www.unicode.org/reports/tr46.
|
||||
// See https://unicode.org/cldr/utility/idna.jsp for a visualization of the
|
||||
// differences between these two standards.
|
||||
package idna // import "golang.org/x/net/idna"
|
||||
|
||||
import ( |
||||
"fmt" |
||||
"strings" |
||||
"unicode/utf8" |
||||
|
||||
"golang.org/x/text/secure/bidirule" |
||||
"golang.org/x/text/unicode/norm" |
||||
) |
||||
|
||||
// NOTE: Unlike common practice in Go APIs, the functions will return a
|
||||
// sanitized domain name in case of errors. Browsers sometimes use a partially
|
||||
// evaluated string as lookup.
|
||||
// TODO: the current error handling is, in my opinion, the least opinionated.
|
||||
// Other strategies are also viable, though:
|
||||
// Option 1) Return an empty string in case of error, but allow the user to
|
||||
// specify explicitly which errors to ignore.
|
||||
// Option 2) Return the partially evaluated string if it is itself a valid
|
||||
// string, otherwise return the empty string in case of error.
|
||||
// Option 3) Option 1 and 2.
|
||||
// Option 4) Always return an empty string for now and implement Option 1 as
|
||||
// needed, and document that the return string may not be empty in case of
|
||||
// error in the future.
|
||||
// I think Option 1 is best, but it is quite opinionated.
|
||||
|
||||
// ToASCII is a wrapper for Punycode.ToASCII.
|
||||
func ToASCII(s string) (string, error) { |
||||
return Punycode.process(s, true) |
||||
} |
||||
|
||||
// ToUnicode is a wrapper for Punycode.ToUnicode.
|
||||
func ToUnicode(s string) (string, error) { |
||||
return Punycode.process(s, false) |
||||
} |
||||
|
||||
// An Option configures a Profile at creation time.
|
||||
type Option func(*options) |
||||
|
||||
// Transitional sets a Profile to use the Transitional mapping as defined in UTS
|
||||
// #46. This will cause, for example, "ß" to be mapped to "ss". Using the
|
||||
// transitional mapping provides a compromise between IDNA2003 and IDNA2008
|
||||
// compatibility. It is used by most browsers when resolving domain names. This
|
||||
// option is only meaningful if combined with MapForLookup.
|
||||
func Transitional(transitional bool) Option { |
||||
return func(o *options) { o.transitional = true } |
||||
} |
||||
|
||||
// VerifyDNSLength sets whether a Profile should fail if any of the IDN parts
|
||||
// are longer than allowed by the RFC.
|
||||
func VerifyDNSLength(verify bool) Option { |
||||
return func(o *options) { o.verifyDNSLength = verify } |
||||
} |
||||
|
||||
// RemoveLeadingDots removes leading label separators. Leading runes that map to
|
||||
// dots, such as U+3002 IDEOGRAPHIC FULL STOP, are removed as well.
|
||||
//
|
||||
// This is the behavior suggested by the UTS #46 and is adopted by some
|
||||
// browsers.
|
||||
func RemoveLeadingDots(remove bool) Option { |
||||
return func(o *options) { o.removeLeadingDots = remove } |
||||
} |
||||
|
||||
// ValidateLabels sets whether to check the mandatory label validation criteria
|
||||
// as defined in Section 5.4 of RFC 5891. This includes testing for correct use
|
||||
// of hyphens ('-'), normalization, validity of runes, and the context rules.
|
||||
func ValidateLabels(enable bool) Option { |
||||
return func(o *options) { |
||||
// Don't override existing mappings, but set one that at least checks
|
||||
// normalization if it is not set.
|
||||
if o.mapping == nil && enable { |
||||
o.mapping = normalize |
||||
} |
||||
o.trie = trie |
||||
o.validateLabels = enable |
||||
o.fromPuny = validateFromPunycode |
||||
} |
||||
} |
||||
|
||||
// StrictDomainName limits the set of permissable ASCII characters to those
|
||||
// allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the
|
||||
// hyphen). This is set by default for MapForLookup and ValidateForRegistration.
|
||||
//
|
||||
// This option is useful, for instance, for browsers that allow characters
|
||||
// outside this range, for example a '_' (U+005F LOW LINE). See
|
||||
// http://www.rfc-editor.org/std/std3.txt for more details This option
|
||||
// corresponds to the UseSTD3ASCIIRules option in UTS #46.
|
||||
func StrictDomainName(use bool) Option { |
||||
return func(o *options) { |
||||
o.trie = trie |
||||
o.useSTD3Rules = use |
||||
o.fromPuny = validateFromPunycode |
||||
} |
||||
} |
||||
|
||||
// NOTE: the following options pull in tables. The tables should not be linked
|
||||
// in as long as the options are not used.
|
||||
|
||||
// BidiRule enables the Bidi rule as defined in RFC 5893. Any application
|
||||
// that relies on proper validation of labels should include this rule.
|
||||
func BidiRule() Option { |
||||
return func(o *options) { o.bidirule = bidirule.ValidString } |
||||
} |
||||
|
||||
// ValidateForRegistration sets validation options to verify that a given IDN is
|
||||
// properly formatted for registration as defined by Section 4 of RFC 5891.
|
||||
func ValidateForRegistration() Option { |
||||
return func(o *options) { |
||||
o.mapping = validateRegistration |
||||
StrictDomainName(true)(o) |
||||
ValidateLabels(true)(o) |
||||
VerifyDNSLength(true)(o) |
||||
BidiRule()(o) |
||||
} |
||||
} |
||||
|
||||
// MapForLookup sets validation and mapping options such that a given IDN is
|
||||
// transformed for domain name lookup according to the requirements set out in
|
||||
// Section 5 of RFC 5891. The mappings follow the recommendations of RFC 5894,
|
||||
// RFC 5895 and UTS 46. It does not add the Bidi Rule. Use the BidiRule option
|
||||
// to add this check.
|
||||
//
|
||||
// The mappings include normalization and mapping case, width and other
|
||||
// compatibility mappings.
|
||||
func MapForLookup() Option { |
||||
return func(o *options) { |
||||
o.mapping = validateAndMap |
||||
StrictDomainName(true)(o) |
||||
ValidateLabels(true)(o) |
||||
RemoveLeadingDots(true)(o) |
||||
} |
||||
} |
||||
|
||||
type options struct { |
||||
transitional bool |
||||
useSTD3Rules bool |
||||
validateLabels bool |
||||
verifyDNSLength bool |
||||
removeLeadingDots bool |
||||
|
||||
trie *idnaTrie |
||||
|
||||
// fromPuny calls validation rules when converting A-labels to U-labels.
|
||||
fromPuny func(p *Profile, s string) error |
||||
|
||||
// mapping implements a validation and mapping step as defined in RFC 5895
|
||||
// or UTS 46, tailored to, for example, domain registration or lookup.
|
||||
mapping func(p *Profile, s string) (string, error) |
||||
|
||||
// bidirule, if specified, checks whether s conforms to the Bidi Rule
|
||||
// defined in RFC 5893.
|
||||
bidirule func(s string) bool |
||||
} |
||||
|
||||
// A Profile defines the configuration of a IDNA mapper.
|
||||
type Profile struct { |
||||
options |
||||
} |
||||
|
||||
func apply(o *options, opts []Option) { |
||||
for _, f := range opts { |
||||
f(o) |
||||
} |
||||
} |
||||
|
||||
// New creates a new Profile.
|
||||
//
|
||||
// With no options, the returned Profile is the most permissive and equals the
|
||||
// Punycode Profile. Options can be passed to further restrict the Profile. The
|
||||
// MapForLookup and ValidateForRegistration options set a collection of options,
|
||||
// for lookup and registration purposes respectively, which can be tailored by
|
||||
// adding more fine-grained options, where later options override earlier
|
||||
// options.
|
||||
func New(o ...Option) *Profile { |
||||
p := &Profile{} |
||||
apply(&p.options, o) |
||||
return p |
||||
} |
||||
|
||||
// ToASCII converts a domain or domain label to its ASCII form. For example,
|
||||
// ToASCII("bücher.example.com") is "xn--bcher-kva.example.com", and
|
||||
// ToASCII("golang") is "golang". If an error is encountered it will return
|
||||
// an error and a (partially) processed result.
|
||||
func (p *Profile) ToASCII(s string) (string, error) { |
||||
return p.process(s, true) |
||||
} |
||||
|
||||
// ToUnicode converts a domain or domain label to its Unicode form. For example,
|
||||
// ToUnicode("xn--bcher-kva.example.com") is "bücher.example.com", and
|
||||
// ToUnicode("golang") is "golang". If an error is encountered it will return
|
||||
// an error and a (partially) processed result.
|
||||
func (p *Profile) ToUnicode(s string) (string, error) { |
||||
pp := *p |
||||
pp.transitional = false |
||||
return pp.process(s, false) |
||||
} |
||||
|
||||
// String reports a string with a description of the profile for debugging
|
||||
// purposes. The string format may change with different versions.
|
||||
func (p *Profile) String() string { |
||||
s := "" |
||||
if p.transitional { |
||||
s = "Transitional" |
||||
} else { |
||||
s = "NonTransitional" |
||||
} |
||||
if p.useSTD3Rules { |
||||
s += ":UseSTD3Rules" |
||||
} |
||||
if p.validateLabels { |
||||
s += ":ValidateLabels" |
||||
} |
||||
if p.verifyDNSLength { |
||||
s += ":VerifyDNSLength" |
||||
} |
||||
return s |
||||
} |
||||
|
||||
var ( |
||||
// Punycode is a Profile that does raw punycode processing with a minimum
|
||||
// of validation.
|
||||
Punycode *Profile = punycode |
||||
|
||||
// Lookup is the recommended profile for looking up domain names, according
|
||||
// to Section 5 of RFC 5891. The exact configuration of this profile may
|
||||
// change over time.
|
||||
Lookup *Profile = lookup |
||||
|
||||
// Display is the recommended profile for displaying domain names.
|
||||
// The configuration of this profile may change over time.
|
||||
Display *Profile = display |
||||
|
||||
// Registration is the recommended profile for checking whether a given
|
||||
// IDN is valid for registration, according to Section 4 of RFC 5891.
|
||||
Registration *Profile = registration |
||||
|
||||
punycode = &Profile{} |
||||
lookup = &Profile{options{ |
||||
transitional: true, |
||||
useSTD3Rules: true, |
||||
validateLabels: true, |
||||
removeLeadingDots: true, |
||||
trie: trie, |
||||
fromPuny: validateFromPunycode, |
||||
mapping: validateAndMap, |
||||
bidirule: bidirule.ValidString, |
||||
}} |
||||
display = &Profile{options{ |
||||
useSTD3Rules: true, |
||||
validateLabels: true, |
||||
removeLeadingDots: true, |
||||
trie: trie, |
||||
fromPuny: validateFromPunycode, |
||||
mapping: validateAndMap, |
||||
bidirule: bidirule.ValidString, |
||||
}} |
||||
registration = &Profile{options{ |
||||
useSTD3Rules: true, |
||||
validateLabels: true, |
||||
verifyDNSLength: true, |
||||
trie: trie, |
||||
fromPuny: validateFromPunycode, |
||||
mapping: validateRegistration, |
||||
bidirule: bidirule.ValidString, |
||||
}} |
||||
|
||||
// TODO: profiles
|
||||
// Register: recommended for approving domain names: don't do any mappings
|
||||
// but rather reject on invalid input. Bundle or block deviation characters.
|
||||
) |
||||
|
||||
type labelError struct{ label, code_ string } |
||||
|
||||
func (e labelError) code() string { return e.code_ } |
||||
func (e labelError) Error() string { |
||||
return fmt.Sprintf("idna: invalid label %q", e.label) |
||||
} |
||||
|
||||
type runeError rune |
||||
|
||||
func (e runeError) code() string { return "P1" } |
||||
func (e runeError) Error() string { |
||||
return fmt.Sprintf("idna: disallowed rune %U", e) |
||||
} |
||||
|
||||
// process implements the algorithm described in section 4 of UTS #46,
|
||||
// see https://www.unicode.org/reports/tr46.
|
||||
func (p *Profile) process(s string, toASCII bool) (string, error) { |
||||
var err error |
||||
if p.mapping != nil { |
||||
s, err = p.mapping(p, s) |
||||
} |
||||
// Remove leading empty labels.
|
||||
if p.removeLeadingDots { |
||||
for ; len(s) > 0 && s[0] == '.'; s = s[1:] { |
||||
} |
||||
} |
||||
// It seems like we should only create this error on ToASCII, but the
|
||||
// UTS 46 conformance tests suggests we should always check this.
|
||||
if err == nil && p.verifyDNSLength && s == "" { |
||||
err = &labelError{s, "A4"} |
||||
} |
||||
labels := labelIter{orig: s} |
||||
for ; !labels.done(); labels.next() { |
||||
label := labels.label() |
||||
if label == "" { |
||||
// Empty labels are not okay. The label iterator skips the last
|
||||
// label if it is empty.
|
||||
if err == nil && p.verifyDNSLength { |
||||
err = &labelError{s, "A4"} |
||||
} |
||||
continue |
||||
} |
||||
if strings.HasPrefix(label, acePrefix) { |
||||
u, err2 := decode(label[len(acePrefix):]) |
||||
if err2 != nil { |
||||
if err == nil { |
||||
err = err2 |
||||
} |
||||
// Spec says keep the old label.
|
||||
continue |
||||
} |
||||
labels.set(u) |
||||
if err == nil && p.validateLabels { |
||||
err = p.fromPuny(p, u) |
||||
} |
||||
if err == nil { |
||||
// This should be called on NonTransitional, according to the
|
||||
// spec, but that currently does not have any effect. Use the
|
||||
// original profile to preserve options.
|
||||
err = p.validateLabel(u) |
||||
} |
||||
} else if err == nil { |
||||
err = p.validateLabel(label) |
||||
} |
||||
} |
||||
if toASCII { |
||||
for labels.reset(); !labels.done(); labels.next() { |
||||
label := labels.label() |
||||
if !ascii(label) { |
||||
a, err2 := encode(acePrefix, label) |
||||
if err == nil { |
||||
err = err2 |
||||
} |
||||
label = a |
||||
labels.set(a) |
||||
} |
||||
n := len(label) |
||||
if p.verifyDNSLength && err == nil && (n == 0 || n > 63) { |
||||
err = &labelError{label, "A4"} |
||||
} |
||||
} |
||||
} |
||||
s = labels.result() |
||||
if toASCII && p.verifyDNSLength && err == nil { |
||||
// Compute the length of the domain name minus the root label and its dot.
|
||||
n := len(s) |
||||
if n > 0 && s[n-1] == '.' { |
||||
n-- |
||||
} |
||||
if len(s) < 1 || n > 253 { |
||||
err = &labelError{s, "A4"} |
||||
} |
||||
} |
||||
return s, err |
||||
} |
||||
|
||||
func normalize(p *Profile, s string) (string, error) { |
||||
return norm.NFC.String(s), nil |
||||
} |
||||
|
||||
func validateRegistration(p *Profile, s string) (string, error) { |
||||
if !norm.NFC.IsNormalString(s) { |
||||
return s, &labelError{s, "V1"} |
||||
} |
||||
for i := 0; i < len(s); { |
||||
v, sz := trie.lookupString(s[i:]) |
||||
// Copy bytes not copied so far.
|
||||
switch p.simplify(info(v).category()) { |
||||
// TODO: handle the NV8 defined in the Unicode idna data set to allow
|
||||
// for strict conformance to IDNA2008.
|
||||
case valid, deviation: |
||||
case disallowed, mapped, unknown, ignored: |
||||
r, _ := utf8.DecodeRuneInString(s[i:]) |
||||
return s, runeError(r) |
||||
} |
||||
i += sz |
||||
} |
||||
return s, nil |
||||
} |
||||
|
||||
func validateAndMap(p *Profile, s string) (string, error) { |
||||
var ( |
||||
err error |
||||
b []byte |
||||
k int |
||||
) |
||||
for i := 0; i < len(s); { |
||||
v, sz := trie.lookupString(s[i:]) |
||||
start := i |
||||
i += sz |
||||
// Copy bytes not copied so far.
|
||||
switch p.simplify(info(v).category()) { |
||||
case valid: |
||||
continue |
||||
case disallowed: |
||||
if err == nil { |
||||
r, _ := utf8.DecodeRuneInString(s[start:]) |
||||
err = runeError(r) |
||||
} |
||||
continue |
||||
case mapped, deviation: |
||||
b = append(b, s[k:start]...) |
||||
b = info(v).appendMapping(b, s[start:i]) |
||||
case ignored: |
||||
b = append(b, s[k:start]...) |
||||
// drop the rune
|
||||
case unknown: |
||||
b = append(b, s[k:start]...) |
||||
b = append(b, "\ufffd"...) |
||||
} |
||||
k = i |
||||
} |
||||
if k == 0 { |
||||
// No changes so far.
|
||||
s = norm.NFC.String(s) |
||||
} else { |
||||
b = append(b, s[k:]...) |
||||
if norm.NFC.QuickSpan(b) != len(b) { |
||||
b = norm.NFC.Bytes(b) |
||||
} |
||||
// TODO: the punycode converters require strings as input.
|
||||
s = string(b) |
||||
} |
||||
return s, err |
||||
} |
||||
|
||||
// A labelIter allows iterating over domain name labels.
|
||||
type labelIter struct { |
||||
orig string |
||||
slice []string |
||||
curStart int |
||||
curEnd int |
||||
i int |
||||
} |
||||
|
||||
func (l *labelIter) reset() { |
||||
l.curStart = 0 |
||||
l.curEnd = 0 |
||||
l.i = 0 |
||||
} |
||||
|
||||
func (l *labelIter) done() bool { |
||||
return l.curStart >= len(l.orig) |
||||
} |
||||
|
||||
func (l *labelIter) result() string { |
||||
if l.slice != nil { |
||||
return strings.Join(l.slice, ".") |
||||
} |
||||
return l.orig |
||||
} |
||||
|
||||
func (l *labelIter) label() string { |
||||
if l.slice != nil { |
||||
return l.slice[l.i] |
||||
} |
||||
p := strings.IndexByte(l.orig[l.curStart:], '.') |
||||
l.curEnd = l.curStart + p |
||||
if p == -1 { |
||||
l.curEnd = len(l.orig) |
||||
} |
||||
return l.orig[l.curStart:l.curEnd] |
||||
} |
||||
|
||||
// next sets the value to the next label. It skips the last label if it is empty.
|
||||
func (l *labelIter) next() { |
||||
l.i++ |
||||
if l.slice != nil { |
||||
if l.i >= len(l.slice) || l.i == len(l.slice)-1 && l.slice[l.i] == "" { |
||||
l.curStart = len(l.orig) |
||||
} |
||||
} else { |
||||
l.curStart = l.curEnd + 1 |
||||
if l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' { |
||||
l.curStart = len(l.orig) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func (l *labelIter) set(s string) { |
||||
if l.slice == nil { |
||||
l.slice = strings.Split(l.orig, ".") |
||||
} |
||||
l.slice[l.i] = s |
||||
} |
||||
|
||||
// acePrefix is the ASCII Compatible Encoding prefix.
|
||||
const acePrefix = "xn--" |
||||
|
||||
func (p *Profile) simplify(cat category) category { |
||||
switch cat { |
||||
case disallowedSTD3Mapped: |
||||
if p.useSTD3Rules { |
||||
cat = disallowed |
||||
} else { |
||||
cat = mapped |
||||
} |
||||
case disallowedSTD3Valid: |
||||
if p.useSTD3Rules { |
||||
cat = disallowed |
||||
} else { |
||||
cat = valid |
||||
} |
||||
case deviation: |
||||
if !p.transitional { |
||||
cat = valid |
||||
} |
||||
case validNV8, validXV8: |
||||
// TODO: handle V2008
|
||||
cat = valid |
||||
} |
||||
return cat |
||||
} |
||||
|
||||
func validateFromPunycode(p *Profile, s string) error { |
||||
if !norm.NFC.IsNormalString(s) { |
||||
return &labelError{s, "V1"} |
||||
} |
||||
for i := 0; i < len(s); { |
||||
v, sz := trie.lookupString(s[i:]) |
||||
if c := p.simplify(info(v).category()); c != valid && c != deviation { |
||||
return &labelError{s, "V6"} |
||||
} |
||||
i += sz |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
const ( |
||||
zwnj = "\u200c" |
||||
zwj = "\u200d" |
||||
) |
||||
|
||||
type joinState int8 |
||||
|
||||
const ( |
||||
stateStart joinState = iota |
||||
stateVirama |
||||
stateBefore |
||||
stateBeforeVirama |
||||
stateAfter |
||||
stateFAIL |
||||
) |
||||
|
||||
var joinStates = [][numJoinTypes]joinState{ |
||||
stateStart: { |
||||
joiningL: stateBefore, |
||||
joiningD: stateBefore, |
||||
joinZWNJ: stateFAIL, |
||||
joinZWJ: stateFAIL, |
||||
joinVirama: stateVirama, |
||||
}, |
||||
stateVirama: { |
||||
joiningL: stateBefore, |
||||
joiningD: stateBefore, |
||||
}, |
||||
stateBefore: { |
||||
joiningL: stateBefore, |
||||
joiningD: stateBefore, |
||||
joiningT: stateBefore, |
||||
joinZWNJ: stateAfter, |
||||
joinZWJ: stateFAIL, |
||||
joinVirama: stateBeforeVirama, |
||||
}, |
||||
stateBeforeVirama: { |
||||
joiningL: stateBefore, |
||||
joiningD: stateBefore, |
||||
joiningT: stateBefore, |
||||
}, |
||||
stateAfter: { |
||||
joiningL: stateFAIL, |
||||
joiningD: stateBefore, |
||||
joiningT: stateAfter, |
||||
joiningR: stateStart, |
||||
joinZWNJ: stateFAIL, |
||||
joinZWJ: stateFAIL, |
||||
joinVirama: stateAfter, // no-op as we can't accept joiners here
|
||||
}, |
||||
stateFAIL: { |
||||
0: stateFAIL, |
||||
joiningL: stateFAIL, |
||||
joiningD: stateFAIL, |
||||
joiningT: stateFAIL, |
||||
joiningR: stateFAIL, |
||||
joinZWNJ: stateFAIL, |
||||
joinZWJ: stateFAIL, |
||||
joinVirama: stateFAIL, |
||||
}, |
||||
} |
||||
|
||||
// validateLabel validates the criteria from Section 4.1. Item 1, 4, and 6 are
|
||||
// already implicitly satisfied by the overall implementation.
|
||||
func (p *Profile) validateLabel(s string) error { |
||||
if s == "" { |
||||
if p.verifyDNSLength { |
||||
return &labelError{s, "A4"} |
||||
} |
||||
return nil |
||||
} |
||||
if p.bidirule != nil && !p.bidirule(s) { |
||||
return &labelError{s, "B"} |
||||
} |
||||
if !p.validateLabels { |
||||
return nil |
||||
} |
||||
trie := p.trie // p.validateLabels is only set if trie is set.
|
||||
if len(s) > 4 && s[2] == '-' && s[3] == '-' { |
||||
return &labelError{s, "V2"} |
||||
} |
||||
if s[0] == '-' || s[len(s)-1] == '-' { |
||||
return &labelError{s, "V3"} |
||||
} |
||||
// TODO: merge the use of this in the trie.
|
||||
v, sz := trie.lookupString(s) |
||||
x := info(v) |
||||
if x.isModifier() { |
||||
return &labelError{s, "V5"} |
||||
} |
||||
// Quickly return in the absence of zero-width (non) joiners.
|
||||
if strings.Index(s, zwj) == -1 && strings.Index(s, zwnj) == -1 { |
||||
return nil |
||||
} |
||||
st := stateStart |
||||
for i := 0; ; { |
||||
jt := x.joinType() |
||||
if s[i:i+sz] == zwj { |
||||
jt = joinZWJ |
||||
} else if s[i:i+sz] == zwnj { |
||||
jt = joinZWNJ |
||||
} |
||||
st = joinStates[st][jt] |
||||
if x.isViramaModifier() { |
||||
st = joinStates[st][joinVirama] |
||||
} |
||||
if i += sz; i == len(s) { |
||||
break |
||||
} |
||||
v, sz = trie.lookupString(s[i:]) |
||||
x = info(v) |
||||
} |
||||
if st == stateFAIL || st == stateAfter { |
||||
return &labelError{s, "C"} |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func ascii(s string) bool { |
||||
for i := 0; i < len(s); i++ { |
||||
if s[i] >= utf8.RuneSelf { |
||||
return false |
||||
} |
||||
} |
||||
return true |
||||
} |
@ -0,0 +1,203 @@ |
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package idna |
||||
|
||||
// This file implements the Punycode algorithm from RFC 3492.
|
||||
|
||||
import ( |
||||
"math" |
||||
"strings" |
||||
"unicode/utf8" |
||||
) |
||||
|
||||
// These parameter values are specified in section 5.
|
||||
//
|
||||
// All computation is done with int32s, so that overflow behavior is identical
|
||||
// regardless of whether int is 32-bit or 64-bit.
|
||||
const ( |
||||
base int32 = 36 |
||||
damp int32 = 700 |
||||
initialBias int32 = 72 |
||||
initialN int32 = 128 |
||||
skew int32 = 38 |
||||
tmax int32 = 26 |
||||
tmin int32 = 1 |
||||
) |
||||
|
||||
func punyError(s string) error { return &labelError{s, "A3"} } |
||||
|
||||
// decode decodes a string as specified in section 6.2.
|
||||
func decode(encoded string) (string, error) { |
||||
if encoded == "" { |
||||
return "", nil |
||||
} |
||||
pos := 1 + strings.LastIndex(encoded, "-") |
||||
if pos == 1 { |
||||
return "", punyError(encoded) |
||||
} |
||||
if pos == len(encoded) { |
||||
return encoded[:len(encoded)-1], nil |
||||
} |
||||
output := make([]rune, 0, len(encoded)) |
||||
if pos != 0 { |
||||
for _, r := range encoded[:pos-1] { |
||||
output = append(output, r) |
||||
} |
||||
} |
||||
i, n, bias := int32(0), initialN, initialBias |
||||
for pos < len(encoded) { |
||||
oldI, w := i, int32(1) |
||||
for k := base; ; k += base { |
||||
if pos == len(encoded) { |
||||
return "", punyError(encoded) |
||||
} |
||||
digit, ok := decodeDigit(encoded[pos]) |
||||
if !ok { |
||||
return "", punyError(encoded) |
||||
} |
||||
pos++ |
||||
i += digit * w |
||||
if i < 0 { |
||||
return "", punyError(encoded) |
||||
} |
||||
t := k - bias |
||||
if t < tmin { |
||||
t = tmin |
||||
} else if t > tmax { |
||||
t = tmax |
||||
} |
||||
if digit < t { |
||||
break |
||||
} |
||||
w *= base - t |
||||
if w >= math.MaxInt32/base { |
||||
return "", punyError(encoded) |
||||
} |
||||
} |
||||
x := int32(len(output) + 1) |
||||
bias = adapt(i-oldI, x, oldI == 0) |
||||
n += i / x |
||||
i %= x |
||||
if n > utf8.MaxRune || len(output) >= 1024 { |
||||
return "", punyError(encoded) |
||||
} |
||||
output = append(output, 0) |
||||
copy(output[i+1:], output[i:]) |
||||
output[i] = n |
||||
i++ |
||||
} |
||||
return string(output), nil |
||||
} |
||||
|
||||
// encode encodes a string as specified in section 6.3 and prepends prefix to
|
||||
// the result.
|
||||
//
|
||||
// The "while h < length(input)" line in the specification becomes "for
|
||||
// remaining != 0" in the Go code, because len(s) in Go is in bytes, not runes.
|
||||
func encode(prefix, s string) (string, error) { |
||||
output := make([]byte, len(prefix), len(prefix)+1+2*len(s)) |
||||
copy(output, prefix) |
||||
delta, n, bias := int32(0), initialN, initialBias |
||||
b, remaining := int32(0), int32(0) |
||||
for _, r := range s { |
||||
if r < 0x80 { |
||||
b++ |
||||
output = append(output, byte(r)) |
||||
} else { |
||||
remaining++ |
||||
} |
||||
} |
||||
h := b |
||||
if b > 0 { |
||||
output = append(output, '-') |
||||
} |
||||
for remaining != 0 { |
||||
m := int32(0x7fffffff) |
||||
for _, r := range s { |
||||
if m > r && r >= n { |
||||
m = r |
||||
} |
||||
} |
||||
delta += (m - n) * (h + 1) |
||||
if delta < 0 { |
||||
return "", punyError(s) |
||||
} |
||||
n = m |
||||
for _, r := range s { |
||||
if r < n { |
||||
delta++ |
||||
if delta < 0 { |
||||
return "", punyError(s) |
||||
} |
||||
continue |
||||
} |
||||
if r > n { |
||||
continue |
||||
} |
||||
q := delta |
||||
for k := base; ; k += base { |
||||
t := k - bias |
||||
if t < tmin { |
||||
t = tmin |
||||
} else if t > tmax { |
||||
t = tmax |
||||
} |
||||
if q < t { |
||||
break |
||||
} |
||||
output = append(output, encodeDigit(t+(q-t)%(base-t))) |
||||
q = (q - t) / (base - t) |
||||
} |
||||
output = append(output, encodeDigit(q)) |
||||
bias = adapt(delta, h+1, h == b) |
||||
delta = 0 |
||||
h++ |
||||
remaining-- |
||||
} |
||||
delta++ |
||||
n++ |
||||
} |
||||
return string(output), nil |
||||
} |
||||
|
||||
func decodeDigit(x byte) (digit int32, ok bool) { |
||||
switch { |
||||
case '0' <= x && x <= '9': |
||||
return int32(x - ('0' - 26)), true |
||||
case 'A' <= x && x <= 'Z': |
||||
return int32(x - 'A'), true |
||||
case 'a' <= x && x <= 'z': |
||||
return int32(x - 'a'), true |
||||
} |
||||
return 0, false |
||||
} |
||||
|
||||
func encodeDigit(digit int32) byte { |
||||
switch { |
||||
case 0 <= digit && digit < 26: |
||||
return byte(digit + 'a') |
||||
case 26 <= digit && digit < 36: |
||||
return byte(digit + ('0' - 26)) |
||||
} |
||||
panic("idna: internal error in punycode encoding") |
||||
} |
||||
|
||||
// adapt is the bias adaptation function specified in section 6.1.
|
||||
func adapt(delta, numPoints int32, firstTime bool) int32 { |
||||
if firstTime { |
||||
delta /= damp |
||||
} else { |
||||
delta /= 2 |
||||
} |
||||
delta += delta / numPoints |
||||
k := int32(0) |
||||
for delta > ((base-tmin)*tmax)/2 { |
||||
delta /= base - tmin |
||||
k += base |
||||
} |
||||
return k + (base-tmin+1)*delta/(delta+skew) |
||||
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,72 @@ |
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package idna |
||||
|
||||
// appendMapping appends the mapping for the respective rune. isMapped must be
|
||||
// true. A mapping is a categorization of a rune as defined in UTS #46.
|
||||
func (c info) appendMapping(b []byte, s string) []byte { |
||||
index := int(c >> indexShift) |
||||
if c&xorBit == 0 { |
||||
s := mappings[index:] |
||||
return append(b, s[1:s[0]+1]...) |
||||
} |
||||
b = append(b, s...) |
||||
if c&inlineXOR == inlineXOR { |
||||
// TODO: support and handle two-byte inline masks
|
||||
b[len(b)-1] ^= byte(index) |
||||
} else { |
||||
for p := len(b) - int(xorData[index]); p < len(b); p++ { |
||||
index++ |
||||
b[p] ^= xorData[index] |
||||
} |
||||
} |
||||
return b |
||||
} |
||||
|
||||
// Sparse block handling code.
|
||||
|
||||
type valueRange struct { |
||||
value uint16 // header: value:stride
|
||||
lo, hi byte // header: lo:n
|
||||
} |
||||
|
||||
type sparseBlocks struct { |
||||
values []valueRange |
||||
offset []uint16 |
||||
} |
||||
|
||||
var idnaSparse = sparseBlocks{ |
||||
values: idnaSparseValues[:], |
||||
offset: idnaSparseOffset[:], |
||||
} |
||||
|
||||
// Don't use newIdnaTrie to avoid unconditional linking in of the table.
|
||||
var trie = &idnaTrie{} |
||||
|
||||
// lookup determines the type of block n and looks up the value for b.
|
||||
// For n < t.cutoff, the block is a simple lookup table. Otherwise, the block
|
||||
// is a list of ranges with an accompanying value. Given a matching range r,
|
||||
// the value for b is by r.value + (b - r.lo) * stride.
|
||||
func (t *sparseBlocks) lookup(n uint32, b byte) uint16 { |
||||
offset := t.offset[n] |
||||
header := t.values[offset] |
||||
lo := offset + 1 |
||||
hi := lo + uint16(header.lo) |
||||
for lo < hi { |
||||
m := lo + (hi-lo)/2 |
||||
r := t.values[m] |
||||
if r.lo <= b && b <= r.hi { |
||||
return r.value + uint16(b-r.lo)*header.value |
||||
} |
||||
if b < r.lo { |
||||
hi = m |
||||
} else { |
||||
lo = m + 1 |
||||
} |
||||
} |
||||
return 0 |
||||
} |
@ -0,0 +1,119 @@ |
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
package idna |
||||
|
||||
// This file contains definitions for interpreting the trie value of the idna
|
||||
// trie generated by "go run gen*.go". It is shared by both the generator
|
||||
// program and the resultant package. Sharing is achieved by the generator
|
||||
// copying gen_trieval.go to trieval.go and changing what's above this comment.
|
||||
|
||||
// info holds information from the IDNA mapping table for a single rune. It is
|
||||
// the value returned by a trie lookup. In most cases, all information fits in
|
||||
// a 16-bit value. For mappings, this value may contain an index into a slice
|
||||
// with the mapped string. Such mappings can consist of the actual mapped value
|
||||
// or an XOR pattern to be applied to the bytes of the UTF8 encoding of the
|
||||
// input rune. This technique is used by the cases packages and reduces the
|
||||
// table size significantly.
|
||||
//
|
||||
// The per-rune values have the following format:
|
||||
//
|
||||
// if mapped {
|
||||
// if inlinedXOR {
|
||||
// 15..13 inline XOR marker
|
||||
// 12..11 unused
|
||||
// 10..3 inline XOR mask
|
||||
// } else {
|
||||
// 15..3 index into xor or mapping table
|
||||
// }
|
||||
// } else {
|
||||
// 15..14 unused
|
||||
// 13 mayNeedNorm
|
||||
// 12..11 attributes
|
||||
// 10..8 joining type
|
||||
// 7..3 category type
|
||||
// }
|
||||
// 2 use xor pattern
|
||||
// 1..0 mapped category
|
||||
//
|
||||
// See the definitions below for a more detailed description of the various
|
||||
// bits.
|
||||
type info uint16 |
||||
|
||||
const ( |
||||
catSmallMask = 0x3 |
||||
catBigMask = 0xF8 |
||||
indexShift = 3 |
||||
xorBit = 0x4 // interpret the index as an xor pattern
|
||||
inlineXOR = 0xE000 // These bits are set if the XOR pattern is inlined.
|
||||
|
||||
joinShift = 8 |
||||
joinMask = 0x07 |
||||
|
||||
// Attributes
|
||||
attributesMask = 0x1800 |
||||
viramaModifier = 0x1800 |
||||
modifier = 0x1000 |
||||
rtl = 0x0800 |
||||
|
||||
mayNeedNorm = 0x2000 |
||||
) |
||||
|
||||
// A category corresponds to a category defined in the IDNA mapping table.
|
||||
type category uint16 |
||||
|
||||
const ( |
||||
unknown category = 0 // not currently defined in unicode.
|
||||
mapped category = 1 |
||||
disallowedSTD3Mapped category = 2 |
||||
deviation category = 3 |
||||
) |
||||
|
||||
const ( |
||||
valid category = 0x08 |
||||
validNV8 category = 0x18 |
||||
validXV8 category = 0x28 |
||||
disallowed category = 0x40 |
||||
disallowedSTD3Valid category = 0x80 |
||||
ignored category = 0xC0 |
||||
) |
||||
|
||||
// join types and additional rune information
|
||||
const ( |
||||
joiningL = (iota + 1) |
||||
joiningD |
||||
joiningT |
||||
joiningR |
||||
|
||||
//the following types are derived during processing
|
||||
joinZWJ |
||||
joinZWNJ |
||||
joinVirama |
||||
numJoinTypes |
||||
) |
||||
|
||||
func (c info) isMapped() bool { |
||||
return c&0x3 != 0 |
||||
} |
||||
|
||||
func (c info) category() category { |
||||
small := c & catSmallMask |
||||
if small != 0 { |
||||
return category(small) |
||||
} |
||||
return category(c & catBigMask) |
||||
} |
||||
|
||||
func (c info) joinType() info { |
||||
if c.isMapped() { |
||||
return 0 |
||||
} |
||||
return (c >> joinShift) & joinMask |
||||
} |
||||
|
||||
func (c info) isModifier() bool { |
||||
return c&(modifier|catSmallMask) == modifier |
||||
} |
||||
|
||||
func (c info) isViramaModifier() bool { |
||||
return c&(attributesMask|catSmallMask) == viramaModifier |
||||
} |
@ -0,0 +1,168 @@ |
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package socks |
||||
|
||||
import ( |
||||
"context" |
||||
"errors" |
||||
"io" |
||||
"net" |
||||
"strconv" |
||||
"time" |
||||
) |
||||
|
||||
var ( |
||||
noDeadline = time.Time{} |
||||
aLongTimeAgo = time.Unix(1, 0) |
||||
) |
||||
|
||||
func (d *Dialer) connect(ctx context.Context, c net.Conn, address string) (_ net.Addr, ctxErr error) { |
||||
host, port, err := splitHostPort(address) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() { |
||||
c.SetDeadline(deadline) |
||||
defer c.SetDeadline(noDeadline) |
||||
} |
||||
if ctx != context.Background() { |
||||
errCh := make(chan error, 1) |
||||
done := make(chan struct{}) |
||||
defer func() { |
||||
close(done) |
||||
if ctxErr == nil { |
||||
ctxErr = <-errCh |
||||
} |
||||
}() |
||||
go func() { |
||||
select { |
||||
case <-ctx.Done(): |
||||
c.SetDeadline(aLongTimeAgo) |
||||
errCh <- ctx.Err() |
||||
case <-done: |
||||
errCh <- nil |
||||
} |
||||
}() |
||||
} |
||||
|
||||
b := make([]byte, 0, 6+len(host)) // the size here is just an estimate
|
||||
b = append(b, Version5) |
||||
if len(d.AuthMethods) == 0 || d.Authenticate == nil { |
||||
b = append(b, 1, byte(AuthMethodNotRequired)) |
||||
} else { |
||||
ams := d.AuthMethods |
||||
if len(ams) > 255 { |
||||
return nil, errors.New("too many authentication methods") |
||||
} |
||||
b = append(b, byte(len(ams))) |
||||
for _, am := range ams { |
||||
b = append(b, byte(am)) |
||||
} |
||||
} |
||||
if _, ctxErr = c.Write(b); ctxErr != nil { |
||||
return |
||||
} |
||||
|
||||
if _, ctxErr = io.ReadFull(c, b[:2]); ctxErr != nil { |
||||
return |
||||
} |
||||
if b[0] != Version5 { |
||||
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0]))) |
||||
} |
||||
am := AuthMethod(b[1]) |
||||
if am == AuthMethodNoAcceptableMethods { |
||||
return nil, errors.New("no acceptable authentication methods") |
||||
} |
||||
if d.Authenticate != nil { |
||||
if ctxErr = d.Authenticate(ctx, c, am); ctxErr != nil { |
||||
return |
||||
} |
||||
} |
||||
|
||||
b = b[:0] |
||||
b = append(b, Version5, byte(d.cmd), 0) |
||||
if ip := net.ParseIP(host); ip != nil { |
||||
if ip4 := ip.To4(); ip4 != nil { |
||||
b = append(b, AddrTypeIPv4) |
||||
b = append(b, ip4...) |
||||
} else if ip6 := ip.To16(); ip6 != nil { |
||||
b = append(b, AddrTypeIPv6) |
||||
b = append(b, ip6...) |
||||
} else { |
||||
return nil, errors.New("unknown address type") |
||||
} |
||||
} else { |
||||
if len(host) > 255 { |
||||
return nil, errors.New("FQDN too long") |
||||
} |
||||
b = append(b, AddrTypeFQDN) |
||||
b = append(b, byte(len(host))) |
||||
b = append(b, host...) |
||||
} |
||||
b = append(b, byte(port>>8), byte(port)) |
||||
if _, ctxErr = c.Write(b); ctxErr != nil { |
||||
return |
||||
} |
||||
|
||||
if _, ctxErr = io.ReadFull(c, b[:4]); ctxErr != nil { |
||||
return |
||||
} |
||||
if b[0] != Version5 { |
||||
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0]))) |
||||
} |
||||
if cmdErr := Reply(b[1]); cmdErr != StatusSucceeded { |
||||
return nil, errors.New("unknown error " + cmdErr.String()) |
||||
} |
||||
if b[2] != 0 { |
||||
return nil, errors.New("non-zero reserved field") |
||||
} |
||||
l := 2 |
||||
var a Addr |
||||
switch b[3] { |
||||
case AddrTypeIPv4: |
||||
l += net.IPv4len |
||||
a.IP = make(net.IP, net.IPv4len) |
||||
case AddrTypeIPv6: |
||||
l += net.IPv6len |
||||
a.IP = make(net.IP, net.IPv6len) |
||||
case AddrTypeFQDN: |
||||
if _, err := io.ReadFull(c, b[:1]); err != nil { |
||||
return nil, err |
||||
} |
||||
l += int(b[0]) |
||||
default: |
||||
return nil, errors.New("unknown address type " + strconv.Itoa(int(b[3]))) |
||||
} |
||||
if cap(b) < l { |
||||
b = make([]byte, l) |
||||
} else { |
||||
b = b[:l] |
||||
} |
||||
if _, ctxErr = io.ReadFull(c, b); ctxErr != nil { |
||||
return |
||||
} |
||||
if a.IP != nil { |
||||
copy(a.IP, b) |
||||
} else { |
||||
a.Name = string(b[:len(b)-2]) |
||||
} |
||||
a.Port = int(b[len(b)-2])<<8 | int(b[len(b)-1]) |
||||
return &a, nil |
||||
} |
||||
|
||||
func splitHostPort(address string) (string, int, error) { |
||||
host, port, err := net.SplitHostPort(address) |
||||
if err != nil { |
||||
return "", 0, err |
||||
} |
||||
portnum, err := strconv.Atoi(port) |
||||
if err != nil { |
||||
return "", 0, err |
||||
} |
||||
if 1 > portnum || portnum > 0xffff { |
||||
return "", 0, errors.New("port number out of range " + port) |
||||
} |
||||
return host, portnum, nil |
||||
} |
@ -0,0 +1,317 @@ |
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package socks provides a SOCKS version 5 client implementation.
|
||||
//
|
||||
// SOCKS protocol version 5 is defined in RFC 1928.
|
||||
// Username/Password authentication for SOCKS version 5 is defined in
|
||||
// RFC 1929.
|
||||
package socks |
||||
|
||||
import ( |
||||
"context" |
||||
"errors" |
||||
"io" |
||||
"net" |
||||
"strconv" |
||||
) |
||||
|
||||
// A Command represents a SOCKS command.
|
||||
type Command int |
||||
|
||||
func (cmd Command) String() string { |
||||
switch cmd { |
||||
case CmdConnect: |
||||
return "socks connect" |
||||
case cmdBind: |
||||
return "socks bind" |
||||
default: |
||||
return "socks " + strconv.Itoa(int(cmd)) |
||||
} |
||||
} |
||||
|
||||
// An AuthMethod represents a SOCKS authentication method.
|
||||
type AuthMethod int |
||||
|
||||
// A Reply represents a SOCKS command reply code.
|
||||
type Reply int |
||||
|
||||
func (code Reply) String() string { |
||||
switch code { |
||||
case StatusSucceeded: |
||||
return "succeeded" |
||||
case 0x01: |
||||
return "general SOCKS server failure" |
||||
case 0x02: |
||||
return "connection not allowed by ruleset" |
||||
case 0x03: |
||||
return "network unreachable" |
||||
case 0x04: |
||||
return "host unreachable" |
||||
case 0x05: |
||||
return "connection refused" |
||||
case 0x06: |
||||
return "TTL expired" |
||||
case 0x07: |
||||
return "command not supported" |
||||
case 0x08: |
||||
return "address type not supported" |
||||
default: |
||||
return "unknown code: " + strconv.Itoa(int(code)) |
||||
} |
||||
} |
||||
|
||||
// Wire protocol constants.
|
||||
const ( |
||||
Version5 = 0x05 |
||||
|
||||
AddrTypeIPv4 = 0x01 |
||||
AddrTypeFQDN = 0x03 |
||||
AddrTypeIPv6 = 0x04 |
||||
|
||||
CmdConnect Command = 0x01 // establishes an active-open forward proxy connection
|
||||
cmdBind Command = 0x02 // establishes a passive-open forward proxy connection
|
||||
|
||||
AuthMethodNotRequired AuthMethod = 0x00 // no authentication required
|
||||
AuthMethodUsernamePassword AuthMethod = 0x02 // use username/password
|
||||
AuthMethodNoAcceptableMethods AuthMethod = 0xff // no acceptable authentication methods
|
||||
|
||||
StatusSucceeded Reply = 0x00 |
||||
) |
||||
|
||||
// An Addr represents a SOCKS-specific address.
|
||||
// Either Name or IP is used exclusively.
|
||||
type Addr struct { |
||||
Name string // fully-qualified domain name
|
||||
IP net.IP |
||||
Port int |
||||
} |
||||
|
||||
func (a *Addr) Network() string { return "socks" } |
||||
|
||||
func (a *Addr) String() string { |
||||
if a == nil { |
||||
return "<nil>" |
||||
} |
||||
port := strconv.Itoa(a.Port) |
||||
if a.IP == nil { |
||||
return net.JoinHostPort(a.Name, port) |
||||
} |
||||
return net.JoinHostPort(a.IP.String(), port) |
||||
} |
||||
|
||||
// A Conn represents a forward proxy connection.
|
||||
type Conn struct { |
||||
net.Conn |
||||
|
||||
boundAddr net.Addr |
||||
} |
||||
|
||||
// BoundAddr returns the address assigned by the proxy server for
|
||||
// connecting to the command target address from the proxy server.
|
||||
func (c *Conn) BoundAddr() net.Addr { |
||||
if c == nil { |
||||
return nil |
||||
} |
||||
return c.boundAddr |
||||
} |
||||
|
||||
// A Dialer holds SOCKS-specific options.
|
||||
type Dialer struct { |
||||
cmd Command // either CmdConnect or cmdBind
|
||||
proxyNetwork string // network between a proxy server and a client
|
||||
proxyAddress string // proxy server address
|
||||
|
||||
// ProxyDial specifies the optional dial function for
|
||||
// establishing the transport connection.
|
||||
ProxyDial func(context.Context, string, string) (net.Conn, error) |
||||
|
||||
// AuthMethods specifies the list of request authention
|
||||
// methods.
|
||||
// If empty, SOCKS client requests only AuthMethodNotRequired.
|
||||
AuthMethods []AuthMethod |
||||
|
||||
// Authenticate specifies the optional authentication
|
||||
// function. It must be non-nil when AuthMethods is not empty.
|
||||
// It must return an error when the authentication is failed.
|
||||
Authenticate func(context.Context, io.ReadWriter, AuthMethod) error |
||||
} |
||||
|
||||
// DialContext connects to the provided address on the provided
|
||||
// network.
|
||||
//
|
||||
// The returned error value may be a net.OpError. When the Op field of
|
||||
// net.OpError contains "socks", the Source field contains a proxy
|
||||
// server address and the Addr field contains a command target
|
||||
// address.
|
||||
//
|
||||
// See func Dial of the net package of standard library for a
|
||||
// description of the network and address parameters.
|
||||
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { |
||||
if err := d.validateTarget(network, address); err != nil { |
||||
proxy, dst, _ := d.pathAddrs(address) |
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} |
||||
} |
||||
if ctx == nil { |
||||
proxy, dst, _ := d.pathAddrs(address) |
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")} |
||||
} |
||||
var err error |
||||
var c net.Conn |
||||
if d.ProxyDial != nil { |
||||
c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress) |
||||
} else { |
||||
var dd net.Dialer |
||||
c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress) |
||||
} |
||||
if err != nil { |
||||
proxy, dst, _ := d.pathAddrs(address) |
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} |
||||
} |
||||
a, err := d.connect(ctx, c, address) |
||||
if err != nil { |
||||
c.Close() |
||||
proxy, dst, _ := d.pathAddrs(address) |
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} |
||||
} |
||||
return &Conn{Conn: c, boundAddr: a}, nil |
||||
} |
||||
|
||||
// DialWithConn initiates a connection from SOCKS server to the target
|
||||
// network and address using the connection c that is already
|
||||
// connected to the SOCKS server.
|
||||
//
|
||||
// It returns the connection's local address assigned by the SOCKS
|
||||
// server.
|
||||
func (d *Dialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) { |
||||
if err := d.validateTarget(network, address); err != nil { |
||||
proxy, dst, _ := d.pathAddrs(address) |
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} |
||||
} |
||||
if ctx == nil { |
||||
proxy, dst, _ := d.pathAddrs(address) |
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")} |
||||
} |
||||
a, err := d.connect(ctx, c, address) |
||||
if err != nil { |
||||
proxy, dst, _ := d.pathAddrs(address) |
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} |
||||
} |
||||
return a, nil |
||||
} |
||||
|
||||
// Dial connects to the provided address on the provided network.
|
||||
//
|
||||
// Unlike DialContext, it returns a raw transport connection instead
|
||||
// of a forward proxy connection.
|
||||
//
|
||||
// Deprecated: Use DialContext or DialWithConn instead.
|
||||
func (d *Dialer) Dial(network, address string) (net.Conn, error) { |
||||
if err := d.validateTarget(network, address); err != nil { |
||||
proxy, dst, _ := d.pathAddrs(address) |
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} |
||||
} |
||||
var err error |
||||
var c net.Conn |
||||
if d.ProxyDial != nil { |
||||
c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress) |
||||
} else { |
||||
c, err = net.Dial(d.proxyNetwork, d.proxyAddress) |
||||
} |
||||
if err != nil { |
||||
proxy, dst, _ := d.pathAddrs(address) |
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} |
||||
} |
||||
if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil { |
||||
c.Close() |
||||
return nil, err |
||||
} |
||||
return c, nil |
||||
} |
||||
|
||||
func (d *Dialer) validateTarget(network, address string) error { |
||||
switch network { |
||||
case "tcp", "tcp6", "tcp4": |
||||
default: |
||||
return errors.New("network not implemented") |
||||
} |
||||
switch d.cmd { |
||||
case CmdConnect, cmdBind: |
||||
default: |
||||
return errors.New("command not implemented") |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func (d *Dialer) pathAddrs(address string) (proxy, dst net.Addr, err error) { |
||||
for i, s := range []string{d.proxyAddress, address} { |
||||
host, port, err := splitHostPort(s) |
||||
if err != nil { |
||||
return nil, nil, err |
||||
} |
||||
a := &Addr{Port: port} |
||||
a.IP = net.ParseIP(host) |
||||
if a.IP == nil { |
||||
a.Name = host |
||||
} |
||||
if i == 0 { |
||||
proxy = a |
||||
} else { |
||||
dst = a |
||||
} |
||||
} |
||||
return |
||||
} |
||||
|
||||
// NewDialer returns a new Dialer that dials through the provided
|
||||
// proxy server's network and address.
|
||||
func NewDialer(network, address string) *Dialer { |
||||
return &Dialer{proxyNetwork: network, proxyAddress: address, cmd: CmdConnect} |
||||
} |
||||
|
||||
const ( |
||||
authUsernamePasswordVersion = 0x01 |
||||
authStatusSucceeded = 0x00 |
||||
) |
||||
|
||||
// UsernamePassword are the credentials for the username/password
|
||||
// authentication method.
|
||||
type UsernamePassword struct { |
||||
Username string |
||||
Password string |
||||
} |
||||
|
||||
// Authenticate authenticates a pair of username and password with the
|
||||
// proxy server.
|
||||
func (up *UsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth AuthMethod) error { |
||||
switch auth { |
||||
case AuthMethodNotRequired: |
||||
return nil |
||||
case AuthMethodUsernamePassword: |
||||
if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) == 0 || len(up.Password) > 255 { |
||||
return errors.New("invalid username/password") |
||||
} |
||||
b := []byte{authUsernamePasswordVersion} |
||||
b = append(b, byte(len(up.Username))) |
||||
b = append(b, up.Username...) |
||||
b = append(b, byte(len(up.Password))) |
||||
b = append(b, up.Password...) |
||||
// TODO(mikio): handle IO deadlines and cancelation if
|
||||
// necessary
|
||||
if _, err := rw.Write(b); err != nil { |
||||
return err |
||||
} |
||||
if _, err := io.ReadFull(rw, b[:2]); err != nil { |
||||
return err |
||||
} |
||||
if b[0] != authUsernamePasswordVersion { |
||||
return errors.New("invalid username/password version") |
||||
} |
||||
if b[1] != authStatusSucceeded { |
||||
return errors.New("username/password authentication failed") |
||||
} |
||||
return nil |
||||
} |
||||
return errors.New("unsupported authentication method " + strconv.Itoa(int(auth))) |
||||
} |
@ -0,0 +1,54 @@ |
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proxy |
||||
|
||||
import ( |
||||
"context" |
||||
"net" |
||||
) |
||||
|
||||
// A ContextDialer dials using a context.
|
||||
type ContextDialer interface { |
||||
DialContext(ctx context.Context, network, address string) (net.Conn, error) |
||||
} |
||||
|
||||
// Dial works like DialContext on net.Dialer but using a dialer returned by FromEnvironment.
|
||||
//
|
||||
// The passed ctx is only used for returning the Conn, not the lifetime of the Conn.
|
||||
//
|
||||
// Custom dialers (registered via RegisterDialerType) that do not implement ContextDialer
|
||||
// can leak a goroutine for as long as it takes the underlying Dialer implementation to timeout.
|
||||
//
|
||||
// A Conn returned from a successful Dial after the context has been cancelled will be immediately closed.
|
||||
func Dial(ctx context.Context, network, address string) (net.Conn, error) { |
||||
d := FromEnvironment() |
||||
if xd, ok := d.(ContextDialer); ok { |
||||
return xd.DialContext(ctx, network, address) |
||||
} |
||||
return dialContext(ctx, d, network, address) |
||||
} |
||||
|
||||
// WARNING: this can leak a goroutine for as long as the underlying Dialer implementation takes to timeout
|
||||
// A Conn returned from a successful Dial after the context has been cancelled will be immediately closed.
|
||||
func dialContext(ctx context.Context, d Dialer, network, address string) (net.Conn, error) { |
||||
var ( |
||||
conn net.Conn |
||||
done = make(chan struct{}, 1) |
||||
err error |
||||
) |
||||
go func() { |
||||
conn, err = d.Dial(network, address) |
||||
close(done) |
||||
if conn != nil && ctx.Err() != nil { |
||||
conn.Close() |
||||
} |
||||
}() |
||||
select { |
||||
case <-ctx.Done(): |
||||
err = ctx.Err() |
||||
case <-done: |
||||
} |
||||
return conn, err |
||||
} |
@ -0,0 +1,31 @@ |
||||
// 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 proxy |
||||
|
||||
import ( |
||||
"context" |
||||
"net" |
||||
) |
||||
|
||||
type direct struct{} |
||||
|
||||
// Direct implements Dialer by making network connections directly using net.Dial or net.DialContext.
|
||||
var Direct = direct{} |
||||
|
||||
var ( |
||||
_ Dialer = Direct |
||||
_ ContextDialer = Direct |
||||
) |
||||
|
||||
// Dial directly invokes net.Dial with the supplied parameters.
|
||||
func (direct) Dial(network, addr string) (net.Conn, error) { |
||||
return net.Dial(network, addr) |
||||
} |
||||
|
||||
// DialContext instantiates a net.Dialer and invokes its DialContext receiver with the supplied parameters.
|
||||
func (direct) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { |
||||
var d net.Dialer |
||||
return d.DialContext(ctx, network, addr) |
||||
} |
@ -0,0 +1,155 @@ |
||||
// 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 proxy |
||||
|
||||
import ( |
||||
"context" |
||||
"net" |
||||
"strings" |
||||
) |
||||
|
||||
// A PerHost directs connections to a default Dialer unless the host name
|
||||
// requested matches one of a number of exceptions.
|
||||
type PerHost struct { |
||||
def, bypass Dialer |
||||
|
||||
bypassNetworks []*net.IPNet |
||||
bypassIPs []net.IP |
||||
bypassZones []string |
||||
bypassHosts []string |
||||
} |
||||
|
||||
// NewPerHost returns a PerHost Dialer that directs connections to either
|
||||
// defaultDialer or bypass, depending on whether the connection matches one of
|
||||
// the configured rules.
|
||||
func NewPerHost(defaultDialer, bypass Dialer) *PerHost { |
||||
return &PerHost{ |
||||
def: defaultDialer, |
||||
bypass: bypass, |
||||
} |
||||
} |
||||
|
||||
// Dial connects to the address addr on the given network through either
|
||||
// defaultDialer or bypass.
|
||||
func (p *PerHost) Dial(network, addr string) (c net.Conn, err error) { |
||||
host, _, err := net.SplitHostPort(addr) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return p.dialerForRequest(host).Dial(network, addr) |
||||
} |
||||
|
||||
// DialContext connects to the address addr on the given network through either
|
||||
// defaultDialer or bypass.
|
||||
func (p *PerHost) DialContext(ctx context.Context, network, addr string) (c net.Conn, err error) { |
||||
host, _, err := net.SplitHostPort(addr) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
d := p.dialerForRequest(host) |
||||
if x, ok := d.(ContextDialer); ok { |
||||
return x.DialContext(ctx, network, addr) |
||||
} |
||||
return dialContext(ctx, d, network, addr) |
||||
} |
||||
|
||||
func (p *PerHost) dialerForRequest(host string) Dialer { |
||||
if ip := net.ParseIP(host); ip != nil { |
||||
for _, net := range p.bypassNetworks { |
||||
if net.Contains(ip) { |
||||
return p.bypass |
||||
} |
||||
} |
||||
for _, bypassIP := range p.bypassIPs { |
||||
if bypassIP.Equal(ip) { |
||||
return p.bypass |
||||
} |
||||
} |
||||
return p.def |
||||
} |
||||
|
||||
for _, zone := range p.bypassZones { |
||||
if strings.HasSuffix(host, zone) { |
||||
return p.bypass |
||||
} |
||||
if host == zone[1:] { |
||||
// For a zone ".example.com", we match "example.com"
|
||||
// too.
|
||||
return p.bypass |
||||
} |
||||
} |
||||
for _, bypassHost := range p.bypassHosts { |
||||
if bypassHost == host { |
||||
return p.bypass |
||||
} |
||||
} |
||||
return p.def |
||||
} |
||||
|
||||
// AddFromString parses a string that contains comma-separated values
|
||||
// specifying hosts that should use the bypass proxy. Each value is either an
|
||||
// IP address, a CIDR range, a zone (*.example.com) or a host name
|
||||
// (localhost). A best effort is made to parse the string and errors are
|
||||
// ignored.
|
||||
func (p *PerHost) AddFromString(s string) { |
||||
hosts := strings.Split(s, ",") |
||||
for _, host := range hosts { |
||||
host = strings.TrimSpace(host) |
||||
if len(host) == 0 { |
||||
continue |
||||
} |
||||
if strings.Contains(host, "/") { |
||||
// We assume that it's a CIDR address like 127.0.0.0/8
|
||||
if _, net, err := net.ParseCIDR(host); err == nil { |
||||
p.AddNetwork(net) |
||||
} |
||||
continue |
||||
} |
||||
if ip := net.ParseIP(host); ip != nil { |
||||
p.AddIP(ip) |
||||
continue |
||||
} |
||||
if strings.HasPrefix(host, "*.") { |
||||
p.AddZone(host[1:]) |
||||
continue |
||||
} |
||||
p.AddHost(host) |
||||
} |
||||
} |
||||
|
||||
// AddIP specifies an IP address that will use the bypass proxy. Note that
|
||||
// this will only take effect if a literal IP address is dialed. A connection
|
||||
// to a named host will never match an IP.
|
||||
func (p *PerHost) AddIP(ip net.IP) { |
||||
p.bypassIPs = append(p.bypassIPs, ip) |
||||
} |
||||
|
||||
// AddNetwork specifies an IP range that will use the bypass proxy. Note that
|
||||
// this will only take effect if a literal IP address is dialed. A connection
|
||||
// to a named host will never match.
|
||||
func (p *PerHost) AddNetwork(net *net.IPNet) { |
||||
p.bypassNetworks = append(p.bypassNetworks, net) |
||||
} |
||||
|
||||
// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
|
||||
// "example.com" matches "example.com" and all of its subdomains.
|
||||
func (p *PerHost) AddZone(zone string) { |
||||
if strings.HasSuffix(zone, ".") { |
||||
zone = zone[:len(zone)-1] |
||||
} |
||||
if !strings.HasPrefix(zone, ".") { |
||||
zone = "." + zone |
||||
} |
||||
p.bypassZones = append(p.bypassZones, zone) |
||||
} |
||||
|
||||
// AddHost specifies a host name that will use the bypass proxy.
|
||||
func (p *PerHost) AddHost(host string) { |
||||
if strings.HasSuffix(host, ".") { |
||||
host = host[:len(host)-1] |
||||
} |
||||
p.bypassHosts = append(p.bypassHosts, host) |
||||
} |
@ -0,0 +1,149 @@ |
||||
// 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 proxy provides support for a variety of protocols to proxy network
|
||||
// data.
|
||||
package proxy // import "golang.org/x/net/proxy"
|
||||
|
||||
import ( |
||||
"errors" |
||||
"net" |
||||
"net/url" |
||||
"os" |
||||
"sync" |
||||
) |
||||
|
||||
// A Dialer is a means to establish a connection.
|
||||
// Custom dialers should also implement ContextDialer.
|
||||
type Dialer interface { |
||||
// Dial connects to the given address via the proxy.
|
||||
Dial(network, addr string) (c net.Conn, err error) |
||||
} |
||||
|
||||
// Auth contains authentication parameters that specific Dialers may require.
|
||||
type Auth struct { |
||||
User, Password string |
||||
} |
||||
|
||||
// FromEnvironment returns the dialer specified by the proxy-related
|
||||
// variables in the environment and makes underlying connections
|
||||
// directly.
|
||||
func FromEnvironment() Dialer { |
||||
return FromEnvironmentUsing(Direct) |
||||
} |
||||
|
||||
// FromEnvironmentUsing returns the dialer specify by the proxy-related
|
||||
// variables in the environment and makes underlying connections
|
||||
// using the provided forwarding Dialer (for instance, a *net.Dialer
|
||||
// with desired configuration).
|
||||
func FromEnvironmentUsing(forward Dialer) Dialer { |
||||
allProxy := allProxyEnv.Get() |
||||
if len(allProxy) == 0 { |
||||
return forward |
||||
} |
||||
|
||||
proxyURL, err := url.Parse(allProxy) |
||||
if err != nil { |
||||
return forward |
||||
} |
||||
proxy, err := FromURL(proxyURL, forward) |
||||
if err != nil { |
||||
return forward |
||||
} |
||||
|
||||
noProxy := noProxyEnv.Get() |
||||
if len(noProxy) == 0 { |
||||
return proxy |
||||
} |
||||
|
||||
perHost := NewPerHost(proxy, forward) |
||||
perHost.AddFromString(noProxy) |
||||
return perHost |
||||
} |
||||
|
||||
// proxySchemes is a map from URL schemes to a function that creates a Dialer
|
||||
// from a URL with such a scheme.
|
||||
var proxySchemes map[string]func(*url.URL, Dialer) (Dialer, error) |
||||
|
||||
// RegisterDialerType takes a URL scheme and a function to generate Dialers from
|
||||
// a URL with that scheme and a forwarding Dialer. Registered schemes are used
|
||||
// by FromURL.
|
||||
func RegisterDialerType(scheme string, f func(*url.URL, Dialer) (Dialer, error)) { |
||||
if proxySchemes == nil { |
||||
proxySchemes = make(map[string]func(*url.URL, Dialer) (Dialer, error)) |
||||
} |
||||
proxySchemes[scheme] = f |
||||
} |
||||
|
||||
// FromURL returns a Dialer given a URL specification and an underlying
|
||||
// Dialer for it to make network requests.
|
||||
func FromURL(u *url.URL, forward Dialer) (Dialer, error) { |
||||
var auth *Auth |
||||
if u.User != nil { |
||||
auth = new(Auth) |
||||
auth.User = u.User.Username() |
||||
if p, ok := u.User.Password(); ok { |
||||
auth.Password = p |
||||
} |
||||
} |
||||
|
||||
switch u.Scheme { |
||||
case "socks5", "socks5h": |
||||
addr := u.Hostname() |
||||
port := u.Port() |
||||
if port == "" { |
||||
port = "1080" |
||||
} |
||||
return SOCKS5("tcp", net.JoinHostPort(addr, port), auth, forward) |
||||
} |
||||
|
||||
// If the scheme doesn't match any of the built-in schemes, see if it
|
||||
// was registered by another package.
|
||||
if proxySchemes != nil { |
||||
if f, ok := proxySchemes[u.Scheme]; ok { |
||||
return f(u, forward) |
||||
} |
||||
} |
||||
|
||||
return nil, errors.New("proxy: unknown scheme: " + u.Scheme) |
||||
} |
||||
|
||||
var ( |
||||
allProxyEnv = &envOnce{ |
||||
names: []string{"ALL_PROXY", "all_proxy"}, |
||||
} |
||||
noProxyEnv = &envOnce{ |
||||
names: []string{"NO_PROXY", "no_proxy"}, |
||||
} |
||||
) |
||||
|
||||
// envOnce looks up an environment variable (optionally by multiple
|
||||
// names) once. It mitigates expensive lookups on some platforms
|
||||
// (e.g. Windows).
|
||||
// (Borrowed from net/http/transport.go)
|
||||
type envOnce struct { |
||||
names []string |
||||
once sync.Once |
||||
val string |
||||
} |
||||
|
||||
func (e *envOnce) Get() string { |
||||
e.once.Do(e.init) |
||||
return e.val |
||||
} |
||||
|
||||
func (e *envOnce) init() { |
||||
for _, n := range e.names { |
||||
e.val = os.Getenv(n) |
||||
if e.val != "" { |
||||
return |
||||
} |
||||
} |
||||
} |
||||
|
||||
// reset is used by tests
|
||||
func (e *envOnce) reset() { |
||||
e.once = sync.Once{} |
||||
e.val = "" |
||||
} |
@ -0,0 +1,42 @@ |
||||
// 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 proxy |
||||
|
||||
import ( |
||||
"context" |
||||
"net" |
||||
|
||||
"golang.org/x/net/internal/socks" |
||||
) |
||||
|
||||
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given
|
||||
// address with an optional username and password.
|
||||
// See RFC 1928 and RFC 1929.
|
||||
func SOCKS5(network, address string, auth *Auth, forward Dialer) (Dialer, error) { |
||||
d := socks.NewDialer(network, address) |
||||
if forward != nil { |
||||
if f, ok := forward.(ContextDialer); ok { |
||||
d.ProxyDial = func(ctx context.Context, network string, address string) (net.Conn, error) { |
||||
return f.DialContext(ctx, network, address) |
||||
} |
||||
} else { |
||||
d.ProxyDial = func(ctx context.Context, network string, address string) (net.Conn, error) { |
||||
return dialContext(ctx, forward, network, address) |
||||
} |
||||
} |
||||
} |
||||
if auth != nil { |
||||
up := socks.UsernamePassword{ |
||||
Username: auth.User, |
||||
Password: auth.Password, |
||||
} |
||||
d.AuthMethods = []socks.AuthMethod{ |
||||
socks.AuthMethodNotRequired, |
||||
socks.AuthMethodUsernamePassword, |
||||
} |
||||
d.Authenticate = up.Authenticate |
||||
} |
||||
return d, nil |
||||
} |
@ -0,0 +1,17 @@ |
||||
// Copyright 2018 The Go Authors. All rights reserved. |
||||
// Use of this source code is governed by a BSD-style |
||||
// license that can be found in the LICENSE file. |
||||
|
||||
// +build !gccgo |
||||
|
||||
#include "textflag.h" |
||||
|
||||
// |
||||
// System calls for ppc64, AIX are implemented in runtime/syscall_aix.go |
||||
// |
||||
|
||||
TEXT ·syscall6(SB),NOSPLIT,$0-88 |
||||
JMP syscall·syscall6(SB) |
||||
|
||||
TEXT ·rawSyscall6(SB),NOSPLIT,$0-88 |
||||
JMP syscall·rawSyscall6(SB) |
@ -0,0 +1,36 @@ |
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Minimal copy of x/sys/unix so the cpu package can make a
|
||||
// system call on AIX without depending on x/sys/unix.
|
||||
// (See golang.org/issue/32102)
|
||||
|
||||
// +build aix,ppc64
|
||||
// +build !gccgo
|
||||
|
||||
package cpu |
||||
|
||||
import ( |
||||
"syscall" |
||||
"unsafe" |
||||
) |
||||
|
||||
//go:cgo_import_dynamic libc_getsystemcfg getsystemcfg "libc.a/shr_64.o"
|
||||
|
||||
//go:linkname libc_getsystemcfg libc_getsystemcfg
|
||||
|
||||
type syscallFunc uintptr |
||||
|
||||
var libc_getsystemcfg syscallFunc |
||||
|
||||
type errno = syscall.Errno |
||||
|
||||
// Implemented in runtime/syscall_aix.go.
|
||||
func rawSyscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err errno) |
||||
func syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err errno) |
||||
|
||||
func callgetsystemcfg(label int) (r1 uintptr, e1 errno) { |
||||
r1, _, e1 = syscall6(uintptr(unsafe.Pointer(&libc_getsystemcfg)), 1, uintptr(label), 0, 0, 0, 0, 0) |
||||
return |
||||
} |
@ -0,0 +1,54 @@ |
||||
// Copyright 2019 The Go Authors. All rights reserved. |
||||
// Use of this source code is governed by a BSD-style |
||||
// license that can be found in the LICENSE file. |
||||
|
||||
// +build riscv64,!gccgo |
||||
|
||||
#include "textflag.h" |
||||
|
||||
// |
||||
// System calls for linux/riscv64. |
||||
// |
||||
// Where available, just jump to package syscall's implementation of |
||||
// these functions. |
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56 |
||||
JMP syscall·Syscall(SB) |
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 |
||||
JMP syscall·Syscall6(SB) |
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48 |
||||
CALL runtime·entersyscall(SB) |
||||
MOV a1+8(FP), A0 |
||||
MOV a2+16(FP), A1 |
||||
MOV a3+24(FP), A2 |
||||
MOV $0, A3 |
||||
MOV $0, A4 |
||||
MOV $0, A5 |
||||
MOV $0, A6 |
||||
MOV trap+0(FP), A7 // syscall entry |
||||
ECALL |
||||
MOV A0, r1+32(FP) // r1 |
||||
MOV A1, r2+40(FP) // r2 |
||||
CALL runtime·exitsyscall(SB) |
||||
RET |
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 |
||||
JMP syscall·RawSyscall(SB) |
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 |
||||
JMP syscall·RawSyscall6(SB) |
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48 |
||||
MOV a1+8(FP), A0 |
||||
MOV a2+16(FP), A1 |
||||
MOV a3+24(FP), A2 |
||||
MOV ZERO, A3 |
||||
MOV ZERO, A4 |
||||
MOV ZERO, A5 |
||||
MOV trap+0(FP), A7 // syscall entry |
||||
ECALL |
||||
MOV A0, r1+32(FP) |
||||
MOV A1, r2+40(FP) |
||||
RET |
@ -0,0 +1,29 @@ |
||||
// Copyright 2019 The Go Authors. All rights reserved. |
||||
// Use of this source code is governed by a BSD-style |
||||
// license that can be found in the LICENSE file. |
||||
|
||||
// +build !gccgo |
||||
|
||||
#include "textflag.h" |
||||
|
||||
// |
||||
// System call support for arm64, OpenBSD |
||||
// |
||||
|
||||
// Just jump to package syscall's implementation for all these functions. |
||||
// The runtime may know about them. |
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56 |
||||
JMP syscall·Syscall(SB) |
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 |
||||
JMP syscall·Syscall6(SB) |
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104 |
||||
JMP syscall·Syscall9(SB) |
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 |
||||
JMP syscall·RawSyscall(SB) |
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 |
||||
JMP syscall·RawSyscall6(SB) |
@ -0,0 +1,355 @@ |
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// Parse the header files for OpenBSD and generate a Go usable sysctl MIB.
|
||||
//
|
||||
// Build a MIB with each entry being an array containing the level, type and
|
||||
// a hash that will contain additional entries if the current entry is a node.
|
||||
// We then walk this MIB and create a flattened sysctl name to OID hash.
|
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"bufio" |
||||
"fmt" |
||||
"os" |
||||
"path/filepath" |
||||
"regexp" |
||||
"sort" |
||||
"strings" |
||||
) |
||||
|
||||
var ( |
||||
goos, goarch string |
||||
) |
||||
|
||||
// cmdLine returns this programs's commandline arguments.
|
||||
func cmdLine() string { |
||||
return "go run mksysctl_openbsd.go " + strings.Join(os.Args[1:], " ") |
||||
} |
||||
|
||||
// buildTags returns build tags.
|
||||
func buildTags() string { |
||||
return fmt.Sprintf("%s,%s", goarch, goos) |
||||
} |
||||
|
||||
// reMatch performs regular expression match and stores the substring slice to value pointed by m.
|
||||
func reMatch(re *regexp.Regexp, str string, m *[]string) bool { |
||||
*m = re.FindStringSubmatch(str) |
||||
if *m != nil { |
||||
return true |
||||
} |
||||
return false |
||||
} |
||||
|
||||
type nodeElement struct { |
||||
n int |
||||
t string |
||||
pE *map[string]nodeElement |
||||
} |
||||
|
||||
var ( |
||||
debugEnabled bool |
||||
mib map[string]nodeElement |
||||
node *map[string]nodeElement |
||||
nodeMap map[string]string |
||||
sysCtl []string |
||||
) |
||||
|
||||
var ( |
||||
ctlNames1RE = regexp.MustCompile(`^#define\s+(CTL_NAMES)\s+{`) |
||||
ctlNames2RE = regexp.MustCompile(`^#define\s+(CTL_(.*)_NAMES)\s+{`) |
||||
ctlNames3RE = regexp.MustCompile(`^#define\s+((.*)CTL_NAMES)\s+{`) |
||||
netInetRE = regexp.MustCompile(`^netinet/`) |
||||
netInet6RE = regexp.MustCompile(`^netinet6/`) |
||||
netRE = regexp.MustCompile(`^net/`) |
||||
bracesRE = regexp.MustCompile(`{.*}`) |
||||
ctlTypeRE = regexp.MustCompile(`{\s+"(\w+)",\s+(CTLTYPE_[A-Z]+)\s+}`) |
||||
fsNetKernRE = regexp.MustCompile(`^(fs|net|kern)_`) |
||||
) |
||||
|
||||
func debug(s string) { |
||||
if debugEnabled { |
||||
fmt.Fprintln(os.Stderr, s) |
||||
} |
||||
} |
||||
|
||||
// Walk the MIB and build a sysctl name to OID mapping.
|
||||
func buildSysctl(pNode *map[string]nodeElement, name string, oid []int) { |
||||
lNode := pNode // local copy of pointer to node
|
||||
var keys []string |
||||
for k := range *lNode { |
||||
keys = append(keys, k) |
||||
} |
||||
sort.Strings(keys) |
||||
|
||||
for _, key := range keys { |
||||
nodename := name |
||||
if name != "" { |
||||
nodename += "." |
||||
} |
||||
nodename += key |
||||
|
||||
nodeoid := append(oid, (*pNode)[key].n) |
||||
|
||||
if (*pNode)[key].t == `CTLTYPE_NODE` { |
||||
if _, ok := nodeMap[nodename]; ok { |
||||
lNode = &mib |
||||
ctlName := nodeMap[nodename] |
||||
for _, part := range strings.Split(ctlName, ".") { |
||||
lNode = ((*lNode)[part]).pE |
||||
} |
||||
} else { |
||||
lNode = (*pNode)[key].pE |
||||
} |
||||
buildSysctl(lNode, nodename, nodeoid) |
||||
} else if (*pNode)[key].t != "" { |
||||
oidStr := []string{} |
||||
for j := range nodeoid { |
||||
oidStr = append(oidStr, fmt.Sprintf("%d", nodeoid[j])) |
||||
} |
||||
text := "\t{ \"" + nodename + "\", []_C_int{ " + strings.Join(oidStr, ", ") + " } }, \n" |
||||
sysCtl = append(sysCtl, text) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func main() { |
||||
// Get the OS (using GOOS_TARGET if it exist)
|
||||
goos = os.Getenv("GOOS_TARGET") |
||||
if goos == "" { |
||||
goos = os.Getenv("GOOS") |
||||
} |
||||
// Get the architecture (using GOARCH_TARGET if it exists)
|
||||
goarch = os.Getenv("GOARCH_TARGET") |
||||
if goarch == "" { |
||||
goarch = os.Getenv("GOARCH") |
||||
} |
||||
// Check if GOOS and GOARCH environment variables are defined
|
||||
if goarch == "" || goos == "" { |
||||
fmt.Fprintf(os.Stderr, "GOARCH or GOOS not defined in environment\n") |
||||
os.Exit(1) |
||||
} |
||||
|
||||
mib = make(map[string]nodeElement) |
||||
headers := [...]string{ |
||||
`sys/sysctl.h`, |
||||
`sys/socket.h`, |
||||
`sys/tty.h`, |
||||
`sys/malloc.h`, |
||||
`sys/mount.h`, |
||||
`sys/namei.h`, |
||||
`sys/sem.h`, |
||||
`sys/shm.h`, |
||||
`sys/vmmeter.h`, |
||||
`uvm/uvmexp.h`, |
||||
`uvm/uvm_param.h`, |
||||
`uvm/uvm_swap_encrypt.h`, |
||||
`ddb/db_var.h`, |
||||
`net/if.h`, |
||||
`net/if_pfsync.h`, |
||||
`net/pipex.h`, |
||||
`netinet/in.h`, |
||||
`netinet/icmp_var.h`, |
||||
`netinet/igmp_var.h`, |
||||
`netinet/ip_ah.h`, |
||||
`netinet/ip_carp.h`, |
||||
`netinet/ip_divert.h`, |
||||
`netinet/ip_esp.h`, |
||||
`netinet/ip_ether.h`, |
||||
`netinet/ip_gre.h`, |
||||
`netinet/ip_ipcomp.h`, |
||||
`netinet/ip_ipip.h`, |
||||
`netinet/pim_var.h`, |
||||
`netinet/tcp_var.h`, |
||||
`netinet/udp_var.h`, |
||||
`netinet6/in6.h`, |
||||
`netinet6/ip6_divert.h`, |
||||
`netinet6/pim6_var.h`, |
||||
`netinet/icmp6.h`, |
||||
`netmpls/mpls.h`, |
||||
} |
||||
|
||||
ctls := [...]string{ |
||||
`kern`, |
||||
`vm`, |
||||
`fs`, |
||||
`net`, |
||||
//debug /* Special handling required */
|
||||
`hw`, |
||||
//machdep /* Arch specific */
|
||||
`user`, |
||||
`ddb`, |
||||
//vfs /* Special handling required */
|
||||
`fs.posix`, |
||||
`kern.forkstat`, |
||||
`kern.intrcnt`, |
||||
`kern.malloc`, |
||||
`kern.nchstats`, |
||||
`kern.seminfo`, |
||||
`kern.shminfo`, |
||||
`kern.timecounter`, |
||||
`kern.tty`, |
||||
`kern.watchdog`, |
||||
`net.bpf`, |
||||
`net.ifq`, |
||||
`net.inet`, |
||||
`net.inet.ah`, |
||||
`net.inet.carp`, |
||||
`net.inet.divert`, |
||||
`net.inet.esp`, |
||||
`net.inet.etherip`, |
||||
`net.inet.gre`, |
||||
`net.inet.icmp`, |
||||
`net.inet.igmp`, |
||||
`net.inet.ip`, |
||||
`net.inet.ip.ifq`, |
||||
`net.inet.ipcomp`, |
||||
`net.inet.ipip`, |
||||
`net.inet.mobileip`, |
||||
`net.inet.pfsync`, |
||||
`net.inet.pim`, |
||||
`net.inet.tcp`, |
||||
`net.inet.udp`, |
||||
`net.inet6`, |
||||
`net.inet6.divert`, |
||||
`net.inet6.ip6`, |
||||
`net.inet6.icmp6`, |
||||
`net.inet6.pim6`, |
||||
`net.inet6.tcp6`, |
||||
`net.inet6.udp6`, |
||||
`net.mpls`, |
||||
`net.mpls.ifq`, |
||||
`net.key`, |
||||
`net.pflow`, |
||||
`net.pfsync`, |
||||
`net.pipex`, |
||||
`net.rt`, |
||||
`vm.swapencrypt`, |
||||
//vfsgenctl /* Special handling required */
|
||||
} |
||||
|
||||
// Node name "fixups"
|
||||
ctlMap := map[string]string{ |
||||
"ipproto": "net.inet", |
||||
"net.inet.ipproto": "net.inet", |
||||
"net.inet6.ipv6proto": "net.inet6", |
||||
"net.inet6.ipv6": "net.inet6.ip6", |
||||
"net.inet.icmpv6": "net.inet6.icmp6", |
||||
"net.inet6.divert6": "net.inet6.divert", |
||||
"net.inet6.tcp6": "net.inet.tcp", |
||||
"net.inet6.udp6": "net.inet.udp", |
||||
"mpls": "net.mpls", |
||||
"swpenc": "vm.swapencrypt", |
||||
} |
||||
|
||||
// Node mappings
|
||||
nodeMap = map[string]string{ |
||||
"net.inet.ip.ifq": "net.ifq", |
||||
"net.inet.pfsync": "net.pfsync", |
||||
"net.mpls.ifq": "net.ifq", |
||||
} |
||||
|
||||
mCtls := make(map[string]bool) |
||||
for _, ctl := range ctls { |
||||
mCtls[ctl] = true |
||||
} |
||||
|
||||
for _, header := range headers { |
||||
debug("Processing " + header) |
||||
file, err := os.Open(filepath.Join("/usr/include", header)) |
||||
if err != nil { |
||||
fmt.Fprintf(os.Stderr, "%v\n", err) |
||||
os.Exit(1) |
||||
} |
||||
s := bufio.NewScanner(file) |
||||
for s.Scan() { |
||||
var sub []string |
||||
if reMatch(ctlNames1RE, s.Text(), &sub) || |
||||
reMatch(ctlNames2RE, s.Text(), &sub) || |
||||
reMatch(ctlNames3RE, s.Text(), &sub) { |
||||
if sub[1] == `CTL_NAMES` { |
||||
// Top level.
|
||||
node = &mib |
||||
} else { |
||||
// Node.
|
||||
nodename := strings.ToLower(sub[2]) |
||||
ctlName := "" |
||||
if reMatch(netInetRE, header, &sub) { |
||||
ctlName = "net.inet." + nodename |
||||
} else if reMatch(netInet6RE, header, &sub) { |
||||
ctlName = "net.inet6." + nodename |
||||
} else if reMatch(netRE, header, &sub) { |
||||
ctlName = "net." + nodename |
||||
} else { |
||||
ctlName = nodename |
||||
ctlName = fsNetKernRE.ReplaceAllString(ctlName, `$1.`) |
||||
} |
||||
|
||||
if val, ok := ctlMap[ctlName]; ok { |
||||
ctlName = val |
||||
} |
||||
if _, ok := mCtls[ctlName]; !ok { |
||||
debug("Ignoring " + ctlName + "...") |
||||
continue |
||||
} |
||||
|
||||
// Walk down from the top of the MIB.
|
||||
node = &mib |
||||
for _, part := range strings.Split(ctlName, ".") { |
||||
if _, ok := (*node)[part]; !ok { |
||||
debug("Missing node " + part) |
||||
(*node)[part] = nodeElement{n: 0, t: "", pE: &map[string]nodeElement{}} |
||||
} |
||||
node = (*node)[part].pE |
||||
} |
||||
} |
||||
|
||||
// Populate current node with entries.
|
||||
i := -1 |
||||
for !strings.HasPrefix(s.Text(), "}") { |
||||
s.Scan() |
||||
if reMatch(bracesRE, s.Text(), &sub) { |
||||
i++ |
||||
} |
||||
if !reMatch(ctlTypeRE, s.Text(), &sub) { |
||||
continue |
||||
} |
||||
(*node)[sub[1]] = nodeElement{n: i, t: sub[2], pE: &map[string]nodeElement{}} |
||||
} |
||||
} |
||||
} |
||||
err = s.Err() |
||||
if err != nil { |
||||
fmt.Fprintf(os.Stderr, "%v\n", err) |
||||
os.Exit(1) |
||||
} |
||||
file.Close() |
||||
} |
||||
buildSysctl(&mib, "", []int{}) |
||||
|
||||
sort.Strings(sysCtl) |
||||
text := strings.Join(sysCtl, "") |
||||
|
||||
fmt.Printf(srcTemplate, cmdLine(), buildTags(), text) |
||||
} |
||||
|
||||
const srcTemplate = `// %s
|
||||
// Code generated by the command above; DO NOT EDIT.
|
||||
|
||||
// +build %s
|
||||
|
||||
package unix |
||||
|
||||
type mibentry struct { |
||||
ctlname string |
||||
ctloid []_C_int |
||||
} |
||||
|
||||
var sysctlMib = []mibentry { |
||||
%s |
||||
} |
||||
` |
@ -1,265 +0,0 @@ |
||||
#!/usr/bin/env perl |
||||
|
||||
# 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. |
||||
|
||||
# |
||||
# Parse the header files for OpenBSD and generate a Go usable sysctl MIB. |
||||
# |
||||
# Build a MIB with each entry being an array containing the level, type and |
||||
# a hash that will contain additional entries if the current entry is a node. |
||||
# We then walk this MIB and create a flattened sysctl name to OID hash. |
||||
# |
||||
|
||||
use strict; |
||||
|
||||
if($ENV{'GOARCH'} eq "" || $ENV{'GOOS'} eq "") { |
||||
print STDERR "GOARCH or GOOS not defined in environment\n"; |
||||
exit 1; |
||||
} |
||||
|
||||
my $debug = 0; |
||||
my %ctls = (); |
||||
|
||||
my @headers = qw ( |
||||
sys/sysctl.h |
||||
sys/socket.h |
||||
sys/tty.h |
||||
sys/malloc.h |
||||
sys/mount.h |
||||
sys/namei.h |
||||
sys/sem.h |
||||
sys/shm.h |
||||
sys/vmmeter.h |
||||
uvm/uvmexp.h |
||||
uvm/uvm_param.h |
||||
uvm/uvm_swap_encrypt.h |
||||
ddb/db_var.h |
||||
net/if.h |
||||
net/if_pfsync.h |
||||
net/pipex.h |
||||
netinet/in.h |
||||
netinet/icmp_var.h |
||||
netinet/igmp_var.h |
||||
netinet/ip_ah.h |
||||
netinet/ip_carp.h |
||||
netinet/ip_divert.h |
||||
netinet/ip_esp.h |
||||
netinet/ip_ether.h |
||||
netinet/ip_gre.h |
||||
netinet/ip_ipcomp.h |
||||
netinet/ip_ipip.h |
||||
netinet/pim_var.h |
||||
netinet/tcp_var.h |
||||
netinet/udp_var.h |
||||
netinet6/in6.h |
||||
netinet6/ip6_divert.h |
||||
netinet6/pim6_var.h |
||||
netinet/icmp6.h |
||||
netmpls/mpls.h |
||||
); |
||||
|
||||
my @ctls = qw ( |
||||
kern |
||||
vm |
||||
fs |
||||
net |
||||
#debug # Special handling required |
||||
hw |
||||
#machdep # Arch specific |
||||
user |
||||
ddb |
||||
#vfs # Special handling required |
||||
fs.posix |
||||
kern.forkstat |
||||
kern.intrcnt |
||||
kern.malloc |
||||
kern.nchstats |
||||
kern.seminfo |
||||
kern.shminfo |
||||
kern.timecounter |
||||
kern.tty |
||||
kern.watchdog |
||||
net.bpf |
||||
net.ifq |
||||
net.inet |
||||
net.inet.ah |
||||
net.inet.carp |
||||
net.inet.divert |
||||
net.inet.esp |
||||
net.inet.etherip |
||||
net.inet.gre |
||||
net.inet.icmp |
||||
net.inet.igmp |
||||
net.inet.ip |
||||
net.inet.ip.ifq |
||||
net.inet.ipcomp |
||||
net.inet.ipip |
||||
net.inet.mobileip |
||||
net.inet.pfsync |
||||
net.inet.pim |
||||
net.inet.tcp |
||||
net.inet.udp |
||||
net.inet6 |
||||
net.inet6.divert |
||||
net.inet6.ip6 |
||||
net.inet6.icmp6 |
||||
net.inet6.pim6 |
||||
net.inet6.tcp6 |
||||
net.inet6.udp6 |
||||
net.mpls |
||||
net.mpls.ifq |
||||
net.key |
||||
net.pflow |
||||
net.pfsync |
||||
net.pipex |
||||
net.rt |
||||
vm.swapencrypt |
||||
#vfsgenctl # Special handling required |
||||
); |
||||
|
||||
# Node name "fixups" |
||||
my %ctl_map = ( |
||||
"ipproto" => "net.inet", |
||||
"net.inet.ipproto" => "net.inet", |
||||
"net.inet6.ipv6proto" => "net.inet6", |
||||
"net.inet6.ipv6" => "net.inet6.ip6", |
||||
"net.inet.icmpv6" => "net.inet6.icmp6", |
||||
"net.inet6.divert6" => "net.inet6.divert", |
||||
"net.inet6.tcp6" => "net.inet.tcp", |
||||
"net.inet6.udp6" => "net.inet.udp", |
||||
"mpls" => "net.mpls", |
||||
"swpenc" => "vm.swapencrypt" |
||||
); |
||||
|
||||
# Node mappings |
||||
my %node_map = ( |
||||
"net.inet.ip.ifq" => "net.ifq", |
||||
"net.inet.pfsync" => "net.pfsync", |
||||
"net.mpls.ifq" => "net.ifq" |
||||
); |
||||
|
||||
my $ctlname; |
||||
my %mib = (); |
||||
my %sysctl = (); |
||||
my $node; |
||||
|
||||
sub debug() { |
||||
print STDERR "$_[0]\n" if $debug; |
||||
} |
||||
|
||||
# Walk the MIB and build a sysctl name to OID mapping. |
||||
sub build_sysctl() { |
||||
my ($node, $name, $oid) = @_; |
||||
my %node = %{$node}; |
||||
my @oid = @{$oid}; |
||||
|
||||
foreach my $key (sort keys %node) { |
||||
my @node = @{$node{$key}}; |
||||
my $nodename = $name.($name ne '' ? '.' : '').$key; |
||||
my @nodeoid = (@oid, $node[0]); |
||||
if ($node[1] eq 'CTLTYPE_NODE') { |
||||
if (exists $node_map{$nodename}) { |
||||
$node = \%mib; |
||||
$ctlname = $node_map{$nodename}; |
||||
foreach my $part (split /\./, $ctlname) { |
||||
$node = \%{@{$$node{$part}}[2]}; |
||||
} |
||||
} else { |
||||
$node = $node[2]; |
||||
} |
||||
&build_sysctl($node, $nodename, \@nodeoid); |
||||
} elsif ($node[1] ne '') { |
||||
$sysctl{$nodename} = \@nodeoid; |
||||
} |
||||
} |
||||
} |
||||
|
||||
foreach my $ctl (@ctls) { |
||||
$ctls{$ctl} = $ctl; |
||||
} |
||||
|
||||
# Build MIB |
||||
foreach my $header (@headers) { |
||||
&debug("Processing $header..."); |
||||
open HEADER, "/usr/include/$header" || |
||||
print STDERR "Failed to open $header\n"; |
||||
while (<HEADER>) { |
||||
if ($_ =~ /^#define\s+(CTL_NAMES)\s+{/ || |
||||
$_ =~ /^#define\s+(CTL_(.*)_NAMES)\s+{/ || |
||||
$_ =~ /^#define\s+((.*)CTL_NAMES)\s+{/) { |
||||
if ($1 eq 'CTL_NAMES') { |
||||
# Top level. |
||||
$node = \%mib; |
||||
} else { |
||||
# Node. |
||||
my $nodename = lc($2); |
||||
if ($header =~ /^netinet\//) { |
||||
$ctlname = "net.inet.$nodename"; |
||||
} elsif ($header =~ /^netinet6\//) { |
||||
$ctlname = "net.inet6.$nodename"; |
||||
} elsif ($header =~ /^net\//) { |
||||
$ctlname = "net.$nodename"; |
||||
} else { |
||||
$ctlname = "$nodename"; |
||||
$ctlname =~ s/^(fs|net|kern)_/$1\./; |
||||
} |
||||
if (exists $ctl_map{$ctlname}) { |
||||
$ctlname = $ctl_map{$ctlname}; |
||||
} |
||||
if (not exists $ctls{$ctlname}) { |
||||
&debug("Ignoring $ctlname..."); |
||||
next; |
||||
} |
||||
|
||||
# Walk down from the top of the MIB. |
||||
$node = \%mib; |
||||
foreach my $part (split /\./, $ctlname) { |
||||
if (not exists $$node{$part}) { |
||||
&debug("Missing node $part"); |
||||
$$node{$part} = [ 0, '', {} ]; |
||||
} |
||||
$node = \%{@{$$node{$part}}[2]}; |
||||
} |
||||
} |
||||
|
||||
# Populate current node with entries. |
||||
my $i = -1; |
||||
while (defined($_) && $_ !~ /^}/) { |
||||
$_ = <HEADER>; |
||||
$i++ if $_ =~ /{.*}/; |
||||
next if $_ !~ /{\s+"(\w+)",\s+(CTLTYPE_[A-Z]+)\s+}/; |
||||
$$node{$1} = [ $i, $2, {} ]; |
||||
} |
||||
} |
||||
} |
||||
close HEADER; |
||||
} |
||||
|
||||
&build_sysctl(\%mib, "", []); |
||||
|
||||
print <<EOF; |
||||
// mksysctl_openbsd.pl |
||||
// Code generated by the command above; DO NOT EDIT. |
||||
|
||||
// +build $ENV{'GOARCH'},$ENV{'GOOS'} |
||||
|
||||
package unix; |
||||
|
||||
type mibentry struct { |
||||
ctlname string |
||||
ctloid []_C_int |
||||
} |
||||
|
||||
var sysctlMib = []mibentry { |
||||
EOF |
||||
|
||||
foreach my $name (sort keys %sysctl) { |
||||
my @oid = @{$sysctl{$name}}; |
||||
print "\t{ \"$name\", []_C_int{ ", join(', ', @oid), " } }, \n"; |
||||
} |
||||
|
||||
print <<EOF; |
||||
} |
||||
EOF |
@ -0,0 +1,37 @@ |
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build arm64,openbsd
|
||||
|
||||
package unix |
||||
|
||||
func setTimespec(sec, nsec int64) Timespec { |
||||
return Timespec{Sec: sec, Nsec: nsec} |
||||
} |
||||
|
||||
func setTimeval(sec, usec int64) Timeval { |
||||
return Timeval{Sec: sec, Usec: usec} |
||||
} |
||||
|
||||
func SetKevent(k *Kevent_t, fd, mode, flags int) { |
||||
k.Ident = uint64(fd) |
||||
k.Filter = int16(mode) |
||||
k.Flags = uint16(flags) |
||||
} |
||||
|
||||
func (iov *Iovec) SetLen(length int) { |
||||
iov.Len = uint64(length) |
||||
} |
||||
|
||||
func (msghdr *Msghdr) SetControllen(length int) { |
||||
msghdr.Controllen = uint32(length) |
||||
} |
||||
|
||||
func (cmsg *Cmsghdr) SetLen(length int) { |
||||
cmsg.Len = uint32(length) |
||||
} |
||||
|
||||
// SYS___SYSCTL is used by syscall_bsd.go for all BSDs, but in modern versions
|
||||
// of openbsd/amd64 the syscall is called sysctl instead of __sysctl.
|
||||
const SYS___SYSCTL = SYS_SYSCTL |
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue