github.com/mier85/go-sensor@v1.30.1-0.20220920111756-9bf41b3bc7e0/util.go (about)

     1  // (c) Copyright IBM Corp. 2021
     2  // (c) Copyright Instana Inc. 2016
     3  
     4  package instana
     5  
     6  import (
     7  	"bufio"
     8  	"bytes"
     9  	"encoding/binary"
    10  	"errors"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"math/rand"
    14  	"os"
    15  	"strconv"
    16  	"strings"
    17  	"sync"
    18  	"time"
    19  )
    20  
    21  var (
    22  	seededIDGen  = rand.New(rand.NewSource(time.Now().UnixNano()))
    23  	seededIDLock sync.Mutex
    24  )
    25  
    26  func randomID() int64 {
    27  	seededIDLock.Lock()
    28  	defer seededIDLock.Unlock()
    29  	return int64(seededIDGen.Int63())
    30  }
    31  
    32  // FormatID converts an Instana ID to a value that can be used in
    33  // context propagation (such as HTTP headers). More specifically,
    34  // this converts a signed 64 bit integer into an unsigned hex string.
    35  // The resulting string is always padded with 0 to be 16 characters long.
    36  func FormatID(id int64) string {
    37  	// FIXME: We're assuming LittleEndian here
    38  
    39  	// Convert uint64 to hex string equivalent and return that
    40  	return padHexString(strconv.FormatUint(uint64(id), 16), 64)
    41  }
    42  
    43  // FormatLongID converts a 128-bit Instana ID passed in two quad words to an
    44  // unsigned hex string suitable for context propagation.
    45  func FormatLongID(hi, lo int64) string {
    46  	return FormatID(hi) + FormatID(lo)
    47  }
    48  
    49  func padHexString(s string, bitSize int) string {
    50  	if len(s) >= bitSize>>2 {
    51  		return s
    52  	}
    53  
    54  	return strings.Repeat("0", bitSize>>2-len(s)) + s
    55  }
    56  
    57  // ParseID converts an header context value into an Instana ID.  More
    58  // specifically, this converts an unsigned 64 bit hex value into a signed
    59  // 64bit integer.
    60  func ParseID(header string) (int64, error) {
    61  	// FIXME: We're assuming LittleEndian here
    62  
    63  	// Parse unsigned 64 bit hex string into unsigned 64 bit base 10 integer
    64  	unsignedID, err := strconv.ParseUint(header, 16, 64)
    65  	if err != nil {
    66  		return 0, fmt.Errorf("context corrupted; could not convert value: %s", err)
    67  	}
    68  
    69  	// Write out _unsigned_ 64bit integer to byte buffer
    70  	buf := bytes.NewBuffer(nil)
    71  	if err := binary.Write(buf, binary.LittleEndian, unsignedID); err != nil {
    72  		return 0, fmt.Errorf("context corrupted; could not convert value: %s", err)
    73  	}
    74  
    75  	// Read bytes back into _signed_ 64 bit integer
    76  	var signedID int64
    77  	if err := binary.Read(buf, binary.LittleEndian, &signedID); err != nil {
    78  		return 0, fmt.Errorf("context corrupted; could not convert value: %s", err)
    79  	}
    80  
    81  	return signedID, nil
    82  }
    83  
    84  // ParseLongID converts an header context value into a 128-bit Instana ID. Both high and low
    85  // quad words are returned as signed integers.
    86  func ParseLongID(header string) (hi int64, lo int64, err error) {
    87  	if len(header) > 16 {
    88  		hi, err = ParseID(header[:len(header)-16])
    89  		if err != nil {
    90  			return 0, 0, fmt.Errorf("failed to parse the higher 4 bytes of a 128-bit integer: %s", err)
    91  		}
    92  
    93  		header = header[len(header)-16:]
    94  	}
    95  
    96  	lo, err = ParseID(header)
    97  
    98  	return hi, lo, err
    99  }
   100  
   101  // ID2Header calls instana.FormatID() and returns its result and a nil error.
   102  // This is kept here for backward compatibility with go-sensor@v1.x
   103  //
   104  // Deprecated: please use instana.FormatID() instead
   105  func ID2Header(id int64) (string, error) {
   106  	return FormatID(id), nil
   107  }
   108  
   109  // Header2ID calls instana.ParseID() and returns its result.
   110  // This is kept here for backward compatibility with go-sensor@v1.x
   111  //
   112  // Deprecated: please use instana.ParseID() instead
   113  func Header2ID(header string) (int64, error) {
   114  	return ParseID(header)
   115  }
   116  
   117  func getProcCommandLine() (string, []string, bool) {
   118  	var cmdlinePath string = "/proc/" + strconv.Itoa(os.Getpid()) + "/cmdline"
   119  
   120  	cmdline, err := ioutil.ReadFile(cmdlinePath)
   121  	if err != nil {
   122  		return "", nil, false
   123  	}
   124  
   125  	parts := strings.FieldsFunc(string(cmdline), func(c rune) bool {
   126  		return c == '\u0000'
   127  	})
   128  
   129  	return parts[0], parts[1:], true
   130  }
   131  
   132  func getProcessEnv() map[string]string {
   133  	osEnv := os.Environ()
   134  
   135  	env := make(map[string]string, len(osEnv))
   136  	for _, envVar := range osEnv {
   137  		idx := strings.Index(envVar, "=")
   138  		if idx < 0 {
   139  			continue
   140  		}
   141  
   142  		env[envVar[:idx]] = envVar[idx+1:]
   143  	}
   144  
   145  	return env
   146  }
   147  
   148  func getDefaultGateway(routeTableFile string) (string, error) {
   149  	routeTable, err := os.Open(routeTableFile)
   150  	if err != nil {
   151  		return "", fmt.Errorf("failed to open %s: %s", routeTableFile, err)
   152  	}
   153  	defer routeTable.Close()
   154  
   155  	s := bufio.NewScanner(routeTable)
   156  	for s.Scan() {
   157  		entry := strings.Split(s.Text(), "\t")
   158  		if len(entry) < 3 {
   159  			continue
   160  		}
   161  
   162  		destination := entry[1]
   163  		if destination == "00000000" {
   164  			gatewayHex := []rune(entry[2])
   165  
   166  			gateway, err := hexGatewayToAddr(gatewayHex)
   167  			if err != nil {
   168  				return "", err
   169  			}
   170  
   171  			return gateway, nil
   172  		}
   173  	}
   174  
   175  	if err := s.Err(); err != nil {
   176  		return "", fmt.Errorf("failed to read %s: %s", routeTableFile, err)
   177  	}
   178  
   179  	return "", nil
   180  }
   181  
   182  // hexGatewayToAddr converts the hex representation of the gateway address to string.
   183  func hexGatewayToAddr(gateway []rune) (string, error) {
   184  	// gateway address is encoded in reverse order in hex
   185  	if len(gateway) != 8 {
   186  		return "", errors.New("invalid gateway length")
   187  	}
   188  
   189  	var octets [4]uint8
   190  	for i, hexOctet := range [4]string{
   191  		string(gateway[6:8]), // first octet of IP Address
   192  		string(gateway[4:6]), // second octet
   193  		string(gateway[2:4]), // third octet
   194  		string(gateway[0:2]), // last octet
   195  	} {
   196  		octet, err := strconv.ParseUint(hexOctet, 16, 8)
   197  		if err != nil {
   198  			return "", err
   199  		}
   200  
   201  		octets[i] = uint8(octet)
   202  	}
   203  
   204  	return fmt.Sprintf("%v.%v.%v.%v", octets[0], octets[1], octets[2], octets[3]), nil
   205  }