Get permanent MAC address - go

Is there an easy way to get the permanent MAC Address using Go?

package main
import (
"fmt"
"log"
"net"
)
func getMacAddr() ([]string, error) {
ifas, err := net.Interfaces()
if err != nil {
return nil, err
}
var as []string
for _, ifa := range ifas {
a := ifa.HardwareAddr.String()
if a != "" {
as = append(as, a)
}
}
return as, nil
}
func main() {
as, err := getMacAddr()
if err != nil {
log.Fatal(err)
}
for _, a := range as {
fmt.Println(a)
}
}

Related

I only have *.pem and *.key files, how to make sure the http client is not attacked

I only got the two files *.pem and *.key through the certificate authority.
I am worried about man-in-the-middle attacks, which will replace the certificate and deceive the client, so I wrote the following code, the client directly reads the *.pem file and compares the certificate obtained by http to ensure that the connection is correct when they are the same.
I would like to know if my solution is correct and if there is a better solution to my problem?
server code
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
_, _ = fmt.Fprintf(w, "hello %s", time.Now().String())
})
err := http.ListenAndServeTLS(":443", `xx.pem`, `xx.key`, nil)
if err != nil {
panic(err)
}
}
client code
package main
import (
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io"
"net/http"
"os"
)
func main() {
err := getPem()
if err != nil {
panic(err)
}
err = httpGet()
if err != nil {
panic(err)
}
}
var (
pemRawCerts [][]byte // xx.pem读取的原始数据
errCheckPem = errors.New("check xx.pem error")
)
func getPem() error {
certPEMBlock, err := os.ReadFile(`xxx.pem`)
if err != nil {
return err
}
var certDERBlock *pem.Block
for {
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
if certDERBlock == nil {
break
}
if certDERBlock.Type == "CERTIFICATE" {
tmp := make([]byte, len(certDERBlock.Bytes))
copy(tmp, certDERBlock.Bytes)
pemRawCerts = append(pemRawCerts, tmp)
}
}
return nil
}
func httpGet() error {
c := http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
VerifyPeerCertificate: func(data [][]byte, verifiedChains [][]*x509.Certificate) error {
if len(data) == len(pemRawCerts) {
for i := len(data) - 1; i >= 0; i-- {
if !bytes.Equal(data[i], pemRawCerts[i]) {
return errCheckPem
}
}
// Only when every item of the certificate is correct can the current connection be determined to be ok
return nil
}
return errCheckPem
},
},
},
}
resp, err := c.Get("https://janbar.com")
if err != nil {
return err
}
defer resp.Body.Close()
n, err := io.Copy(io.Discard, resp.Body)
fmt.Println(n)
return err
}

How to terminate a console input request when a new input is requested

I need to terminate an existing console input request when a new one is requested. The following code is an attempt to close an existing request using a channel but it does not seem to terminate the input request.
package main
import (
"bufio"
"fmt"
"log"
"os"
"strings"
"time"
)
func main() {
go Confirm("you are a programmer, aint you?")
time.Sleep(2 * time.Second)
Confirm("do you love go?")
}
var cancelChannel chan struct{}
func Confirm(s string) bool {
//check if channel type holds a value then close the channel to remove previous confirmation input
if cancelChannel != nil {
fmt.Println("channel to be closed")
close(cancelChannel)
}
cancelChannel = make(chan struct{})
reader := bufio.NewReader(os.Stdin)
for {
fmt.Printf("%s [y/n]: ", s)
response, err := reader.ReadString('\n')
if err != nil {
log.Fatal(err)
}
response = strings.ToLower(strings.TrimSpace(response))
if response == "y" || response == "yes" {
return true
} else if response == "n" || response == "no" {
return false
}
if _, ok := <-cancelChannel; !ok {
fmt.Println("channel closed")
return false
}
}
}
As #JimB mentioned in comment you can't interrupt read on stdin although there is kinda shady trick how you can achieve it. It's possible to duplicate os.Stdin file descriptor using syscall (not recommended) and open it as non blocking file.
package main
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"io/fs"
"io/ioutil"
"os"
"syscall"
"time"
)
func setNonblock(f *os.File) error {
c, err := f.SyscallConn()
if err != nil {
return err
}
var err2 error
err = c.Control(func(fd uintptr) {
err2 = syscall.SetNonblock(int(fd), true)
})
if err != nil {
return err
}
return err2
}
func nonBlockingFile(f *os.File) (*os.File, error) {
if err := setNonblock(f); err != nil {
return nil, err
}
fd, err := syscall.Dup(int(f.Fd()))
if err != nil {
return nil, err
}
f2 := os.NewFile(uintptr(fd), f.Name())
return f2, nil
}
func read(ctx context.Context, f *os.File) (io.Reader, error) {
r, err := nonBlockingFile(f)
if err != nil {
return nil, err
}
go func() {
defer r.Close()
<-ctx.Done()
}()
buff := bytes.NewBuffer([]byte{})
for {
_, err := io.Copy(buff, r)
if err != nil {
if errors.Is(err, fs.ErrClosed) {
break
}
panic(err)
}
}
return buff, nil
}
func main() {
ctx1, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(time.Second * 2)
cancel()
}()
buf1, err := read(ctx1, os.Stdin)
if err != nil {
panic(err)
}
ctx2, _ := context.WithTimeout(context.Background(), time.Second*2)
buf2, err := read(ctx2, os.Stdin)
fmt.Println("buf1")
fmt.Println(ioutil.ReadAll(buf1))
fmt.Println("buf2")
fmt.Println(ioutil.ReadAll(buf2))
}
Go on and explore the simplicity offer by go
https://pkg.go.dev/context#WithCancel
You can have a context that returning CancelFunc then you use context.WithCancel.
And execute cancel func if you want to terminate.
This is the good practice way, you can also do a dirty os.Exit(0) in another case.

How to parse QueryString

I have a string such as username=Test1234&currency=THB and I need to get value of username or currency
qry, _ := url.Parse(string(qryString))
An example is as follows:
package main
import (
"fmt"
"net/url"
)
func main() {
address := "http://example.com?name=poloxue&age=11"
u, err := url.Parse(address)
if err != nil {
panic(err)
}
fmt.Println(u.RawQuery)
q, err := url.ParseQuery(u.RawQuery)
if err != nil {
panic(err)
}
fmt.Println(q.Get("name"))
}
Output:
name=poloxue&age=11
poloxue

how to generate multiple uuid and md5 files in golang

Hi I've generated Md5 and uuid in golang but now I want generate it for multiple files using command line arguments, so what exactly I've to do. This is how I've generated my md5 and uuid:
package main
import (
"crypto/rand"
"crypto/md5"
"fmt"
"io"
"os"
"log"
"text/template"
)
type Data struct {
Uuid string
Md5 string
}
func main() {
uuid, err := newUUID()
if err != nil {
fmt.Printf("error: %v\n", err)
}
fmt.Printf("UUID: %s\n", uuid)
md5 := Getmd5(uuid)
fmt.Printf("Checksum: %s\n",md5)
fillData := Data{uuid, md5}
file, err := os.Create("text.txt")
if err != nil {
return
}
defer file.Close()
templ, err := template.ParseFiles("template.html")
if err !=nil{
log.Fatalln(err)
}
err = templ.Execute(file,fillData)
if err != nil{
log.Fatalln(err)
}
}
// newUUID generates a random UUID according to RFC 4122
func newUUID() (string, error) {
uuid := make([]byte, 16)
n, err := io.ReadFull(rand.Reader, uuid)
if n != len(uuid) || err != nil {
return "", err
}
// variant bits
uuid[8] = uuid[8]&^0xc0 | 0x80
// version 4 (pseudo-random)
uuid[6] = uuid[6]&^0xf0 | 0x40
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
}
func Getmd5(uuid string) (string) {
data := []byte(uuid)
//md5_buffer := fmt.Sprintf("%x", md5.Sum(data))
md5_buffer := md5.Sum(data)
return fmt.Sprintf("{0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x};\n",md5_buffer[0:1],
md5_buffer[1:2],md5_buffer[2:3],md5_buffer[3:4],md5_buffer[4:5],md5_buffer[5:6],md5_buffer[6:7],md5_buffer[7:8],
md5_buffer[8:9],md5_buffer[9:10],md5_buffer[10:11],md5_buffer[11:12],md5_buffer[12:13],md5_buffer[13:14],md5_buffer[14:15],
md5_buffer[15:16])
}
Can anyone help me out?
You can use os.Args to accept command line arguements
os.Args provides access to raw command-line arguments. Note that the first value in this slice is the path to the program, and os.Args[1:] holds the arguments to the program.
Your program will look like this, have a look at createFile and getNumberOfFiles functions and the main
package main
import (
"crypto/md5"
"crypto/rand"
"errors"
"fmt"
"io"
"log"
"os"
"strconv"
"text/template"
)
type Data struct {
Uuid string
Md5 string
}
func createFile(uuid string) {
md5 := Getmd5(uuid)
fmt.Printf("Checksum: %s\n", md5)
fillData := Data{uuid, md5}
file, err := os.Create(uuid + ".txt")
if err != nil {
return
}
defer file.Close()
templ, err := template.ParseFiles("template.html")
if err != nil {
log.Fatalln(err)
}
err = templ.Execute(file, fillData)
if err != nil {
log.Fatalln(err)
}
}
func getNumberOfFiles() (num int, err error) {
if len(os.Args) == 1 {
return 0, errors.New("Not enough arguements")
}
if num, err = strconv.Atoi(os.Args[1]); err != nil {
return
}
return num, nil
}
func main() {
numberOfFiles, err := getNumberOfFiles()
if err != nil {
fmt.Println(err.Error())
}
fmt.Printf("Creating %d files", numberOfFiles)
for i := 0; i < numberOfFiles; i++ {
uuid, err := newUUID()
if err != nil {
fmt.Printf("error: %v\n", err)
}
createFile(uuid)
}
}
// newUUID generates a random UUID according to RFC 4122
func newUUID() (string, error) {
uuid := make([]byte, 16)
n, err := io.ReadFull(rand.Reader, uuid)
if n != len(uuid) || err != nil {
return "", err
}
// variant bits
uuid[8] = uuid[8]&^0xc0 | 0x80
// version 4 (pseudo-random)
uuid[6] = uuid[6]&^0xf0 | 0x40
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
}
func Getmd5(uuid string) string {
data := []byte(uuid)
//md5_buffer := fmt.Sprintf("%x", md5.Sum(data))
md5_buffer := md5.Sum(data)
return fmt.Sprintf("{0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x};\n", md5_buffer[0:1],
md5_buffer[1:2], md5_buffer[2:3], md5_buffer[3:4], md5_buffer[4:5], md5_buffer[5:6], md5_buffer[6:7], md5_buffer[7:8],
md5_buffer[8:9], md5_buffer[9:10], md5_buffer[10:11], md5_buffer[11:12], md5_buffer[12:13], md5_buffer[13:14], md5_buffer[14:15],
md5_buffer[15:16])
}

How to retrieve address of current machine?

The following grabs the local IP addresses:
package main
import (
"fmt"
"net"
)
func main() {
a, _ := net.LookupHost("localhost")
fmt.Printf("Addresses: %#+v\n",a)
}
Is this how you would normally get the local IP address, filtering the slice manually according to need?
Here's a quick and dirty modification of a code snippet originally posted by Russ Cox to the golang-nuts google group:
package main
import (
"fmt"
"net"
"os"
)
func main() {
tt, err := net.Interfaces()
if err != nil {
panic(err)
}
for _, t := range tt {
aa, err := t.Addrs()
if err != nil {
panic(err)
}
for _, a := range aa {
ipnet, ok := a.(*net.IPNet)
if !ok {
continue
}
v4 := ipnet.IP.To4()
if v4 == nil || v4[0] == 127 { // loopback address
continue
}
fmt.Printf("%v\n", v4)
}
os.Exit(0)
}
os.Exit(1)
}
Finding the correct IP address can be a problem because a typical server and development machine may have multiple interfaces. For example $ifconfig on my Mac returns the following interfaces lo0, gif0, stf0, en0, en1, en2, bridge0, p2p0, vmnet1, vmnet8, tap0, fw0, en4
Basically, you need to know your environment.
It's not pretty, but for what it's worth, this is what I use on a production Ubuntu server. It also works on my development Mac 10.9.2, who know what it does on Windows.
package main
import (
"net"
"strings"
)
func findIPAddress() string {
if interfaces, err := net.Interfaces(); err == nil {
for _, interfac := range interfaces {
if interfac.HardwareAddr.String() != "" {
if strings.Index(interfac.Name, "en") == 0 ||
strings.Index(interfac.Name, "eth") == 0 {
if addrs, err := interfac.Addrs(); err == nil {
for _, addr := range addrs {
if addr.Network() == "ip+net" {
pr := strings.Split(addr.String(), "/")
if len(pr) == 2 && len(strings.Split(pr[0], ".")) == 4 {
return pr[0]
}
}
}
}
}
}
}
}
return ""
}
func main() {
println(findIPAddress())
}
I have one addition: The current solutions shown above are not working at least on FreeBSD 10 because the system returns the addresses as CIDR notation e.g. 192.168.1.2/32! Therefore, it is necessary to change the solution a little bit:
package main
import (
"fmt"
"net"
"os"
"strings"
)
func main() {
addrs, err := net.InterfaceAddrs()
if err != nil {
fmt.Println("Error: " + err.Error())
os.Exit(1)
}
for _, a := range addrs {
text := a.String()
if strings.Contains(text, `/`) {
text = text[:strings.Index(text, `/`)]
}
ip := net.ParseIP(text)
if !ip.IsLoopback() && !ip.IsUnspecified() {
fmt.Println(ip)
}
}
}
The part ...
if strings.Contains(text, `/`) {
text = text[:strings.Index(text, `/`)]
}
... detects if / is part of the address and delete this part!
Best regards,
Thorsten
These slight modifications worked for me:
package main
import (
"fmt"
"net"
"os"
)
func myip() {
os.Stdout.WriteString("myip:\n")
addrs, err := net.InterfaceAddrs()
if err != nil {
fmt.Errorf("error: %v\n", err.Error())
return
}
for _, a := range addrs {
ip := net.ParseIP(a.String())
fmt.Printf("addr: %v loopback=%v\n", a, ip.IsLoopback())
}
fmt.Println()
}
func myip2() {
os.Stdout.WriteString("myip2:\n")
tt, err := net.Interfaces()
if err != nil {
fmt.Errorf("error: %v\n", err.Error())
return
}
for _, t := range tt {
aa, err := t.Addrs()
if err != nil {
fmt.Errorf("error: %v\n", err.Error())
continue
}
for _, a := range aa {
ip := net.ParseIP(a.String())
fmt.Printf("%v addr: %v loopback=%v\n", t.Name, a, ip.IsLoopback())
}
}
fmt.Println()
}
func main() {
fmt.Println("myip -- begin")
myip()
myip2()
fmt.Println("myip -- end")
}

Resources