How to read utf16 text file to string in golang? - unicode

I can read the file to bytes array
but when I convert it to string
it treat the utf16 bytes as ascii
How to convert it correctly?
package main
import ("fmt"
"os"
"bufio"
)
func main(){
// read whole the file
f, err := os.Open("test.txt")
if err != nil {
fmt.Printf("error opening file: %v\n",err)
os.Exit(1)
}
r := bufio.NewReader(f)
var s,b,e = r.ReadLine()
if e==nil{
fmt.Println(b)
fmt.Println(s)
fmt.Println(string(s))
}
}
output:
false
[255 254 91 0 83 0 99 0 114 0 105 0 112 0 116 0 32 0 73 0 110 0 102 0 111 0 93 0
13 0]
S c r i p t I n f o ]
Update:
After I tested the two examples, I have understanded what is the exact problem now.
In windows, if I add the line break (CR+LF) at the end of the line, the CR will be read in the line. Because the readline function cannot handle unicode correctly ([OD OA]=ok, [OD 00 OA 00]=not ok).
If the readline function can recognize unicode, it should understand [OD 00 OA 00] and return []uint16 rather than []bytes.
So I think I should not use bufio.NewReader as it is not able to read utf16, I don't see bufio.NewReader.ReadLine can accept parameter as flag to indicate the reading text is utf8, utf16le/be or utf32. Is there any readline function for unicode text in go library?

The latest version of golang.org/x/text/encoding/unicode makes it easier to do this because it includes unicode.BOMOverride, which will intelligently interpret the BOM.
Here is ReadFileUTF16(), which is like os.ReadFile() but decodes UTF-16.
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"strings"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
)
// Similar to ioutil.ReadFile() but decodes UTF-16. Useful when
// reading data from MS-Windows systems that generate UTF-16BE files,
// but will do the right thing if other BOMs are found.
func ReadFileUTF16(filename string) ([]byte, error) {
// Read the file into a []byte:
raw, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
// Make an tranformer that converts MS-Win default to UTF8:
win16be := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)
// Make a transformer that is like win16be, but abides by BOM:
utf16bom := unicode.BOMOverride(win16be.NewDecoder())
// Make a Reader that uses utf16bom:
unicodeReader := transform.NewReader(bytes.NewReader(raw), utf16bom)
// decode and print:
decoded, err := ioutil.ReadAll(unicodeReader)
return decoded, err
}
func main() {
data, err := ReadFileUTF16("inputfile.txt")
if err != nil {
log.Fatal(err)
}
final := strings.Replace(string(data), "\r\n", "\n", -1)
fmt.Println(final)
}
Here is NewScannerUTF16 which is like os.Open() but returns a scanner.
package main
import (
"bufio"
"fmt"
"log"
"os"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
)
type utfScanner interface {
Read(p []byte) (n int, err error)
}
// Creates a scanner similar to os.Open() but decodes the file as UTF-16.
// Useful when reading data from MS-Windows systems that generate UTF-16BE
// files, but will do the right thing if other BOMs are found.
func NewScannerUTF16(filename string) (utfScanner, error) {
// Read the file into a []byte:
file, err := os.Open(filename)
if err != nil {
return nil, err
}
// Make an tranformer that converts MS-Win default to UTF8:
win16be := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)
// Make a transformer that is like win16be, but abides by BOM:
utf16bom := unicode.BOMOverride(win16be.NewDecoder())
// Make a Reader that uses utf16bom:
unicodeReader := transform.NewReader(file, utf16bom)
return unicodeReader, nil
}
func main() {
s, err := NewScannerUTF16("inputfile.txt")
if err != nil {
log.Fatal(err)
}
scanner := bufio.NewScanner(s)
for scanner.Scan() {
fmt.Println(scanner.Text()) // Println will add back the final '\n'
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading inputfile:", err)
}
}
FYI: I have put these functions into an open source module and have made further improvements. See https://github.com/TomOnTime/utfutil/

UTF16, UTF8, and Byte Order Marks are defined by the Unicode Consortium: UTF-16 FAQ, UTF-8 FAQ, and Byte Order Mark (BOM) FAQ.
Issue 4802: bufio: reading lines is too cumbersome
Reading lines from a file is too cumbersome in Go.
People are often drawn to bufio.Reader.ReadLine because of its name,
but it has a weird signature, returning (line []byte, isPrefix bool,
err error), and requires a lot of work.
ReadSlice and ReadString require a delimiter byte, which is almost
always the obvious and unsightly '\n', and also can return both a line
and an EOF
Revision: f685026a2d38
bufio: new Scanner interface
Add a new, simple interface for scanning (probably textual) data,
based on a new type called Scanner. It does its own internal
buffering, so should be plausibly efficient even without injecting a
bufio.Reader. The format of the input is defined by a "split
function", by default splitting into lines.
go1.1beta1 released
You can download binary and source distributions from the usual place:
https://code.google.com/p/go/downloads/list?q=go1.1beta1
Here's a program which uses the Unicode rules to convert UTF16 text file lines to Go UTF8 encoded strings. The code has been revised to take advantage of the new bufio.Scanner interface in Go 1.1.
package main
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"os"
"runtime"
"unicode/utf16"
"unicode/utf8"
)
// UTF16BytesToString converts UTF-16 encoded bytes, in big or little endian byte order,
// to a UTF-8 encoded string.
func UTF16BytesToString(b []byte, o binary.ByteOrder) string {
utf := make([]uint16, (len(b)+(2-1))/2)
for i := 0; i+(2-1) < len(b); i += 2 {
utf[i/2] = o.Uint16(b[i:])
}
if len(b)/2 < len(utf) {
utf[len(utf)-1] = utf8.RuneError
}
return string(utf16.Decode(utf))
}
// UTF-16 endian byte order
const (
unknownEndian = iota
bigEndian
littleEndian
)
// dropCREndian drops a terminal \r from the endian data.
func dropCREndian(data []byte, t1, t2 byte) []byte {
if len(data) > 1 {
if data[len(data)-2] == t1 && data[len(data)-1] == t2 {
return data[0 : len(data)-2]
}
}
return data
}
// dropCRBE drops a terminal \r from the big endian data.
func dropCRBE(data []byte) []byte {
return dropCREndian(data, '\x00', '\r')
}
// dropCRLE drops a terminal \r from the little endian data.
func dropCRLE(data []byte) []byte {
return dropCREndian(data, '\r', '\x00')
}
// dropCR drops a terminal \r from the data.
func dropCR(data []byte) ([]byte, int) {
var endian = unknownEndian
switch ld := len(data); {
case ld != len(dropCRLE(data)):
endian = littleEndian
case ld != len(dropCRBE(data)):
endian = bigEndian
}
return data, endian
}
// SplitFunc is a split function for a Scanner that returns each line of
// text, stripped of any trailing end-of-line marker. The returned line may
// be empty. The end-of-line marker is one optional carriage return followed
// by one mandatory newline. In regular expression notation, it is `\r?\n`.
// The last non-empty line of input will be returned even if it has no
// newline.
func ScanUTF16LinesFunc(byteOrder binary.ByteOrder) (bufio.SplitFunc, func() binary.ByteOrder) {
// Function closure variables
var endian = unknownEndian
switch byteOrder {
case binary.BigEndian:
endian = bigEndian
case binary.LittleEndian:
endian = littleEndian
}
const bom = 0xFEFF
var checkBOM bool = endian == unknownEndian
// Scanner split function
splitFunc := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if checkBOM {
checkBOM = false
if len(data) > 1 {
switch uint16(bom) {
case uint16(data[0])<<8 | uint16(data[1]):
endian = bigEndian
return 2, nil, nil
case uint16(data[1])<<8 | uint16(data[0]):
endian = littleEndian
return 2, nil, nil
}
}
}
// Scan for newline-terminated lines.
i := 0
for {
j := bytes.IndexByte(data[i:], '\n')
if j < 0 {
break
}
i += j
switch e := i % 2; e {
case 1: // UTF-16BE
if endian != littleEndian {
if i > 1 {
if data[i-1] == '\x00' {
endian = bigEndian
// We have a full newline-terminated line.
return i + 1, dropCRBE(data[0 : i-1]), nil
}
}
}
case 0: // UTF-16LE
if endian != bigEndian {
if i+1 < len(data) {
i++
if data[i] == '\x00' {
endian = littleEndian
// We have a full newline-terminated line.
return i + 1, dropCRLE(data[0 : i-1]), nil
}
}
}
}
i++
}
// If we're at EOF, we have a final, non-terminated line. Return it.
if atEOF {
// drop CR.
advance = len(data)
switch endian {
case bigEndian:
data = dropCRBE(data)
case littleEndian:
data = dropCRLE(data)
default:
data, endian = dropCR(data)
}
if endian == unknownEndian {
if runtime.GOOS == "windows" {
endian = littleEndian
} else {
endian = bigEndian
}
}
return advance, data, nil
}
// Request more data.
return 0, nil, nil
}
// Endian byte order function
orderFunc := func() (byteOrder binary.ByteOrder) {
switch endian {
case bigEndian:
byteOrder = binary.BigEndian
case littleEndian:
byteOrder = binary.LittleEndian
}
return byteOrder
}
return splitFunc, orderFunc
}
func main() {
file, err := os.Open("utf16.le.txt")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer file.Close()
fmt.Println(file.Name())
rdr := bufio.NewReader(file)
scanner := bufio.NewScanner(rdr)
var bo binary.ByteOrder // unknown, infer from data
// bo = binary.LittleEndian // windows
splitFunc, orderFunc := ScanUTF16LinesFunc(bo)
scanner.Split(splitFunc)
for scanner.Scan() {
b := scanner.Bytes()
s := UTF16BytesToString(b, orderFunc())
fmt.Println(len(s), s)
fmt.Println(len(b), b)
}
fmt.Println(orderFunc())
if err := scanner.Err(); err != nil {
fmt.Println(err)
}
}
Output:
utf16.le.txt
15 "Hello, 世界"
22 [34 0 72 0 101 0 108 0 108 0 111 0 44 0 32 0 22 78 76 117 34 0]
0
0 []
15 "Hello, 世界"
22 [34 0 72 0 101 0 108 0 108 0 111 0 44 0 32 0 22 78 76 117 34 0]
LittleEndian
utf16.be.txt
15 "Hello, 世界"
22 [0 34 0 72 0 101 0 108 0 108 0 111 0 44 0 32 78 22 117 76 0 34]
0
0 []
15 "Hello, 世界"
22 [0 34 0 72 0 101 0 108 0 108 0 111 0 44 0 32 78 22 117 76 0 34]
BigEndian

Here is the simplest way to read it:
package main
import (
"bufio"
"fmt"
"log"
"os"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
)
func main() {
file, err := os.Open("./text.txt")
if err != nil {
log.Fatal(err)
}
scanner := bufio.NewScanner(transform.NewReader(file, unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()))
for scanner.Scan() {
fmt.Printf(scanner.Text())
}
}
since Windows use little-endian order by default link, we use unicode.UseBOM policy to retrieve BOM from the text, and unicode.LittleEndian as a fallback

For example:
package main
import (
"errors"
"fmt"
"log"
"unicode/utf16"
)
func utf16toString(b []uint8) (string, error) {
if len(b)&1 != 0 {
return "", errors.New("len(b) must be even")
}
// Check BOM
var bom int
if len(b) >= 2 {
switch n := int(b[0])<<8 | int(b[1]); n {
case 0xfffe:
bom = 1
fallthrough
case 0xfeff:
b = b[2:]
}
}
w := make([]uint16, len(b)/2)
for i := range w {
w[i] = uint16(b[2*i+bom&1])<<8 | uint16(b[2*i+(bom+1)&1])
}
return string(utf16.Decode(w)), nil
}
func main() {
// Simulated data from e.g. a file
b := []byte{255, 254, 91, 0, 83, 0, 99, 0, 114, 0, 105, 0, 112, 0, 116, 0, 32, 0, 73, 0, 110, 0, 102, 0, 111, 0, 93, 0, 13, 0}
s, err := utf16toString(b)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%q", s)
}
(Also here)
Output:
"[Script Info]\r"

If you want anything to print as a string you could use fmt.Sprint
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
// read whole the file
f, err := os.Open("test.txt")
if err != nil {
fmt.Printf("error opening file: %v\n", err)
return
}
r := bufio.NewReader(f)
var s, _, e = r.ReadLine()
if e != nil {
fmt.Println(e)
return
}
fmt.Println(fmt.Sprint(string(s)))
}

Related

How to convert string to sqlx.types.JSONText?

I am using gqlgen, sqlx and pgx. Trying to use custom scalar to store as jonb type in postgres database.
// graph/model/item.go
type Attributes types.JSONText
// Marshal here
...
func (a *Attributes) UnmarshalGQL(v interface{}) error {
switch v := v.(type) {
case []byte:
log.Println(" >> unmarshal.byte:", v)
json.Unmarshal(v, &a)
return nil
case string:
log.Println(" >> unmarshal.string:", v) // >> unmarshal.string: {"target": "localhost"}
json.Unmarshal([]byte(v), a) // This gives `null` in postgres
log.Println(" >> unmarshal.aT:", reflect.TypeOf(a)) // >> unmarshal.aT: *model.Attributes
log.Println(" >> unmarshal.aV:", reflect.ValueOf(a)) // >> unmarshal.aV: &[]
return nil
default:
return errors.New(fmt.Sprintf("Unsupported type: %T", v))
}
}
The desired result of a *Attributes should be {"target": "localhost"}, to store as jsonb in postgres:
| id | quantity | attributes |
|----|----------|-------------------------|
| 1 | 5 | {"target": "localhost"} |
What I am doing wrong?
Edit: Add sample mutation.
This is the sample mutation:
mutation itemCreate {
itemCreate(input: {
quantity: 5,
attributes: "{\"target\": \"localhost\"}"
})
}
Edit: Add sqlx query.
The query to insert:
func (d *ItemDb) ItemCreate(i *model.ItemInput) (*model.Item, error) {
log.Println(" >> i.Attributes:", i.Attributes) // >> i.Attributes: &[123 34 116 97 114 103 101 116 34 58 32 34 108 111 99 97 108 104 111 115 116 34 125]
item := &model.Item{}
if err := d.Get(item, `INSERT INTO items
(quantity, attributes)
VALUES ($1, $2)
RETURNING *`, i.Quantity, i.Attributes); err != nil {
return nil, err
}
return item, nil
}
If v contains literal json in either []byte or string then there's no need for json.Unmarshal, all that should be needed is a conversion.
func (a *Attributes) UnmarshalGQL(v interface{}) error {
switch v := v.(type) {
case []byte:
*a = Attributes(v)
case string:
*a = Attributes(v)
default:
return errors.New(fmt.Sprintf("Unsupported type: %T", v))
}
return nil
}

How to implement a Bit-Flipping attack on AES-CBC?

Performing a task with root-me.org, as I understand from the attack statement, it is necessary to perform a byte shift to receive is_member = true After reading some instructions, I supplement the message with a block and accordingly do xor with the previous one, but when I pass the token, the next decryption output
b'[id=546815648;name=iziziz;is_member=false;mail=blablacar;pad=00]r\xe5\xf2\x1dM\xa5\xcae\xff\x16\xa2\xc6\xbe\xd8~I'
Tell me what I could miss, my code:
var TOKEN =
"IRZjBh6GxjeYI7YZvxwfBHmxjY+Wd7bPr7s73wWwLHKaR+N8fPDIjT8/AlUIDSzniMgqCV9bJArQbec64kPYXQ=="
// [id=546815648;name=iziziz;is_member=false;mail=blablacar;pad=00]
func main() {
//block 16
tokenHex, err := base64.StdEncoding.DecodeString(TOKEN) // lenght 64
if err != nil {
log.Fatal(err)
}
block := 16
for i := 0; i < len(tokenHex); i += block {
fmt.Println(tokenHex[i : i+block])
}
tmp := make([]byte, block)
for i := 0; i < block; i++ {
tmp[i] = byte('a')
}
tokenHex = append(tokenHex,tmp...)
expected := []byte(`;is_member=true]`)
//last block
current := []byte{136 ,200 ,42 ,9 ,95 ,91, 36, 10 ,208 ,109, 231, 58, 226, 67 ,216, 93}
for i := 0; i < 16; i++ {
xor := expected[i] ^ current[i]
tokenHex[64+i] ^= xor
}
fmt.Println(base64.StdEncoding.EncodeToString(tokenHex))
}

go insert composite type array unsupported type

sql
CREATE TABLE public.tiantang_page (
href varchar NOT NULL,
status int4 NOT NULL,
description varchar NOT NULL,
urls url[] NULL
);
CREATE TYPE url AS (
url varchar,
status int4);
insert composite type array
type url struct {
url string
status int
}
var urls [1]url
urls[0] = url{
url: "",
status: 0,
}
update := "UPDATE \"public\".\"tiantang_page\" SET \"urls\"=$1 where \"href\"=$2;"
r, err := db.Exec(update, pq.Array(urls),href)
if err != nil {
log.Fatal(err)
}
error
sql: converting argument $1 type: unsupported type parsetest.url, a struct
library
https://godoc.org/github.com/lib/pq
Note that custom composite types are not fully supported by lib/pq.
If all you want is to be able to store the urls then the simplest approach would be to implement the driver.Valuer interface on the url type and then use it as you do with pq.Array:
func (u url) Value() (driver.Value, error) {
return fmt.Sprintf("(%s,%d)", u.url, u.status), nil
}
// ...
r, err := db.Exec(update, pq.Array(urls), href)
more info on that can be found here: https://github.com/lib/pq/issues/544
Note that I haven't tried this with arrays, only with slices, so you may have to switch from using an array to using a slice, i.e. instead of var urls [1]url you would use var urls = make([]url, 1).
If you also want to be able to retrieve the array of urls back from the db, then you'll have to implement the sql.Scanner interface, however here the pq.Array is not very reliable and you'll have to implement the scanner on the slice type and do all the parsing yourself.
The general format of composite types is (val1, val2, ...) note that you have to put double quotes around values that contain commas or parentheses. For example to construct a value of the url type you would use the literal expression: (http://example.com,4). More info in the docs.
The format for an array of composite types is {"(val1, val2, ...)" [, ...]}, note that in this case if you need to put double quotes around the values you need to escape them. For example {"(http://example.com,4)","(\"http://example.com/?list=foo,bar,baz\",3)"}
So as you can see the more complex the data in the composite type the more complex will be the parsing as well.
Here's a crude example (does not handle quoted values):
type urlslice []url
func (s *urlslice) Scan(src interface{}) error {
var a []byte // the pq array as bytes
switch v := src.(type) {
case []byte:
a = v
case string:
a = []byte(v)
case nil:
*s = nil
return nil
default:
return fmt.Errorf("urlslice.Scan unexpected src type %T", src)
}
a = a[1 : len(a)-1] // drop curly braces
for i := 0; i < len(a); i++ {
if a[i] == '"' && (len(a) > (i+1) && a[i+1] == '(') { // element start?
i += 2 // move past `"(`
j := i // start of url.url
u := url{}
for ; i < len(a) && a[i] != ','; i++ {
}
u.url = string(a[j:i])
i += 1 // move past `,`
j = i // start of url.status
for ; i < len(a) && a[i] != ')'; i++ {
}
i64, err := strconv.ParseInt(string(a[j:i]), 10, 64)
if err != nil {
return err
}
u.status = int(i64)
*s = append(*s, u)
i += 2 // move past `)",`
}
}
return nil
}
for completeness, here's the Valuer interface implemented by the slice type, again not handling proper quoting of values that may require it:
func (s urlslice) Value() (driver.Value, error) {
data := []byte{'{'}
for _, url := range s {
data = append(data, '"', '(')
data = append(data, []byte(url.url)...)
data = append(data, ',')
data = strconv.AppendInt(data, int64(url.status), 10)
data = append(data, ')', '"', ',')
}
data[len(data)-1] = '}' // replace last ',' with '}' to close the array
return data, nil
}
With the urlslice implementing the two interfaces directly you can stop using pq.Array.
var urls = urlslice{{
url: "http://example.com",
status: 4,
}}
update := `UPDATE "public"."tiantang_page" SET "urls"=$1 where "href"=$2`
r, err := db.Exec(update, urls, href)
if err != nil {
log.Fatal(err)
}
var urls2 urlslice
selurls := `SELECT "urls" FROM "public"."tiantang_page" where "href" = $1`
if err := db.QueryRow(selurls, href).Scan(&urls2); err != nil {
log.Fatal(err)
}
Please keep in mind that both of the above examples should be considered only as hints of the direction to take in solving this problem. Not only are the two examples incomplete in that they don't handle quoted values, but they are also not very elegant implementations.
Reasonably complete composite literal parser:
type parseState int
const (
state_initial parseState = iota // start
state_value_start // no bytes read from value yet
state_value // unquoted value
state_quoted // inside quote
state_value_end // after a close quote
state_end // after close paren
)
func parseComposite(in []byte) ([]string, error) {
state := state_initial
ret := []string{}
val := []byte{}
for _, b := range in {
switch state {
case state_initial:
if b != '(' {
return nil, fmt.Errorf("initial character not ')': %v", in)
} else {
state = state_value_start
}
case state_value_start:
if b == '"' {
state = state_quoted
continue
}
fallthrough
case state_value:
if b == ',' {
ret = append(ret, string(val))
val = nil
state = state_value_start
} else if b == ')' {
ret = append(ret, string(val))
val = nil
state = state_end
} else {
val = append(val, b)
}
case state_quoted:
if b == '"' {
ret = append(ret, string(val))
val = nil
state = state_value_end
} else {
val = append(val, b)
}
case state_value_end:
if b == ',' {
state = state_value_start
} else if b == ')' {
state = state_end
} else {
return nil, fmt.Errorf("invalid delimiter after closing quote: %v", in)
}
case state_end:
return nil, fmt.Errorf("trailing bytes: %v", in)
}
}
if state != state_end {
return nil, fmt.Errorf("unterminated value: %v", in)
}
return ret, nil
}

What is the correct use of select statement in Go?

Good Day everyone
I have been learning the fundaments of go and how to use its channel-based concurrency paradigm.
However, while playing with some code I wrote focusing on the select statement I found a strange behavior:
func main() {
even := make(chan int)
odd := make(chan int)
quit := make(chan bool)
//send
go send(even, odd, quit)
//receive
receive(even, odd, quit)
fmt.Println("Exiting")
}
func send(e, o chan<- int, q chan<- bool) {
for i := 0; i < 100; i++ {
if i%2 == 0 {
e <- i
} else {
o <- i
}
}
close(e)
close(o)
q <- true
close(q)
}
func receive(e, o <-chan int, q <-chan bool) {
for cont, i := true, 0; cont; i++ {
fmt.Println("value of i", i, cont)
select {
case v := <-e:
fmt.Println("From even channel:", v)
case v := <-o:
fmt.Println("from odd channel:", v)
case v := <-q:
fmt.Println("Got exit message", v)
// return // have also tried this instead
cont = false
}
}
}
when I run this simple program sometimes the i accumulator ends up with more than a 100 being printed to the console, and instead of finishing up with a "from odd channel: 99", the for loop continues on outputting one or more zeroes from even/odd channels randomly, as if the quit channel's message was being somewhat being delayed onto its case and instead the odd/even channels were sending more integers thus quitting the for loop not exactly after the odd/even channels have been closed.
value of i 97 true
from odd channel: 97
value of i 98 true
From even channel: 98
value of i 99 true
from odd channel: 99
value of i 100 true
From even channel: 0
value of i 101 true
From even channel: 0
value of i 102 true
from odd channel: 0
value of i 103 true
From even channel: 0
value of i 104 true
Got exit message true
Exiting
I have tried to search for the correct use of the case statement but I haven´t been able to find the problem with my code.
It seems like the same behavior can be reproduced on the go playground: my code
thanks for your attention put on my question.
The program is printing 0 because receive on a closed channel returns the zero value. Here's one way to accomplish your goal.
First, eliminate the q channel. Closing the o and e channels is sufficient to indicate that the sender is done.
func send(e, o chan<- int, q chan<- bool) {
for i := 0; i < 100; i++ {
if i%2 == 0 {
e <- i
} else {
o <- i
}
}
close(e)
close(o)
}
When receiving values, use the two value receive to detect when the zero value is returned because the channel is closed. Set the channel to nil when the channel is closed. Receive on a nil channel does not yield a value. Loop until both channels are nil.
func receive(e, o <-chan int, q <-chan bool) {
for e != nil && o != nil {
select {
case v, ok := <-e:
if !ok {
e = nil
continue
}
fmt.Println("From even channel:", v)
case v, ok := <-o:
if !ok {
o = nil
continue
}
fmt.Println("From odd channel:", v)
}
}
}
playground example

raw socket didn't receive icmp response

I'm trying to send an icmp message whose TTL is just 1, and expect to receive a time exceeded message. that message does come(I see it from wireshark), but my program blocks on syscall.Recvfrom. Anyone knows why?
icmp.go
package main
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"os"
"syscall"
)
type ICMP struct {
Type uint8
Code uint8
Checksum uint16
Identifier uint16
SeqNo uint16
}
func Checksum(data []byte) uint16 {
var (
sum uint32
length int = len(data)
index int
)
for length > 1 {
sum += uint32(data[index])<<8 + uint32(data[index+1])
index += 2
length -= 2
}
if length > 0 {
sum += uint32(data[index])
}
sum += (sum >> 16)
return uint16(^sum)
}
func main() {
h := Header{
Version: 4,
Len: 20,
TotalLen: 20 + 8,
TTL: 1,
Protocol: 1,
// Dst:
}
argc := len(os.Args)
if argc < 2 {
fmt.Println("usage: program + host")
return
}
ipAddr, _ := net.ResolveIPAddr("ip", os.Args[1])
h.Dst = ipAddr.IP
icmpReq := ICMP{
Type: 8,
Code: 0,
Identifier: 0,
SeqNo: 0,
}
out, err := h.Marshal()
if err != nil {
fmt.Println("ip header error", err)
return
}
var icmpBuf bytes.Buffer
binary.Write(&icmpBuf, binary.BigEndian, icmpReq)
icmpReq.Checksum = Checksum(icmpBuf.Bytes())
icmpBuf.Reset()
binary.Write(&icmpBuf, binary.BigEndian, icmpReq)
fd, _ := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
addr := syscall.SockaddrInet4{
Port: 0,
}
copy(addr.Addr[:], ipAddr.IP[12:16])
pkg := append(out, icmpBuf.Bytes()...)
fmt.Println("ip length", len(pkg))
if err := syscall.Sendto(fd, pkg, 0, &addr); err != nil {
fmt.Println("Sendto err:", err)
}
var recvBuf []byte
if nBytes, rAddr, err := syscall.Recvfrom(fd, recvBuf, 0); err == nil {
fmt.Printf("recv %d bytes from %v\n", nBytes, rAddr)
}
}
additionally, I use header.go and helper.go from https://github.com/golang/net/tree/master/ipv4
As Andy pointed out, the raw(7) man page says:
An IPPROTO_RAW socket is send only. If you really want to receive
all IP packets, use a packet(7) socket with the ETH_P_IP protocol.
Note that packet sockets don't reassemble IP fragments, unlike raw
sockets.
I know I can receive ICMP reply if I set IPPROTO_ICMP as the protocol when I create the socket, but I need to set TTL to 1 which must be done in IP layer. Therefore I send the ICMP request with IPPROTO_RAW socket, after that I use net.ListenIP to receive ICMP messages. Here is the code:
package main
import (
"bytes"
"encoding/binary"
"log"
"net"
"os"
"syscall"
)
const icmpID uint16 = 43565 // use a magic number for now
type ICMP struct {
Type uint8
Code uint8
Checksum uint16
Identifier uint16
SeqNo uint16
}
func Checksum(data []byte) uint16 {
var (
sum uint32
length int = len(data)
index int
)
for length > 1 {
sum += uint32(data[index])<<8 + uint32(data[index+1])
index += 2
length -= 2
}
if length > 0 {
sum += uint32(data[index])
}
sum += (sum >> 16)
return uint16(^sum)
}
func main() {
h := Header{
Version: 4,
Len: 20,
TotalLen: 20 + 8,
TTL: 1,
Protocol: 1,
}
argc := len(os.Args)
if argc < 2 {
log.Println("usage: program + host")
return
}
ipAddr, _ := net.ResolveIPAddr("ip", os.Args[1])
h.Dst = ipAddr.IP
icmpReq := ICMP{
Type: 8,
Code: 0,
Identifier: icmpID,
SeqNo: 1,
}
out, err := h.Marshal()
if err != nil {
log.Println("ip header error", err)
return
}
var icmpBuf bytes.Buffer
binary.Write(&icmpBuf, binary.BigEndian, icmpReq)
icmpReq.Checksum = Checksum(icmpBuf.Bytes())
icmpBuf.Reset()
binary.Write(&icmpBuf, binary.BigEndian, icmpReq)
fd, _ := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
addr := syscall.SockaddrInet4{
Port: 0,
}
copy(addr.Addr[:], ipAddr.IP[12:16])
pkg := append(out, icmpBuf.Bytes()...)
if err := syscall.Sendto(fd, pkg, 0, &addr); err != nil {
log.Println("Sendto err:", err)
}
laddr, err := net.ResolveIPAddr("ip4:icmp", "0.0.0.0")
if err != nil {
log.Fatal(err)
}
c, err := net.ListenIP("ip4:icmp", laddr)
if err != nil {
log.Fatal(err)
}
for {
buf := make([]byte, 2048)
n, raddr, err := c.ReadFrom(buf)
if err != nil {
log.Println(err)
continue
}
icmpType := buf[0]
if icmpType == 11 {
if n == 36 { // Time exceeded messages
// A time exceeded message contain IP header(20 bytes) and first 64 bits of the original payload
id := binary.BigEndian.Uint16(buf[32:34])
log.Println("recv id", id)
if id == icmpID {
log.Println("recv Time Exceeded from", raddr)
}
}
}
}
}
Actually, I am writing a traceroute in go, if anyone is interested about that, the whole code is in github.
I think you need to give IPPROTO_ICMP as the protocol when you create your socket. The raw(7) man page says that an IPPROTO_RAW socket is send only. Also, if you use IPPROTO_ICMP, you don't give the IP header. (Note: I haven't actually tried this in Go.)