I'm trying to write a password generator. It requires that characters be in ASCII representation, but I'm trying to use crypto/rand. This provides numbers in big.Int format, though, and I need to convert the relevant lower 8 bits to a form usable in a string. I've tried converting from big.Int to uint8 with no luck so far.
Is there a good and easy method to do this? I have seen answers involving using encoding/binary to convert from int64 to [8]uint8, but those seem needlessly complex for my purpose. Any guidance at all would be appreciated :).
package main
import (
"fmt"
"math/big"
)
func main() {
mpInt := big.NewInt(0x123456789abcdef0)
b := byte(mpInt.Int64())
fmt.Printf("%02x", b)
}
f0
In action: http://play.golang.org/p/92PbLdiVsP
EDIT: Evan Shaw correctly points out in a comment bellow that the above code is actually incorrect for big Ints outside of int64 limits. Instead of pulling it from big.Bytes (which makes a copy of all of the bits representing a big.Int IIRC) it's probably more performant to use big.And:
package main
import (
"fmt"
"log"
"math/big"
)
func lsB(n *big.Int) byte {
var m big.Int
return byte(m.And(m.SetInt64(255), n).Int64())
}
func main() {
var mpInt big.Int
if _, ok := mpInt.SetString("0x123456789abcdef012342", 0); !ok {
log.Fatal()
}
fmt.Printf("%02x", lsB(&mpInt))
}
Playground: http://play.golang.org/p/pgkGEFgb8-
If you want to get bytes out of crypto/rand, I'd skip the use of big.Int and rand.Int() entirely and use either rand.Read() or rand.Reader:
package main
import (
"crypto/rand"
"fmt"
"io"
)
// If you want just a byte at a time ...
// (you could change 'byte' to int8 if you prefer)
func secureRandomByte() byte {
data := make([]byte, 1)
if _, err := rand.Read(data); err != nil {
// handle error
panic(err)
}
return data[0]
}
// If you want to read multiple bytes at a time ...
func genPassword(len int) (string, error) {
data := make([]byte, len)
if _, err := io.ReadFull(rand.Reader, data); err != nil {
// handle error
return "", err
}
for i := range data {
// XXX quick munge into something printable
data[i] &= 0x3F
data[i] += 0x20
}
return string(data), nil
}
func main() {
b := secureRandomByte()
fmt.Printf("%T = %v\n", b, b)
fmt.Println(genPassword(16))
}
Related
I've accidentally spotted a bug when parts of a message from previous connection go to the next message.
I have a basic server with client. I have removed all the error handling to avoid bloating the examples too much.
Also I've replaced some Printf's with time.Sleep since I just don't have a chance to break the connection in time to reproduce the bug because it reads the data too fast.
The "package" is a simple structure, where the first 4 bytes is the length and then goes the content.
Client code:
package main
import (
"encoding/binary"
"fmt"
"net"
)
func main() {
conn, _ := net.Dial("tcp", "0.0.0.0:8081")
defer conn.Close()
str := "msadsakdjsajdklsajdklsajdk"
// Creating a package
buf := make([]byte, len(str)+4)
copy(buf[4:], str)
binary.LittleEndian.PutUint32(buf[:4], uint32(len(str)))
for {
_, err := conn.Write(buf)
if err != nil {
fmt.Println(err)
return
}
}
}
Server code:
package main
import (
"encoding/binary"
"fmt"
"net"
"sync"
"time"
)
func ReadConnection(conn net.Conn, buf []byte) (err error) {
maxLen := cap(buf)
readSize := 0
for readSize < maxLen {
// instead of Printf
time.Sleep(time.Nanosecond * 10)
readN, err := conn.Read(buf[readSize:])
if err != nil {
return err
}
readSize += readN
}
return nil
}
func handleConnection(conn net.Conn, waitGroup *sync.WaitGroup) {
waitGroup.Add(1)
defer conn.Close()
defer waitGroup.Done()
fmt.Printf("Serving %s\n", conn.RemoteAddr().String())
var packageSize int32 = 0
int32Buf := make([]byte, 4)
for {
// read the length
conn.Read(int32Buf)
packageSize = int32(binary.LittleEndian.Uint32(int32Buf))
// assuming the length should be 26
if packageSize > 26 {
fmt.Println("Package size error")
return
}
// read the content
packageBuf := make([]byte, packageSize)
if err := ReadConnection(conn, packageBuf); err != nil {
fmt.Printf("ERR: %s\n", err)
return
}
// instead of Printf
time.Sleep(time.Nanosecond * 100)
}
}
func main() {
//establish connection
listener, _ := net.Listen("tcp", "0.0.0.0:8081")
defer listener.Close()
waitGroup := sync.WaitGroup{}
for {
conn, err := listener.Accept()
if err != nil {
break
}
go handleConnection(conn, &waitGroup)
}
waitGroup.Wait()
}
So for some reason, int32Buf receives the last 2 bytes from a previous message (d, k) and the first 2 bytes of the length, resulting in [107, 100, 26, 0] bytes slice, when it should be [26, 0, 0, 0].
And of course, the rest of the data contains remaining two zeroes:
conn.Read(int32Buf)
You need to check the return value of conn.Read and compare it against your expectations. You are assuming in your code that conn.Read will always completely fill the given buffer of 4 bytes.
This assumption is wrong, i.e. it might actually read less data. Specifically it might read only 2 bytes in which case you'll end up with \x1a\x00\x00\x00 in your buffer which still translates to a message length of 26. Only, the first 2 bytes of the message will actually be the last 2 bytes of the length which were not included in the last read. This means after reading the 26 bytes it will not have read the full message. 2 bytes are legt and will be included into the next message - this is what you observed.
To be sure that the exact size of the buffer is read check the return values of conn.Read or use io.ReadFull. After you've done this it works as expected (from the comment):
Ok, now it works perfect
So why does this happened only in context of a new connection? Maybe because the additional load due to another connection changed the behavior slightly but significantly enough. Still, these are not the data read from a different connection but data from the current one contrary to the description in the question. This could be easily checked by using different messages with different clients.
I'm trying to open an RFCOMM socket from Go. I have a test setup where the client connects to an echo server. The part that I'm stuck on is setting up the Bluetooth address. In C you have str2ba, which converts the MAC string to a byte array of six elements. In Go it appears it expects the device to be a uint16. I'm not sure how this would work.
package main
import (
"fmt"
"log"
"syscall"
"golang.org/x/sys/unix"
)
func main() {
fd, err := unix.Socket(syscall.AF_BLUETOOTH, syscall.SOCK_STREAM, unix.BTPROTO_RFCOMM)
if err != nil {
log.Fatalf("%v\n", err)
}
addr := unix.SockaddrHCI{Dev: 1, Channel: 1}
unix.Connect(fd, addr)
unix.Write(fd, []byte("Hello"))
var data []byte
unix.Read(fd, data)
fmt.Printf("Received: %v\n", string(data))
}
The Dev member in unix.SockaddrHCI is a uint16 and I think this is to represent the Bluetooth MAC. Is this correct?
Thanks.
I think that topicstarter already doesn't need this answer, but I needed it yesterday )) Kinda found right answer, so let me share the code that worked in my case (it was a HC06 bluetooth module for Arduino). I assume that module already was bt discovered and paired (e.g by means of bluetoothctl).
package main
import (
"strconv"
"strings"
"syscall"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
func main() {
mac := str2ba("00:19:10:08:FE:08") // YOUR BLUETOOTH MAC ADDRESS HERE
fd, err := unix.Socket(syscall.AF_BLUETOOTH, syscall.SOCK_STREAM, unix.BTPROTO_RFCOMM)
check(err)
addr := &unix.SockaddrRFCOMM{Addr: mac, Channel: 1}
var data = make([]byte, 50)
logrus.Print("connecting...")
err = unix.Connect(fd, addr)
check(err)
defer unix.Close(fd)
logrus.Println("done")
for {
n, err := unix.Read(fd, data)
check(err)
if n > 0 {
logrus.Infof("Received: %v\n", string(data[:n]))
}
}
}
func check(err error) {
if err != nil {
logrus.Fatal(err)
}
}
// str2ba converts MAC address string representation to little-endian byte array
func str2ba(addr string) [6]byte {
a := strings.Split(addr, ":")
var b [6]byte
for i, tmp := range a {
u, _ := strconv.ParseUint(tmp, 16, 8)
b[len(b)-1-i] = byte(u)
}
return b
}
What I am looking is equivalent of Document.parse()
in golang, that allows me create bson from json directly? I do not want to create intermediate Go structs for marshaling
The gopkg.in/mgo.v2/bson package has a function called UnmarshalJSON which does exactly what you want.
The data parameter should hold you JSON string as []byte value.
func UnmarshalJSON(data []byte, value interface{}) error
UnmarshalJSON unmarshals a JSON value that may hold non-standard syntax as defined in BSON's extended JSON specification.
Example:
var bdoc interface{}
err = bson.UnmarshalJSON([]byte(`{"id": 1,"name": "A green door","price": 12.50,"tags": ["home", "green"]}`),&bdoc)
if err != nil {
panic(err)
}
err = c.Insert(&bdoc)
if err != nil {
panic(err)
}
mongo-go-driver has a function bson.UnmarshalExtJSON that does the job.
Here's the example:
var doc interface{}
err := bson.UnmarshalExtJSON([]byte(`{"foo":"bar"}`), true, &doc)
if err != nil {
// handle error
}
There is no longer a way to do this directly with supported libraries (e.g. the mongo-go-driver). You would need to write your own converter based on the bson spec.
Edit: here's one that by now has seen a few Terabytes of use in prod.
https://github.com/dustinevan/mongo/blob/main/bsoncv/bsoncv.go
I do not want to create intermediate Go structs for marshaling
If you do want/need to create an intermediate Go BSON structs, you could use a conversion module such github.com/sindbach/json-to-bson-go. For example:
import (
"fmt"
"github.com/sindbach/json-to-bson-go/convert"
"github.com/sindbach/json-to-bson-go/options"
)
func main() {
doc := `{"foo": "buildfest", "bar": {"$numberDecimal":"2021"} }`
opt := options.NewOptions()
result, _ := convert.Convert([]byte(doc), opt)
fmt.Println(result)
}
Will produce output:
package main
import "go.mongodb.org/mongo-driver/bson/primitive"
type Example struct {
Foo string `bson:"foo"`
Bar primitive.Decimal128 `bson:"bar"`
}
This module is compatible with the official MongoDB Go driver, and as you can see it supports Extended JSON formats.
You can also visit https://json-to-bson-map.netlify.app to try the module in action. You can paste a JSON document, and see the Go BSON structs as output.
A simple converter that uses go.mongodb.org/mongo-driver/bson/bsonrw:
func JsonToBson(message []byte) ([]byte, error) {
reader, err := bsonrw.NewExtJSONValueReader(bytes.NewReader(message), true)
if err != nil {
return []byte{}, err
}
buf := &bytes.Buffer{}
writer, _ := bsonrw.NewBSONValueWriter(buf)
err = bsonrw.Copier{}.CopyDocument(writer, reader)
if err != nil {
return []byte{}, err
}
marshaled := buf.Bytes()
return marshaled, nil
}
I try to populate the exported fields of a struct with content fetched from a MongoDb-database using the labix.org/v2/mgo package.
mgo uses the labix.org/v2/mgo/bson package to unmarshal the data. But the unmarshaller sets all unexported fields to their zero value.
Is there any way to prevent this behavior?
Working example:
package main
import (
"fmt"
"labix.org/v2/mgo/bson"
)
type Sub struct{ Int int }
type Player struct {
Name string
unexpInt int
unexpPoint *Sub
}
func main() {
dta,err := bson.Marshal(bson.M{"name": "ANisus"})
if err != nil {
panic(err)
}
p := &Player{unexpInt: 12, unexpPoint: &Sub{42}}
fmt.Printf("Before: %+v\n", p)
err = bson.Unmarshal(dta, p)
if err != nil {
panic(err)
}
fmt.Printf("After: %+v\n", p)
}
Output:
Before: &{Name: unexpInt:12 unexpPoint:0xf84005f500}
After: &{Name:ANisus unexpInt:0 unexpPoint:<nil>}
This is not possible. As you can see in the source code, struct values are explicitly being set to their zero value before filling in any fields.
There is no option to disable this behaviour. It is presumably in place to make sure the result of Unmarshal() only depends on the BSON data and not any prior state.
I am writing a data structure that needs to hash an arbitrary object. The following function seems to fail if I give an int is the parameter.
func Hash( obj interface{} ) []byte {
digest := md5.New()
if err := binary.Write(digest, binary.LittleEndian, obj); err != nil {
panic(err)
}
return digest.Sum()
}
Calling this on an int results in:
panic: binary.Write: invalid type int
What is the right way to do this?
I found that a good way to do this is to serialize the object using the "gob" package, along the following lines:
var (
digest = md5.New()
encoder = gob.NewEncoder(digest)
)
func Hash(obj interface{}) []byte {
digest.Reset()
if err := encoder.Encode(obj); err != nil {
panic(err)
}
return digest.Sum()
}
Edit: This does not work as intended (see below).
binary.Write writes "a fixed-size value or a pointer to a fixed-size value." Type int is not a fixed size value; int is "either 32 or 64 bits." Use a fixed-size value like int32.