Голан: имя константы, учитывая ее значение

Как вы получаете имя константы с учетом ее значения?

В частности (и чтобы получить более понятное понимание), я работаю с пакетом 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 снова, чтобы обновить сгенерированный код.