github.com/pion/dtls/v2@v2.2.12/pkg/protocol/extension/server_name.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
     2  // SPDX-License-Identifier: MIT
     3  
     4  package extension
     5  
     6  import (
     7  	"strings"
     8  
     9  	"golang.org/x/crypto/cryptobyte"
    10  )
    11  
    12  const serverNameTypeDNSHostName = 0
    13  
    14  // ServerName allows the client to inform the server the specific
    15  // name it wishes to contact. Useful if multiple DNS names resolve
    16  // to one IP
    17  //
    18  // https://tools.ietf.org/html/rfc6066#section-3
    19  type ServerName struct {
    20  	ServerName string
    21  }
    22  
    23  // TypeValue returns the extension TypeValue
    24  func (s ServerName) TypeValue() TypeValue {
    25  	return ServerNameTypeValue
    26  }
    27  
    28  // Marshal encodes the extension
    29  func (s *ServerName) Marshal() ([]byte, error) {
    30  	var b cryptobyte.Builder
    31  	b.AddUint16(uint16(s.TypeValue()))
    32  	b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
    33  		b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
    34  			b.AddUint8(serverNameTypeDNSHostName)
    35  			b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
    36  				b.AddBytes([]byte(s.ServerName))
    37  			})
    38  		})
    39  	})
    40  	return b.Bytes()
    41  }
    42  
    43  // Unmarshal populates the extension from encoded data
    44  func (s *ServerName) Unmarshal(data []byte) error {
    45  	val := cryptobyte.String(data)
    46  	var extension uint16
    47  	val.ReadUint16(&extension)
    48  	if TypeValue(extension) != s.TypeValue() {
    49  		return errInvalidExtensionType
    50  	}
    51  
    52  	var extData cryptobyte.String
    53  	val.ReadUint16LengthPrefixed(&extData)
    54  
    55  	var nameList cryptobyte.String
    56  	if !extData.ReadUint16LengthPrefixed(&nameList) || nameList.Empty() {
    57  		return errInvalidSNIFormat
    58  	}
    59  	for !nameList.Empty() {
    60  		var nameType uint8
    61  		var serverName cryptobyte.String
    62  		if !nameList.ReadUint8(&nameType) ||
    63  			!nameList.ReadUint16LengthPrefixed(&serverName) ||
    64  			serverName.Empty() {
    65  			return errInvalidSNIFormat
    66  		}
    67  		if nameType != serverNameTypeDNSHostName {
    68  			continue
    69  		}
    70  		if len(s.ServerName) != 0 {
    71  			// Multiple names of the same name_type are prohibited.
    72  			return errInvalidSNIFormat
    73  		}
    74  		s.ServerName = string(serverName)
    75  		// An SNI value may not include a trailing dot.
    76  		if strings.HasSuffix(s.ServerName, ".") {
    77  			return errInvalidSNIFormat
    78  		}
    79  	}
    80  	return nil
    81  }