github.com/pawelgaczynski/gain@v0.4.0-alpha.0.20230821120126-41f1e60a18da/pkg/socket/socktoaddr.go (about)

     1  // Copyright (c) 2023 Paweł Gaczyński
     2  // Copyright (c) 2019 Andy Pan
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package socket
    17  
    18  import (
    19  	"net"
    20  	"syscall"
    21  	"unsafe"
    22  
    23  	bsPool "github.com/pawelgaczynski/gain/pkg/pool/byteslice"
    24  )
    25  
    26  var ipv4InIPv6Prefix = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff}
    27  
    28  const (
    29  	netIPBytesSize        = 16
    30  	sockAddrPortBitOffset = 8
    31  	decimalBase           = 10
    32  	int32size             = 32
    33  )
    34  
    35  func RawAnyToSockaddrInet4(rsa *syscall.RawSockaddrAny) (*syscall.SockaddrInet4, error) {
    36  	if rsa == nil {
    37  		return nil, syscall.EINVAL
    38  	}
    39  
    40  	if rsa.Addr.Family != syscall.AF_INET {
    41  		return nil, syscall.EAFNOSUPPORT
    42  	}
    43  
    44  	rsaPointer := (*syscall.RawSockaddrInet4)(unsafe.Pointer(rsa))
    45  	sockAddr := new(syscall.SockaddrInet4)
    46  	p := (*[2]byte)(unsafe.Pointer(&rsaPointer.Port))
    47  
    48  	sockAddr.Port = int(p[0])<<sockAddrPortBitOffset + int(p[1])
    49  	for i := 0; i < len(sockAddr.Addr); i++ {
    50  		sockAddr.Addr[i] = rsaPointer.Addr[i]
    51  	}
    52  
    53  	return sockAddr, nil
    54  }
    55  
    56  // SockaddrToTCPOrUnixAddr converts a Sockaddr to a net.TCPAddr or net.UnixAddr.
    57  // Returns nil if conversion fails.
    58  func SockaddrToTCPOrUnixAddr(sysSockAddr syscall.Sockaddr) net.Addr {
    59  	switch sockAddr := sysSockAddr.(type) {
    60  	case *syscall.SockaddrInet4:
    61  		ip := sockaddrInet4ToIP(sockAddr)
    62  
    63  		return &net.TCPAddr{IP: ip, Port: sockAddr.Port}
    64  
    65  	case *syscall.SockaddrInet6:
    66  		ip, zone := sockaddrInet6ToIPAndZone(sockAddr)
    67  
    68  		return &net.TCPAddr{IP: ip, Port: sockAddr.Port, Zone: zone}
    69  
    70  	case *syscall.SockaddrUnix:
    71  		return &net.UnixAddr{Name: sockAddr.Name, Net: "unix"}
    72  	}
    73  
    74  	return nil
    75  }
    76  
    77  // SockaddrToUDPAddr converts a Sockaddr to a net.UDPAddr
    78  // Returns nil if conversion fails.
    79  func SockaddrToUDPAddr(sysSockAddr syscall.Sockaddr) net.Addr {
    80  	switch sockAddr := sysSockAddr.(type) {
    81  	case *syscall.SockaddrInet4:
    82  		ip := sockaddrInet4ToIP(sockAddr)
    83  
    84  		return &net.UDPAddr{IP: ip, Port: sockAddr.Port}
    85  
    86  	case *syscall.SockaddrInet6:
    87  		ip, zone := sockaddrInet6ToIPAndZone(sockAddr)
    88  
    89  		return &net.UDPAddr{IP: ip, Port: sockAddr.Port, Zone: zone}
    90  	}
    91  
    92  	return nil
    93  }
    94  
    95  // sockaddrInet4ToIPAndZone converts a SockaddrInet4 to a net.IP.
    96  // It returns nil if conversion fails.
    97  func sockaddrInet4ToIP(sa *syscall.SockaddrInet4) net.IP {
    98  	ip := bsPool.Get(netIPBytesSize)
    99  	// ipv4InIPv6Prefix
   100  	copy(ip[0:12], ipv4InIPv6Prefix)
   101  	copy(ip[12:16], sa.Addr[:])
   102  
   103  	return ip
   104  }
   105  
   106  // sockaddrInet6ToIPAndZone converts a SockaddrInet6 to a net.IP with IPv6 Zone.
   107  // It returns nil if conversion fails.
   108  func sockaddrInet6ToIPAndZone(sa *syscall.SockaddrInet6) (net.IP, string) {
   109  	ip := bsPool.Get(netIPBytesSize)
   110  	copy(ip, sa.Addr[:])
   111  
   112  	return ip, ip6ZoneToString(int(sa.ZoneId))
   113  }
   114  
   115  // ip6ZoneToString converts an IP6 Zone unix int to a net string
   116  // returns "" if zone is 0.
   117  func ip6ZoneToString(zone int) string {
   118  	if zone == 0 {
   119  		return ""
   120  	}
   121  
   122  	if ifi, err := net.InterfaceByIndex(zone); err == nil {
   123  		return ifi.Name
   124  	}
   125  
   126  	return int2decimal(uint(zone))
   127  }
   128  
   129  // BytesToString converts byte slice to a string without memory allocation.
   130  //
   131  // Note it may break if the implementation of string or slice header changes in the future go versions.
   132  func BytesToString(b []byte) string {
   133  	/* #nosec G103 */
   134  	return *(*string)(unsafe.Pointer(&b))
   135  }
   136  
   137  // Convert int to decimal string.
   138  func int2decimal(value uint) string {
   139  	if value == 0 {
   140  		return "0"
   141  	}
   142  
   143  	// Assemble decimal in reverse order.
   144  	byteSlice := bsPool.Get(int32size)
   145  	bytesSliceLen := len(byteSlice)
   146  
   147  	for ; value > 0; value /= 10 {
   148  		bytesSliceLen--
   149  		byteSlice[bytesSliceLen] = byte(value%decimalBase) + '0'
   150  	}
   151  
   152  	return BytesToString(byteSlice[bytesSliceLen:])
   153  }