package util
import (
"crypto/rand"
"encoding/base32"
"encoding/binary"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcapgo"
log "github.com/sirupsen/logrus"
"hash/adler32"
"io/ioutil"
"net"
"os"
"strconv"
"strings"
"time"
)
// WritePcap writes the provided data to a given pcap file
func WritePcap ( file string , data [ ] byte ) {
f , err := os . Create ( file )
if err != nil {
log . Errorf ( "Error writing pcap file %s" , file )
}
defer f . Close ( )
r , err := pcapgo . NewNgWriter ( f , layers . LinkTypeEthernet )
if err != nil {
log . Errorf ( "Error writing pcap file %s" , file )
}
defer r . Flush ( )
ci := gopacket . CaptureInfo {
Timestamp : time . Now ( ) ,
CaptureLength : len ( data ) ,
Length : len ( data ) ,
}
err = r . WritePacket ( ci , data )
}
// WritePIDFile writes the processes PID to a given file
func WritePIDFile ( filename string ) {
if filename == "" {
return
}
if err := ioutil . WriteFile ( filename , [ ] byte ( strconv . Itoa ( os . Getpid ( ) ) + "\n" ) , 0644 ) ; err != nil {
log . Errorf ( "Error writing PID file %s" , filename )
}
}
// GenerateMac generates a random MAC address
func GenerateMac ( customMAC string ) net . HardwareAddr {
if customMAC != "" {
ret , _ := net . ParseMAC ( customMAC )
return ret
}
buf := make ( [ ] byte , 6 )
var mac net . HardwareAddr
if _ , err := rand . Read ( buf ) ; err != nil {
log . Error ( "Error generating random mac" )
ret , _ := net . ParseMAC ( "52:54:00:12:34:aa" )
return ret
}
// Set local bit, ensure unicast address
buf [ 0 ] = ( buf [ 0 ] | 2 ) & 0xfe
mac = append ( mac , buf [ 0 ] , buf [ 1 ] , buf [ 2 ] , buf [ 3 ] , buf [ 4 ] , buf [ 5 ] )
return mac
}
// GenerateXID generates a random XID for DHCP
func GenerateXID ( ) [ ] byte {
buf := make ( [ ] byte , 4 )
if _ , err := rand . Read ( buf ) ; err != nil {
log . Error ( "Error generating random xid" )
return [ ] byte { 36 , 23 , 250 , 224 } // chosen by fair dice roll
// guaranteed to be random --> https://xkcd.com/221/
}
return buf
}
// GenerateUId generates a random UId string as hostname
func GenerateUId ( path string ) string {
adl := adler32 . Checksum ( [ ] byte ( path ) )
byt := make ( [ ] byte , 4 )
for i := uint32 ( 0 ) ; i < 4 ; i ++ {
byt [ i ] = byte ( ( adl >> ( 8 * i ) ) & 0xff )
}
b32 := base32 . StdEncoding . EncodeToString ( byt )
return strings . ReplaceAll ( b32 , "=" , "" )
}
// If is a helper type to form expressive ternary expressions being the
// concatenation of a type conversion and a method call such as:
//
// i := If(cond).Int(a, b)
//
// For details, see https://stackoverflow.com/a/59375088/1705598
type If bool
// If returns a if c is true, b otherwise.
func ( c If ) If ( a , b interface { } ) interface { } {
if c {
return a
}
return b
}
// MAC returns a if c is true, b otherwise.
func ( c If ) MAC ( a , b net . HardwareAddr ) net . HardwareAddr {
if c {
return a
}
return b
}
// IP returns a if c is true, b otherwise.
func ( c If ) IP ( a , b net . IP ) net . IP {
if c {
return a
}
return b
}
// Source: https://github.com/aler9/landiscover/blob/main/utils.go, MIT LICENSE
// DnsQueryDecode takes encoded hostnames from Microsoft and decodes it
func DnsQueryDecode ( data [ ] byte , start int ) ( string , int ) {
var read [ ] byte
toread := uint8 ( 0 )
pos := start
for ; true ; pos ++ {
if pos >= len ( data ) { // decoding terminated before null character
return "" , - 1
}
if data [ pos ] == 0x00 {
if toread > 0 { // decoding terminated before part parsing
return "" , - 1
}
break // query correctly decoded
}
if toread == 0 { // we need a size or pointer
if len ( read ) > 0 { // add separator
read = append ( read , '.' )
}
if ( data [ pos ] & 0xC0 ) == 0xC0 { // pointer
ptr := int ( binary . BigEndian . Uint16 ( data [ pos : pos + 2 ] ) & 0x3FFF )
pos ++ // skip next byte
substr , subread := DnsQueryDecode ( data , ptr )
if subread <= 0 {
return "" , - 1
}
read = append ( read , [ ] byte ( substr ) ... )
break // query correctly decoded
} else { // size
toread = data [ pos ]
}
} else { // byte inside part
read = append ( read , data [ pos ] )
toread --
}
}
return string ( read ) , pos + 1 - start
}
// DnsQueryEncode takes string hostnames and encodes it to Microsoft format
func DnsQueryEncode ( in string ) [ ] byte {
tmp := strings . Split ( in , "." )
l := 0
for _ , part := range tmp {
bpart := [ ] byte ( part )
l ++
l += len ( bpart )
}
l ++
ret := make ( [ ] byte , l )
i := 0
for _ , part := range tmp {
bpart := [ ] byte ( part )
ret [ i ] = uint8 ( len ( bpart ) )
i ++
copy ( ret [ i : ] , bpart )
i += len ( bpart )
}
ret [ i ] = uint8 ( 0 )
return ret
}
// Prefixes for traffic from (out) or to (in) VM
const (
Out = ">> "
In = "<< "
)