Голан: имя константы, учитывая ее значение
Как вы получаете имя константы с учетом ее значения?
В частности (и чтобы получить более понятное понимание), я работаю с пакетом crypto/tls
. Cipher suites определяются как константы:
const (
TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
)
После успешного рукопожатия с сервером я могу связаться с Ciphersuite, согласованным через соединение:
c, _ := tls.Dial("tcp", "somedomain.com:443", nil)
// Suppose everything went right
// This is the Ciphersuite for the conn:
cipher := c.ConnectionState().Ciphersuite
cipher
здесь находится uint16. Есть ли способ показать его как строку, например TLS_RSA_WITH_3DES_EDE_CBC_SHA
, если это было согласовано?
Ответы
Ответ 1
Я боюсь, что это невозможно.
Константы разрешаются во время компиляции, и в пакете reflect
нет ничего, что позволяет вам получить имя.
Я предлагаю создать карту с именами:
var constLookup = map[uint16]string{
tls.TLS_RSA_WITH_RC4_128_SHA: `TLS_RSA_WITH_RC4_128_SHA`,
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA: `TLS_RSA_WITH_3DES_EDE_CBC_SHA`,
...
}
Ответ 2
Примечание. Начиная с версии 1.4, код String()
ниже может быть автоматически сгенерирован с использованием функции Go new generate
в сочетании с командой stringer
. См. здесь для получения дополнительной информации.
Помимо ответа ANisus, вы можете сделать следующее.
package main
import "fmt"
import "crypto/tls"
type Ciphersuite uint16
const (
TLS_RSA_WITH_RC4_128_SHA = Ciphersuite(tls.TLS_RSA_WITH_RC4_128_SHA)
TLS_RSA_WITH_3DES_EDE_CBC_SHA = Ciphersuite(tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA)
TLS_RSA_WITH_AES_128_CBC_SHA = Ciphersuite(tls.TLS_RSA_WITH_AES_128_CBC_SHA)
TLS_RSA_WITH_AES_256_CBC_SHA = Ciphersuite(tls.TLS_RSA_WITH_AES_256_CBC_SHA)
TLS_ECDHE_RSA_WITH_RC4_128_SHA = Ciphersuite(tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA)
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = Ciphersuite(tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA)
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = Ciphersuite(tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = Ciphersuite(tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA)
)
func (cs Ciphersuite) String() string {
switch cs {
case TLS_RSA_WITH_RC4_128_SHA:
return "TLS_RSA_WITH_RC4_128_SHA"
case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
return "TLS_RSA_WITH_3DES_EDE_CBC_SHA"
case TLS_RSA_WITH_AES_128_CBC_SHA:
return "TLS_RSA_WITH_AES_128_CBC_SHA"
case TLS_RSA_WITH_AES_256_CBC_SHA:
return "TLS_RSA_WITH_AES_256_CBC_SHA"
case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
return "TLS_ECDHE_RSA_WITH_RC4_128_SHA"
case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
return "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"
case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
return "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"
case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"
}
return "Unknown"
}
func main() {
cs := TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
fmt.Printf("0x%04x = %s\n", uint16(cs), cs)
cs = TLS_RSA_WITH_RC4_128_SHA
fmt.Printf("0x%04x = %s\n", uint16(cs), cs)
cs = TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
fmt.Printf("0x%04x = %s\n", uint16(cs), cs)
}
Вы можете протестировать его на go playground.
Ответ 3
Вы можете использовать go generate
, чтобы создать это для вас.
Установите stringer
, используя
go get golang.org/x/tools/cmd/stringer
Теперь определите type
для const
. Вы можете сделать это так:
type TLSType uint16
const (
TLS_RSA_WITH_RC4_128_SHA TLSType = 0x0005
TLS_RSA_WITH_3DES_EDE_CBC_SHA TLSType = 0x000a
TLS_RSA_WITH_AES_128_CBC_SHA TLSType = 0x002f
TLS_RSA_WITH_AES_256_CBC_SHA TLSType = 0x0035
TLS_ECDHE_RSA_WITH_RC4_128_SHA TLSType = 0xc011
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA TLSType = 0xc012
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA TLSType = 0xc013
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA TLSType = 0xc014
)
Затем добавьте эту строку в свой файл (важно сделать интервал)
//go:generate stringer -type=TLSType
Теперь перейдите на терминал в папке, в которой находится ваша программа, и запустите
go generate
Это создаст файл с именем tlstype_string.go
в вашем кодовом каталоге, который является сгенерированным кодом, который реализует интерфейс fmt.Stringer
для вашего TLSType
. Откройте его, если вы хотите увидеть реализацию, но вам это не нужно. Он будет работать.
Теперь, когда вы запускаете go build
или go run *.go
в этом каталоге, исходные файлы будут использовать сгенерированный код.
Полная программа будет выглядеть примерно так.
package main
import (
"fmt"
)
//go:generate stringer -type=TLSType
type TLSType uint16
const (
TLS_RSA_WITH_RC4_128_SHA TLSType = 0x0005
TLS_RSA_WITH_3DES_EDE_CBC_SHA TLSType = 0x000a
TLS_RSA_WITH_AES_128_CBC_SHA TLSType = 0x002f
TLS_RSA_WITH_AES_256_CBC_SHA TLSType = 0x0035
TLS_ECDHE_RSA_WITH_RC4_128_SHA TLSType = 0xc011
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA TLSType = 0xc012
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA TLSType = 0xc013
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA TLSType = 0xc014
)
func main() {
fmt.Println("This const will be printed with its name instead of its number:")
fmt.Println(TLS_RSA_WITH_RC4_128_SHA)
fmt.Println()
fmt.Println("So will this one:")
fmt.Println(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA)
fmt.Println()
fmt.Println("Or maybe we want to give it a value and convert:")
fmt.Println(TLSType(0xc011))
fmt.Println()
fmt.Println("No problem:")
fmt.Println(TLSType(0xc013))
}
Какие выходы
This const will be printed with its name instead of its number:
TLS_RSA_WITH_RC4_128_SHA
So will this one:
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
Or maybe we want to give it a value and convert:
TLS_ECDHE_RSA_WITH_RC4_128_SHA
No problem:
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
Если вы добавите новые значения const
, просто запустите go generate
снова, чтобы обновить сгенерированный код.