Update dependencies

This commit is contained in:
bluepython508
2024-11-01 17:33:34 +00:00
parent 033ac0b400
commit 5cdfab398d
3596 changed files with 1033483 additions and 259 deletions

View File

@@ -0,0 +1,231 @@
// Copyright 2017-2018 DigitalOcean.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package smbios
import (
"bufio"
"bytes"
"encoding/binary"
"io"
)
const (
// headerLen is the length of the Header structure.
headerLen = 4
// typeEndOfTable indicates the end of a stream of Structures.
typeEndOfTable = 127
)
var (
// Byte slices used to help parsing string-sets.
null = []byte{0x00}
endStringSet = []byte{0x00, 0x00}
)
// A Decoder decodes Structures from a stream.
type Decoder struct {
br *bufio.Reader
b []byte
}
// Stream locates and opens a stream of SMBIOS data and the SMBIOS entry
// point from an operating system-specific location. The stream must be
// closed after decoding to free its resources.
//
// If no suitable location is found, an error is returned.
func Stream() (io.ReadCloser, EntryPoint, error) {
rc, ep, err := stream()
if err != nil {
return nil, nil, err
}
// The io.ReadCloser from stream could be any one of a number of types
// depending on the source of the SMBIOS stream information.
//
// To prevent the caller from potentially tampering with something dangerous
// like mmap'd memory by using a type assertion, we make the io.ReadCloser
// into an opaque and unexported type to prevent type assertion.
return &opaqueReadCloser{rc: rc}, ep, nil
}
// NewDecoder creates a Decoder which decodes Structures from the input stream.
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{
br: bufio.NewReader(r),
b: make([]byte, 1024),
}
}
// Decode decodes Structures from the Decoder's stream until an End-of-table
// structure is found.
func (d *Decoder) Decode() ([]*Structure, error) {
var ss []*Structure
for {
s, err := d.next()
if err != nil {
return nil, err
}
// End-of-table structure indicates end of stream.
ss = append(ss, s)
if s.Header.Type == typeEndOfTable {
break
}
}
return ss, nil
}
// next decodes the next Structure from the stream.
func (d *Decoder) next() (*Structure, error) {
h, err := d.parseHeader()
if err != nil {
return nil, err
}
// Length of formatted section is length specified by header, minus
// the length of the header itself.
l := int(h.Length) - headerLen
fb, err := d.parseFormatted(l)
if err != nil {
return nil, err
}
ss, err := d.parseStrings()
if err != nil {
return nil, err
}
return &Structure{
Header: *h,
Formatted: fb,
Strings: ss,
}, nil
}
// parseHeader parses a Structure's Header from the stream.
func (d *Decoder) parseHeader() (*Header, error) {
if _, err := io.ReadFull(d.br, d.b[:headerLen]); err != nil {
return nil, err
}
return &Header{
Type: d.b[0],
Length: d.b[1],
Handle: binary.LittleEndian.Uint16(d.b[2:4]),
}, nil
}
// parseFormatted parses a Structure's formatted data from the stream.
func (d *Decoder) parseFormatted(l int) ([]byte, error) {
// Guard against malformed input length.
if l < 0 {
return nil, io.ErrUnexpectedEOF
}
if l == 0 {
// No formatted data.
return nil, nil
}
if _, err := io.ReadFull(d.br, d.b[:l]); err != nil {
return nil, err
}
// Make a copy to free up the internal buffer.
fb := make([]byte, len(d.b[:l]))
copy(fb, d.b[:l])
return fb, nil
}
// parseStrings parses a Structure's strings from the stream, if they
// are present.
func (d *Decoder) parseStrings() ([]string, error) {
term, err := d.br.Peek(2)
if err != nil {
return nil, err
}
// If no string-set present, discard delimeter and end parsing.
if bytes.Equal(term, endStringSet) {
if _, err := d.br.Discard(2); err != nil {
return nil, err
}
return nil, nil
}
var ss []string
for {
s, more, err := d.parseString()
if err != nil {
return nil, err
}
// When final string is received, end parse loop.
ss = append(ss, s)
if !more {
break
}
}
return ss, nil
}
// parseString parses a single string from the stream, and returns if
// any more strings are present.
func (d *Decoder) parseString() (str string, more bool, err error) {
// We initially read bytes because it's more efficient to manipulate bytes
// and allocate a string once we're all done.
//
// Strings are null-terminated.
raw, err := d.br.ReadBytes(0x00)
if err != nil {
return "", false, err
}
b := bytes.TrimRight(raw, "\x00")
peek, err := d.br.Peek(1)
if err != nil {
return "", false, err
}
if !bytes.Equal(peek, null) {
// Next byte isn't null; more strings to come.
return string(b), true, nil
}
// If two null bytes appear in a row, end of string-set.
// Discard the null and indicate no more strings.
if _, err := d.br.Discard(1); err != nil {
return "", false, err
}
return string(b), false, nil
}
var _ io.ReadCloser = &opaqueReadCloser{}
// An opaqueReadCloser masks the type of the underlying io.ReadCloser to
// prevent type assertions.
type opaqueReadCloser struct {
rc io.ReadCloser
}
func (rc *opaqueReadCloser) Read(b []byte) (int, error) { return rc.rc.Read(b) }
func (rc *opaqueReadCloser) Close() error { return rc.rc.Close() }

17
vendor/github.com/digitalocean/go-smbios/smbios/doc.go generated vendored Normal file
View File

@@ -0,0 +1,17 @@
// Copyright 2017-2018 DigitalOcean.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package smbios provides detection and access to System Management BIOS (SMBIOS)
// and Desktop Management Interface (DMI) data and structures.
package smbios

View File

@@ -0,0 +1,267 @@
// Copyright 2017-2018 DigitalOcean.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package smbios
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
)
// Anchor strings used to detect entry points.
var (
// Used when searching for an entry point in memory.
magicPrefix = []byte("_SM")
// Used to determine specific entry point types.
magic32 = []byte("_SM_")
magic64 = []byte("_SM3_")
magicDMI = []byte("_DMI_")
)
// An EntryPoint is an SMBIOS entry point. EntryPoints contain various
// properties about SMBIOS.
//
// Use a type assertion to access detailed EntryPoint information.
type EntryPoint interface {
// Table returns the memory address and maximum size of the SMBIOS table.
Table() (address, size int)
// Version returns the system's SMBIOS version.
Version() (major, minor, revision int)
}
// ParseEntryPoint parses an EntryPoint from the input stream.
func ParseEntryPoint(r io.Reader) (EntryPoint, error) {
// Prevent unbounded reads since this structure should be small.
b, err := ioutil.ReadAll(io.LimitReader(r, 64))
if err != nil {
return nil, err
}
if l := len(b); l < 4 {
return nil, fmt.Errorf("too few bytes for SMBIOS entry point magic: %d", l)
}
switch {
case bytes.HasPrefix(b, magic32):
return parse32(b)
case bytes.HasPrefix(b, magic64):
return parse64(b)
}
return nil, fmt.Errorf("unrecognized SMBIOS entry point magic: %v", b[0:4])
}
var _ EntryPoint = &EntryPoint32Bit{}
// EntryPoint32Bit is the SMBIOS 32-bit Entry Point structure, used starting
// in SMBIOS 2.1.
type EntryPoint32Bit struct {
Anchor string
Checksum uint8
Length uint8
Major uint8
Minor uint8
MaxStructureSize uint16
EntryPointRevision uint8
FormattedArea [5]byte
IntermediateAnchor string
IntermediateChecksum uint8
StructureTableLength uint16
StructureTableAddress uint32
NumberStructures uint16
BCDRevision uint8
}
// Table implements EntryPoint.
func (e *EntryPoint32Bit) Table() (address, size int) {
return int(e.StructureTableAddress), int(e.StructureTableLength)
}
// Version implements EntryPoint.
func (e *EntryPoint32Bit) Version() (major, minor, revision int) {
return int(e.Major), int(e.Minor), 0
}
// parse32 parses an EntryPoint32Bit from b.
func parse32(b []byte) (*EntryPoint32Bit, error) {
l := len(b)
// Correct minimum length as of SMBIOS 3.1.1.
const expLen = 31
if l < expLen {
return nil, fmt.Errorf("expected SMBIOS 32-bit entry point minimum length of at least %d, but got: %d", expLen, l)
}
// Allow more data in the buffer than the actual length, for when the
// entry point is being read from system memory.
length := b[5]
if l < int(length) {
return nil, fmt.Errorf("expected SMBIOS 32-bit entry point actual length of at least %d, but got: %d", length, l)
}
// Look for intermediate anchor with DMI magic.
iAnchor := b[16:21]
if !bytes.Equal(iAnchor, magicDMI) {
return nil, fmt.Errorf("incorrect DMI magic in SMBIOS 32-bit entry point: %v", iAnchor)
}
// Entry point checksum occurs at index 4, compute and verify it.
const epChkIndex = 4
epChk := b[epChkIndex]
if err := checksum(epChk, epChkIndex, b[:length]); err != nil {
return nil, err
}
// Since we already computed the checksum for the outer entry point,
// no real need to compute it for the intermediate entry point.
ep := &EntryPoint32Bit{
Anchor: string(b[0:4]),
Checksum: epChk,
Length: length,
Major: b[6],
Minor: b[7],
MaxStructureSize: binary.LittleEndian.Uint16(b[8:10]),
EntryPointRevision: b[10],
IntermediateAnchor: string(iAnchor),
IntermediateChecksum: b[21],
StructureTableLength: binary.LittleEndian.Uint16(b[22:24]),
StructureTableAddress: binary.LittleEndian.Uint32(b[24:28]),
NumberStructures: binary.LittleEndian.Uint16(b[28:30]),
BCDRevision: b[30],
}
copy(ep.FormattedArea[:], b[10:15])
return ep, nil
}
var _ EntryPoint = &EntryPoint64Bit{}
// EntryPoint64Bit is the SMBIOS 64-bit Entry Point structure, used starting
// in SMBIOS 3.0.
type EntryPoint64Bit struct {
Anchor string
Checksum uint8
Length uint8
Major uint8
Minor uint8
Revision uint8
EntryPointRevision uint8
Reserved uint8
StructureTableMaxSize uint32
StructureTableAddress uint64
}
// Table implements EntryPoint.
func (e *EntryPoint64Bit) Table() (address, size int) {
return int(e.StructureTableAddress), int(e.StructureTableMaxSize)
}
// Version implements EntryPoint.
func (e *EntryPoint64Bit) Version() (major, minor, revision int) {
return int(e.Major), int(e.Minor), int(e.Revision)
}
const (
// expLen64 is the expected minimum length of a 64-bit entry point.
// Correct minimum length as of SMBIOS 3.1.1.
expLen64 = 24
// chkIndex64 is the index of the checksum byte in a 64-bit entry point.
chkIndex64 = 5
)
// parse64 parses an EntryPoint64Bit from b.
func parse64(b []byte) (*EntryPoint64Bit, error) {
l := len(b)
// Ensure expected minimum length.
if l < expLen64 {
return nil, fmt.Errorf("expected SMBIOS 64-bit entry point minimum length of at least %d, but got: %d", expLen64, l)
}
// Allow more data in the buffer than the actual length, for when the
// entry point is being read from system memory.
length := b[6]
if l < int(length) {
return nil, fmt.Errorf("expected SMBIOS 64-bit entry point actual length of at least %d, but got: %d", length, l)
}
// Checksum occurs at index 5, compute and verify it.
chk := b[chkIndex64]
if err := checksum(chk, chkIndex64, b); err != nil {
return nil, err
}
return &EntryPoint64Bit{
Anchor: string(b[0:5]),
Checksum: chk,
Length: length,
Major: b[7],
Minor: b[8],
Revision: b[9],
EntryPointRevision: b[10],
Reserved: b[11],
StructureTableMaxSize: binary.LittleEndian.Uint32(b[12:16]),
StructureTableAddress: binary.LittleEndian.Uint64(b[16:24]),
}, nil
}
// checksum computes the checksum of b using the starting value of start, and
// skipping the checksum byte which occurs at index chkIndex.
//
// checksum assumes that b has already had its bounds checked.
func checksum(start uint8, chkIndex int, b []byte) error {
chk := start
for i := range b {
// Checksum computation does not include index of checksum byte.
if i == chkIndex {
continue
}
chk += b[i]
}
if chk != 0 {
return fmt.Errorf("invalid entry point checksum %#02x from initial checksum %#02x", chk, start)
}
return nil
}
// WindowsEntryPoint contains SMBIOS Table entry point data returned from
// GetSystemFirmwareTable. As raw access to the underlying memory is not given,
// the full breadth of information is not available.
type WindowsEntryPoint struct {
Size uint32
MajorVersion byte
MinorVersion byte
Revision byte
}
// Table implements EntryPoint. The returned address will always be 0, as it
// is not returned by GetSystemFirmwareTable.
func (e *WindowsEntryPoint) Table() (address, size int) {
return 0, int(e.Size)
}
// Version implements EntryPoint.
func (e *WindowsEntryPoint) Version() (major, minor, revision int) {
return int(e.MajorVersion), int(e.MinorVersion), int(e.Revision)
}

View File

@@ -0,0 +1,35 @@
// Copyright 2017-2018 DigitalOcean.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build gofuzz
package smbios
import (
"bytes"
)
func Fuzz(data []byte) int {
return fuzzDecoder(data)
}
func fuzzDecoder(data []byte) int {
d := NewDecoder(bytes.NewReader(data))
if _, err := d.Decode(); err != nil {
return 0
}
return 1
}

View File

@@ -0,0 +1,65 @@
// Copyright 2017-2018 DigitalOcean.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build linux
package smbios
import (
"io"
"os"
)
const (
// sysfs locations for SMBIOS information.
sysfsDMI = "/sys/firmware/dmi/tables/DMI"
sysfsEntryPoint = "/sys/firmware/dmi/tables/smbios_entry_point"
)
// stream opens the SMBIOS entry point and an SMBIOS structure stream.
func stream() (io.ReadCloser, EntryPoint, error) {
// First, check for the sysfs location present in modern kernels.
_, err := os.Stat(sysfsEntryPoint)
switch {
case err == nil:
return sysfsStream(sysfsEntryPoint, sysfsDMI)
case os.IsNotExist(err):
// Fall back to the standard UNIX-like system method.
return devMemStream()
default:
return nil, nil, err
}
}
// sysfsStream reads the SMBIOS entry point and structure stream from
// two files; usually the modern sysfs locations.
func sysfsStream(entryPoint, dmi string) (io.ReadCloser, EntryPoint, error) {
epf, err := os.Open(entryPoint)
if err != nil {
return nil, nil, err
}
defer epf.Close()
ep, err := ParseEntryPoint(epf)
if err != nil {
return nil, nil, err
}
sf, err := os.Open(dmi)
if err != nil {
return nil, nil, err
}
return sf, ep, nil
}

View File

@@ -0,0 +1,125 @@
// Copyright 2017-2018 DigitalOcean.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package smbios
import (
"bytes"
"errors"
"io"
"io/ioutil"
"os"
)
const (
// devMem is the UNIX-like system memory device location used to
// find SMBIOS information.
devMem = "/dev/mem"
// SMBIOS specification indicates that the entry point should exist
// between these two memory addresses.
startAddr = 0x000f0000
endAddr = 0x000fffff
)
// memoryStream reads the SMBIOS entry point and structure stream from
// an io.ReadSeeker (usually system memory).
//
// memoryStream is an entry point for tests.
func memoryStream(rs io.ReadSeeker, startAddr, endAddr int) (io.ReadCloser, EntryPoint, error) {
// Try to find the entry point.
addr, err := findEntryPoint(rs, startAddr, endAddr)
if err != nil {
return nil, nil, err
}
// Found it; seek to the location of the entry point.
if _, err := rs.Seek(int64(addr), io.SeekStart); err != nil {
return nil, nil, err
}
// Read the entry point and determine where the SMBIOS table is.
ep, err := ParseEntryPoint(rs)
if err != nil {
return nil, nil, err
}
// Seek to the start of the SMBIOS table.
tableAddr, tableSize := ep.Table()
if _, err := rs.Seek(int64(tableAddr), io.SeekStart); err != nil {
return nil, nil, err
}
// Make a copy of the memory so we don't return a handle to system memory
// to the caller.
out := make([]byte, tableSize)
if _, err := io.ReadFull(rs, out); err != nil {
return nil, nil, err
}
return ioutil.NopCloser(bytes.NewReader(out)), ep, nil
}
// findEntryPoint attempts to locate the entry point structure in the io.ReadSeeker
// using the start and end bound as hints for its location.
func findEntryPoint(rs io.ReadSeeker, start, end int) (int, error) {
// Begin searching at the start bound.
if _, err := rs.Seek(int64(start), io.SeekStart); err != nil {
return 0, err
}
// Iterate one "paragraph" of memory at a time until we either find the entry point
// or reach the end bound.
const paragraph = 16
b := make([]byte, paragraph)
var (
addr int
found bool
)
for addr = start; addr < end; addr += paragraph {
if _, err := io.ReadFull(rs, b); err != nil {
return 0, err
}
// Both the 32-bit and 64-bit entry point have a similar prefix.
if bytes.HasPrefix(b, magicPrefix) {
found = true
break
}
}
if !found {
return 0, errors.New("no SMBIOS entry point found in memory")
}
// Return the exact memory location of the entry point.
return addr, nil
}
// devMemStream reads the SMBIOS entry point and structure stream from
// the UNIX-like system /dev/mem device.
//
// This is UNIX-like system specific, but since it doesn't employ any system
// calls or OS-dependent constants, it remains in this file for simplicity.
func devMemStream() (io.ReadCloser, EntryPoint, error) {
mem, err := os.Open(devMem)
if err != nil {
return nil, nil, err
}
defer mem.Close()
return memoryStream(mem, startAddr, endAddr)
}

View File

@@ -0,0 +1,28 @@
// Copyright 2017-2018 DigitalOcean.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build !dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
package smbios
import (
"fmt"
"io"
"runtime"
)
// stream is not implemented for unsupported platforms.
func stream() (io.ReadCloser, EntryPoint, error) {
return nil, nil, fmt.Errorf("opening SMBIOS stream not implemented on %q", runtime.GOOS)
}

View File

@@ -0,0 +1,30 @@
// Copyright 2017-2018 DigitalOcean.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build dragonfly freebsd netbsd openbsd solaris
// Linux intentionally omitted because it has an alternative method that
// is used before attempting /dev/mem access. See stream_linux.go.
package smbios
import (
"io"
)
// stream opens the SMBIOS entry point and an SMBIOS structure stream.
func stream() (io.ReadCloser, EntryPoint, error) {
// Use the standard UNIX-like system method.
return devMemStream()
}

View File

@@ -0,0 +1,155 @@
// Copyright 2017-2018 DigitalOcean.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package smbios
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"io/ioutil"
"syscall"
"unsafe"
)
// firmwareTableProviderSigRSMB is the identifier for the raw SMBIOS firmware table
// provider.
// It is equal to the ASCII characters 'RSMB' packed into a uint32.
// In the C++ example code in the MSDN documentation, this is specified using
// multi-byte character literals, which are automatically coerced to an integer by
// the C++ compiler.
const firmwareTableProviderSigRSMB uint32 = 0x52534d42
// smbiosDataHeaderSize is size of the "header" (non-variable) part of the
// RawSMBIOSData struct. This serves as both the offset to the actual
// SMBIOS table data, and the minimum possible size of a valid RawSMBIOSDATA
// struct (with a table length of 0).
const rawSMBIOSDataHeaderSize = 8
var (
libKernel32 = syscall.NewLazyDLL("kernel32.dll")
// MSDN Documentation for GetSystemFirmwareTable:
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724379(v=vs.85).aspx
procGetSystemFirmwareTable = libKernel32.NewProc("GetSystemFirmwareTable")
)
// nativeEndian returns the native byte order of this system.
func nativeEndian() binary.ByteOrder {
// Determine endianness by interpreting a uint16 as a byte slice.
v := uint16(1)
b := *(*[2]byte)(unsafe.Pointer(&v))
if b[0] == 1 {
return binary.LittleEndian
}
return binary.BigEndian
}
// windowsStream parses the data returned from GetSystemFirmwareTable('RSMB',...)
// and returns a stream and entrypoint that can be used to decode the system's
// SMBIOS table.
//
// When calling GetSystemFirmwareTable with FirmwareTableProviderSignature = "RSMB",
// Windows will write a RawSMBIOSData struct into the output buffer.
// Thus, `buf` is expected to contain a valid RawSMBIOSData structure.
//
// From windows.h:
//
// struct RawSMBIOSData {
// BYTE Used20CallingMethod;
// BYTE SMBIOSMajorVersion;
// BYTE SMBIOSMinorVersion;
// BYTE DMIRevision;
// DWORD Length;
// BYTE SMBIOSTableData[];
// }
//
// Note: a DWORD is equivalent to a uint32
// See: https://msdn.microsoft.com/en-us/library/cc230318.aspx
func windowsStream(buf []byte) (io.ReadCloser, EntryPoint, error) {
bufLen := uint32(len(buf))
// Do an additional check to make sure the actual amount written is sane.
if bufLen < rawSMBIOSDataHeaderSize {
return nil, nil, fmt.Errorf("GetSystemFirmwareTable wrote less data than expected: wrote %d bytes, expected at least 8 bytes", bufLen)
}
tableSize := nativeEndian().Uint32(buf[4:8])
if rawSMBIOSDataHeaderSize+tableSize > bufLen {
return nil, nil, errors.New("reported SMBIOS table size exceeds buffer")
}
entryPoint := &WindowsEntryPoint{
MajorVersion: buf[1],
MinorVersion: buf[2],
Revision: buf[3],
Size: tableSize,
}
tableBuff := buf[rawSMBIOSDataHeaderSize : rawSMBIOSDataHeaderSize+tableSize]
return ioutil.NopCloser(bytes.NewReader(tableBuff)), entryPoint, nil
}
func stream() (io.ReadCloser, EntryPoint, error) {
// Call first with empty buffer to get size.
r1, _, err := procGetSystemFirmwareTable.Call(
uintptr(firmwareTableProviderSigRSMB), // FirmwareTableProviderSignature = 'RSMB'
0, // FirmwareTableID = 0
0, // pFirmwareTableBuffer = NULL
0, // BufferSize = 0
)
// LazyProc.Call will always return err != nil, so we need to check the primary
// return value (r1) to determine whether or not an error occurred.
// In this case, r1 should contain the size of the needed buffer, so it will only
// be 0 if the function call failed for some reason.
//
// Godoc for LazyProc.Call:
// https://golang.org/pkg/syscall/?GOOS=windows&GOARCH=amd64#LazyProc.Call
if r1 == 0 {
return nil, nil, fmt.Errorf("failed to determine size of buffer needed: %v", err)
}
if r1 < rawSMBIOSDataHeaderSize {
return nil, nil, fmt.Errorf("reported buffer size smaller than expected: reported %d, expected >= 8", r1)
}
bufferSize := uint32(r1)
buffer := make([]byte, bufferSize)
r1, _, err = procGetSystemFirmwareTable.Call(
uintptr(firmwareTableProviderSigRSMB), // FirmwareTableProviderSignature = 'RSMB'
0, // FirmwareTableID = 0
uintptr(unsafe.Pointer(&buffer[0])), // pFirmwareTableBuffer = &buffer
uintptr(bufferSize), // BufferSize = bufferSize
)
bytesWritten := uint32(r1)
// Check for the two possible failure cases documented in API:
if bytesWritten > bufferSize {
return nil, nil, fmt.Errorf("buffer size was too small, somehow: have %d bytes, Windows wanted %d bytes", bufferSize, bytesWritten)
}
if bytesWritten == 0 {
return nil, nil, fmt.Errorf("failed to read SMBIOS data: %v", err)
}
// At this point, bytesWritten <= bufferSize, which means the call succeeded as
// per the MSDN documentation.
return windowsStream(buffer[:bytesWritten])
}

View File

@@ -0,0 +1,29 @@
// Copyright 2017-2018 DigitalOcean.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package smbios
// A Header is a Structure's header.
type Header struct {
Type uint8
Length uint8
Handle uint16
}
// A Structure is an SMBIOS structure.
type Structure struct {
Header Header
Formatted []byte
Strings []string
}