├── code ├── 06 │ ├── 06.3 │ │ ├── gofuzz_test.go │ │ ├── corpus.zip │ │ ├── suppressions │ │ │ ├── 8cbede20ad9d530818fd357f8f046057ebb6f7e0 │ │ │ ├── 0fad3395539b4ffbbd22b457868a6c2f87c18457 │ │ │ ├── 8b21b5678278e10daa110f0c85c6802003702e06 │ │ │ ├── b34668fc3badc1bd2b13e3461f12b60d69acc548 │ │ │ ├── 3c85b0906fcb8dfb9fbf5035333f8e955b3d7b1c │ │ │ ├── 3574328da5110d545dcd91e9df5bf1b6d3942570 │ │ │ └── d2f7c5b79258d20c8dbd9d4ca3241b85a93e7418 │ │ ├── crashers │ │ │ ├── a59a2ad5701156b88c6a132e1340fe006f67280c.quoted │ │ │ ├── 171e8e5ca3e3d609322376915dcfa3dd56938845 │ │ │ ├── 3f5b7d448a0791f5739fa0a2371bb2492b64f835 │ │ │ ├── 49dfc363adbbe5aac9c2f8afbb0591c3ef1de2c3 │ │ │ ├── a59a2ad5701156b88c6a132e1340fe006f67280c │ │ │ ├── 49dfc363adbbe5aac9c2f8afbb0591c3ef1de2c3.quoted │ │ │ ├── 3f5b7d448a0791f5739fa0a2371bb2492b64f835.quoted │ │ │ ├── 171e8e5ca3e3d609322376915dcfa3dd56938845.quoted │ │ │ ├── 49dfc363adbbe5aac9c2f8afbb0591c3ef1de2c3.output │ │ │ ├── a59a2ad5701156b88c6a132e1340fe006f67280c.output │ │ │ ├── 3f5b7d448a0791f5739fa0a2371bb2492b64f835.output │ │ │ └── 171e8e5ca3e3d609322376915dcfa3dd56938845.output │ │ ├── int-overflow-test.go │ │ ├── goos-goarch.go │ │ ├── Fuzz.go │ │ ├── int-pointer-size.go │ │ ├── test-crash-3f.go │ │ ├── test-crash-49.go │ │ └── test-crash-a5.go │ └── 06.2 │ │ ├── crashers │ │ ├── 17ee301be06245aa20945bc3ff3c4838abe13b52 │ │ ├── 17ee301be06245aa20945bc3ff3c4838abe13b52.quoted │ │ └── 17ee301be06245aa20945bc3ff3c4838abe13b52.output │ │ ├── corpus.zip │ │ ├── Fuzz.go │ │ ├── test1.go │ │ ├── suppressions │ │ └── ce7eb9cb7943ef3a148eec15298e36694e30d395 │ │ └── test2.go ├── 05 │ └── 05.1 │ │ ├── test2.png │ │ ├── 01-black-rectangle.png │ │ └── 05.1-01-png-chunk-extraction.go ├── 01 │ └── 01-01-HelloWorld.go ├── 02 │ ├── 02.2 │ │ ├── 02.2-10-defer1.go │ │ ├── 02.2-04-if1.go │ │ ├── 02.2-03-incdec.go │ │ ├── 02.2-01-for1.go │ │ ├── 02.2-11-defer2.go │ │ ├── 02.2-05-if2.go │ │ ├── 02.2-02-for2.go │ │ ├── 02.2-06-else.go │ │ ├── 02.2-08-switch2.go │ │ ├── 02.2-09-switch3.go │ │ └── 02.2-07-switch1.go │ ├── 02.3 │ │ ├── 02.3-05-slice3.go │ │ ├── 02.3-02-array.go │ │ ├── 02.3-04-slice2.go │ │ ├── 02.3-06-slice4.go │ │ ├── 02.3-07-slice-append.go │ │ ├── 02.3-08-range.go │ │ ├── 02.3-03-slice1.go │ │ └── 02.3-01-structs.go │ ├── 02.1 │ │ ├── 02.1-01-multiply.go │ │ ├── 02.1-02-addTwo.go │ │ ├── 02.1-07-const.go │ │ ├── 02.1-09-init.go │ │ ├── 02.1-05-short-declaration.go │ │ ├── 02.1-04-variables.go │ │ ├── 02.1-08-rawstring.go │ │ ├── 02.1-03-addTwo2.go │ │ └── 02.1-06-casting.go │ ├── 02.5 │ │ ├── 02.5-02-scan1.go │ │ ├── 02.5-03-bufioreadstring.go │ │ ├── 02.5-04-bufioreadbytes.go │ │ ├── 02.5-05-maps.go │ │ └── 02.5-01-print-verbs.go │ ├── 02.6 │ │ ├── 02.6-01-goroutine1.go │ │ ├── 02.6-03-goroutine3.go │ │ ├── 02.6-07-channel4.go │ │ ├── 02.6-02-goroutine2.go │ │ ├── 02.6-06-channel3.go │ │ ├── 02.6-09-channel6.go │ │ ├── 02.6-04-channel1.go │ │ ├── 02.6-08-channel5.go │ │ ├── 02.6-10-channel7.go │ │ ├── 02.6-05-channel2.go │ │ ├── 02.6-11-channel8.go │ │ ├── 02.6-12-waitgroup1.go │ │ └── 02.6-13-waitgroup2.go │ ├── 02.7 │ │ ├── 02.7-02-errors2.go │ │ └── 02.7-01-errors1.go │ └── 02.4 │ │ ├── 02.4-08-stringer2.go │ │ ├── 02.4-07-stringer1.go │ │ ├── 02.4-04-interface2.go │ │ ├── 02.4-05-interface3.go │ │ ├── 02.4-06-typeswitch.go │ │ ├── 02.4-01-method1.go │ │ ├── 02.4-03-interface1.go │ │ └── 02.4-02-method2.go ├── 03 │ ├── 03.2 │ │ ├── 03.2-01-basic-logging.go │ │ ├── 03.2-04-log-flags.go │ │ ├── 03.2-05-log-prefix.go │ │ ├── 03.2-02-log-file.go │ │ └── 03.2-03-log-multiple-files.go │ └── 03.1 │ │ ├── 03.1-05-args.go │ │ ├── 03.1-01-flag1.go │ │ ├── 03.1-02-flag2.go │ │ ├── 03.1-03-flag3.go │ │ ├── 03.1-06-subcommand.go │ │ └── 03.1-04-flag4.go └── 04 │ ├── 04.5 │ └── testinputfile.txt │ ├── 04.1 │ ├── 04.1-02-basic-tcp2.go │ ├── 04.1-04-basic-udp.go │ ├── 04.1-05-udp-dialudp.go │ ├── 04.1-03-basic-tcp-dialtcp.go │ └── 04.1-01-basic-tcp1.go │ ├── 04.2 │ ├── 04.2-01-tcpserver1.go │ ├── 04.2-02-tcpserver2.go │ └── 04.2-03-tcpserver3.go │ ├── 04.4 │ ├── 04.4-04-sshclient-run-combinedoutput.go │ ├── 04.4-05-sshclient-run-run.go │ ├── 04.4-01-sshclient-login-password.go │ ├── 04.4-02-sshclient-check-host.go │ └── 04.4-03-sshclient-login-key.go │ └── 04.3 │ └── 04.3-01-tcp-proxy.go ├── content ├── images │ ├── 04 │ │ ├── 04.5-01.png │ │ ├── 04.5-02.png │ │ ├── 04.2-01-telnet-test1.png │ │ └── 04.2-02-telnet-test2.png │ ├── 05 │ │ ├── 05.1-03-ihdr.png │ │ ├── 05.1-02-header.png │ │ ├── 05.1-04-tool-operation.png │ │ └── 05.1-01-black-rectangle.png │ └── 06 │ │ ├── 06.2-01-fuzz.png │ │ ├── 06.3-01-ram.png │ │ ├── 06.2-03-running.png │ │ ├── 06.3-02-fuzzer-crash.png │ │ └── 06.2-02-go-fuzz-build.png ├── 05.0.md ├── 06.0.md ├── 03.0.md ├── 04.0.md ├── 02.0.md ├── 06.1.md ├── 02.7.md ├── 04.3.md ├── 01.md ├── 02.2.md ├── 04.2.md ├── 02.1.md ├── 03.2.md ├── 02.3.md ├── 04.1.md └── 05.1.md ├── .gitignore ├── @TODO.md └── README.md /code/06/06.3/gofuzz_test.go: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /code/06/06.2/crashers/17ee301be06245aa20945bc3ff3c4838abe13b52: -------------------------------------------------------------------------------- 1 | 0.0.0.0/40 -------------------------------------------------------------------------------- /code/06/06.2/crashers/17ee301be06245aa20945bc3ff3c4838abe13b52.quoted: -------------------------------------------------------------------------------- 1 | "0.0.0.0/40" 2 | -------------------------------------------------------------------------------- /code/05/05.1/test2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/code/05/05.1/test2.png -------------------------------------------------------------------------------- /code/06/06.2/corpus.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/code/06/06.2/corpus.zip -------------------------------------------------------------------------------- /code/06/06.3/corpus.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/code/06/06.3/corpus.zip -------------------------------------------------------------------------------- /content/images/04/04.5-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/content/images/04/04.5-01.png -------------------------------------------------------------------------------- /content/images/04/04.5-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/content/images/04/04.5-02.png -------------------------------------------------------------------------------- /code/06/06.3/suppressions/8cbede20ad9d530818fd357f8f046057ebb6f7e0: -------------------------------------------------------------------------------- 1 | program hanged (timeout 10 seconds) 2 | 3 | exit status 1 -------------------------------------------------------------------------------- /content/images/05/05.1-03-ihdr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/content/images/05/05.1-03-ihdr.png -------------------------------------------------------------------------------- /content/images/06/06.2-01-fuzz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/content/images/06/06.2-01-fuzz.png -------------------------------------------------------------------------------- /content/images/06/06.3-01-ram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/content/images/06/06.3-01-ram.png -------------------------------------------------------------------------------- /code/05/05.1/01-black-rectangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/code/05/05.1/01-black-rectangle.png -------------------------------------------------------------------------------- /content/images/05/05.1-02-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/content/images/05/05.1-02-header.png -------------------------------------------------------------------------------- /content/images/06/06.2-03-running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/content/images/06/06.2-03-running.png -------------------------------------------------------------------------------- /code/01/01-01-HelloWorld.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | fmt.Println("Hello World!") 8 | } 9 | -------------------------------------------------------------------------------- /code/06/06.3/crashers/a59a2ad5701156b88c6a132e1340fe006f67280c.quoted: -------------------------------------------------------------------------------- 1 | "II*\x00\b\x00\x00\x000000\x05\x00\x00\x00\x00\xa000" + 2 | "00" 3 | -------------------------------------------------------------------------------- /content/images/04/04.2-01-telnet-test1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/content/images/04/04.2-01-telnet-test1.png -------------------------------------------------------------------------------- /content/images/04/04.2-02-telnet-test2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/content/images/04/04.2-02-telnet-test2.png -------------------------------------------------------------------------------- /content/images/06/06.3-02-fuzzer-crash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/content/images/06/06.3-02-fuzzer-crash.png -------------------------------------------------------------------------------- /content/images/05/05.1-04-tool-operation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/content/images/05/05.1-04-tool-operation.png -------------------------------------------------------------------------------- /content/images/06/06.2-02-go-fuzz-build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/content/images/06/06.2-02-go-fuzz-build.png -------------------------------------------------------------------------------- /content/images/05/05.1-01-black-rectangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/content/images/05/05.1-01-black-rectangle.png -------------------------------------------------------------------------------- /code/06/06.3/crashers/171e8e5ca3e3d609322376915dcfa3dd56938845: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/code/06/06.3/crashers/171e8e5ca3e3d609322376915dcfa3dd56938845 -------------------------------------------------------------------------------- /code/06/06.3/crashers/3f5b7d448a0791f5739fa0a2371bb2492b64f835: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/code/06/06.3/crashers/3f5b7d448a0791f5739fa0a2371bb2492b64f835 -------------------------------------------------------------------------------- /code/06/06.3/crashers/49dfc363adbbe5aac9c2f8afbb0591c3ef1de2c3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/code/06/06.3/crashers/49dfc363adbbe5aac9c2f8afbb0591c3ef1de2c3 -------------------------------------------------------------------------------- /code/06/06.3/crashers/a59a2ad5701156b88c6a132e1340fe006f67280c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parsiya/Hacking-with-Go/HEAD/code/06/06.3/crashers/a59a2ad5701156b88c6a132e1340fe006f67280c -------------------------------------------------------------------------------- /code/06/06.3/int-overflow-test.go: -------------------------------------------------------------------------------- 1 | // Testing overflow on int. 2 | package main 3 | 4 | import "fmt" 5 | 6 | func main() { 7 | i := int(2684354560) 8 | fmt.Println(i) 9 | } 10 | -------------------------------------------------------------------------------- /code/02/02.2/02.2-10-defer1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | defer fmt.Println("This runs after main") 7 | 8 | fmt.Println("Main ended") 9 | } 10 | -------------------------------------------------------------------------------- /code/02/02.2/02.2-04-if1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | a := 10 8 | b := 20 9 | 10 | if b > a { 11 | fmt.Println(b, ">", a) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /code/02/02.3/02.3-05-slice3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | characters := [...]string{"Ender", "Petra", "Mazer"} 8 | 9 | fmt.Println(characters) 10 | } 11 | -------------------------------------------------------------------------------- /code/06/06.2/Fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package iprange 4 | 5 | func Fuzz(data []byte) int { 6 | _, err := ParseList(string(data)) 7 | if err != nil { 8 | return 0 9 | } 10 | return 1 11 | } 12 | -------------------------------------------------------------------------------- /code/02/02.2/02.2-03-incdec.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | // var sum int 7 | sum, i := 0 8 | // This will not work 9 | sum = i++ 10 | fmt.Println(sum) 11 | } 12 | -------------------------------------------------------------------------------- /code/02/02.1/02.1-01-multiply.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func multiply(x int, y int) int { 6 | return x * y 7 | } 8 | 9 | func main() { 10 | fmt.Println(multiply(10, 20)) 11 | } 12 | -------------------------------------------------------------------------------- /code/02/02.1/02.1-02-addTwo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func addTwo(x int, y int) (int, int) { 6 | return x + 2, y + 2 7 | } 8 | 9 | func main() { 10 | fmt.Println(addTwo(10, 20)) 11 | } 12 | -------------------------------------------------------------------------------- /code/02/02.1/02.1-07-const.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | const Whatever = "whatever" 6 | 7 | func main() { 8 | fmt.Println(Whatever) 9 | 10 | const One = 1 11 | fmt.Println(One) 12 | } 13 | -------------------------------------------------------------------------------- /code/02/02.1/02.1-09-init.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func init() { 6 | fmt.Println("Executing init function!") 7 | } 8 | 9 | func main() { 10 | fmt.Println("Executing main!") 11 | } 12 | -------------------------------------------------------------------------------- /code/06/06.3/goos-goarch.go: -------------------------------------------------------------------------------- 1 | // Get OS and architecture. 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "runtime" 7 | ) 8 | 9 | func main() { 10 | fmt.Println(runtime.GOOS) 11 | fmt.Println(runtime.GOARCH) 12 | } 13 | -------------------------------------------------------------------------------- /code/02/02.2/02.2-01-for1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | // var sum int 7 | sum := 0 8 | for i := 0; i < 20; i++ { 9 | sum += i 10 | } 11 | 12 | fmt.Println(sum) 13 | } 14 | -------------------------------------------------------------------------------- /code/02/02.2/02.2-11-defer2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | num := 1 7 | defer fmt.Println("After main returns", num) 8 | 9 | num++ 10 | fmt.Println("Inside main", num) 11 | } 12 | -------------------------------------------------------------------------------- /code/02/02.2/02.2-05-if2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | if var1 := 20; var1 > 10 { 8 | fmt.Println("Inside if:", var1) 9 | } 10 | // Cannot use the variable var1 here 11 | } 12 | -------------------------------------------------------------------------------- /code/06/06.3/Fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package exif 4 | 5 | import "bytes" 6 | 7 | func Fuzz(data []byte) int { 8 | _, err := Decode(bytes.NewReader(data)) 9 | if err != nil { 10 | return 0 11 | } 12 | return 1 13 | } 14 | -------------------------------------------------------------------------------- /code/02/02.1/02.1-05-short-declaration.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | sampleInt, sampleBoolean, sampleString := 30, true, "Hello" 7 | 8 | fmt.Println(sampleInt, sampleBoolean, sampleString) 9 | } 10 | -------------------------------------------------------------------------------- /code/02/02.2/02.2-02-for2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | // var sum int 7 | sum, i := 0 8 | for i < 20 { // while (i<20) 9 | sum += i 10 | i++ 11 | } 12 | 13 | fmt.Println(sum) 14 | } 15 | -------------------------------------------------------------------------------- /code/02/02.5/02.5-02-scan1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | var s string 8 | n, err := fmt.Scanln(&s) 9 | if err != nil { 10 | panic(err) 11 | } 12 | 13 | fmt.Printf("Entered %d word(s): %s", n, s) 14 | } 15 | -------------------------------------------------------------------------------- /code/06/06.2/test1.go: -------------------------------------------------------------------------------- 1 | // Small program to test panic when calling Uint32(nil). 2 | package main 3 | 4 | import ( 5 | "encoding/binary" 6 | ) 7 | 8 | func main() { 9 | _ = binary.BigEndian.Uint32(nil) 10 | // _ = binary.BigEndian.Uint32([]byte(nil)) 11 | 12 | } 13 | -------------------------------------------------------------------------------- /code/06/06.3/crashers/49dfc363adbbe5aac9c2f8afbb0591c3ef1de2c3.quoted: -------------------------------------------------------------------------------- 1 | "MM\x00*\x00\x00\x00\b\x00\a0000000000" + 2 | "00000000000000000000" + 3 | "000000000000000000\x87i" + 4 | "\x00\x04\x00\x00\x00\x0000000000000000" + 5 | "00000000000000000000" + 6 | "00000000000000" 7 | -------------------------------------------------------------------------------- /code/02/02.1/02.1-04-variables.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | var a, b int = 10, 20 7 | var sampleInt, sampleBoolean, sampleString = 30, true, "Hello" 8 | 9 | fmt.Println(a, b, sampleInt, sampleBoolean, sampleString) 10 | } 11 | -------------------------------------------------------------------------------- /code/02/02.1/02.1-08-rawstring.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | rawstr := 8 | 9 | `First line 10 | 11 | some new lines 12 | 13 | more new lines 14 | 15 | "double quotes" 16 | ` 17 | 18 | fmt.Print(rawstr) 19 | } 20 | -------------------------------------------------------------------------------- /code/02/02.1/02.1-03-addTwo2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func addTwo2(x int, y int) (xPlusTwo int, yPlusTwo int) { 6 | xPlusTwo = x + 2 7 | yPlusTwo = y + 2 8 | 9 | return xPlusTwo, yPlusTwo 10 | } 11 | 12 | func main() { 13 | fmt.Println(addTwo2(20, 30)) 14 | } 15 | -------------------------------------------------------------------------------- /code/02/02.2/02.2-06-else.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | if var1 := 20; var1 > 100 { 8 | fmt.Println("Inside if:", var1) 9 | } else { 10 | // Can use var1 here 11 | fmt.Println("Inside else:", var1) 12 | } 13 | // Cannot use var1 here 14 | } 15 | -------------------------------------------------------------------------------- /content/05.0.md: -------------------------------------------------------------------------------- 1 | # 05 - Parsing Files 2 | Usually when we need to open and parse file formats, the normal parsers are not useful. Either files are badly formatted or something is hidden. Making our own file parser is the way to go. 3 | 4 | ## Table of Contents 5 | 6 | - [05.1 - Extracting PNG Chunks](05.1.md) 7 | -------------------------------------------------------------------------------- /code/03/03.2/03.2-01-basic-logging.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | func main() { 8 | 9 | a, b := 10, 20 10 | 11 | log.Print("Use Print to log.") 12 | log.Println("Ditto for Println.") 13 | log.Printf("Use Printf and format strings. %d + %d = %d", a, b, a+b) 14 | } 15 | -------------------------------------------------------------------------------- /code/02/02.6/02.6-01-goroutine1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func PrintMe(t int, count int) { 6 | for i := 0; i < count; i++ { 7 | fmt.Printf("Printing from %d\n", t) 8 | } 9 | } 10 | 11 | func main() { 12 | 13 | go PrintMe(0, 10) 14 | 15 | fmt.Println("Main finished!") 16 | } 17 | -------------------------------------------------------------------------------- /code/02/02.6/02.6-03-goroutine3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | go func() { 8 | for i := 0; i < 10; i++ { 9 | fmt.Printf("Printing from %d\n", 0) 10 | } 11 | }() 12 | 13 | // Wait for a keypress 14 | fmt.Scanln() 15 | fmt.Println("Main finished!") 16 | } 17 | -------------------------------------------------------------------------------- /code/02/02.1/02.1-06-casting.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | var a, b int = 20, 30 9 | // Need to convert a and b to float32 before the division 10 | var div float32 = float32(a) / float32(b) 11 | // Cast float32 to int 12 | var divInt = int(div) 13 | fmt.Println(div, divInt) 14 | } 15 | -------------------------------------------------------------------------------- /code/06/06.2/suppressions/ce7eb9cb7943ef3a148eec15298e36694e30d395: -------------------------------------------------------------------------------- 1 | panic: runtime error: index out of range 2 | encoding/binary.binary.bigEndian.Uint32 3 | github.com/malfunkt/iprange.(*ipParserImpl).Parse 4 | github.com/malfunkt/iprange.ipParse 5 | github.com/malfunkt/iprange.ParseList 6 | github.com/malfunkt/iprange.Fuzz 7 | go-fuzz-dep.Main 8 | main.main 9 | -------------------------------------------------------------------------------- /code/02/02.6/02.6-07-channel4.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | fourChan := make(chan int, 2) 8 | 9 | close(fourChan) 10 | 11 | i2 := 10 12 | fmt.Println("i2 before reading from closed channel", i2) // 10 13 | i2, ok := <-fourChan 14 | fmt.Printf("i2: %d - ok: %t", i2, ok) // i2: 10 - ok: false 15 | } 16 | -------------------------------------------------------------------------------- /code/06/06.3/suppressions/0fad3395539b4ffbbd22b457868a6c2f87c18457: -------------------------------------------------------------------------------- 1 | panic: runtime error: index out of range 2 | github.com/xor-gate/goexif2/tiff.(*Tag).Int64 3 | github.com/xor-gate/goexif2/exif.loadSubDir 4 | github.com/xor-gate/goexif2/exif.(*parser).Parse 5 | github.com/xor-gate/goexif2/exif.Decode 6 | github.com/xor-gate/goexif2/exif.Fuzz 7 | go-fuzz-dep.Main 8 | main.main 9 | -------------------------------------------------------------------------------- /code/02/02.6/02.6-02-goroutine2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func PrintMe(t int, count int) { 6 | for i := 0; i < count; i++ { 7 | fmt.Printf("Printing from %d\n", t) 8 | } 9 | } 10 | 11 | func main() { 12 | 13 | go PrintMe(0, 10) 14 | 15 | // Wait for a keypress 16 | fmt.Scanln() 17 | fmt.Println("Main finished!") 18 | } 19 | -------------------------------------------------------------------------------- /code/06/06.2/test2.go: -------------------------------------------------------------------------------- 1 | // Small program to investigate a panic in iprange for invalid masks. 2 | package main 3 | 4 | import "github.com/malfunkt/iprange" 5 | 6 | func main() { 7 | _ = Fuzz([]byte("0.0.0.0/40")) 8 | } 9 | 10 | func Fuzz(data []byte) int { 11 | _, err := iprange.ParseList(string(data)) 12 | if err != nil { 13 | return 0 14 | } 15 | return 1 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | 16 | # Ignore stash 17 | stash/* 18 | -------------------------------------------------------------------------------- /code/02/02.6/02.6-06-channel3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | fourChan := make(chan int, 2) 8 | 9 | // Send 10 to channel 10 | fourChan <- 10 11 | fmt.Printf("Sent %d to channel\n", 10) 12 | 13 | // Receive int from channel 14 | // We can also receive directly 15 | fmt.Printf("Received %d from channel\n", <-fourChan) 16 | } 17 | -------------------------------------------------------------------------------- /code/06/06.3/suppressions/8b21b5678278e10daa110f0c85c6802003702e06: -------------------------------------------------------------------------------- 1 | fatal error: out of memory 2 | runtime.systemstack_switch 3 | runtime.(*mcache).nextFree 4 | runtime.mallocgc 5 | runtime.newobject 6 | errors.New 7 | fmt.Errorf 8 | github.com/xor-gate/goexif2/exif.Decode 9 | github.com/xor-gate/goexif2/exif.Fuzz 10 | go-fuzz-dep.Main 11 | main.main 12 | runtime.main 13 | runtime.goexit 14 | -------------------------------------------------------------------------------- /code/02/02.3/02.3-02-array.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | var a [5]int 8 | a[0] = 10 9 | a[4] = 20 10 | 11 | fmt.Println(a) // [10 0 0 0 20] 12 | 13 | // Array can be initialized during creation 14 | // characters[2] is empty 15 | characters := [3]string{"Ender", "Pentra"} 16 | 17 | fmt.Println(characters) // [Ender Pentra ] 18 | } 19 | -------------------------------------------------------------------------------- /code/02/02.3/02.3-04-slice2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | // Slice literal of type struct, the underlying array is created automatically 8 | sliceStruct := []struct { 9 | a, b int 10 | }{ 11 | {1, 2}, 12 | {3, 4}, 13 | {5, 6}, // need this comma in the end otherwise it will not work 14 | } 15 | 16 | fmt.Println(sliceStruct) 17 | } 18 | -------------------------------------------------------------------------------- /code/04/04.5/testinputfile.txt: -------------------------------------------------------------------------------- 1 | 127.0.0.1:22 2 | 127.0.0:22 3 | 127.0.0.1: 4 | 127.0.0.1 5 | 127.0.0.1:1233:333 6 | 127.0.0.1:99999 7 | [2001:db8::68]:1234 8 | [2001:db8::68] 9 | [2001:db]:1234 10 | [2001:db]1234 11 | [2001:db::333333]:1234 12 | [2001:db8::68]:1233:333 13 | [2001:db8::68]:99999 14 | 2002:db8::68:1234 15 | 2002:db8::68 16 | 2002:db:1234 17 | 2002:db1234 18 | 2002:db::333333:1234 -------------------------------------------------------------------------------- /code/06/06.3/int-pointer-size.go: -------------------------------------------------------------------------------- 1 | // Get int and pointer size. 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "unsafe" 7 | ) 8 | 9 | func main() { 10 | var i int 11 | var p *int 12 | var p2 *float32 13 | 14 | fmt.Printf("Size of int : %d\n", unsafe.Sizeof(i)) 15 | fmt.Printf("Size of *int : %d\n", unsafe.Sizeof(p)) 16 | fmt.Printf("Size of *float32 : %d\n", unsafe.Sizeof(p2)) 17 | } 18 | -------------------------------------------------------------------------------- /code/06/06.3/suppressions/b34668fc3badc1bd2b13e3461f12b60d69acc548: -------------------------------------------------------------------------------- 1 | fatal error: out of memory 2 | runtime.systemstack_switch 3 | runtime.(*mcache).nextFree 4 | runtime.mallocgc 5 | runtime.makeslice 6 | github.com/xor-gate/goexif2/exif.newAppSec 7 | github.com/xor-gate/goexif2/exif.Decode 8 | github.com/xor-gate/goexif2/exif.Fuzz 9 | go-fuzz-dep.Main 10 | main.main 11 | runtime.main 12 | runtime.goexit 13 | -------------------------------------------------------------------------------- /code/02/02.6/02.6-09-channel6.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | c := make(chan bool) 8 | 9 | go func() { 10 | for i := 0; i < 10; i++ { 11 | fmt.Printf("Printing from %d\n", 0) 12 | } 13 | 14 | // Send true to channel when we are done 15 | c <- true 16 | }() 17 | 18 | // Main will wait until it receives something from c 19 | <-c 20 | } 21 | -------------------------------------------------------------------------------- /code/02/02.6/02.6-04-channel1.go: -------------------------------------------------------------------------------- 1 | // This will not run 2 | 3 | package main 4 | 5 | import "fmt" 6 | 7 | func main() { 8 | 9 | fourChan := make(chan int) 10 | 11 | i1 := 10 12 | 13 | // Send i1 to channel 14 | fourChan <- i1 15 | fmt.Printf("Sent %d to channel\n", i1) 16 | 17 | // Receive int from channel 18 | i2 := <-fourChan 19 | fmt.Printf("Received %d from channel\n", i2) 20 | } 21 | -------------------------------------------------------------------------------- /code/06/06.3/crashers/3f5b7d448a0791f5739fa0a2371bb2492b64f835.quoted: -------------------------------------------------------------------------------- 1 | "II*\x00\b\x00\x00\x00\t\x000000000000" + 2 | "00000000000000000000" + 3 | "00000000000000000000" + 4 | "00000000000000000000" + 5 | "00000000000000000000" + 6 | "000000i\x87\x04\x00\x01\x00\x00\x00\xac\x00\x00\x0000" + 7 | "00000000000000000000" + 8 | "00000000000000000000" + 9 | "0000000000000000\x05\x00\x00\x00" + 10 | "\x00\xe00000" 11 | -------------------------------------------------------------------------------- /code/02/02.5/02.5-03-bufioreadstring.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | 11 | reader := bufio.NewReader(os.Stdin) 12 | // ReadString will read until first new line 13 | input, err := reader.ReadString('\n') // Need to pass '\n' as char (byte) 14 | if err != nil { 15 | panic(err) 16 | } 17 | 18 | fmt.Printf("Entered %s", input) 19 | } 20 | -------------------------------------------------------------------------------- /code/06/06.3/suppressions/3c85b0906fcb8dfb9fbf5035333f8e955b3d7b1c: -------------------------------------------------------------------------------- 1 | panic: runtime error: makeslice: len out of range 2 | github.com/xor-gate/goexif2/tiff.(*Tag).convertVals 3 | github.com/xor-gate/goexif2/tiff.DecodeTag 4 | github.com/xor-gate/goexif2/tiff.DecodeDir 5 | github.com/xor-gate/goexif2/tiff.Decode 6 | github.com/xor-gate/goexif2/exif.Decode 7 | github.com/xor-gate/goexif2/exif.Fuzz 8 | go-fuzz-dep.Main 9 | main.main 10 | -------------------------------------------------------------------------------- /code/02/02.2/02.2-08-switch2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" // This is not cryptographically secure! 6 | "time" 7 | ) 8 | 9 | func main() { 10 | // Seeding rand 11 | rand.Seed(time.Now().UnixNano()) 12 | fmt.Println("Choosing a random number:") 13 | 14 | switch num := rand.Intn(100); { 15 | case num < 50: 16 | fmt.Println("Less than 50") 17 | default: 18 | fmt.Println("More than 50") 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /code/02/02.2/02.2-09-switch3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" // This is not cryptographically secure! 6 | "time" 7 | ) 8 | 9 | func main() { 10 | // Seeding rand 11 | rand.Seed(time.Now().UnixNano()) 12 | fmt.Println("Choosing a random number:") 13 | 14 | num := rand.Intn(100) 15 | switch { 16 | case num < 50: 17 | fmt.Println("Less than 50") 18 | default: 19 | fmt.Println("More than 50") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /code/03/03.1/03.1-05-args.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | ) 7 | 8 | func main() { 9 | // Set flag 10 | _ = flag.Int("flag1", 0, "flag1 description") 11 | // Parse all flags 12 | flag.Parse() 13 | // Enumererate flag.Args() 14 | for _, v := range flag.Args() { 15 | fmt.Println(v) 16 | } 17 | // Enumerate using flag.Arg(i) 18 | for i := 0; i < flag.NArg(); i++ { 19 | fmt.Println(flag.Arg(i)) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /code/02/02.2/02.2-07-switch1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" // This is not cryptographically secure! 6 | "time" 7 | ) 8 | 9 | func main() { 10 | // Seeding rand 11 | rand.Seed(time.Now().UnixNano()) 12 | fmt.Println("Choosing a random number:") 13 | 14 | switch num := rand.Intn(3); num { 15 | case 1: 16 | fmt.Println("1") 17 | case 2: 18 | fmt.Println("2") 19 | default: 20 | fmt.Println("3") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /code/02/02.7/02.7-02-errors2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | func Sqrt(x float64) (float64, error) { 9 | 10 | if x < 0 { 11 | // fmt.Errorf creates a new error type. No need to use errors.New here. 12 | return 0, fmt.Errorf("cannot Sqrt negative number: %v", float64(x)) 13 | } 14 | 15 | return math.Sqrt(x), nil 16 | } 17 | 18 | func main() { 19 | fmt.Println(Sqrt(2)) 20 | fmt.Println(Sqrt(-2)) 21 | } 22 | -------------------------------------------------------------------------------- /code/03/03.2/03.2-04-log-flags.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | 10 | a, b := 10, 20 11 | 12 | // New logger will output to stdout with flags 13 | // Only log date and file 14 | myLog := log.New(os.Stdout, "", log.Ldate|log.Lshortfile) 15 | 16 | myLog.Print("Use Print to log.") 17 | myLog.Println("Ditto for Println.") 18 | myLog.Printf("Use Printf and format strings. %d + %d = %d", a, b, a+b) 19 | } 20 | -------------------------------------------------------------------------------- /code/06/06.3/suppressions/3574328da5110d545dcd91e9df5bf1b6d3942570: -------------------------------------------------------------------------------- 1 | panic: runtime error: makeslice: len out of range 2 | github.com/xor-gate/goexif2/tiff.(*Tag).convertVals 3 | github.com/xor-gate/goexif2/tiff.DecodeTag 4 | github.com/xor-gate/goexif2/tiff.DecodeDir 5 | github.com/xor-gate/goexif2/exif.loadSubDir 6 | github.com/xor-gate/goexif2/exif.(*parser).Parse 7 | github.com/xor-gate/goexif2/exif.Decode 8 | github.com/xor-gate/goexif2/exif.Fuzz 9 | go-fuzz-dep.Main 10 | main.main 11 | -------------------------------------------------------------------------------- /code/06/06.3/test-crash-3f.go: -------------------------------------------------------------------------------- 1 | // Sample app to test crash 3f for xor-gate/goexif2. 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | 8 | "github.com/xor-gate/goexif2/exif" 9 | ) 10 | 11 | func main() { 12 | f, err := os.Open("crashers\\3f5b7d448a0791f5739fa0a2371bb2492b64f835") 13 | if err != nil { 14 | panic(err) 15 | } 16 | defer f.Close() 17 | 18 | _, err = exif.Decode(f) 19 | if err != nil { 20 | fmt.Println("err:", err) 21 | return 22 | } 23 | fmt.Println("no err") 24 | } 25 | -------------------------------------------------------------------------------- /code/06/06.3/test-crash-49.go: -------------------------------------------------------------------------------- 1 | // Sample app to test crash a5 for xor-gate/goexif2. 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | 8 | "github.com/xor-gate/goexif2/exif" 9 | ) 10 | 11 | func main() { 12 | f, err := os.Open("crashers\\49dfc363adbbe5aac9c2f8afbb0591c3ef1de2c3") 13 | if err != nil { 14 | panic(err) 15 | } 16 | defer f.Close() 17 | 18 | _, err = exif.Decode(f) 19 | if err != nil { 20 | fmt.Println("err:", err) 21 | return 22 | } 23 | fmt.Println("no err") 24 | } 25 | -------------------------------------------------------------------------------- /code/06/06.3/test-crash-a5.go: -------------------------------------------------------------------------------- 1 | // Sample app to test crash a5 for xor-gate/goexif2. 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | 8 | "github.com/xor-gate/goexif2/exif" 9 | ) 10 | 11 | func main() { 12 | f, err := os.Open("crashers\\a59a2ad5701156b88c6a132e1340fe006f67280c") 13 | if err != nil { 14 | panic(err) 15 | } 16 | defer f.Close() 17 | 18 | _, err = exif.Decode(f) 19 | if err != nil { 20 | fmt.Println("err:", err) 21 | return 22 | } 23 | fmt.Println("no err") 24 | } 25 | -------------------------------------------------------------------------------- /code/03/03.2/03.2-05-log-prefix.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | 10 | a, b := 10, 20 11 | 12 | // New logger will output to stdout with prefix "Log1: " and flags 13 | // Note the space in prefix 14 | myLog := log.New(os.Stdout, "Log1: ", log.Ldate|log.Lshortfile) 15 | 16 | myLog.Print("Use Print to log.") 17 | myLog.Println("Ditto for Println.") 18 | myLog.Printf("Use Printf and format strings. %d + %d = %d", a, b, a+b) 19 | } 20 | -------------------------------------------------------------------------------- /code/02/02.4/02.4-08-stringer2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type IPAddr [4]byte 6 | 7 | // TODO: Add a "String() string" method to IPAddr. 8 | func (ip IPAddr) String() string { 9 | return fmt.Sprintf("%v.%v.%v.%v", ip[0], ip[1], ip[2], ip[3]) 10 | } 11 | 12 | func main() { 13 | hosts := map[string]IPAddr{ 14 | "loopback": {127, 0, 0, 1}, 15 | "googleDNS": {8, 8, 8, 8}, 16 | } 17 | for name, ip := range hosts { 18 | fmt.Printf("%v: %v\n", name, ip) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /code/02/02.6/02.6-08-channel5.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | fourChan := make(chan int, 10) 8 | 9 | go func() { 10 | // Send 0-9 to channel 11 | for i := 0; i < 10; i++ { 12 | fourChan <- i 13 | } 14 | }() 15 | 16 | go func() { 17 | // Receive from channel 18 | for v := range fourChan { 19 | fmt.Println(v) 20 | } 21 | }() 22 | 23 | // Wait for goroutines to finish 24 | fmt.Scanln() 25 | fmt.Println("Main Finished!") 26 | } 27 | -------------------------------------------------------------------------------- /code/06/06.3/suppressions/d2f7c5b79258d20c8dbd9d4ca3241b85a93e7418: -------------------------------------------------------------------------------- 1 | fatal error: out of memory 2 | runtime.systemstack_switch 3 | runtime.mallocgc 4 | runtime.makeslice 5 | github.com/xor-gate/goexif2/tiff.(*Tag).convertVals 6 | github.com/xor-gate/goexif2/tiff.DecodeTag 7 | github.com/xor-gate/goexif2/tiff.DecodeDir 8 | github.com/xor-gate/goexif2/tiff.Decode 9 | github.com/xor-gate/goexif2/exif.Decode 10 | github.com/xor-gate/goexif2/exif.Fuzz 11 | go-fuzz-dep.Main 12 | main.main 13 | runtime.main 14 | runtime.goexit 15 | -------------------------------------------------------------------------------- /code/02/02.4/02.4-07-stringer1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Define a struct 6 | type Tuple struct { 7 | A, B int 8 | } 9 | 10 | // Create a Stringer for Tuple 11 | func (t Tuple) String() string { 12 | // Sprintf is similar to the equivalent in C 13 | return fmt.Sprintf("A: %d, B: %d", t.A, t.B) 14 | } 15 | 16 | func main() { 17 | 18 | tuple1 := Tuple{10, 10} 19 | tuple2 := Tuple{20, 20} 20 | fmt.Println(tuple1) // A: 10, B: 10 21 | fmt.Println(tuple2) // A: 20, B: 20 22 | } 23 | -------------------------------------------------------------------------------- /code/02/02.3/02.3-06-slice4.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | ints := [...]int{0, 1, 2, 3, 4, 5} 8 | fmt.Println(ints) 9 | 10 | slice1 := ints[2:6] 11 | 12 | // len=4 and cap=4 (from 3rd item of the array until the end) 13 | printSlice(slice1) 14 | 15 | slice1 = ints[2:4] 16 | 17 | // len=2 but cap will remain 4 18 | printSlice(slice1) 19 | } 20 | 21 | // Copied from the tour 22 | func printSlice(s []int) { 23 | fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s) 24 | } 25 | -------------------------------------------------------------------------------- /code/02/02.6/02.6-10-channel7.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | c := make(chan int, 2) 8 | 9 | for i := 0; i < 10; i++ { 10 | 11 | select { 12 | case c <- i: 13 | // If we can write to channel, send something to it 14 | fmt.Println("Sent to channel", i) 15 | case i2 := <-c: 16 | // If we can read from channel, read from it and print 17 | fmt.Println("Received from channel", i2) 18 | default: 19 | // This is run when nothing else can be done 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /code/02/02.5/02.5-04-bufioreadbytes.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | 11 | reader := bufio.NewReader(os.Stdin) 12 | // Read bytes until the new line 13 | input, err := reader.ReadBytes('\n') // Need to pass '\n' as char (byte) 14 | if err != nil { 15 | panic(err) 16 | } 17 | 18 | // Print type of "input" and its value 19 | fmt.Printf("Entered type %T, %v\n", input, input) 20 | // Print bytes as string 21 | fmt.Printf("Print bytes as string with %%s %s", input) 22 | } 23 | -------------------------------------------------------------------------------- /code/02/02.7/02.7-01-errors1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type ErrNegativeSqrt float64 9 | 10 | func (e ErrNegativeSqrt) Error() string { 11 | return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e)) 12 | } 13 | 14 | func Sqrt(x float64) (float64, error) { 15 | 16 | if x < 0 { 17 | return 0, ErrNegativeSqrt(x) 18 | } 19 | 20 | // Don't need else here - why? 21 | return math.Sqrt(x), nil 22 | } 23 | 24 | func main() { 25 | fmt.Println(Sqrt(2)) 26 | fmt.Println(Sqrt(-2)) 27 | } 28 | -------------------------------------------------------------------------------- /code/02/02.4/02.4-04-interface2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | var emptyInterface interface{} 6 | 7 | type Tuple struct { 8 | A, B int 9 | } 10 | 11 | func main() { 12 | 13 | // Use int 14 | int1 := 10 15 | emptyInterface = int1 16 | fmt.Println(emptyInterface) // 10 17 | 18 | // Use float 19 | float1 := 1.2345 20 | emptyInterface = float1 21 | fmt.Println(emptyInterface) // 1.2345 22 | 23 | // Use custom struct 24 | tuple1 := Tuple{5, 5} 25 | emptyInterface = tuple1 26 | fmt.Println(emptyInterface) // {5 5} 27 | } 28 | -------------------------------------------------------------------------------- /content/06.0.md: -------------------------------------------------------------------------------- 1 | # 06 - Go-Fuzz 2 | This section talks about [Go-Fuzz][go-fuzz]. Go-Fuzz is a coverage-guided fuzzer for Go code. This section might be renamed to `Fuzzing` when new content arrives. 3 | 4 | Start by reading the Quickstart guide and a few of the examples. Then move to sections 2 and 3 for hands-on practice. 5 | 6 | ## Table of Contents 7 | 8 | - [06.1 - Go-Fuzz Quickstart](06.1.md) 9 | - [06.2 - Fuzzing iprange with Go-Fuzz](06.2.md) 10 | - [06.2 - Fuzzing goexif2 with Go-Fuzz](06.3.md) 11 | 12 | 13 | [go-fuzz]: https://github.com/dvyukov/go-fuzz -------------------------------------------------------------------------------- /code/02/02.3/02.3-07-slice-append.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | // Create a slice pointing to an int array 8 | s1 := make([]int, 5) 9 | 10 | fmt.Println(s1) // [0 0 0 0 0] 11 | 12 | for i := 0; i < len(s1); i++ { 13 | s1[i] = i 14 | } 15 | 16 | fmt.Println(s1) // [0 1 2 3 4] 17 | 18 | s2 := make([]int, 3) 19 | 20 | for i := 0; i < len(s2); i++ { 21 | s2[i] = i 22 | } 23 | 24 | fmt.Println(s2) // [0 1 2] 25 | 26 | s3 := append(s1, s2...) 27 | 28 | fmt.Println(s3) // [0 1 2 3 4 0 1 2] 29 | } 30 | -------------------------------------------------------------------------------- /code/02/02.4/02.4-05-interface3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | var interface1 interface{} = 1234.5 7 | 8 | // Only print f1 if cast was successful 9 | if f1, ok := interface1.(float64); ok { 10 | fmt.Println("Float") 11 | fmt.Println(f1) // 1234.5 12 | } 13 | 14 | f2 := interface1.(float64) 15 | fmt.Println(f2) // 1234.5 No panic but not recommended 16 | 17 | // This will trigger a panic 18 | // i1 = interface1.(int) 19 | 20 | i2, ok := interface1.(int) // No panic 21 | fmt.Println(i2, ok) // 0 false 22 | } 23 | -------------------------------------------------------------------------------- /code/02/02.6/02.6-05-channel2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | fourChan := make(chan int) 8 | 9 | go func() { 10 | // Send i1 to channel 11 | i1 := 10 12 | fourChan <- i1 // fourChan <- 10 13 | fmt.Printf("Sent %d to channel\n", i1) 14 | }() 15 | 16 | go func() { 17 | // Receive int from channel 18 | i2 := <-fourChan 19 | fmt.Println(i2) 20 | fmt.Printf("Received %d from channel\n", i2) 21 | }() 22 | 23 | // Wait for goroutines to finish 24 | fmt.Scanln() 25 | fmt.Println("Main Finished!") 26 | } 27 | -------------------------------------------------------------------------------- /code/02/02.6/02.6-11-channel8.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Directed write-only channel 6 | func Sender(c chan<- int) { 7 | for i := 0; i < 10; i++ { 8 | fmt.Println("Sent", i) 9 | c <- i 10 | } 11 | } 12 | 13 | func Receiver(c <-chan int) { 14 | for i := range c { 15 | fmt.Println("Received", i) 16 | } 17 | } 18 | 19 | func main() { 20 | 21 | fourChan := make(chan int) 22 | 23 | go Sender(fourChan) 24 | go Receiver(fourChan) 25 | 26 | // Wait for goroutines to finish 27 | fmt.Scanln() 28 | fmt.Println("Main Finished!") 29 | } 30 | -------------------------------------------------------------------------------- /code/03/03.2/03.2-02-log-file.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | 10 | // Create a file 11 | logFile, err := os.Create("log1.txt") 12 | if err != nil { 13 | panic("Could not open file") 14 | } 15 | 16 | // Close the file after main returns 17 | defer logFile.Close() 18 | 19 | a, b := 10, 20 20 | 21 | // We will not use the other options 22 | myLog := log.New(logFile, "", 0) 23 | 24 | myLog.Print("Use Print to log.") 25 | myLog.Println("Ditto for Println.") 26 | myLog.Printf("Use Printf and format strings. %d + %d = %d", a, b, a+b) 27 | } 28 | -------------------------------------------------------------------------------- /code/02/02.4/02.4-06-typeswitch.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func printType(i interface{}) { 6 | // Do a type switch on interface 7 | switch val := i.(type) { 8 | // If an int is passed 9 | case int: 10 | fmt.Println("int") 11 | case string: 12 | fmt.Println("string") 13 | case float64: 14 | fmt.Println("float64") 15 | default: 16 | fmt.Println("Other:", val) 17 | } 18 | } 19 | 20 | func main() { 21 | printType(10) // int 22 | printType("Hello") // string 23 | printType(156.32) // float64 24 | printType(nil) // Other: 25 | printType(false) // Other: false 26 | } 27 | -------------------------------------------------------------------------------- /code/03/03.1/03.1-01-flag1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | ) 7 | 8 | func main() { 9 | 10 | // Declare flags 11 | // Remember, flag methods return pointers 12 | ipPtr := flag.String("ip", "127.0.0.1", "target IP") 13 | 14 | var port int 15 | flag.IntVar(&port, "port", 8080, "Port") 16 | 17 | verbosePtr := flag.Bool("verbose", true, "verbosity") 18 | 19 | // Parse flags 20 | flag.Parse() 21 | 22 | // Hack IP:port 23 | fmt.Printf("Hacking %s:%d!\n", *ipPtr, port) 24 | 25 | // Display progress if verbose flag is set 26 | if *verbosePtr { 27 | fmt.Printf("Pew pew!\n") 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /content/03.0.md: -------------------------------------------------------------------------------- 1 | # 03 - Useful Go packages 2 | This is where this repo is going to divert a bit from BH books. This section is going to be simple guides to Go packages that will be used later. For example the `flag` package is used to create and process command line parameters and is a building block of almost every security script that hopes to be re-used. Some packages like `net` are more complex and better learned in action while building/using tools. 3 | 4 | As I move forward and learn more, I will return and add more tutorials here. 5 | 6 | - [03.1 - flag package](03.1.md): Parsing command line parameters. 7 | - [03.2 - log package](03.2.md): Logging. 8 | -------------------------------------------------------------------------------- /code/03/03.1/03.1-02-flag2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | ) 7 | 8 | func main() { 9 | 10 | // Declare flags 11 | // Remember, flag methods return pointers 12 | ipPtr := flag.String("ip", "127.0.0.1", "target IP") 13 | 14 | var port int 15 | flag.IntVar(&port, "port", 8080, "Port") 16 | 17 | verbosePtr := flag.Bool("verbose", false, "verbosity") 18 | 19 | // Parse flags 20 | flag.Parse() 21 | 22 | // Hack IP:port 23 | fmt.Printf("Hacking %s:%d!\n", *ipPtr, port) 24 | 25 | // Display progress if verbose flag is set 26 | if *verbosePtr { 27 | fmt.Printf("Pew pew!\n") 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /code/02/02.3/02.3-08-range.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | characters := [3]string{"Ender", "Petra", "Mazer"} 7 | for i, v := range characters { 8 | fmt.Println(i, v) 9 | } 10 | 11 | // 0 Ender 12 | // 1 Petra 13 | // 2 Mazer 14 | 15 | fmt.Println("-----------") 16 | 17 | // Only using index 18 | for i := range characters { 19 | fmt.Println(i, characters[i]) 20 | } 21 | 22 | fmt.Println("-----------") 23 | 24 | // Ignoring index 25 | for _, v := range characters { 26 | // No non-elaborate way to get index here 27 | fmt.Println(v) 28 | } 29 | 30 | // Ender 31 | // Petra 32 | // Mazer 33 | } 34 | -------------------------------------------------------------------------------- /code/02/02.3/02.3-03-slice1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | // Create an array of strings with 3 members 8 | characters := [3]string{"Ender", "Petra", "Mazer"} 9 | 10 | // Last index is exclusive 11 | // allMembers []string := characters[0:3] 12 | var allMembers []string = characters[0:3] 13 | fmt.Println("All members", allMembers) 14 | 15 | var lastTwo []string = characters[1:3] 16 | fmt.Println("Last two members", lastTwo) 17 | 18 | // Replace Mazer with Bean 19 | fmt.Println("Replacing Mazer with Bean") 20 | allMembers[2] = "Bean" 21 | 22 | fmt.Println("All members after Bean swap", characters) 23 | 24 | fmt.Println("Last two members after Bean swap", lastTwo) 25 | } 26 | -------------------------------------------------------------------------------- /code/03/03.1/03.1-03-flag3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | ) 7 | 8 | // Declare flag variables 9 | var ( 10 | ip string 11 | port int 12 | verbose bool 13 | ) 14 | 15 | func init() { 16 | // Declare flags 17 | // Remember, flag methods return pointers 18 | flag.StringVar(&ip, "ip", "127.0.0.1", "target IP") 19 | 20 | flag.IntVar(&port, "port", 8080, "Port") 21 | 22 | flag.BoolVar(&verbose, "verbose", false, "verbosity") 23 | } 24 | 25 | func main() { 26 | 27 | // Parse flags 28 | flag.Parse() 29 | 30 | // Hack IP:port 31 | fmt.Printf("Hacking %s:%d!\n", ip, port) 32 | 33 | // Display progress if verbose flag is set 34 | if verbose { 35 | fmt.Printf("Pew pew!\n") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /code/02/02.4/02.4-01-method1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Create a new type for []string 6 | type StringSlice []string 7 | 8 | // Define the method for StringSlice 9 | func (x StringSlice) PrintSlice() { 10 | for _, v := range x { 11 | fmt.Println(v) 12 | } 13 | } 14 | 15 | func main() { 16 | 17 | // Create an array of strings with 3 members 18 | characters := [3]string{"Ender", "Petra", "Mazer"} 19 | 20 | // Create a StringSlice 21 | var allMembers StringSlice = characters[0:3] 22 | 23 | // Now we can call the method on it 24 | allMembers.PrintSlice() 25 | 26 | // Ender 27 | // Petra 28 | // Mazer 29 | 30 | // allMembers.PrintSlice() 31 | // allMembers.PrintSlice undefined (type []string has no field or method PrintSlice) 32 | } 33 | -------------------------------------------------------------------------------- /content/04.0.md: -------------------------------------------------------------------------------- 1 | # 04 - Go networking 2 | Now that we are done with the basics and packages, we can start learning about networking. Following Black Hat Python, let's start with networking basics and then move on. 3 | 4 | Go's networking capabilities are in the [net][net-pkg] package and its sub-packages like [net/http][net-http-pkg]. The Python equivalent to `net` is `socket` and `net/http` can be compared to the 3rd party `Requests` module. 5 | 6 | ## Table of Contents 7 | 8 | - [04.1 - Basic TCP and UDP clients](04.1.md) 9 | - [04.2 - TCP servers](04.2.md) 10 | - [04.3 - TCP proxy](04.3.md) 11 | - [04.4 - SSH clients](04.4.md) 12 | - [04.5 - SSH Harvester](04.5.md) 13 | 14 | 15 | 16 | [net-pkg]: https://golang.org/pkg/net/ 17 | [net-http-pkg]: https://golang.org/pkg/net/http/ 18 | -------------------------------------------------------------------------------- /content/02.0.md: -------------------------------------------------------------------------------- 1 | # 02 - Basics 2 | This is a quick introduction to Go. This section assumes you know other programming languages (most likely Python) and are familiar with basic programming structures. 3 | 4 | These notes were originally created during the tutorials at [Tour of Go][tour-of-go] and some other sources. Then more were added to make it a reference/cheat sheet. 5 | 6 | ## Table of Contents 7 | 8 | - [02.1 - Packages, functions, variables, basic types, casting and constants](02.1.md) 9 | - [02.2 - for, if, else, switch and defer](02.2.md) 10 | - [02.3 - Pointers, structs, arrays, slices and range](02.3.md) 11 | - [02.4 - Methods and interfaces](02.4.md) 12 | - [02.5 - Printf, Scanf, bufio readers and maps](02.5.md) 13 | - [02.6 - Goroutines and channels](02.6.md) 14 | - [02.7 - Error handling](02.7.md) 15 | 16 | 17 | 18 | [tour-of-go]: https://tour.golang.org/ 19 | -------------------------------------------------------------------------------- /code/02/02.4/02.4-03-interface1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Define new interface 6 | type MyPrinter interface { 7 | MyPrint() 8 | } 9 | 10 | // Define a type for int 11 | type MyInt int 12 | 13 | // Define MyPrint() fro MyInt 14 | func (i MyInt) MyPrint() { 15 | fmt.Println(i) 16 | } 17 | 18 | // Define a type for float64 19 | type MyFloat float64 20 | 21 | // Define MyPrint() for MyFloat 22 | func (f MyFloat) MyPrint() { 23 | fmt.Println(f) 24 | } 25 | 26 | func main() { 27 | 28 | // Define interface 29 | var interface1 MyPrinter 30 | 31 | f1 := MyFloat(1.2345) 32 | // Assign a float to interface 33 | interface1 = f1 34 | // Call MyPrint() on interface 35 | interface1.MyPrint() // 1.2345 36 | 37 | i1 := MyInt(10) 38 | // Assign an int to interface 39 | interface1 = i1 40 | // Call MyPrint() on interface 41 | interface1.MyPrint() // 10 42 | } 43 | -------------------------------------------------------------------------------- /code/06/06.3/crashers/171e8e5ca3e3d609322376915dcfa3dd56938845.quoted: -------------------------------------------------------------------------------- 1 | "\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x01\x00H\x00H\x00\x00" + 2 | "\xff\xe1\a\xe5Exif\x00\x00II*\x00\b\x00\x00\x00\v\x00" + 3 | "\x0f\x01\x02\x00\x14\x00\x00\x00\x92\x00\x00\x00\x10\x01\x02\x00\b\x00\x00\x00" + 4 | "\xa6\x00\x00\x00\x12\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\x00\x1a\x01\x05\x00" + 5 | "\x00\x00\x00@\x06\x00\x00\x1c\x00\x00\x00\n\x00\x00\x002006:" + 6 | "10:22 15:44:29\x002006:" + 7 | "10:22 15:44:29\x00\x04\x00\x00\x00\x01" + 8 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x1e\x00\x00\x00\n\x00\x00\x00\x9e" + 9 | "\x00\x00\x00\n\x00\x00\x00ASCII\x00\x00\x00 " + 10 | " " + 11 | " " + 12 | " " + 13 | " " + 14 | " " + 15 | " \x00\x00\x00\x00\x00d\x00\x00" + 16 | "\x0077c6274bd589ad50395" + 17 | "891e84a8b673b\x00\x00\x02\x00\x01\x00\x02" + 18 | "\x00\x04\x00\x00\x00R98\x00\x02\x00\a\x00\x04\x00\x00\x00010" + 19 | "0\x00\x00\x00\x00" 20 | -------------------------------------------------------------------------------- /code/02/02.6/02.6-12-waitgroup1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // generateStrings generated n strings and sends them to channel. 6 | // Channel is closed when string generation is done. 7 | func generateStrings(n int, c chan<- string) { 8 | 9 | // Close channel when done 10 | defer close(c) 11 | // Generate strings 12 | for i := 0; i < n; i++ { 13 | c <- fmt.Sprintf("String #%d", i) 14 | } 15 | } 16 | 17 | // consumeString reads strings from channel and prints them. 18 | func consumeString(s string) { 19 | fmt.Printf("Consumed %s\n", s) 20 | } 21 | 22 | func main() { 23 | // Create channel 24 | c := make(chan string) 25 | // Generate strings 26 | go generateStrings(10, c) 27 | 28 | for { 29 | select { 30 | // Read from channel 31 | case s, ok := <-c: 32 | // If channel is closed stop processing and return 33 | if !ok { 34 | fmt.Println("Processing finished") 35 | return 36 | } 37 | // Consume the string read from channel 38 | go consumeString(s) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /code/03/03.2/03.2-03-log-multiple-files.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | ) 10 | 11 | func main() { 12 | 13 | // Create a file 14 | logFile, err := os.Create("log1.txt") 15 | if err != nil { 16 | panic("Could not open file") 17 | } 18 | 19 | // Close the file after main returns 20 | defer logFile.Close() 21 | 22 | // Create a second file 23 | logFile2, err := os.Create("log2.txt") 24 | if err != nil { 25 | panic("Could not open file2") 26 | } 27 | 28 | defer logFile2.Close() 29 | 30 | // Create a buffer 31 | var buflog bytes.Buffer 32 | 33 | multiW := io.MultiWriter(logFile, logFile2, &buflog, os.Stdout) 34 | 35 | a, b := 10, 20 36 | 37 | // Log to multiW 38 | myLog := log.New(multiW, "", 0) 39 | 40 | myLog.Print("Use Print to log.") 41 | myLog.Println("Ditto for Println.") 42 | myLog.Printf("Use Printf and format strings. %d + %d = %d", a, b, a+b) 43 | 44 | // Print buffer 45 | fmt.Println("Buffer:") 46 | fmt.Println(buflog.String()) 47 | } 48 | -------------------------------------------------------------------------------- /code/02/02.5/02.5-05-maps.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type intMap map[int]int 6 | 7 | // Create a Stringer for this map type 8 | func (i intMap) String() string { 9 | 10 | var s string 11 | s += fmt.Sprintf("Map type %T\n", i) 12 | s += fmt.Sprintf("Length: %d\n", len(i)) 13 | 14 | // Iterate through all key/value pairs 15 | for k, v := range i { 16 | s += fmt.Sprintf("[%v] = %v\n", k, v) 17 | } 18 | return s 19 | } 20 | 21 | func main() { 22 | 23 | // Create a map 24 | map1 := make(intMap) 25 | 26 | // Add key/value pairs 27 | map1[0] = 10 28 | map1[5] = 20 29 | 30 | // Print map - Stringer will be called 31 | fmt.Println(map1) 32 | // Map type main.intMap 33 | // Length: 2 34 | // [0] = 10 35 | // [5] = 20 36 | 37 | // Delete a key/value pair 38 | delete(map1, 0) 39 | 40 | fmt.Println(map1) 41 | // Map type main.intMap 42 | // Length: 1 43 | // [5] = 20 44 | 45 | // Create a map on the spot using members 46 | map2 := map[string]string{"key1": "value1", "key2": "value2"} 47 | 48 | fmt.Println(map2) 49 | // map[key1:value1 key2:value2] 50 | } 51 | -------------------------------------------------------------------------------- /code/02/02.3/02.3-01-structs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Student struct { 6 | FirstName string 7 | LastName string 8 | } 9 | 10 | func main() { 11 | // Make an instance 12 | studentOne := Student{"Ender", "Wiggin"} 13 | 14 | // Now we can access fields 15 | fmt.Println(studentOne.FirstName) 16 | 17 | // We can just assign fields using names, anything not assigned will be 18 | // initialized with "zero" as we have seen before 19 | studentTwo := Student{FirstName: "Petra"} 20 | 21 | // We will print "{Petra }" notice the space after Petra which is supposed 22 | // to be the delimiter between the fields, LastName is nil because it is not 23 | // given a value 24 | fmt.Println(studentTwo) 25 | 26 | // Can also make a pointer to a struct 27 | p := &studentOne 28 | 29 | // Now instead of *p.LastName (doesn't work) we can just use p.LastName 30 | // fmt.Println((*p).LastName) will not work with error message: invalid indirect of p (type Student) 31 | fmt.Println(p.LastName) 32 | 33 | // Which is the same as 34 | fmt.Println(studentOne.LastName) 35 | 36 | // We can just create a pointer out of the blue 37 | p2 := &Student{"Hercule", "Poirot"} 38 | fmt.Println(p2) 39 | } 40 | -------------------------------------------------------------------------------- /code/02/02.4/02.4-02-method2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Tuple type 6 | type Tuple struct { 7 | A, B int 8 | } 9 | 10 | // Should not change the value of the object as it works on a copy of it 11 | func (x Tuple) ModifyTupleValue() { 12 | x.A = 2 13 | x.B = 2 14 | } 15 | 16 | // Should change the value of the object 17 | func (x *Tuple) ModifyTuplePointer() { 18 | x.A = 3 19 | x.B = 3 20 | } 21 | 22 | type IntSlice []int 23 | 24 | func (x IntSlice) PrintSlice() { 25 | fmt.Println(x) 26 | } 27 | 28 | // Modifies the IntSlice although it's by value 29 | func (x IntSlice) ModifySliceValue() { 30 | x[0] = 1 31 | } 32 | 33 | // Modifies the IntSlice 34 | func (x *IntSlice) ModifySlicePointer() { 35 | (*x)[0] = 2 36 | } 37 | 38 | func main() { 39 | 40 | tup := Tuple{1, 1} 41 | 42 | tup.ModifyTupleValue() 43 | fmt.Println(tup) // {1 1} - Does not change 44 | 45 | tup.ModifyTuplePointer() 46 | fmt.Println(tup) // {3 3} - Modified by pointer receiver 47 | 48 | var slice1 IntSlice = make([]int, 5) 49 | slice1.PrintSlice() // [0 0 0 0 0] 50 | 51 | slice1.ModifySliceValue() 52 | slice1.PrintSlice() // [1 0 0 0 0] 53 | 54 | slice1.ModifySlicePointer() 55 | slice1.PrintSlice() // [2 0 0 0 0] 56 | } 57 | -------------------------------------------------------------------------------- /code/02/02.6/02.6-13-waitgroup2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | var wg sync.WaitGroup 9 | 10 | // generateStrings generated n strings and sends them to channel. 11 | // Channel is closed when string generation is done. 12 | func generateStrings(n int, c chan<- string) { 13 | 14 | // Close channel when done 15 | defer close(c) 16 | // Generate strings 17 | for i := 0; i < n; i++ { 18 | c <- fmt.Sprintf("String #%d", i) 19 | } 20 | } 21 | 22 | // consumeString reads strings from channel and prints them. 23 | func consumeString(s string) { 24 | // Decrease waitgroup's counter by one 25 | defer wg.Done() 26 | fmt.Printf("Consumed %s\n", s) 27 | } 28 | 29 | func main() { 30 | // Create channel 31 | c := make(chan string) 32 | // Generate strings 33 | go generateStrings(10, c) 34 | 35 | for { 36 | select { 37 | // Read from channel 38 | case s, ok := <-c: 39 | // If channel is closed stop processing and return 40 | if !ok { 41 | // Wait for all goroutines to finish 42 | wg.Wait() 43 | // Return 44 | fmt.Println("Processing finished") 45 | return 46 | } 47 | // Increase wg counter by one for each goroutine 48 | // Note this is happening inside main before spawning the goroutine 49 | wg.Add(1) 50 | // Consume the string 51 | go consumeString(s) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /code/06/06.2/crashers/17ee301be06245aa20945bc3ff3c4838abe13b52.output: -------------------------------------------------------------------------------- 1 | 1 2 | 80000000 3 | 0.0.0.0 4 | 40 5 | 6 | 7 | panic: runtime error: index out of range 8 | 9 | goroutine 1 [running]: 10 | encoding/binary.binary.bigEndian.Uint32(...) 11 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build049016974/goroot/src/encoding/binary/binary.go:111 12 | github.com/malfunkt/iprange.(*ipParserImpl).Parse(0xc04209d800, 0x526cc0, 0xc042083040, 0x0) 13 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build049016974/gopath/src/github.com/malfunkt/iprange/y.go:510 +0x2be1 14 | github.com/malfunkt/iprange.ipParse(0x526cc0, 0xc042083040, 0xa) 15 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build049016974/gopath/src/github.com/malfunkt/iprange/y.go:308 +0x8f 16 | github.com/malfunkt/iprange.ParseList(0xc042075ed0, 0xa, 0xa, 0x200000, 0xc042075ed0, 0xa, 0x8) 17 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build049016974/gopath/src/github.com/malfunkt/iprange/y.go:63 +0xd6 18 | github.com/malfunkt/iprange.Fuzz(0x3750000, 0xa, 0x200000, 0x3) 19 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build049016974/gopath/src/github.com/malfunkt/iprange/fuzz.go:4 +0x84 20 | go-fuzz-dep.Main(0x5196e0) 21 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build049016974/goroot/src/go-fuzz-dep/main.go:49 +0xb4 22 | main.main() 23 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build049016974/gopath/src/github.com/malfunkt/iprange/go.fuzz.main/main.go:10 +0x34 24 | exit status 2 -------------------------------------------------------------------------------- /code/06/06.3/crashers/49dfc363adbbe5aac9c2f8afbb0591c3ef1de2c3.output: -------------------------------------------------------------------------------- 1 | panic: runtime error: index out of range 2 | 3 | goroutine 1 [running]: 4 | github.com/xor-gate/goexif2/tiff.(*Tag).Int64(...) 5 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/tiff/tag.go:363 6 | github.com/xor-gate/goexif2/exif.loadSubDir(0xc042080510, 0x547f15, 0xe, 0xc042080390, 0xc042080540, 0xc042089d68) 7 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/exif/exif.go:211 +0x704 8 | github.com/xor-gate/goexif2/exif.(*parser).Parse(0x613170, 0xc042080510, 0xc0420804b0, 0x0) 9 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/exif/exif.go:190 +0x174 10 | github.com/xor-gate/goexif2/exif.Decode(0x560240, 0xc042080480, 0x5ae92f8f, 0x212abedc, 0x1e9999) 11 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/exif/exif.go:331 +0x503 12 | github.com/xor-gate/goexif2/exif.Fuzz(0x38f0000, 0x72, 0x200000, 0xc042047f48) 13 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/exif/Fuzz.go:8 +0xba 14 | go-fuzz-dep.Main(0x550580) 15 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/goroot/src/go-fuzz-dep/main.go:49 +0xb4 16 | main.main() 17 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/exif/go.fuzz.main/main.go:10 +0x34 18 | exit status 2 -------------------------------------------------------------------------------- /code/04/04.1/04.1-02-basic-tcp2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "net" 8 | ) 9 | 10 | var ( 11 | host, port string 12 | ) 13 | 14 | func init() { 15 | flag.StringVar(&port, "port", "80", "target port") 16 | flag.StringVar(&host, "host", "example.com", "target host") 17 | } 18 | 19 | func main() { 20 | 21 | // Converting host and port to host:port 22 | t := net.JoinHostPort(host, port) 23 | 24 | // Create a connection to server 25 | conn, err := net.Dial("tcp", t) 26 | if err != nil { 27 | panic(err) 28 | } 29 | 30 | // Write the GET request to connection 31 | // Note we are closing the HTTP connection with the Connection: close header 32 | // Fprintf writes to an io.writer 33 | req := "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n" 34 | fmt.Fprintf(conn, req) 35 | 36 | // Another way to do it to directly write bytes to conn with conn.Write 37 | // However we must first convert the string to bytes with []byte("string") 38 | // reqBytes := []byte(req) 39 | // conn.Write(reqBytes) 40 | 41 | // Reading the response 42 | 43 | // Create a new reader from connection 44 | connReader := bufio.NewReader(conn) 45 | 46 | // Read until a null byte (not safe in general) 47 | // Response will not be completely read if it has a null byte 48 | if status, err := connReader.ReadString(byte(0x00)); err != nil { 49 | fmt.Println(err) 50 | fmt.Println(status) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /code/04/04.1/04.1-04-basic-udp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "net" 8 | "time" 9 | ) 10 | 11 | var ( 12 | host, port string 13 | ) 14 | 15 | func init() { 16 | flag.StringVar(&port, "port", "80", "target port") 17 | flag.StringVar(&host, "host", "example.com", "target host") 18 | } 19 | 20 | func main() { 21 | 22 | // Converting host and port to host:port 23 | t := net.JoinHostPort(host, port) 24 | 25 | // Create a connection to server with 5 second timeout 26 | conn, err := net.DialTimeout("udp", t, 5*time.Second) 27 | if err != nil { 28 | panic(err) 29 | } 30 | 31 | // Write the GET request to connection 32 | // Note we are closing the HTTP connection with the Connection: close header 33 | // Fprintf writes to an io.writer 34 | req := "UDP PAYLOAD" 35 | fmt.Fprintf(conn, req) 36 | 37 | // Another way to do it to directly write bytes to conn with conn.Write 38 | // However we must first convert the string to bytes with []byte("string") 39 | // reqBytes := []byte(req) 40 | // conn.Write(reqBytes) 41 | 42 | // Reading the response 43 | 44 | // Create a new reader from connection 45 | connReader := bufio.NewReader(conn) 46 | 47 | // Read until a null byte (not safe in general) 48 | // Response will not be completely read if it has a null byte 49 | if status, err := connReader.ReadString(byte(0x00)); err != nil { 50 | fmt.Println(err) 51 | fmt.Println(status) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /code/04/04.2/04.2-01-tcpserver1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io" 7 | "net" 8 | ) 9 | 10 | var ( 11 | bindHost, bindPort string 12 | ) 13 | 14 | func init() { 15 | flag.StringVar(&bindPort, "port", "12345", "bind port") 16 | flag.StringVar(&bindHost, "host", "127.0.0.1", "bind host") 17 | } 18 | 19 | // handleConnectionNoLog echoes everything back without logging (easiest) 20 | func handleConnectionNoLog(conn net.Conn) { 21 | 22 | rAddr := conn.RemoteAddr().String() 23 | defer fmt.Printf("Closed connection from %v\n", rAddr) 24 | 25 | // This will accomplish the echo if we do not want to log 26 | io.Copy(conn, conn) 27 | } 28 | 29 | func main() { 30 | 31 | flag.Parse() 32 | 33 | // Converting host and port to host:port 34 | t := net.JoinHostPort(bindHost, bindPort) 35 | 36 | // Listen for connections on BindIP:BindPort 37 | ln, err := net.Listen("tcp", t) 38 | if err != nil { 39 | // If we cannot bind, print the error and quit 40 | panic(err) 41 | } 42 | 43 | // Wait for connections 44 | for { 45 | // Accept a connection 46 | conn, err := ln.Accept() 47 | if err != nil { 48 | // If there was an error, print it and go back to listening 49 | fmt.Println(err) 50 | continue 51 | } 52 | 53 | fmt.Printf("Received connection from %v\n", conn.RemoteAddr().String()) 54 | 55 | // Spawn a new goroutine to handle the connection 56 | go handleConnectionNoLog(conn) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /@TODO.md: -------------------------------------------------------------------------------- 1 | # TODO list 2 | 3 | - ~~Refactor and edit chapter 1.~~ 4 | - ~~Refactor and edit chapter 2.~~ 5 | - ~~Split code inside chapter 02 by section.~~ 6 | - Use goroutines to break simple encryption. 7 | - Encryption/Decryption chapter. 8 | - Encoding/Decoding chapter. 9 | - Byte manipulation chapter. 10 | + `bytes.Buffer` or better the `bytes` package. 11 | - ~~Add `fmt.scan` examples to `02.5`.~~ 12 | - ~~Add error handling to `02.4`.~~ 13 | - ~~Add raw strings to `02.1`.~~ 14 | - ~~Add defer example to the end of `02.2`.~~ 15 | - Create a wav file parser as an example of format RE and parsing with Go. 16 | - Adapt the two blog posts about Go-Fuzz into tutorials here. 17 | - Write an lnk parser. 18 | 19 | ## Books 20 | Ranked by priority DESC: 21 | 22 | 1. [Black Hat Python][black-hat-python] 23 | 2. [Automate the Boring Stuff with Python][automate-boring-stuff] 24 | 3. [Gray Hat C#][gray-hat-csharp] 25 | 4. [Violent Python][violent-python] 26 | 5. [Gray Hat Python][gray-hat-python] 27 | 6. [Effective Python Penetration Testing][effective-python] 28 | 29 | 30 | 31 | 32 | [black-hat-python]: https://www.nostarch.com/blackhatpython 33 | [automate-boring-stuff]: https://automatetheboringstuff.com/ 34 | [violent-python]: http://shop.oreilly.com/product/9781597499576.do 35 | [gray-hat-csharp]: https://www.nostarch.com/grayhatcsharp 36 | [gray-hat-python]: https://www.nostarch.com/ghpython.htm 37 | [effective-python]: https://www.packtpub.com/networking-and-servers/effective-python-penetration-testing -------------------------------------------------------------------------------- /code/04/04.1/04.1-05-udp-dialudp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "net" 8 | ) 9 | 10 | var ( 11 | host, port string 12 | ) 13 | 14 | func init() { 15 | flag.StringVar(&port, "port", "80", "target port") 16 | flag.StringVar(&host, "host", "example.com", "target host") 17 | } 18 | 19 | // CreateUDPAddr converts host and port to *UDPAddr 20 | func CreateUDPAddr(target, port string) (*net.UDPAddr, error) { 21 | return net.ResolveUDPAddr("udp", net.JoinHostPort(host, port)) 22 | } 23 | 24 | func main() { 25 | 26 | // Converting host and port to host:port 27 | a, err := CreateUDPAddr(host, port) 28 | if err != nil { 29 | panic(err) 30 | } 31 | 32 | // Create a connection with DialUDP 33 | connUDP, err := net.DialUDP("udp", nil, a) 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | // Write the GET request to connection 39 | // Note we are closing the HTTP connection with the Connection: close header 40 | // Fprintf writes to an io.writer 41 | req := "UDP PAYLOAD" 42 | fmt.Fprintf(connUDP, req) 43 | 44 | // Reading the response 45 | 46 | // Create a scanner 47 | scanner := bufio.NewScanner(bufio.NewReader(connUDP)) 48 | 49 | // Read from the scanner and print 50 | // Scanner reads until an I/O error 51 | for scanner.Scan() { 52 | fmt.Printf("%s\n", scanner.Text()) 53 | } 54 | 55 | // Check if scanner has quit with an error 56 | if err := scanner.Err(); err != nil { 57 | fmt.Println("Scanner error", err) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /code/04/04.1/04.1-03-basic-tcp-dialtcp.go: -------------------------------------------------------------------------------- 1 | // Basic TCP client using TCPDial and TCP specific methods 2 | package main 3 | 4 | import ( 5 | "bufio" 6 | "flag" 7 | "fmt" 8 | "net" 9 | ) 10 | 11 | var ( 12 | host, port string 13 | ) 14 | 15 | func init() { 16 | flag.StringVar(&port, "port", "80", "target port") 17 | flag.StringVar(&host, "host", "example.com", "target host") 18 | } 19 | 20 | // CreateTCPAddr converts host and port to *TCPAddr 21 | func CreateTCPAddr(target, port string) (*net.TCPAddr, error) { 22 | return net.ResolveTCPAddr("tcp", net.JoinHostPort(host, port)) 23 | } 24 | 25 | func main() { 26 | 27 | // Converting host and port to TCP address 28 | a, err := CreateTCPAddr(host, port) 29 | if err != nil { 30 | panic(err) 31 | } 32 | 33 | // Passing nil for local address 34 | tcpConn, err := net.DialTCP("tcp", nil, a) 35 | if err != nil { 36 | panic(err) 37 | } 38 | 39 | // Write the GET request to connection 40 | // Note we are closing the HTTP connection with the Connection: close header 41 | // Fprintf writes to an io.writer 42 | req := "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n" 43 | fmt.Fprintf(tcpConn, req) 44 | 45 | // Reading the response 46 | 47 | // Create a scanner 48 | scanner := bufio.NewScanner(bufio.NewReader(tcpConn)) 49 | 50 | // Read from the scanner and print 51 | // Scanner reads until an I/O error 52 | for scanner.Scan() { 53 | fmt.Printf("%s\n", scanner.Text()) 54 | } 55 | 56 | // Check if scanner has quit with an error 57 | if err := scanner.Err(); err != nil { 58 | fmt.Println("Scanner error", err) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /code/04/04.1/04.1-01-basic-tcp1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "net" 8 | ) 9 | 10 | var ( 11 | host, port string 12 | ) 13 | 14 | func init() { 15 | flag.StringVar(&port, "port", "80", "target port") 16 | flag.StringVar(&host, "host", "example.com", "target host") 17 | } 18 | 19 | func main() { 20 | 21 | flag.Parse() 22 | 23 | // Converting host and port to host:port 24 | t := net.JoinHostPort(host, port) 25 | 26 | // Create a connection to server 27 | conn, err := net.Dial("tcp", t) 28 | if err != nil { 29 | panic(err) 30 | } 31 | 32 | // Write the GET request to connection 33 | // Note we are closing the HTTP connection with the Connection: close header 34 | // Fprintf writes to an io.writer 35 | req := "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n" 36 | fmt.Fprintf(conn, req) 37 | 38 | // Another way to do it to directly write bytes to conn with conn.Write 39 | // However we must first convert the string to bytes with []byte("string") 40 | // reqBytes := []byte(req) 41 | // conn.Write(reqBytes) 42 | 43 | // Reading the response 44 | 45 | // Create a new reader from connection 46 | connReader := bufio.NewReader(conn) 47 | 48 | // Create a scanner 49 | scanner := bufio.NewScanner(connReader) 50 | 51 | // Combined into one line 52 | // scanner := bufio.NewScanner(bufio.NewReader(conn)) 53 | 54 | // Read from the scanner and print 55 | // Scanner reads until an I/O error 56 | for scanner.Scan() { 57 | fmt.Printf("%s\n", scanner.Text()) 58 | } 59 | 60 | // Check if scanner has quit with an error 61 | if err := scanner.Err(); err != nil { 62 | fmt.Println("Scanner error", err) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /code/06/06.3/crashers/a59a2ad5701156b88c6a132e1340fe006f67280c.output: -------------------------------------------------------------------------------- 1 | panic: runtime error: makeslice: len out of range 2 | 3 | goroutine 1 [running]: 4 | github.com/xor-gate/goexif2/tiff.(*Tag).convertVals(0xc04205a280, 0xc042080480, 0xc04200e090) 5 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/tiff/tag.go:258 +0x88c 6 | github.com/xor-gate/goexif2/tiff.DecodeTag(0x30a0000, 0xc042080480, 0x5605c0, 0x613170, 0x514c20, 0xc04200e06c, 0x0) 7 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/tiff/tag.go:182 +0x623 8 | github.com/xor-gate/goexif2/tiff.DecodeDir(0x30a0000, 0xc042080480, 0x5605c0, 0x613170, 0xc042080480, 0x0, 0x0, 0x0) 9 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/tiff/tiff.go:150 +0x1c5 10 | github.com/xor-gate/goexif2/tiff.Decode(0x560240, 0xc042080480, 0x4, 0x4, 0xc042089df0) 11 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/tiff/tiff.go:99 +0x548 12 | github.com/xor-gate/goexif2/exif.Decode(0x560240, 0xc042080480, 0x5ae92fcc, 0x29751e98, 0x1e9999) 13 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/exif/exif.go:285 +0x330 14 | github.com/xor-gate/goexif2/exif.Fuzz(0x3900000, 0x16, 0x200000, 0xc042047f48) 15 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/exif/Fuzz.go:8 +0xba 16 | go-fuzz-dep.Main(0x550580) 17 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/goroot/src/go-fuzz-dep/main.go:49 +0xb4 18 | main.main() 19 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/exif/go.fuzz.main/main.go:10 +0x34 20 | exit status 2 -------------------------------------------------------------------------------- /content/06.1.md: -------------------------------------------------------------------------------- 1 | # Go-Fuzz Quickstart 2 | 3 | 1. Get Go-fuzz with `go get github.com/dvyukov/go-fuzz`. 4 | 2. Build and install `go-fuzz` and `go-fuzz-build`. 5 | - `cd src\github.com\dvyukov\go-fuzz\go-fuzz` 6 | - `go install` 7 | - `cd ..\go-fuzz-build` 8 | - `go install` 9 | 3. Get the target package and store it in `GOPATH`. I usually keep it under `src\github.com\author\project`. 10 | 4. Create a new file in the target package named `Fuzz.go`. 11 | 5. Create a function named `Fuzz` inside `Fuzz.go` with this signature `func Fuzz(data []byte) int`. 12 | 6. `Fuzz` should return `1` if input is good and `0` otherwise. 13 | 7. Create fuzzing directory, e.g. `go-fuzz-project-name`. 14 | 8. `go-fuzz-build github.com/author/project` (note forward slashes even on Windows). Copy the resulting file (`project-fuzz.zip`) to the fuzzing directory. 15 | 9. Make a directory called `corpus` and store samples there. 16 | 10. `go-fuzz -bin=project-fuzz.zip -workdir=.` to begin fuzzing. 17 | 18 | ## Examples 19 | 20 | - "Fuzzing the new unit testing" by Dmitry Vyukov (Go-Fuzz creator): https://go-talks.appspot.com/github.com/dvyukov/go-fuzz/slides/fuzzing.slide#1 21 | - "go-fuzz github.com/arolek/ase" by Damian Gryski: https://medium.com/@dgryski/go-fuzz-github-com-arolek-ase-3c74d5a3150c 22 | - "Going down the rabbit hole with go-fuzz" by Nemanja Mijailovic: https://mijailovic.net/2017/07/29/go-fuzz/ 23 | - DNS parser, meet Go fuzzer by Filippo Valsorda: https://blog.cloudflare.com/dns-parser-meet-go-fuzzer/ 24 | - "Automated Testing with Go-Fuzz" GothamGo 2015: https://www.youtube.com/watch?v=kOZbFSM7PuI 25 | - "Fuzzing Markdown parser written in Go" by Krzysztof Kowalczyk: https://blog.kowalczyk.info/article/n/fuzzing-markdown-parser-written-in-go.html 26 | 27 | 28 | #### Continue reading ⇒ [06.2 - Fuzzing iprange with Go-Fuzz](06.2.md) -------------------------------------------------------------------------------- /code/02/02.5/02.5-01-print-verbs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type myType struct { 6 | field1 int 7 | field2 string 8 | field3 float64 9 | } 10 | 11 | func main() { 12 | 13 | // int 14 | fmt.Println("int:") 15 | int1 := 123 16 | fmt.Printf("%v\n", int1) // 123 17 | fmt.Printf("%d\n", int1) // 123 18 | fmt.Printf("|%6d|\n", int1) // | 123| 19 | fmt.Printf("|%-6d|\n", int1) // |123 | 20 | fmt.Printf("%T\n", int1) // int 21 | fmt.Printf("%x\n", int1) // 7b 22 | fmt.Printf("%b\n", int1) // 1111011 23 | fmt.Printf("%e\n", int1) // %!e(int=123) 24 | fmt.Printf("%c\n", int1) // { - 0x7B = 123 25 | fmt.Println() 26 | 27 | // float 28 | fmt.Println("float:") 29 | float1 := 1234.56 30 | fmt.Printf("%f\n", float1) // 1234.560000 31 | fmt.Printf("|%3.2f|\n", float1) // |1234.56| 32 | fmt.Printf("|%-3.2f|\n", float1) // |1234.56| 33 | fmt.Printf("%e\n", float1) // 1.234560e+03 34 | fmt.Printf("%E\n", float1) // 1.234560E+03 35 | fmt.Println() 36 | 37 | // string 38 | fmt.Println("string:") 39 | string1 := "Petra" 40 | fmt.Printf("%s\n", string1) // Petra 41 | fmt.Printf("|%10s|\n", string1) // | Petra| 42 | fmt.Printf("|%-10s|\n", string1) // |Petra | 43 | fmt.Printf("%T\n", string1) // string 44 | fmt.Println() 45 | 46 | // boolean 47 | fmt.Println("boolean:") 48 | boolean1 := true 49 | fmt.Printf("%t\n", boolean1) // true 50 | fmt.Printf("%T\n", boolean1) // bool 51 | fmt.Println() 52 | 53 | // struct type 54 | fmt.Println("struct:") 55 | struct1 := myType{10, "Ender", -10.2} 56 | fmt.Printf("%v\n", struct1) // {10 Ender -10.2} 57 | fmt.Printf("%+v\n", struct1) // {field1:10 field2:Ender field3:-10.2} 58 | fmt.Printf("%#v\n", struct1) // main.myType{field1:10, field2:"Ender", field3:-10.2} 59 | fmt.Printf("%T\n", struct1) // main.myType 60 | } 61 | -------------------------------------------------------------------------------- /code/03/03.1/03.1-06-subcommand.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | var ( 10 | sub1 *flag.FlagSet 11 | sub2 *flag.FlagSet 12 | 13 | sub1flag *int 14 | sub2flag1 *string 15 | sub2flag2 int 16 | 17 | usage string 18 | ) 19 | 20 | func init() { 21 | // Declare subcommand sub1 22 | sub1 = flag.NewFlagSet("sub1", flag.ExitOnError) 23 | // int flag for sub1 24 | sub1flag = sub1.Int("sub1flag", 0, "subcommand1 flag") 25 | 26 | // Declare subcommand sub2 27 | sub2 = flag.NewFlagSet("sub2", flag.ContinueOnError) 28 | // string flag for sub2 29 | sub2flag1 = sub2.String("sub2flag1", "", "subcommand2 flag1") 30 | // int flag for sub2 31 | sub2.IntVar(&sub2flag2, "sub2flag2", 0, "subcommand2 flag2") 32 | // Create usage 33 | usage = "sub1 -sub1flag (int)\nsub2 -sub2flag1 (string) -sub2flag2 (int)" 34 | } 35 | 36 | func main() { 37 | // If subcommand is not provided, print error, usage and return 38 | if len(os.Args) < 2 { 39 | fmt.Println("Not enough parameters") 40 | fmt.Println(usage) 41 | return 42 | } 43 | 44 | // Check the sub command 45 | switch os.Args[1] { 46 | 47 | // Parse sub1 48 | case "sub1": 49 | sub1.Parse(os.Args[2:]) 50 | 51 | // Parse sub2 52 | case "sub2": 53 | sub2.Parse(os.Args[2:]) 54 | 55 | // If subcommand is -h or --help 56 | case "-h": 57 | fallthrough 58 | case "--help": 59 | fmt.Printf(usage) 60 | return 61 | default: 62 | fmt.Printf("Invalid subcommand %v", os.Args[1]) 63 | return 64 | } 65 | 66 | // If sub1 was provided and parse, print the flags 67 | if sub1.Parsed() { 68 | fmt.Printf("subcommand1 with flag %v\n", *sub1flag) 69 | return 70 | } 71 | 72 | // If sub2 was provided and parse, print the flags 73 | if sub2.Parsed() { 74 | fmt.Printf("subcommand2 with flags %v, %v\n", *sub2flag1, sub2flag2) 75 | return 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /code/06/06.3/crashers/3f5b7d448a0791f5739fa0a2371bb2492b64f835.output: -------------------------------------------------------------------------------- 1 | panic: runtime error: makeslice: len out of range 2 | 3 | goroutine 1 [running]: 4 | github.com/xor-gate/goexif2/tiff.(*Tag).convertVals(0xc042056820, 0xc042074450, 0xc04200e2e8) 5 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/tiff/tag.go:258 +0x88c 6 | github.com/xor-gate/goexif2/tiff.DecodeTag(0x2964060, 0xc042074450, 0x5605c0, 0x613170, 0x514c20, 0xc04200e2c4, 0x0) 7 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/tiff/tag.go:182 +0x623 8 | github.com/xor-gate/goexif2/tiff.DecodeDir(0x2964060, 0xc042074450, 0x5605c0, 0x613170, 0xc042074450, 0x0, 0xc042074540, 0x0) 9 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/tiff/tiff.go:150 +0x1c5 10 | github.com/xor-gate/goexif2/exif.loadSubDir(0xc0420744e0, 0x547f15, 0xe, 0xc042074390, 0xc042074510, 0xc04207dd68) 11 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/exif/exif.go:220 +0x380 12 | github.com/xor-gate/goexif2/exif.(*parser).Parse(0x613170, 0xc0420744e0, 0xc042074480, 0x0) 13 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/exif/exif.go:190 +0x174 14 | github.com/xor-gate/goexif2/exif.Decode(0x560240, 0xc042074450, 0x5ae9364a, 0x11afa814, 0x16f365) 15 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/exif/exif.go:331 +0x503 16 | github.com/xor-gate/goexif2/exif.Fuzz(0x3990000, 0xba, 0x200000, 0xc042047f48) 17 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/exif/Fuzz.go:8 +0xba 18 | go-fuzz-dep.Main(0x550580) 19 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/goroot/src/go-fuzz-dep/main.go:49 +0xb4 20 | main.main() 21 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/exif/go.fuzz.main/main.go:10 +0x34 22 | exit status 2 -------------------------------------------------------------------------------- /code/04/04.4/04.4-04-sshclient-run-combinedoutput.go: -------------------------------------------------------------------------------- 1 | // SSH login with user/pass. Run a command with CombinedOutput and print output. 2 | 3 | package main 4 | 5 | import ( 6 | "flag" 7 | "fmt" 8 | "net" 9 | "os" 10 | 11 | // Importing crypto/ssh 12 | "golang.org/x/crypto/ssh" 13 | ) 14 | 15 | var ( 16 | username, password, serverIP, serverPort, command string 17 | ) 18 | 19 | // Read flags 20 | func init() { 21 | flag.StringVar(&serverPort, "port", "22", "SSH server port") 22 | flag.StringVar(&serverIP, "ip", "127.0.0.1", "SSH server IP") 23 | flag.StringVar(&username, "user", "", "username") 24 | flag.StringVar(&password, "pass", "", "password") 25 | flag.StringVar(&command, "cmd", "", "command to run") 26 | } 27 | 28 | func main() { 29 | // Parse flags 30 | flag.Parse() 31 | 32 | // Check if username has been submitted - password can be empty 33 | if username == "" { 34 | fmt.Println("Must supply username") 35 | os.Exit(2) 36 | } 37 | 38 | // Create SSH config 39 | config := &ssh.ClientConfig{ 40 | // Username 41 | User: username, 42 | // Each config must have one AuthMethod. In this case we use password 43 | Auth: []ssh.AuthMethod{ 44 | ssh.Password(password), 45 | }, 46 | // This callback function validates the server. 47 | // Danger! We are ignoring host info 48 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), 49 | } 50 | 51 | // Server address 52 | t := net.JoinHostPort(serverIP, serverPort) 53 | 54 | // Connect to the SSH server 55 | sshConn, err := ssh.Dial("tcp", t, config) 56 | if err != nil { 57 | fmt.Printf("Failed to connect to %v\n", t) 58 | fmt.Println(err) 59 | os.Exit(2) 60 | } 61 | 62 | // Create new SSH session 63 | session, err := sshConn.NewSession() 64 | if err != nil { 65 | fmt.Printf("Cannot create SSH session to %v\n", t) 66 | fmt.Println(err) 67 | os.Exit(2) 68 | } 69 | 70 | // Close the session when main returns 71 | defer session.Close() 72 | 73 | // Run a command with CombinedOutput 74 | o, err := session.CombinedOutput(command) 75 | if err != nil { 76 | fmt.Println("Error running command", err) 77 | } 78 | 79 | fmt.Printf("Output:\n%s", o) 80 | } 81 | -------------------------------------------------------------------------------- /code/04/04.4/04.4-05-sshclient-run-run.go: -------------------------------------------------------------------------------- 1 | // SSH login with user/pass. Run a command with Run and print output. 2 | 3 | package main 4 | 5 | import ( 6 | "bytes" 7 | "flag" 8 | "fmt" 9 | "net" 10 | "os" 11 | 12 | // Importing crypto/ssh 13 | "golang.org/x/crypto/ssh" 14 | ) 15 | 16 | var ( 17 | username, password, serverIP, serverPort, command string 18 | ) 19 | 20 | // Read flags 21 | func init() { 22 | flag.StringVar(&serverPort, "port", "22", "SSH server port") 23 | flag.StringVar(&serverIP, "ip", "127.0.0.1", "SSH server IP") 24 | flag.StringVar(&username, "user", "", "username") 25 | flag.StringVar(&password, "pass", "", "password") 26 | flag.StringVar(&command, "cmd", "", "command to run") 27 | } 28 | 29 | func main() { 30 | // Parse flags 31 | flag.Parse() 32 | 33 | // Check if username has been submitted - password can be empty 34 | if username == "" { 35 | fmt.Println("Must supply username") 36 | os.Exit(2) 37 | } 38 | 39 | // Create SSH config 40 | config := &ssh.ClientConfig{ 41 | // Username 42 | User: username, 43 | // Each config must have one AuthMethod. In this case we use password 44 | Auth: []ssh.AuthMethod{ 45 | ssh.Password(password), 46 | }, 47 | // This callback function validates the server. 48 | // Danger! We are ignoring host info 49 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), 50 | } 51 | 52 | // Server address 53 | t := net.JoinHostPort(serverIP, serverPort) 54 | 55 | // Connect to the SSH server 56 | sshConn, err := ssh.Dial("tcp", t, config) 57 | if err != nil { 58 | fmt.Printf("Failed to connect to %v\n", t) 59 | fmt.Println(err) 60 | os.Exit(2) 61 | } 62 | 63 | // Create new SSH session 64 | session, err := sshConn.NewSession() 65 | if err != nil { 66 | fmt.Printf("Cannot create SSH session to %v\n", t) 67 | fmt.Println(err) 68 | os.Exit(2) 69 | } 70 | 71 | // Close the session when main returns 72 | defer session.Close() 73 | 74 | // Create buffers for stdout and stderr 75 | var o, e bytes.Buffer 76 | 77 | session.Stdout = &o 78 | session.Stderr = &e 79 | 80 | // Run a command with Run and read stdout and stderr 81 | if err := session.Run(command); err != nil { 82 | fmt.Println("Error running command", err) 83 | } 84 | 85 | // Convert buffer to string 86 | fmt.Printf("stdout:\n%s\nstderr:\n%s", o.String(), e.String()) 87 | } 88 | -------------------------------------------------------------------------------- /code/03/03.1/03.1-04-flag4.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "flag" 6 | "fmt" 7 | "strings" 8 | "sync" 9 | ) 10 | 11 | // 1. Create a custom type from a string slice 12 | type strList []string 13 | 14 | // 2.1 implement String() 15 | func (str *strList) String() string { 16 | return fmt.Sprintf("%v", *str) 17 | } 18 | 19 | // 2.2 implement Set(*strList) 20 | func (str *strList) Set(s string) error { 21 | // If input was empty, return an error 22 | if s == "" { 23 | return errors.New("nil input") 24 | } 25 | // Split input by "," 26 | *str = strings.Split(s, ",") 27 | // Do not return an error 28 | return nil 29 | } 30 | 31 | // Declare flag variables 32 | var ( 33 | ip strList 34 | port strList 35 | verbose bool 36 | ) 37 | 38 | var wg sync.WaitGroup 39 | 40 | func init() { 41 | // Declare flags 42 | // Remember, flag methods return pointers 43 | flag.Var(&ip, "ip", "target IP") 44 | 45 | flag.Var(&port, "port", "Port") 46 | 47 | flag.BoolVar(&verbose, "verbose", false, "verbosity") 48 | } 49 | 50 | // permutations creates all permutations of ip:port and sends them to a channel. 51 | // This is preferable to returing a []string because we can spawn it in a 52 | // goroutine and process items in the channel while it's running. Also save 53 | // memory by not creating a large []string that contains all permutations. 54 | func permutations(ips strList, ports strList, c chan<- string) { 55 | 56 | // Close channel when done 57 | defer close(c) 58 | for _, i := range ips { 59 | for _, p := range ports { 60 | c <- fmt.Sprintf("%s:%s", i, p) 61 | } 62 | } 63 | } 64 | 65 | // hack spawns a goroutine that "hacks" each target. 66 | // Each goroutine prints a status and display progres if verbose is true 67 | func hack(target string, verbose bool) { 68 | 69 | // Reduce waitgroups counter by one when hack finishes 70 | defer wg.Done() 71 | // Hack the planet! 72 | fmt.Printf("Hacking %s!\n", target) 73 | 74 | // Display progress if verbose flag is set 75 | if verbose { 76 | fmt.Printf("Pew pew!\n") 77 | } 78 | } 79 | 80 | func main() { 81 | 82 | // Parse flags 83 | flag.Parse() 84 | 85 | // Create channel for writing and reading IP:ports 86 | c := make(chan string) 87 | 88 | // Perform the permutation in a goroutine and send the results to a channel 89 | // This way we can start "hacking" during permutation generation and 90 | // not create a huge list of strings in memory 91 | go permutations(ip, port, c) 92 | 93 | for { 94 | select { 95 | // Read a string from channel 96 | case t, ok := <-c: 97 | // If channel is closed 98 | if !ok { 99 | // Wait until all goroutines are done 100 | wg.Wait() 101 | // Print hacking is finished and return 102 | fmt.Println("Hacking finished!") 103 | return 104 | } 105 | // Otherwise increase wg's counter by one 106 | wg.Add(1) 107 | // Spawn a goroutine to hack IP:port read from channel 108 | go hack(t, verbose) 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /code/04/04.2/04.2-02-tcpserver2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io" 7 | "net" 8 | ) 9 | 10 | var ( 11 | bindHost, bindPort string 12 | ) 13 | 14 | func init() { 15 | flag.StringVar(&bindPort, "port", "12345", "bind port") 16 | flag.StringVar(&bindHost, "host", "127.0.0.1", "bind host") 17 | } 18 | 19 | // readSocket reads data from socket if available and passes it to channel 20 | // Note the directed write-only channel designation 21 | func readSocket(conn net.Conn, c chan<- []byte) { 22 | 23 | // Create a buffer to hold data 24 | buf := make([]byte, 2048) 25 | // Store remote IP:port for logging 26 | rAddr := conn.RemoteAddr().String() 27 | 28 | for { 29 | // Read from connection 30 | n, err := conn.Read(buf) 31 | // If connection is closed from the other side 32 | if err == io.EOF { 33 | // Close the connction and return 34 | fmt.Println("Connection closed from", rAddr) 35 | return 36 | } 37 | // For other errors, print the error and return 38 | if err != nil { 39 | fmt.Println("Error reading from socket", err) 40 | return 41 | } 42 | // Print data read from socket 43 | // Note we are only printing and sending the first n bytes. 44 | // n is the number of bytes read from the connection 45 | fmt.Printf("Received from %v: %s\n", rAddr, buf[:n]) 46 | // Send data to channel 47 | c <- buf[:n] 48 | } 49 | } 50 | 51 | // writeSocket reads data from channel and writes it to socket 52 | func writeSocket(conn net.Conn, c <-chan []byte) { 53 | 54 | // Create a buffer to hold data 55 | buf := make([]byte, 2048) 56 | // Store remote IP:port for logging 57 | rAddr := conn.RemoteAddr().String() 58 | 59 | for { 60 | // Read from channel and copy to buffer 61 | buf = <-c 62 | // Write buffer 63 | n, err := conn.Write(buf) 64 | // If connection is closed from the other side 65 | if err == io.EOF { 66 | // Close the connction and return 67 | fmt.Println("Connection closed from", rAddr) 68 | return 69 | } 70 | // For other errors, print the error and return 71 | if err != nil { 72 | fmt.Println("Error writing to socket", err) 73 | return 74 | } 75 | // Log data sent 76 | fmt.Printf("Sent to %v: %s\n", rAddr, buf[:n]) 77 | } 78 | } 79 | 80 | // handleConnectionLog echoes everything back and logs messages received 81 | func handleConnectionLog(conn net.Conn) { 82 | 83 | // Create buffered channel to pass data around 84 | c := make(chan []byte, 2048) 85 | 86 | // Spawn up two goroutines, one for reading and another for writing 87 | 88 | go readSocket(conn, c) 89 | go writeSocket(conn, c) 90 | 91 | } 92 | 93 | func main() { 94 | 95 | flag.Parse() 96 | 97 | // Converting host and port to host:port 98 | t := net.JoinHostPort(bindHost, bindPort) 99 | 100 | // Listen for connections on BindIP:BindPort 101 | ln, err := net.Listen("tcp", t) 102 | if err != nil { 103 | // If we cannot bind, print the error and quit 104 | panic(err) 105 | } 106 | 107 | // Wait for connections 108 | for { 109 | // Accept a connection 110 | conn, err := ln.Accept() 111 | if err != nil { 112 | // If there was an error print it and go back to listening 113 | fmt.Println(err) 114 | continue 115 | } 116 | 117 | fmt.Printf("Received connection from %v\n", conn.RemoteAddr().String()) 118 | 119 | go handleConnectionLog(conn) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /content/02.7.md: -------------------------------------------------------------------------------- 1 | # Error handling 2 | 3 | 4 | 5 | - [Error handling](#error-handling) 6 | - [Errors](#errors) 7 | - [Avoiding if err != nil fatigue](#avoiding-if-err--nil-fatigue) 8 | - [Solution to the Errors exercise](#solution-to-the-errors-exercise) 9 | 10 | 11 | 12 | 13 | ## Error handling 14 | Go does not have try/catch or try/except. Instead almost every function returns (or should return) an error value. It's good practice to return an error value in every function and also check it after reading values from functions/channels/etc. As a result it's very common to see `if err != nil` code blocks. 15 | 16 | Go's error handling is very controversial. Some call it genius and others not so much. For more information read the [Error handling and Go][error-handling-and-go] blog. 17 | 18 | 19 | ## Errors 20 | `error` type is similar to `Stringer()`. 21 | 22 | ``` go 23 | type error interface { 24 | Error() string 25 | } 26 | ``` 27 | 28 | Create a method for the struct type named `Error()` to return error codes/messages. 29 | 30 | ``` go 31 | func (e MyType) Error() string { 32 | return fmt.Sprintf("error message") 33 | } 34 | ``` 35 | 36 | According to Go docs, errors strings should not be capitalized. Most built-in and package methods return an error value if an error occurs, otherwise they will return `nil` for error which means no error. 37 | 38 | 39 | ### Avoiding if err != nil fatigue 40 | Checking for errors after every function call will result in a lot of `if err != nil` blocks. One good way is to create a function to check the error and perform actions based on it. For example: 41 | 42 | ``` go 43 | func checkError(err) { 44 | if err != nil { 45 | // Do something 46 | } 47 | } 48 | ``` 49 | 50 | 51 | ### Solution to the Errors exercise 52 | The errors exercise is part of [tour of go][errors-exercise]. 53 | 54 | ``` go 55 | // 02.7-01-errors1.go 56 | package main 57 | 58 | import ( 59 | "fmt" 60 | "math" 61 | ) 62 | 63 | type ErrNegativeSqrt float64 64 | 65 | func (e ErrNegativeSqrt) Error() string { 66 | return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e)) 67 | } 68 | 69 | func Sqrt(x float64) (float64, error) { 70 | 71 | if x < 0 { 72 | return 0, ErrNegativeSqrt(x) 73 | } 74 | 75 | // Don't need else here - why? 76 | return math.Sqrt(x), nil 77 | } 78 | 79 | func main() { 80 | fmt.Println(Sqrt(2)) 81 | fmt.Println(Sqrt(-2)) 82 | } 83 | ``` 84 | 85 | Instead of creating an `Error()` method, we could create a new error type and return that using `fmt.Errorf`: 86 | 87 | ``` go 88 | // 02.7-02-errors2.go 89 | package main 90 | 91 | import ( 92 | "fmt" 93 | "math" 94 | ) 95 | 96 | func Sqrt(x float64) (float64, error) { 97 | 98 | if x < 0 { 99 | return 0, fmt.Errorf("cannot Sqrt negative number: %v", float64(x)) 100 | } 101 | 102 | return math.Sqrt(x), nil 103 | } 104 | 105 | func main() { 106 | fmt.Println(Sqrt(2)) 107 | fmt.Println(Sqrt(-2)) 108 | } 109 | ``` 110 | 111 | #### Continue reading ⇒ [03 - Useful Go packages](03.0.md) 112 | 113 | 114 | 115 | [errors-exercise]: https://tour.golang.org/methods/20 116 | [error-handling-and-go]: https://blog.golang.org/error-handling-and-go "Error handling and Go - Golang blog" 117 | -------------------------------------------------------------------------------- /code/04/04.4/04.4-01-sshclient-login-password.go: -------------------------------------------------------------------------------- 1 | // Interactive SSH login with user/pass. 2 | 3 | package main 4 | 5 | import ( 6 | "flag" 7 | "fmt" 8 | "io" 9 | "net" 10 | "os" 11 | 12 | // Importing crypto/ssh 13 | "golang.org/x/crypto/ssh" 14 | ) 15 | 16 | var ( 17 | username, password, serverIP, serverPort string 18 | ) 19 | 20 | // Read flags 21 | func init() { 22 | flag.StringVar(&serverPort, "port", "22", "SSH server port") 23 | flag.StringVar(&serverIP, "ip", "127.0.0.1", "SSH server IP") 24 | flag.StringVar(&username, "user", "", "username") 25 | flag.StringVar(&password, "pass", "", "password") 26 | } 27 | 28 | func main() { 29 | // Parse flags 30 | flag.Parse() 31 | 32 | // Check if username has been submitted - password can be empty 33 | if username == "" { 34 | fmt.Println("Must supply username") 35 | os.Exit(2) 36 | } 37 | 38 | // Create SSH config 39 | config := &ssh.ClientConfig{ 40 | // Username 41 | User: username, 42 | // Each config must have one AuthMethod. In this case we use password 43 | Auth: []ssh.AuthMethod{ 44 | ssh.Password(password), 45 | }, 46 | // This callback function validates the server. 47 | // Danger! We are ignoring host info 48 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), 49 | } 50 | 51 | // Server address 52 | t := net.JoinHostPort(serverIP, serverPort) 53 | 54 | // Connect to the SSH server 55 | sshConn, err := ssh.Dial("tcp", t, config) 56 | if err != nil { 57 | fmt.Printf("Failed to connect to %v\n", t) 58 | fmt.Println(err) 59 | os.Exit(2) 60 | } 61 | 62 | // Create new SSH session 63 | session, err := sshConn.NewSession() 64 | if err != nil { 65 | fmt.Printf("Cannot create SSH session to %v\n", t) 66 | fmt.Println(err) 67 | os.Exit(2) 68 | } 69 | 70 | // Close the session when main returns 71 | defer session.Close() 72 | 73 | // For an interactive session we must redirect IO 74 | session.Stdout = os.Stdout 75 | session.Stderr = os.Stderr 76 | input, err := session.StdinPipe() 77 | if err != nil { 78 | fmt.Println("Error redirecting session input", err) 79 | os.Exit(2) 80 | } 81 | 82 | // Setup terminal mode when requesting pty. You can see all terminal modes at 83 | // https://github.com/golang/crypto/blob/master/ssh/session.go#L56 or read 84 | // the RFC for explanation https://tools.ietf.org/html/rfc4254#section-8 85 | termModes := ssh.TerminalModes{ 86 | ssh.ECHO: 0, // Disable echo 87 | } 88 | 89 | // Request pty 90 | // https://tools.ietf.org/html/rfc4254#section-6.2 91 | // First variable is term environment variable value which specifies terminal. 92 | // term doesn't really matter here, we will use "vt220". 93 | // Next are height and width: (40,80) characters and finall termModes. 94 | err = session.RequestPty("vt220", 40, 80, termModes) 95 | if err != nil { 96 | fmt.Println("RequestPty failed", err) 97 | os.Exit(2) 98 | } 99 | 100 | // Also 101 | // if err = session.RequestPty("vt220", 40, 80, termModes); err != nil { 102 | // fmt.Println("RequestPty failed", err) 103 | // os.Exit(2) 104 | // } 105 | 106 | // Now we can start a remote shell 107 | err = session.Shell() 108 | if err != nil { 109 | fmt.Println("shell failed", err) 110 | os.Exit(2) 111 | } 112 | 113 | // Same as above, a different way to check for errors 114 | // if err = session.Shell(); err != nil { 115 | // fmt.Println("shell failed", err) 116 | // os.Exit(2) 117 | // } 118 | 119 | // Endless loop to capture commands 120 | // Note: After exit, we need to ctrl+c to end the application. 121 | for { 122 | io.Copy(input, os.Stdin) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /code/06/06.3/crashers/171e8e5ca3e3d609322376915dcfa3dd56938845.output: -------------------------------------------------------------------------------- 1 | runtime: out of memory: cannot allocate 25769803776-byte block (25832882176 in use) 2 | fatal error: out of memory 3 | 4 | runtime stack: 5 | runtime.throw(0x547da6, 0xd) 6 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/goroot/src/runtime/panic.go:616 +0x88 7 | runtime.largeAlloc(0x600000000, 0x440001, 0x5f8330) 8 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/goroot/src/runtime/malloc.go:828 +0x117 9 | runtime.mallocgc.func1() 10 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/goroot/src/runtime/malloc.go:721 +0x4d 11 | runtime.systemstack(0x0) 12 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/goroot/src/runtime/asm_amd64.s:409 +0x7e 13 | runtime.mstart() 14 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/goroot/src/runtime/proc.go:1175 15 | 16 | goroutine 1 [running]: 17 | runtime.systemstack_switch() 18 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/goroot/src/runtime/asm_amd64.s:363 fp=0xc042451790 sp=0xc042451788 pc=0x44cfe0 19 | runtime.mallocgc(0x600000000, 0x518c80, 0x1, 0x28) 20 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/goroot/src/runtime/malloc.go:720 +0x8e1 fp=0xc042451830 sp=0xc042451790 pc=0x40f011 21 | runtime.makeslice(0x518c80, 0x40000000, 0x40000000, 0x460603, 0xc645b92c60, 0xc645bec1f8) 22 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/goroot/src/runtime/slice.go:61 +0x7e fp=0xc042451860 sp=0xc042451830 pc=0x439b3e 23 | github.com/xor-gate/goexif2/tiff.(*Tag).convertVals(0xc645c12000, 0xc645b92c90, 0xc645bec1f8) 24 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/tiff/tag.go:258 +0x88c fp=0xc042451988 sp=0xc042451860 pc=0x4fd63c 25 | github.com/xor-gate/goexif2/tiff.DecodeTag(0x30a0048, 0xc645b92c90, 0x5605c0, 0x613170, 0x3, 0xc3458b2600, 0x2) 26 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/tiff/tag.go:182 +0x623 fp=0xc042451b50 sp=0xc042451988 pc=0x4fc3f3 27 | github.com/xor-gate/goexif2/tiff.DecodeDir(0x30a0048, 0xc645b92c90, 0x5605c0, 0x613170, 0xc645b92c90, 0x0, 0x0, 0x0) 28 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/tiff/tiff.go:150 +0x1c5 fp=0xc042451c18 sp=0xc042451b50 pc=0x5010e5 29 | github.com/xor-gate/goexif2/tiff.Decode(0x560280, 0xc645b92c90, 0xc645b92c60, 0x560280, 0xc645b92c90) 30 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/tiff/tiff.go:99 +0x548 fp=0xc042451d78 sp=0xc042451c18 pc=0x500518 31 | github.com/xor-gate/goexif2/exif.Decode(0x560240, 0xc645b92c60, 0x5aea8ca7, 0x2cafd418, 0x5dfc57635) 32 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/exif/exif.go:316 +0xa19 fp=0xc042451ec8 sp=0xc042451d78 pc=0x504599 33 | github.com/xor-gate/goexif2/exif.Fuzz(0x39b0000, 0x16d, 0x200000, 0x3) 34 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/exif/Fuzz.go:8 +0xba fp=0xc042451f00 sp=0xc042451ec8 pc=0x5026ea 35 | go-fuzz-dep.Main(0x550580) 36 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/goroot/src/go-fuzz-dep/main.go:49 +0xb4 fp=0xc042451f70 sp=0xc042451f00 pc=0x46f9d4 37 | main.main() 38 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/gopath/src/github.com/xor-gate/goexif2/exif/go.fuzz.main/main.go:10 +0x34 fp=0xc042451f88 sp=0xc042451f70 pc=0x506984 39 | runtime.main() 40 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/goroot/src/runtime/proc.go:198 +0x20e fp=0xc042451fe0 sp=0xc042451f88 pc=0x42a9de 41 | runtime.goexit() 42 | C:/Users/Parsia/AppData/Local/Temp/go-fuzz-build214414686/goroot/src/runtime/asm_amd64.s:2361 +0x1 fp=0xc042451fe8 sp=0xc042451fe0 pc=0x44f871 43 | exit status 2 -------------------------------------------------------------------------------- /code/04/04.2/04.2-03-tcpserver3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io" 7 | "net" 8 | ) 9 | 10 | var ( 11 | bindHost, bindPort string 12 | ) 13 | 14 | func init() { 15 | flag.StringVar(&bindPort, "port", "12345", "bind port") 16 | flag.StringVar(&bindHost, "host", "127.0.0.1", "bind host") 17 | } 18 | 19 | // CreateTCPAddr converts host and port to *TCPAddr 20 | func CreateTCPAddr(host, port string) (*net.TCPAddr, error) { 21 | return net.ResolveTCPAddr("tcp", net.JoinHostPort(host, port)) 22 | } 23 | 24 | // readSocket reads data from socket if available and passes it to channel 25 | // Note the directed write-only channel designation 26 | func readSocket(conn net.Conn, c chan<- []byte) { 27 | 28 | // Create a buffer to hold data 29 | buf := make([]byte, 2048) 30 | // Store remote IP:port for logging 31 | rAddr := conn.RemoteAddr().String() 32 | 33 | for { 34 | // Read from connection 35 | n, err := conn.Read(buf) 36 | // If connection is closed from the other side 37 | if err == io.EOF { 38 | // Close the connction and return 39 | fmt.Println("Connection closed from", rAddr) 40 | return 41 | } 42 | // For other errors, print the error and return 43 | if err != nil { 44 | fmt.Println("Error reading from socket", err) 45 | return 46 | } 47 | // Print data read from socket 48 | // Note we are only printing and sending the first n bytes. 49 | // n is the number of bytes read from the connection 50 | fmt.Printf("Received from %v: %s\n", rAddr, buf[:n]) 51 | // Send data to channel 52 | c <- buf[:n] 53 | } 54 | } 55 | 56 | // writeSocket reads data from channel and writes it to socket 57 | func writeSocket(conn net.Conn, c <-chan []byte) { 58 | 59 | // Create a buffer to hold data 60 | buf := make([]byte, 2048) 61 | // Store remote IP:port for logging 62 | rAddr := conn.RemoteAddr().String() 63 | 64 | for { 65 | // Read from channel and copy to buffer 66 | buf = <-c 67 | // Write buffer 68 | n, err := conn.Write(buf) 69 | // If connection is closed from the other side 70 | if err == io.EOF { 71 | // Close the connction and return 72 | fmt.Println("Connection closed from", rAddr) 73 | return 74 | } 75 | // For other errors, print the error and return 76 | if err != nil { 77 | fmt.Println("Error writing to socket", err) 78 | return 79 | } 80 | // Log data sent 81 | fmt.Printf("Sent to %v: %s\n", rAddr, buf[:n]) 82 | } 83 | } 84 | 85 | // handleConnectionLog echoes everything back and logs messages received 86 | func handleConnectionLog(conn net.Conn) { 87 | 88 | // Create buffered channel to pass data around 89 | c := make(chan []byte, 2048) 90 | 91 | // Spawn up two goroutines, one for reading and another for writing 92 | 93 | go readSocket(conn, c) 94 | go writeSocket(conn, c) 95 | 96 | } 97 | 98 | func main() { 99 | 100 | flag.Parse() 101 | 102 | // Converting host and port to TCP address 103 | t, err := CreateTCPAddr(bindHost, bindPort) 104 | if err != nil { 105 | panic(err) 106 | } 107 | 108 | // Listen for connections on bindHost:bindPort 109 | ln, err := net.ListenTCP("tcp", t) 110 | if err != nil { 111 | // If we cannot bind, print the error and quit 112 | panic(err) 113 | } 114 | 115 | // Wait for connections 116 | for { 117 | // Accept a connection 118 | conn, err := ln.AcceptTCP() 119 | if err != nil { 120 | // If there was an error print it and go back to listening 121 | fmt.Println(err) 122 | continue 123 | } 124 | 125 | fmt.Printf("Received connection from %v\n", conn.RemoteAddr().String()) 126 | 127 | go handleConnectionLog(conn) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /code/04/04.4/04.4-02-sshclient-check-host.go: -------------------------------------------------------------------------------- 1 | // SSH login with custom host verifier (only outputs host information). 2 | 3 | package main 4 | 5 | import ( 6 | "flag" 7 | "fmt" 8 | "io" 9 | "net" 10 | "os" 11 | 12 | // Importing crypto/ssh 13 | "golang.org/x/crypto/ssh" 14 | ) 15 | 16 | var ( 17 | username, password, serverIP, serverPort string 18 | ) 19 | 20 | // Read flags 21 | func init() { 22 | flag.StringVar(&serverPort, "port", "22", "SSH server port") 23 | flag.StringVar(&serverIP, "ip", "127.0.0.1", "SSH server IP") 24 | flag.StringVar(&username, "user", "", "username") 25 | flag.StringVar(&password, "pass", "", "password") 26 | } 27 | 28 | // hostChecker returns a function to be used as callback for HostKeyCallback. 29 | func hostChecker() ssh.HostKeyCallback { 30 | return printServerKey 31 | } 32 | 33 | // printServerKey prints server's info instead of checking it. 34 | // It's of type HostKeyCallback 35 | func printServerKey(hostname string, remote net.Addr, key ssh.PublicKey) error { 36 | // Just print everything 37 | fmt.Printf("Hostname: %v\nRemote address: %v\nServer key: %+v\n", 38 | hostname, remote, key) 39 | // Return nil so connection can continue without checking the server 40 | return nil 41 | } 42 | 43 | func main() { 44 | // Parse flags 45 | flag.Parse() 46 | 47 | // Check if username has been submitted - password can be empty 48 | if username == "" { 49 | fmt.Println("Must supply username") 50 | os.Exit(2) 51 | } 52 | 53 | // Create SSH config 54 | config := &ssh.ClientConfig{ 55 | // Username 56 | User: username, 57 | // Each config must have one AuthMethod. In this case we use password 58 | Auth: []ssh.AuthMethod{ 59 | ssh.Password(password), 60 | }, 61 | // Check the host 62 | HostKeyCallback: hostChecker(), 63 | } 64 | 65 | // Server address 66 | t := net.JoinHostPort(serverIP, serverPort) 67 | 68 | // Connect to the SSH server 69 | sshConn, err := ssh.Dial("tcp", t, config) 70 | if err != nil { 71 | fmt.Printf("Failed to connect to %v\n", t) 72 | fmt.Println(err) 73 | os.Exit(2) 74 | } 75 | 76 | // Create new SSH session 77 | session, err := sshConn.NewSession() 78 | if err != nil { 79 | fmt.Printf("Cannot create SSH session to %v\n", t) 80 | fmt.Println(err) 81 | os.Exit(2) 82 | } 83 | 84 | // Close the session 85 | defer session.Close() 86 | 87 | // For an interactive session we must redirect IO 88 | session.Stdout = os.Stdout 89 | session.Stderr = os.Stderr 90 | input, err := session.StdinPipe() 91 | if err != nil { 92 | fmt.Println("Error redirecting session input", err) 93 | os.Exit(2) 94 | } 95 | 96 | // Setup terminal mode when requesting pty. You can see all terminal modes at 97 | // https://github.com/golang/crypto/blob/master/ssh/session.go#L56 or read 98 | // the RFC for explanation https://tools.ietf.org/html/rfc4254#section-8 99 | termModes := ssh.TerminalModes{ 100 | ssh.ECHO: 0, // Disable echo 101 | } 102 | 103 | // Request pty 104 | // https://tools.ietf.org/html/rfc4254#section-6.2 105 | // First variable is term environment variable value which specifies terminal. 106 | // term doesn't really matter here, we will use "vt220". 107 | // Next are height and width: (40,80) characters and finall termModes. 108 | err = session.RequestPty("vt220", 40, 80, termModes) 109 | if err != nil { 110 | fmt.Println("RequestPty failed", err) 111 | os.Exit(2) 112 | } 113 | 114 | // Now we can start a remote shell 115 | err = session.Shell() 116 | if err != nil { 117 | fmt.Println("shell failed", err) 118 | os.Exit(2) 119 | } 120 | 121 | // Endless loop to capture commands 122 | for { 123 | io.Copy(input, os.Stdin) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /code/04/04.3/04.3-01-tcp-proxy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io" 7 | "net" 8 | ) 9 | 10 | var ( 11 | bindIP, bindPort, destIP, destPort string 12 | ) 13 | 14 | func init() { 15 | flag.StringVar(&bindPort, "bindPort", "12345", "bind port") 16 | flag.StringVar(&bindIP, "bindIP", "127.0.0.1", "bind IP") 17 | flag.StringVar(&destPort, "destPort", "12345", "bind port") 18 | flag.StringVar(&destIP, "destIP", "127.0.0.1", "bind IP") 19 | } 20 | 21 | // readSocket reads data from socket if available and passes it to channel 22 | func readSocket(conn net.Conn, c chan<- []byte) { 23 | 24 | // Create a buffer to hold data 25 | buf := make([]byte, 2048) 26 | // Store remote IP:port for logging 27 | rAddr := conn.RemoteAddr().String() 28 | 29 | for { 30 | // Read from connection 31 | n, err := conn.Read(buf) 32 | // If connection is closed from the other side 33 | if err == io.EOF { 34 | // Close the connction and return 35 | fmt.Println("Connection closed from", rAddr) 36 | return 37 | } 38 | // For other errors, print the error and return 39 | if err != nil { 40 | fmt.Println("Error reading from socket", err) 41 | return 42 | } 43 | // Print data read from socket 44 | // Note we are only printing and sending the first n bytes. 45 | // n is the number of bytes read from the connection 46 | fmt.Printf("Received from %v: %s\n", rAddr, buf[:n]) 47 | // Send data to channel 48 | c <- buf[:n] 49 | } 50 | } 51 | 52 | // writeSocket reads data from channel and writes it to socket 53 | func writeSocket(conn net.Conn, c <-chan []byte) { 54 | 55 | // Create a buffer to hold data 56 | buf := make([]byte, 2048) 57 | // Store remote IP:port for logging 58 | rAddr := conn.RemoteAddr().String() 59 | 60 | for { 61 | // Read from channel and copy to buffer 62 | buf = <-c 63 | // Write buffer 64 | n, err := conn.Write(buf) 65 | // If connection is closed from the other side 66 | if err == io.EOF { 67 | // Close the connction and return 68 | fmt.Println("Connection closed from", rAddr) 69 | return 70 | } 71 | // For other errors, print the error and return 72 | if err != nil { 73 | fmt.Println("Error writing to socket", err) 74 | return 75 | } 76 | // Log data sent 77 | fmt.Printf("Sent to %v: %s\n", rAddr, buf[:n]) 78 | } 79 | } 80 | 81 | // forwardConnection creates a connection to the server and then passes packets 82 | func forwardConnection(clientConn net.Conn) { 83 | 84 | // Converting host and port to destIP:destPort 85 | t := net.JoinHostPort(destIP, destPort) 86 | 87 | // Create a connection to server 88 | serverConn, err := net.Dial("tcp", t) 89 | if err != nil { 90 | fmt.Println(err) 91 | clientConn.Close() 92 | return 93 | } 94 | 95 | // Client to server channel 96 | c2s := make(chan []byte, 2048) 97 | // Server to client channel 98 | s2c := make(chan []byte, 2048) 99 | 100 | go readSocket(clientConn, c2s) 101 | go writeSocket(serverConn, c2s) 102 | go readSocket(serverConn, s2c) 103 | go writeSocket(clientConn, s2c) 104 | 105 | } 106 | func main() { 107 | 108 | flag.Parse() 109 | 110 | // Converting host and port to bindIP:bindPort 111 | t := net.JoinHostPort(bindIP, bindPort) 112 | 113 | // Listen for connections on BindIP:BindPort 114 | ln, err := net.Listen("tcp", t) 115 | if err != nil { 116 | // If we cannot bind, print the error and quit 117 | panic(err) 118 | } 119 | 120 | fmt.Printf("Started listening on %v\n", t) 121 | 122 | // Wait for connections 123 | for { 124 | // Accept a connection 125 | conn, err := ln.Accept() 126 | if err != nil { 127 | // If there was an error print it and go back to listening 128 | fmt.Println(err) 129 | 130 | continue 131 | } 132 | fmt.Printf("Received connection from %v\n", conn.RemoteAddr().String()) 133 | 134 | go forwardConnection(conn) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /code/04/04.4/04.4-03-sshclient-login-key.go: -------------------------------------------------------------------------------- 1 | // Interactive SSH login with SSH key. 2 | 3 | package main 4 | 5 | import ( 6 | "flag" 7 | "fmt" 8 | "io" 9 | "io/ioutil" 10 | "net" 11 | "os" 12 | 13 | // Importing crypto/ssh 14 | "golang.org/x/crypto/ssh" 15 | ) 16 | 17 | var ( 18 | username, serverIP, serverPort, pKeyFile string 19 | ) 20 | 21 | // Read flags 22 | func init() { 23 | flag.StringVar(&serverPort, "port", "22", "SSH server port") 24 | flag.StringVar(&serverIP, "ip", "127.0.0.1", "SSH server IP") 25 | flag.StringVar(&username, "user", "", "username") 26 | flag.StringVar(&pKeyFile, "pkey", "", "unencrypted private key file") 27 | } 28 | 29 | func main() { 30 | // Parse flags 31 | flag.Parse() 32 | 33 | // Check if username and pKey have been submitted 34 | if username == "" { 35 | fmt.Println("Must supply username") 36 | os.Exit(2) 37 | } 38 | 39 | if pKeyFile == "" { 40 | fmt.Println("Must supply private key") 41 | os.Exit(2) 42 | } 43 | 44 | // Now we must read the private key 45 | pKey, err := ioutil.ReadFile(pKeyFile) 46 | if err != nil { 47 | fmt.Println("Failed to read private key from file", err) 48 | os.Exit(2) 49 | } 50 | 51 | // Create a signer with the private key 52 | signer, err := ssh.ParsePrivateKey(pKey) 53 | if err != nil { 54 | fmt.Println("Failed to parse private key", err) 55 | os.Exit(2) 56 | } 57 | 58 | // Create SSH config 59 | config := &ssh.ClientConfig{ 60 | // Username 61 | User: username, 62 | // Each config must have one AuthMethod. Now we use key 63 | Auth: []ssh.AuthMethod{ 64 | ssh.PublicKeys(signer), 65 | }, 66 | // This callback function validates the server. 67 | // Danger! We are ignoring host info 68 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), 69 | } 70 | 71 | // Server address 72 | t := net.JoinHostPort(serverIP, serverPort) 73 | 74 | // Connect to the SSH server 75 | sshConn, err := ssh.Dial("tcp", t, config) 76 | if err != nil { 77 | fmt.Printf("Failed to connect to %v\n", t) 78 | fmt.Println(err) 79 | os.Exit(2) 80 | } 81 | 82 | // Create new SSH session 83 | session, err := sshConn.NewSession() 84 | if err != nil { 85 | fmt.Printf("Cannot create SSH session to %v\n", t) 86 | fmt.Println(err) 87 | os.Exit(2) 88 | } 89 | 90 | // Close the session when main returns 91 | defer session.Close() 92 | 93 | // For an interactive session we must redirect IO 94 | session.Stdout = os.Stdout 95 | session.Stderr = os.Stderr 96 | input, err := session.StdinPipe() 97 | if err != nil { 98 | fmt.Println("Error redirecting session input", err) 99 | os.Exit(2) 100 | } 101 | 102 | // Setup terminal mode when requesting pty. You can see all terminal modes at 103 | // https://github.com/golang/crypto/blob/master/ssh/session.go#L56 or read 104 | // the RFC for explanation https://tools.ietf.org/html/rfc4254#section-8 105 | termModes := ssh.TerminalModes{ 106 | ssh.ECHO: 0, // Disable echo 107 | } 108 | 109 | // Request pty 110 | // https://tools.ietf.org/html/rfc4254#section-6.2 111 | // First variable is term environment variable value which specifies terminal. 112 | // term doesn't really matter here, we will use "vt220". 113 | // Next are height and width: (40,80) characters and finall termModes. 114 | err = session.RequestPty("vt220", 40, 80, termModes) 115 | if err != nil { 116 | fmt.Println("RequestPty failed", err) 117 | os.Exit(2) 118 | } 119 | 120 | // Also 121 | // if err = session.RequestPty("vt220", 40, 80, termModes); err != nil { 122 | // fmt.Println("RequestPty failed", err) 123 | // os.Exit(2) 124 | // } 125 | 126 | // Now we can start a remote shell 127 | err = session.Shell() 128 | if err != nil { 129 | fmt.Println("shell failed", err) 130 | os.Exit(2) 131 | } 132 | 133 | // Same as above, a different way to check for errors 134 | // if err = session.Shell(); err != nil { 135 | // fmt.Println("shell failed", err) 136 | // os.Exit(2) 137 | // } 138 | 139 | // Endless loop to capture commands 140 | // Note: After exit, we need to ctrl+c to end the application. 141 | for { 142 | io.Copy(input, os.Stdin) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /content/04.3.md: -------------------------------------------------------------------------------- 1 | # 04.3 - TCP Proxy 2 | Building a non-TLS terminating TCP proxy is pretty easy. It's very similar to the TCP server we have already created. 3 | 4 | We listen for TCP connections. After one is established, we create a new connection to the forwarding IP:port and send all data. Without logging this can be done with a simple `io.Copy(connDest, connSrc)`. With logging we have to use multiple goroutines (as we have seen before). 5 | 6 | Only forwardConnection is different. Instead of calling `handleConnection` we call `forwardConnection` in a new goroutine. 7 | 8 | Inside, we create a TCP connection to server and two channels. Then we handle each side of the connection like the echo TCP server. 9 | 10 | ``` go 11 | // 04.3-01-tcp-proxy.go 12 | package main 13 | 14 | import ( 15 | "flag" 16 | "fmt" 17 | "io" 18 | "net" 19 | ) 20 | 21 | var ( 22 | bindIP, bindPort, destIP, destPort string 23 | ) 24 | 25 | func init() { 26 | flag.StringVar(&bindPort, "bindPort", "12345", "bind port") 27 | flag.StringVar(&bindIP, "bindIP", "127.0.0.1", "bind IP") 28 | flag.StringVar(&destPort, "destPort", "12345", "bind port") 29 | flag.StringVar(&destIP, "destIP", "127.0.0.1", "bind IP") 30 | } 31 | 32 | // readSocket reads data from socket if available and passes it to channel 33 | func readSocket(conn net.Conn, c chan<- []byte) { 34 | 35 | // Create a buffer to hold data 36 | buf := make([]byte, 2048) 37 | // Store remote IP:port for logging 38 | rAddr := conn.RemoteAddr().String() 39 | 40 | for { 41 | // Read from connection 42 | n, err := conn.Read(buf) 43 | // If connection is closed from the other side 44 | if err == io.EOF { 45 | // Close the connction and return 46 | fmt.Println("Connection closed from", rAddr) 47 | return 48 | } 49 | // For other errors, print the error and return 50 | if err != nil { 51 | fmt.Println("Error reading from socket", err) 52 | return 53 | } 54 | // Print data read from socket 55 | // Note we are only printing and sending the first n bytes. 56 | // n is the number of bytes read from the connection 57 | fmt.Printf("Received from %v: %s\n", rAddr, buf[:n]) 58 | // Send data to channel 59 | c <- buf[:n] 60 | } 61 | } 62 | 63 | // writeSocket reads data from channel and writes it to socket 64 | func writeSocket(conn net.Conn, c <-chan []byte) { 65 | 66 | // Create a buffer to hold data 67 | buf := make([]byte, 2048) 68 | // Store remote IP:port for logging 69 | rAddr := conn.RemoteAddr().String() 70 | 71 | for { 72 | // Read from channel and copy to buffer 73 | buf = <-c 74 | // Write buffer 75 | n, err := conn.Write(buf) 76 | // If connection is closed from the other side 77 | if err == io.EOF { 78 | // Close the connction and return 79 | fmt.Println("Connection closed from", rAddr) 80 | return 81 | } 82 | // For other errors, print the error and return 83 | if err != nil { 84 | fmt.Println("Error writing to socket", err) 85 | return 86 | } 87 | // Log data sent 88 | fmt.Printf("Sent to %v: %s\n", rAddr, buf[:n]) 89 | } 90 | } 91 | 92 | // forwardConnection creates a connection to the server and then passes packets 93 | func forwardConnection(clientConn net.Conn) { 94 | 95 | // Converting host and port to destIP:destPort 96 | t := net.JoinHostPort(destIP, destPort) 97 | 98 | // Create a connection to server 99 | serverConn, err := net.Dial("tcp", t) 100 | if err != nil { 101 | fmt.Println(err) 102 | clientConn.Close() 103 | return 104 | } 105 | 106 | // Client to server channel 107 | c2s := make(chan []byte, 2048) 108 | // Server to client channel 109 | s2c := make(chan []byte, 2048) 110 | 111 | go readSocket(clientConn, c2s) 112 | go writeSocket(serverConn, c2s) 113 | go readSocket(serverConn, s2c) 114 | go writeSocket(clientConn, s2c) 115 | 116 | } 117 | func main() { 118 | 119 | flag.Parse() 120 | 121 | // Converting host and port to bindIP:bindPort 122 | t := net.JoinHostPort(bindIP, bindPort) 123 | 124 | // Listen for connections on BindIP:BindPort 125 | ln, err := net.Listen("tcp", t) 126 | if err != nil { 127 | // If we cannot bind, print the error and quit 128 | panic(err) 129 | } 130 | 131 | fmt.Printf("Started listening on %v\n", t) 132 | 133 | // Wait for connections 134 | for { 135 | // Accept a connection 136 | conn, err := ln.Accept() 137 | if err != nil { 138 | // If there was an error print it and go back to listening 139 | fmt.Println(err) 140 | 141 | continue 142 | } 143 | fmt.Printf("Received connection from %v\n", conn.RemoteAddr().String()) 144 | 145 | go forwardConnection(conn) 146 | } 147 | } 148 | ``` 149 | 150 | #### Continue reading ⇒ [04.4 - SSH clients](04.4.md) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hacking with Go 2 | This is my attempt at filling the gap in Go security tooling. When starting to learn Go, I learned from a lot of tutorials but I could find nothing that is geared towards security professionals. 3 | 4 | These documents are based on the `Gray/Black Hat Python/C#` series of books. I like their style. Join me as I learn more about Go and attempt to introduce Go to security denizens without fluff and through practical applications. 5 | 6 | ## Table of Contents 7 | 8 | - [01 - Setting up a Go development environment](content/01.md) 9 | - [02 - Basics](content/02.0.md) 10 | + [02.1 - Packages, functions, variables, basic types, casting and constants](content/02.1.md) 11 | + [02.2 - for, if, else, switch and defer](content/02.2.md) 12 | + [02.3 - Pointers, structs, arrays, slices and range](content/02.3.md) 13 | + [02.4 - Methods and interfaces](content/02.4.md) 14 | + [02.5 - Printf, Scanf, bufio readers and maps](content/02.5.md) 15 | + [02.6 - Goroutines and channels](content/02.6.md) 16 | + [02.7 - Error handling](content/02.7.md) 17 | - [03 - Useful Go packages - WIP](content/03.0.md) 18 | + [03.1 - flag package](content/03.1.md) 19 | + [03.2 - log package](content/03.2.md) 20 | - [04 - Go networking](content/04.0.md) 21 | + [04.1 - Basic TCP and UDP clients](content/04.1.md) 22 | + [04.2 - TCP servers](content/04.2.md) 23 | + [04.3 - TCP proxy](content/04.3.md) 24 | + [04.4 - SSH clients](content/04.4.md) 25 | + [04.5 - SSH Harvester](content/04.5.md) 26 | - [05 - Parsing Files](content/05.0.md) 27 | + [05.1 - Extracting PNG Chunks](content/05.1.md) 28 | - [06 - Go-Fuzz](content/06.0.md) 29 | + [06.1 - Go-Fuzz Quickstart](content/06.1.md) 30 | + [06.2 - Fuzzing iprange with Go-Fuzz](content/06.2.md) 31 | + [06.2 - Fuzzing goexif2 with Go-Fuzz](content/06.3.md) 32 | 33 | ## Code 34 | 35 | - [01 - Setting up a Go development environment](code/01) 36 | - [02 - Basics](code/02) 37 | - [03 - Useful Go packages](code/03) 38 | - [04 - Go networking](code/04) 39 | - [05 - Parsing Files](code/05) 40 | - [06 - Go-Fuzz](code/06) 41 | 42 | ### FAQ 43 | 44 | **Why not use Python?** 45 | Python reigns supreme in security and for good reason. It's a powerful programming language. There are a lot of supporting libraries out there both in security and for general use. However, I think Go has its merits and can occupy a niche. 46 | 47 | **Why not use other tutorials?** 48 | There are a lot of tutorials for Go out there. None are geared towards security professionals. Our needs are different, we want to write quick and dirty scripts that work (hence Python is so successful). Similar guides are available in Python and other programming languages. 49 | 50 | **Why not just use Black Hat Go?** 51 | There's a book named [Black Hat Go][black-hat-go] by No Starch in production. Looking at the author list, I cannot compete with them in terms of experience and knowledge. That is a proper book with editors and a publisher while I am just some rando learning as I go. It does not take a lot of CPU power to decide the book will be better. 52 | 53 | But the book is not out yet. Today is December 6th 2017 and the book is marked for release in August 2018. The book page does not have any released chapters or material. We can assume it's going to be similar to the other `gray|black hat` books. This repository and that book are inevitably going to have a lot of overlap. Think of this as warm up while we wait. 54 | 55 | Update February 2020: Black Hat Go has been released. Please see the code samples at https://github.com/blackhat-go/bhg. 56 | 57 | **Rewrite in Rust/Haskell** 58 | Honestly I will be very much interested in a similar guide for Rust/Haskell geared for security people. Please let me know if you create one. 59 | 60 | ## Feedback 61 | I am always interested in feedback. There will be errors and there are always better ways to code. Please create an issue here. If this has helped you please let me know, it helps with the grind. 62 | 63 | ## Other resources 64 | There are tons of Go resources online. I am going to try not to re-hash what has been already created. `Hacking with Go` is not meant to be self-contained. When in doubt, use one of these resources or just search. 65 | 66 | The following links helped me get started: 67 | 68 | - GoDoc: [https://godoc.org/][go-doc] 69 | - A Tour of Go: [https://tour.golang.org/][tour-of-go] 70 | - Go by Example: [https://gobyexample.com/][go-by-example] 71 | - Go playground: [https://play.golang.org/][go-playground] 72 | - Effective Go: [https://golang.org/doc/effective_go.html][effective-go] 73 | 74 | Similar resources to `Hacking with Go`: 75 | 76 | - [Security with Go][security-with-go] published by Packt: https://github.com/PacktPublishing/Security-with-Go 77 | - `goHackTools`: https://github.com/dreddsa5dies/goHackTools 78 | - [Go programming language secure coding practices guide][go-scp] 79 | 80 | ## License 81 | 82 | - Code in this repository is licensed under [GPLv3](LICENSE). 83 | - Non-code content is licensed under [Creative Commons Attribution-NonCommercial 4.0][CC-4] (CC BY-NC 4.0). 84 | 85 | 86 | 87 | [black-hat-go]: https://www.nostarch.com/blackhatgo 88 | [go-doc]: https://godoc.org/ 89 | [tour-of-go]: https://tour.golang.org/ 90 | [go-by-example]: https://gobyexample.com/ 91 | [go-playground]: https://play.golang.org/ 92 | [CC-4]: https://creativecommons.org/licenses/by-nc-sa/4.0/ 93 | [effective-go]: https://golang.org/doc/effective_go.html 94 | [security-with-go]: https://www.packtpub.com/networking-and-servers/security-go 95 | [go-scp]: https://github.com/Checkmarx/Go-SCP 96 | -------------------------------------------------------------------------------- /content/01.md: -------------------------------------------------------------------------------- 1 | # 01 - Setting up a Go development environment 2 | I am going to use a Windows 10 x64 Virtual Machine (VM) but Go is available for most popular platforms. I can already hear the infosec pros grunt. The [Getting Started][go-install] section on Go website has how-tos for most popular platforms. You can find binaries and building instructions. 3 | 4 | You can get free Windows VMs from [modern.ie][modern-ie-vms]. Make a snapshot after you everything is set up. They expire in 90 days and you can only re-arm them multiple times. 5 | 6 | 7 | 8 | - [Installation on Windows 10 VM](#installation-on-windows-10-vm) 9 | - [GOPATH](#gopath) 10 | - [Test application](#test-application) 11 | - [Editor](#editor) 12 | - [Go playground](#go-playground) 13 | - [Offline coding](#offline-coding) 14 | - [gofmt](#gofmt) 15 | - [Starting curly brace](#starting-curly-brace) 16 | 17 | 18 | 19 | 20 | ## Installation on Windows 10 VM 21 | 22 | 1. Go to [https://golang.org/doc/install][go-install] and download the MSI binary. 23 | 2. Install the MSI, choose the default location. 24 | 3. Choose a development directory. I have created a shared directory in my VM. This way I can code in host and run the guest. In my case it 's `Z:\Go` where Z is the shared drive/directory. 25 | 4. Set the following environmental variables (installer might have already set some up): 26 | - `GOROOT`: `C:\Go` 27 | - `GOPATH`: `Z:\Go` or the directory from step 3. 28 | 5. Add `C:\Go\Bin` to PATH. 29 | 6. Open a new cmd and run `go env`. You should see what you have setup. 30 | 31 | Output of `go env` in my Windows 10 VM is: 32 | 33 | ``` 34 | $ go env 35 | set GOARCH=amd64 36 | set GOBIN= 37 | set GOEXE=.exe 38 | set GOHOSTARCH=amd64 39 | set GOHOSTOS=windows 40 | set GOOS=windows 41 | set GOPATH=Z:\Go\ 42 | set GORACE= 43 | set GOROOT=C:\Go 44 | set GOTOOLDIR=C:\Go\pkg\tool\windows_amd64 45 | set GCCGO=gccgo 46 | set CC=gcc 47 | set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0 48 | -fdebug-prefix-map=C:\Users\IEUser\AppData\Local\Temp\go-build352203231=/tmp/go-build 49 | -gno-record-gcc-switches 50 | set CXX=g++ 51 | set CGO_ENABLED=1 52 | set CGO_CFLAGS=-g -O2 53 | set CGO_CPPFLAGS= 54 | set CGO_CXXFLAGS=-g -O2 55 | set CGO_FFLAGS=-g -O2 56 | set CGO_LDFLAGS=-g -O2 57 | set PKG_CONFIG=pkg-config 58 | ``` 59 | 60 | 61 | ## GOPATH 62 | You can write Go code anywhere but only code in a `GOPATH` directory can be executed with `go run`2. 63 | 64 | Go to the development path in step 3 of last section and create three directories inside it: 65 | 66 | - `src`: Source code. 67 | - `bin`: Compiled files. 68 | - `pkg`: Executables. 69 | 70 | You can clone this repository in `src` and then run everything in `code`. The directory structure looks like in the Windows 10 VM: 71 | 72 | ``` 73 | Z:\Go>tree /F 74 | 75 | Z:. 76 | ├───bin 77 | ├───pkg 78 | └───src 79 | └───Hacking-with-Go 80 | └───code 81 | └───01 82 | 01-01-HelloWorld.go 83 | ``` 84 | 85 | 86 | ## Test application 87 | Let's write a quick "Hello World" application and run it. 88 | 89 | ``` go 90 | package main 91 | 92 | import "fmt" 93 | 94 | func main() { 95 | 96 | fmt.Println("Hello World!") 97 | } 98 | ``` 99 | 100 | And we can run it with `go run 01-01-HelloWorld.go`. 101 | 102 | ``` go 103 | Z:\Go\src\hacking-with-go\code\01>go run 01-01-HelloWorld.go 104 | Hello World! 105 | ``` 106 | 107 | 108 | ## Editor 109 | Choose whatever you like. There are many editors with Go support (you will see below) to choose from. Some in no particular order are: 110 | 111 | - [SublimeText][sublime-link] using [GoSublime][go-sublime-link] package. 112 | - [Atom][atom-link] via [go-plus][go-plus-link] package. 113 | - [Visual Studio Code][vs-code-link] with [Go extension][vs-code-go]. 114 | - [Vim-go][vim-go]. 115 | - [Emacs go-mode][emacs-go-mode]. 116 | 117 | I personally use Sublime Text 3 and GoSublime. 118 | 119 | 120 | ### Go playground 121 | The online go playground at [https://play.golang.org/][go-playground] is good for prototyping/testing and sharing quick scripts. It's pretty useful when Go is not installed on the machine. For more information read [Inside the Go Playground][inside-go-playground]. 122 | 123 | 124 | ### Offline coding 125 | It's possible to run both the playground and documentation server offline. 126 | 127 | - `godoc -http :1234` will run the the documentation server at `localhost:1234`. 128 | - `go tool tour` will start an offline version of [Tour of Go][tour-of-go] at `localhost:3999`. This allows coding offline in browser in Go playground. 129 | 130 | 131 | ## gofmt 132 | `gofmt` is Go's official formatting tool. It automatically modifies source code. The main reason behind choosing an editor with Go support is running `gofmt` automatically on your code. 133 | 134 | I personally do not agree with `gofmt`. For example it uses tabs (I like spaces). Tab-width is fixed at four (I like two). But it's better if our code adheres to language standards. 135 | 136 | For more information read [go fmt your code][gofmt-your-code]. For usage see [Command gofmt][command-gofmt]. 137 | 138 | 139 | ## Starting curly brace 140 | The starting curly brace needs to be on the same line as the the keyword starting the block (e.g. `for` or `if`). This is a Go standard enforced by the compiler. It's explained in the Go [FAQ][faq-curlybrace]. 141 | 142 | This is wrong: 143 | 144 | ``` go 145 | func main() 146 | { 147 | fmt.Println("Hello World!") 148 | } 149 | ``` 150 | 151 | This is correct: 152 | 153 | ``` go 154 | func main() { 155 | fmt.Println("Hello World!") 156 | } 157 | ``` 158 | #### Continue reading ⇒ [02 - Basics](02.0.md) 159 | 160 | 161 | 162 | [go-install]: https://golang.org/doc/install 163 | [modern-ie-vms]: https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/ 164 | [sublime-link]: https://www.sublimetext.com/ 165 | [atom-link]: https://atom.io/ 166 | [vs-code-link]: https://code.visualstudio.com/ 167 | [goland-link]: https://www.jetbrains.com/go/ 168 | [vim-go]: https://github.com/fatih/vim-go 169 | [emacs-go-mode]: https://github.com/dominikh/go-mode.el 170 | [vs-code-go]: https://marketplace.visualstudio.com/items?itemName=lukehoban.Go 171 | [go-plus-link]: https://atom.io/packages/go-plus 172 | [go-sublime-link]: https://github.com/DisposaBoy/GoSublime 173 | [gofmt-your-code]: https://blog.golang.org/go-fmt-your-code 174 | [command-gofmt]: https://golang.org/cmd/gofmt/ 175 | [go-playground]: https://play.golang.org/ 176 | [inside-go-playground]: https://blog.golang.org/playground 177 | [tour-of-go]: https://tour.golang.org/ 178 | [faq-curlybrace]: https://golang.org/doc/faq#semicolons 179 | -------------------------------------------------------------------------------- /content/02.2.md: -------------------------------------------------------------------------------- 1 | # 02.2 - for, if, else, switch and defer 2 | 3 | 4 | 5 | - [For](#for) 6 | - [++ and --](#-and---) 7 | - [if](#if) 8 | - [Short statements](#short-statements) 9 | - [else](#else) 10 | - [switch](#switch) 11 | - [defer](#defer) 12 | 13 | 14 | 15 | 16 | ## For 17 | Similar to C with two differences: 18 | 19 | - No parenthesis around the three components. Having parenthesis will give result in an error. 20 | - Curly braces `{ }` are always required and the first one needs to be in the same line as for, if, etc. 21 | 22 | It has three components: 23 | 24 | - `for init; condition; post { }` 25 | 26 | ``` go 27 | // 02.2-01-for1.go 28 | package main 29 | 30 | import "fmt" 31 | 32 | func main() { 33 | // var sum int 34 | sum := 0 35 | for i := 0; i < 20; i++ { 36 | sum += i 37 | } 38 | 39 | fmt.Println(sum) 40 | } 41 | ``` 42 | 43 | Init and post (first and last) components are optional and turn `for` into `while`: 44 | 45 | ``` go 46 | // 02.2-02-for2.go 47 | package main 48 | 49 | import "fmt" 50 | 51 | func main() { 52 | // var sum int 53 | sum, i := 0 54 | for i < 20 { // while (i<20) 55 | sum += i 56 | i++ 57 | } 58 | 59 | fmt.Println(sum) 60 | } 61 | ``` 62 | 63 | Without the condition it turns into `for(;;)` or `while(1)` 64 | 65 | ``` go 66 | for { // while(1) 67 | ... 68 | } 69 | ``` 70 | 71 | 72 | ## ++ and -- 73 | Don't be fooled by their use in `for` examples. According to the [FAQ][faq-incdec], they are "statements" and not "expressions." In other words we can use them to increase or decrease a variable by one but cannot assign the result to a different one. 74 | 75 | This will not work: 76 | 77 | ``` go 78 | // 02.2-03-incdec.go 79 | package main 80 | 81 | import "fmt" 82 | 83 | func main() { 84 | // var sum int 85 | sum, i := 0 86 | // This will not work 87 | sum = i++ 88 | fmt.Println(sum) 89 | } 90 | ``` 91 | 92 | ``` 93 | Z:\Go\src\Hacking-with-Go\code\02>go run 02.2-03-incdec.go 94 | # command-line-arguments 95 | .\02.2-03-incdec.go:9:9: syntax error: unexpected ++ at end of statement 96 | ``` 97 | 98 | 99 | ## if 100 | Does not need parenthesis but needs curly braces. 101 | 102 | 103 | ``` go 104 | // 02.2-04-if1.go 105 | package main 106 | 107 | import "fmt" 108 | 109 | func main() { 110 | 111 | a := 10 112 | b := 20 113 | 114 | if b > a { 115 | fmt.Println(b, ">", a) 116 | } 117 | } 118 | ``` 119 | 120 | 121 | ## Short statements 122 | Short statements are interesting. They are statements that are executed before the condition. It's not a unique idea to Go because we have already seen them in `for` constructs in almost every language. 123 | 124 | They can be used in `if`s. 125 | 126 | ``` go 127 | // 02.2-05-if2.go 128 | package main 129 | 130 | import "fmt" 131 | 132 | func main() { 133 | 134 | if var1 := 20; var1 > 10 { 135 | fmt.Println("Inside if:", var1) 136 | } 137 | // Cannot use the variable var1 here 138 | } 139 | ``` 140 | 141 | In this code `var1 := 20` is executed before the `if` condition. Any variables declared in the short statement are only in scope in the `if` block and are destroyed after. 142 | 143 | Short statements are usually used for executing a function and checking the return value with an `if`. 144 | 145 | 146 | ## else 147 | `else` is similar to C else. 148 | 149 | If the corresponding `if` has a short statement then any variables declared in the short statement are also in scope in the `else` block. 150 | 151 | 152 | ``` go 153 | // 02.2-06-else.go 154 | package main 155 | 156 | import "fmt" 157 | 158 | func main() { 159 | 160 | if var1 := 20; var1 > 100 { 161 | fmt.Println("Inside if:", var1) 162 | } else { 163 | // Can use var1 here 164 | fmt.Println("Inside else:", var1) 165 | } 166 | // Cannot use var1 here 167 | } 168 | ``` 169 | 170 | 171 | ## switch 172 | Similar to C switch with some differences: 173 | 174 | - Doesn't automatically go to the next `switch` statement unless you have `fallthrough` in the end. The `fallthrough` only works if it's the last statement in the case. 175 | - Can have a short statement like `if`. 176 | 177 | ``` go 178 | // 02.2-07-switch1.go 179 | package main 180 | 181 | import ( 182 | "fmt" 183 | "math/rand" // This is not cryptographically secure! 184 | "time" 185 | ) 186 | 187 | func main() { 188 | // Seeding rand 189 | rand.Seed(time.Now().UnixNano()) 190 | fmt.Println("Choosing a random number:") 191 | 192 | switch num := rand.Intn(3); num { 193 | case 1: 194 | fmt.Println("1") 195 | case 2: 196 | fmt.Println("2") 197 | default: 198 | fmt.Println("3") 199 | } 200 | } 201 | ``` 202 | 203 | Cases can have `if` conditions if we use a switch with an empty value: 204 | 205 | ``` go 206 | // 02.2-08-switch2.go 207 | package main 208 | 209 | import ( 210 | "fmt" 211 | "math/rand" // This is not cryptographically secure! 212 | "time" 213 | ) 214 | 215 | func main() { 216 | // Seeding rand 217 | rand.Seed(time.Now().UnixNano()) 218 | fmt.Println("Choosing a random number:") 219 | 220 | switch num := rand.Intn(100); { 221 | case num < 50: 222 | fmt.Println("Less than 50") 223 | default: 224 | fmt.Println("More than 50") 225 | } 226 | } 227 | ``` 228 | 229 | The short statement does not have to be part of the switch: 230 | 231 | 232 | ``` go 233 | // 02.2-09-switch3.go 234 | package main 235 | 236 | import ( 237 | "fmt" 238 | "math/rand" // This is not cryptographically secure! 239 | "time" 240 | ) 241 | 242 | func main() { 243 | // Seeding rand 244 | rand.Seed(time.Now().UnixNano()) 245 | fmt.Println("Choosing a random number:") 246 | 247 | num := rand.Intn(100) 248 | switch { 249 | case num < 50: 250 | fmt.Println("Less than 50") 251 | default: 252 | fmt.Println("More than 50") 253 | } 254 | } 255 | ``` 256 | 257 | 258 | ## defer 259 | `defer` is another interesting feature in Go. It defers the execution of a function until the calling function returns. 260 | 261 | It works like a stack, every time program reaches a `defer`, it will push that function with its argument values. When surrounding function returns, deferred functions are popped from the stack and executed. 262 | 263 | ``` go 264 | // 02.2-10-defer1.go 265 | package main 266 | 267 | import "fmt" 268 | 269 | func main() { 270 | defer fmt.Println("This runs after main") 271 | 272 | fmt.Println("Main ended") 273 | } 274 | ``` 275 | 276 | Results in: 277 | 278 | ``` 279 | Z:\Go\src\Hacking-with-Go\code\02>go run 02.2-10-defer1.go 280 | Main ended 281 | This runs after main 282 | ``` 283 | 284 | Argument values are saved when the `defer` statement is reached but it is executed later. 285 | 286 | ``` go 287 | // 02.2-11-defer2.go 288 | package main 289 | 290 | import "fmt" 291 | 292 | func main() { 293 | num := 1 294 | defer fmt.Println("After main returns", num) 295 | 296 | num++ 297 | fmt.Println("Inside main", num) 298 | } 299 | ``` 300 | 301 | ``` 302 | $ go run 02.2-11-defer2.go 303 | Inside main 2 304 | After main returns 1 305 | ``` 306 | 307 | The value of `num` was `1` when the print was deferred. 308 | 309 | #### Continue reading ⇒ [02.3 - Pointers, structs, arrays, slices and range](02.3.md) 310 | 311 | 312 | 313 | [faq-incdec]: https://golang.org/doc/faq#inc_dec 314 | -------------------------------------------------------------------------------- /content/04.2.md: -------------------------------------------------------------------------------- 1 | # 04.2 - TCP servers 2 | Now we will create TCP and UDP servers. 3 | 4 | 5 | 6 | - [net.Listen](#netlisten) 7 | - [No logging with io.Copy\(\)](#no-logging-with-iocopy) 8 | - [Logging with extra goroutines](#logging-with-extra-goroutines) 9 | - [net.TCPListen](#nettcplisten) 10 | - [Lessons learned](#lessons-learned) 11 | 12 | 13 | 14 | 15 | [net package overview][net-pkg-overview] also shows us how to create a generic TCP server. When creating a server we can take advantage of goroutines and spawn one for each connection. 16 | 17 | 18 | ## net.Listen 19 | The generic `net.Listen` method is capable of doing both TCP and UDP. 20 | 21 | 22 | ### No logging with io.Copy() 23 | Building on the example from `net` package we can build a simple TCP server: 24 | 25 | ``` go 26 | // 04.2-01-tcpserver1.go 27 | package main 28 | 29 | import ( 30 | "flag" 31 | "fmt" 32 | "io" 33 | "net" 34 | ) 35 | 36 | var ( 37 | host, port string 38 | ) 39 | 40 | func init() { 41 | flag.StringVar(&port, "port", "12345", "target port") 42 | flag.StringVar(&host, "host", "example.com", "target host") 43 | } 44 | 45 | // handleConnectionNoLog echoes everything back without logging (easiest) 46 | func handleConnectionNoLog(conn net.Conn) { 47 | 48 | rAddr := conn.RemoteAddr().String() 49 | defer fmt.Printf("Closed connection from %v\n", rAddr) 50 | 51 | // This will accomplish the echo if we do not want to log 52 | io.Copy(conn, conn) 53 | } 54 | 55 | func main() { 56 | 57 | flag.Parse() 58 | 59 | // Converting host and port to host:port 60 | t := net.JoinHostPort(host, port) 61 | 62 | // Listen for connections on BindIP:BindPort 63 | ln, err := net.Listen("tcp", t) 64 | if err != nil { 65 | // If we cannot bind, print the error and quit 66 | panic(err) 67 | } 68 | 69 | // Wait for connections 70 | for { 71 | // Accept a connection 72 | conn, err := ln.Accept() 73 | if err != nil { 74 | // If there was an error, print it and go back to listening 75 | fmt.Println(err) 76 | continue 77 | } 78 | 79 | fmt.Printf("Received connection from %v\n", conn.RemoteAddr().String()) 80 | 81 | // Spawn a new goroutine to handle the connection 82 | go handleConnectionNoLog(conn) 83 | } 84 | } 85 | ``` 86 | 87 | Most of the code in `main` is similar to Python. We listen on a `host:port` and then accept each connection. With each new connection, a new goroutine is spawned to handle it. 88 | 89 | ``` go 90 | // handleConnectionNoLog echoes everything back without logging (easiest) 91 | func handleConnectionNoLog(conn net.Conn) { 92 | 93 | rAddr := conn.RemoteAddr().String() 94 | defer fmt.Printf("Closed connection from %v\n", rAddr) 95 | 96 | // This will accomplish the echo if we do not want to log 97 | io.Copy(conn, conn) 98 | } 99 | ``` 100 | 101 | This is where the magic happens: 102 | 103 | - `io.Copy(conn, conn)` 104 | 105 | You copy one connection to the other. It's super easy! And it works. 106 | 107 | We can telnet to the server and see. 108 | 109 | ![TCP server 1 test](images/04/04.2-01-telnet-test1.png) 110 | 111 | 112 | ### Logging with extra goroutines 113 | Things become complicated when we want to log info that we have received. The main structure of the program is the same but we spawn two extra goroutines inside the `handleConnection` goroutine. 114 | 115 | ``` go 116 | // 04.2-02-tcpserver2.go 117 | // handleConnectionLog echoes everything back and logs messages received 118 | func handleConnectionLog(conn net.Conn) { 119 | 120 | // Create buffered channel to pass data around 121 | c := make(chan []byte, 2048) 122 | 123 | // Spawn up two goroutines, one for reading and another for writing 124 | 125 | go readSocket(conn, c) 126 | go writeSocket(conn, c) 127 | 128 | } 129 | ``` 130 | 131 | A buffered channel is created and passed to each goroutine. As you can imagine `readSocket` reads from the connection and writes to channel. Note the argument is a directed channel (this prevents from accidentally reading from it instead of writing): 132 | 133 | ``` go 134 | // readSocket reads data from socket if available and passes it to channel 135 | // Note the directed write-only channel designation 136 | func readSocket(conn net.Conn, c chan<- []byte) { 137 | 138 | // Create a buffer to hold data 139 | buf := make([]byte, 2048) 140 | // Store remote IP:port for logging 141 | rAddr := conn.RemoteAddr().String() 142 | 143 | for { 144 | // Read from connection 145 | n, err := conn.Read(buf) 146 | // If connection is closed from the other side 147 | if err == io.EOF { 148 | // Close the connction and return 149 | fmt.Println("Connection closed from", rAddr) 150 | return 151 | } 152 | // For other errors, print the error and return 153 | if err != nil { 154 | fmt.Println("Error reading from socket", err) 155 | return 156 | } 157 | // Print data read from socket 158 | // Note we are only printing and sending the first n bytes. 159 | // n is the number of bytes read from the connection 160 | fmt.Printf("Received from %v: %s\n", rAddr, buf[:n]) 161 | // Send data to channel 162 | c <- buf[:n] 163 | } 164 | } 165 | ``` 166 | 167 | This is pretty straightforward. The only important part is `n`. `n` is the number of bytes read from the socket after `conn.Read`. When sending the data to the channel we are only interested in the first `n` bytes (if we send the whole buffer, the other side will get 2048 bytes every time). 168 | 169 | 170 | ``` go 171 | // writeSocket reads data from channel and writes it to socket 172 | func writeSocket(conn net.Conn, c <-chan []byte) { 173 | 174 | // Create a buffer to hold data 175 | buf := make([]byte, 2048) 176 | // Store remote IP:port for logging 177 | rAddr := conn.RemoteAddr().String() 178 | 179 | for { 180 | // Read from channel and copy to buffer 181 | buf = <-c 182 | // Write buffer 183 | n, err := conn.Write(buf) 184 | // If connection is closed from the other side 185 | if err == io.EOF { 186 | // Close the connction and return 187 | fmt.Println("Connection closed from", rAddr) 188 | return 189 | } 190 | // For other errors, print the error and return 191 | if err != nil { 192 | fmt.Println("Error writing to socket", err) 193 | return 194 | } 195 | // Log data sent 196 | fmt.Printf("Sent to %v: %s\n", rAddr, buf[:n]) 197 | } 198 | } 199 | ``` 200 | 201 | `writeSocket` is easier. We use a directed channel to read data into a buffer and send it off. This server is also not echo-ing back the first character. 202 | 203 | ![TCP server 2 test](images/04/04.2-02-telnet-test2.png) 204 | 205 | 206 | ## net.TCPListen 207 | As we have seen, there are TCP specific methods in the `net` package. The code is pretty much the same. We just use `TCPListen` and pass a `*TCPAddr` to it. The result is a `TCPConn` which is `net.Conn` under the hoods. Everything else remains the same. 208 | 209 | ``` go 210 | // 04.2-03-tcpserver3.go 211 | 212 | // CreateTCPAddr converts host and port to *TCPAddr 213 | func CreateTCPAddr(host, port string) (*net.TCPAddr, error) { 214 | return net.ResolveTCPAddr("tcp", net.JoinHostPort(host, port)) 215 | } 216 | 217 | func main() { 218 | 219 | // Converting host and port to TCP address 220 | t, err := CreateTCPAddr(bindHost, bindPort) 221 | ... 222 | 223 | // Listen for connections on bindHost:bindPort 224 | ln, err := net.ListenTCP("tcp", t) 225 | ... 226 | 227 | for { 228 | conn, err := ln.AcceptTCP() 229 | ... 230 | 231 | go handleConnectionLog(conn) 232 | } 233 | ... 234 | ``` 235 | 236 | 237 | ## Lessons learned 238 | 239 | 1. `io.Copy(conn, conn)` is magic. 240 | 2. Goroutines are pretty easy to spawn for socket read/writes. 241 | 242 | #### Continue reading ⇒ [04.3 - TCP proxy](04.3.md) -------------------------------------------------------------------------------- /code/05/05.1/05.1-01-png-chunk-extraction.go: -------------------------------------------------------------------------------- 1 | // Simple PNG parser. Can be used to discover and extract hidden chunks. 2 | // Minimal error handling, does not play well with malformed chunks and doesn't 3 | // check chunk CRC32 checksums. 4 | 5 | package main 6 | 7 | import ( 8 | "encoding/binary" 9 | "encoding/json" 10 | "errors" 11 | "flag" 12 | "fmt" 13 | "io" 14 | "os" 15 | ) 16 | 17 | // 89 50 4E 47 0D 0A 1A 0A 18 | var PNGHeader = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A" 19 | var iHDRlength = 13 20 | 21 | // uInt32ToInt converts a 4 byte big-endian buffer to int. 22 | func uInt32ToInt(buf []byte) (int, error) { 23 | if len(buf) == 0 || len(buf) > 4 { 24 | return 0, errors.New("invalid buffer") 25 | } 26 | return int(binary.BigEndian.Uint32(buf)), nil 27 | } 28 | 29 | // Each chunk starts with a uint32 length (big endian), then 4 byte name, 30 | // then data and finally the CRC32 of the chunk data. 31 | type Chunk struct { 32 | Length int // chunk data length 33 | CType string // chunk type 34 | Data []byte // chunk data 35 | Crc32 []byte // CRC32 of chunk data 36 | } 37 | 38 | // Populate will read bytes from the reader and populate a chunk. 39 | func (c *Chunk) Populate(r io.Reader) error { 40 | 41 | // Four byte buffer. 42 | buf := make([]byte, 4) 43 | 44 | // Read first four bytes == chunk length. 45 | if _, err := io.ReadFull(r, buf); err != nil { 46 | return err 47 | } 48 | // Convert bytes to int. 49 | // c.length = int(binary.BigEndian.Uint32(buf)) 50 | var err error 51 | c.Length, err = uInt32ToInt(buf) 52 | if err != nil { 53 | return errors.New("cannot convert length to int") 54 | } 55 | 56 | // Read second four bytes == chunk type. 57 | if _, err := io.ReadFull(r, buf); err != nil { 58 | return err 59 | } 60 | c.CType = string(buf) 61 | 62 | // Read chunk data. 63 | tmp := make([]byte, c.Length) 64 | if _, err := io.ReadFull(r, tmp); err != nil { 65 | return err 66 | } 67 | c.Data = tmp 68 | 69 | // Read CRC32 hash 70 | if _, err := io.ReadFull(r, buf); err != nil { 71 | return err 72 | } 73 | // We don't really care about checking the hash. 74 | c.Crc32 = buf 75 | 76 | return nil 77 | } 78 | 79 | // ----------- 80 | 81 | type PNG struct { 82 | Width int 83 | Height int 84 | BitDepth int 85 | ColorType int 86 | CompressionMethod int 87 | FilterMethod int 88 | InterlaceMethod int 89 | chunks []*Chunk // Not exported == won't appear in JSON string. 90 | NumberOfChunks int 91 | } 92 | 93 | // Parse IHDR chunk. 94 | // https://golang.org/src/image/png/reader.go?#L142 is your friend. 95 | func (png *PNG) parseIHDR(iHDR *Chunk) error { 96 | if iHDR.Length != iHDRlength { 97 | errString := fmt.Sprintf("invalid IHDR length: got %d - expected %d", 98 | iHDR.Length, iHDRlength) 99 | return errors.New(errString) 100 | } 101 | 102 | // IHDR: http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.IHDR 103 | 104 | // Width: 4 bytes 105 | // Height: 4 bytes 106 | // Bit depth: 1 byte 107 | // Color type: 1 byte 108 | // Compression method: 1 byte 109 | // Filter method: 1 byte 110 | // Interlace method: 1 byte 111 | 112 | tmp := iHDR.Data 113 | var err error 114 | 115 | png.Width, err = uInt32ToInt(tmp[0:4]) 116 | if err != nil || png.Width <= 0 { 117 | errString := fmt.Sprintf("invalid width in iHDR - got %x", tmp[0:4]) 118 | return errors.New(errString) 119 | } 120 | 121 | png.Height, err = uInt32ToInt(tmp[4:8]) 122 | if err != nil || png.Height <= 0 { 123 | errString := fmt.Sprintf("invalid height in iHDR - got %x", tmp[4:8]) 124 | return errors.New(errString) 125 | } 126 | 127 | png.BitDepth = int(tmp[8]) 128 | png.ColorType = int(tmp[9]) 129 | 130 | // Only compression method 0 is supported 131 | if int(tmp[10]) != 0 { 132 | errString := fmt.Sprintf("invalid compression method - expected 0 - got %x", 133 | tmp[10]) 134 | return errors.New(errString) 135 | } 136 | png.CompressionMethod = int(tmp[10]) 137 | 138 | // Only filter method 0 is supported 139 | if int(tmp[11]) != 0 { 140 | errString := fmt.Sprintf("invalid filter method - expected 0 - got %x", 141 | tmp[11]) 142 | return errors.New(errString) 143 | } 144 | png.FilterMethod = int(tmp[11]) 145 | 146 | // Only interlace methods 0 and 1 are supported 147 | if int(tmp[12]) != 0 { 148 | errString := fmt.Sprintf("invalid interlace method - expected 0 or 1 - got %x", 149 | tmp[12]) 150 | return errors.New(errString) 151 | } 152 | png.InterlaceMethod = int(tmp[12]) 153 | 154 | return nil 155 | } 156 | 157 | // Populate populates the PNG fields (and other fields). 158 | func (png *PNG) Populate() error { 159 | if err := png.parseIHDR(png.chunks[0]); err != nil { 160 | return err 161 | } 162 | png.NumberOfChunks = len(png.chunks) 163 | return nil 164 | } 165 | 166 | // PrintChunks will return a string containign chunk number, name and the first 20 167 | // bytes of each chunk. 168 | func (png PNG) PrintChunks() string { 169 | var output string 170 | for i, c := range png.chunks { 171 | output += fmt.Sprintf("-----------\n") 172 | output += fmt.Sprintf("Chunk # %d\n", i) 173 | output += fmt.Sprintf("Chunk length: %d\n", c.Length) 174 | output += fmt.Sprintf("Chunk type: %v\n", c.CType) 175 | 176 | limit := 20 177 | if len(c.Data) < 20 { 178 | limit = len(c.Data) 179 | } 180 | output += fmt.Sprintf("Chunk data (20 bytes): % x\n", c.Data[:limit]) 181 | } 182 | return output 183 | } 184 | 185 | // ExportChunk returns a chunk's data as []byte. Numbering starts from 0. 186 | func (png PNG) ExportChunk(chunkNumber int) ([]byte, error) { 187 | if chunkNumber >= png.NumberOfChunks { 188 | errString := fmt.Sprintf("invalid chunk number. Got: %d, "+ 189 | "file has %d chunks. Chunk numbers starts from zero.", 190 | chunkNumber, len(png.chunks)) 191 | return nil, errors.New(errString) 192 | } 193 | return png.chunks[chunkNumber].Data, nil 194 | } 195 | 196 | // ---------- 197 | 198 | var ( 199 | filename string 200 | chunkFlag bool 201 | ) 202 | 203 | func init() { 204 | // Setup flags. 205 | flag.StringVar(&filename, "file", "", "input file") 206 | flag.BoolVar(&chunkFlag, "c", false, "print chunks") 207 | flag.BoolVar(&chunkFlag, "chunks", false, "print chunks") 208 | 209 | // Parse flags. 210 | flag.Parse() 211 | } 212 | 213 | func main() { 214 | 215 | imgFile, err := os.Open(filename) 216 | if err != nil { 217 | panic(err) 218 | } 219 | defer imgFile.Close() 220 | 221 | // Read first 8 bytes == PNG header. 222 | header := make([]byte, 8) 223 | // Read CRC32 hash 224 | if _, err := io.ReadFull(imgFile, header); err != nil { 225 | panic(err) 226 | } 227 | if string(header) != PNGHeader { 228 | fmt.Printf("Wrong PNG header.\nGot %x - Expected %x\n", header, PNGHeader) 229 | return 230 | } 231 | 232 | var png PNG 233 | 234 | // Reset err 235 | err = nil 236 | for err == nil { 237 | var c Chunk 238 | err = (&c).Populate(imgFile) 239 | // Drop the last empty chunk. 240 | if c.CType != "" { 241 | png.chunks = append(png.chunks, &c) 242 | } 243 | } 244 | 245 | if err := (&png).Populate(); err != nil { 246 | fmt.Println("Failed to populate PNG fields.") 247 | panic(err) 248 | } 249 | 250 | fmt.Println("PNG info") 251 | jsoned, err := json.MarshalIndent(png, "", " ") 252 | if err != nil { 253 | fmt.Printf("%+v", png) 254 | } else { 255 | fmt.Println(string(jsoned)) 256 | } 257 | 258 | if chunkFlag { 259 | fmt.Println("\nPrinting chunks\n----------") 260 | fmt.Println(png.PrintChunks()) 261 | fmt.Println("----------") 262 | } 263 | 264 | // Reset err 265 | err = nil 266 | for { 267 | fmt.Printf("Enter chunk number or -1 to quit: ") 268 | var n int 269 | fmt.Scanln(&n) 270 | // Quit if user enters -1. 271 | if n == -1 { 272 | break 273 | } 274 | // Export chunk. 275 | c, err := png.ExportChunk(n) 276 | if err != nil { 277 | fmt.Println(err.Error()) 278 | continue 279 | } 280 | chunkfilename := fmt.Sprintf("%s-chunk-%d", filename, n) 281 | chunkfile, err := os.Create(chunkfilename) 282 | if err != nil { 283 | fmt.Printf(err.Error()) 284 | continue 285 | } 286 | defer chunkfile.Close() 287 | chunkfile.Write(c) 288 | fmt.Println("Chunk saved to ", chunkfilename) 289 | } 290 | fmt.Println("Qutting!") 291 | } 292 | -------------------------------------------------------------------------------- /content/02.1.md: -------------------------------------------------------------------------------- 1 | # 02.1 - Packages, functions, variables, basic types, casting and constants 2 | 3 | 4 | 5 | - [Packages](#packages) 6 | - [Exported names](#exported-names) 7 | - [Functions](#functions) 8 | - [Functions can return multiple values](#functions-can-return-multiple-values) 9 | - [Named return values](#named-return-values) 10 | - [init function](#init-function) 11 | - [Variables](#variables) 12 | - [Initialization](#initialization) 13 | - [Initialization Values](#initialization-values) 14 | - [Short variable declarations](#short-variable-declarations) 15 | - [Basic types](#basic-types) 16 | - [Casting](#casting) 17 | - [Constants](#constants) 18 | - [Raw strings](#raw-strings) 19 | 20 | 21 | 22 | 23 | ## Packages 24 | Go is divided into packages. Packages are the equivalent of modules in Python. Only the `main` package can be executed with `go run`. 25 | 26 | We can import packages with `import`. The Hello World application imported the `fmt` package. Multiple imports are similar: 27 | 28 | ``` go 29 | import ( 30 | "fmt" 31 | "math/rand" 32 | "otherimport" 33 | ) 34 | ``` 35 | 36 | 37 | ### Exported names 38 | In Go, a name is exported if it begins with a capital letter. 39 | 40 | When importing a package, you can refer only to its exported names. Unexported names are not accessible from outside the package. 41 | 42 | 43 | ## Functions 44 | Unlike C, type comes after variable name except for pointers. 45 | 46 | ``` go 47 | // 02.1-01-multiply.go 48 | package main 49 | 50 | import "fmt" 51 | 52 | func multiply(x int, y int) int { 53 | return x * y 54 | } 55 | 56 | func main() { 57 | fmt.Println(multiply(10,20)) 58 | } 59 | ``` 60 | *https://play.golang.org/p/jZrNpGAEWds* 61 | 62 | 63 | ### Functions can return multiple values 64 | A function can return any number of values. Gone are the days when we had to use pointers in function parameters as extra return values. 65 | 66 | ``` go 67 | // 02.1-02-addTwo.go 68 | package main 69 | 70 | import "fmt" 71 | 72 | func addTwo(x int, y int) (int, int) { 73 | return x+2, y+2 74 | } 75 | 76 | func main() { 77 | fmt.Println(addTwo(10,20)) 78 | } 79 | ``` 80 | *https://play.golang.org/p/sH0LeYIBpOM* 81 | 82 | If multiple variables have the same type we can declare them like this: 83 | 84 | ``` go 85 | func addTwo(x, y int) (int, int) { 86 | return x+2, y+2 87 | } 88 | ``` 89 | *https://play.golang.org/p/Dwl94tWctK8* 90 | 91 | 92 | ### Named return values 93 | Return values can be named. If so, they are treated as variables defined in the function. 94 | 95 | A return statement without arguments returns the named return values. This is known as a "naked" return. _Using named return values and naked return is frowned upon unless it helps readability._ 96 | 97 | ``` go 98 | // 02.1-03-addTwo2.go 99 | package main 100 | 101 | import "fmt" 102 | 103 | func addTwo2(x int, y int) (xPlusTwo int, yPlusTwo int) { 104 | xPlusTwo = x + 2 105 | yPlusTwo = y + 2 106 | 107 | return xPlusTwo, yPlusTwo 108 | } 109 | 110 | func main() { 111 | fmt.Println(addTwo2(20,30)) 112 | } 113 | ``` 114 | *https://play.golang.org/p/wiC9HJ0uxDN* 115 | 116 | ### init function 117 | `init` function is used to set up the state. A common practice is to declare [flags][internal-flag] in it. 118 | 119 | 1. Imported packages are initialized. 120 | 2. Variable declarations evaluate their initializers. 121 | 3. `init` function executes. 122 | 123 | ``` go 124 | // 02.1-09-init.go 125 | package main 126 | 127 | import "fmt" 128 | 129 | func init() { 130 | fmt.Println("Executing init function!") 131 | } 132 | 133 | func main() { 134 | fmt.Println("Executing main!") 135 | } 136 | ``` 137 | *https://play.golang.org/p/HfL8YjGMsmw* 138 | 139 | Resulting in: 140 | 141 | ``` 142 | $ go run 02.1-09-init.go 143 | Executing init function! 144 | Executing main! 145 | ``` 146 | 147 | Like any other function, variables declared in `init` are only valid there. 148 | 149 | 150 | ## Variables 151 | Use `var`. 152 | 153 | - `var x int` 154 | 155 | Can be combined for multiple variables: 156 | 157 | - `var x,y int` == `var x int, y int` 158 | 159 | 160 | ### Initialization 161 | Variables can be initialized. 162 | 163 | - `var a, b int = 10, 20` 164 | 165 | or 166 | 167 | - `var a int = 10` 168 | - `var b int = 20` 169 | 170 | If initialized value is present during declaration, type can be omitted: 171 | 172 | - `var sampleInt, sampleBoolean, sampleString = 30, true, "Hello"` 173 | 174 | or 175 | 176 | - `var sampleInt = 30` 177 | - `var sampleBoolean = true` 178 | - `var sampleString = "Hello"` 179 | 180 | ``` go 181 | // 02.1-04-variables.go 182 | package main 183 | 184 | import "fmt" 185 | 186 | func main() { 187 | var a, b int = 10, 20 188 | var sampleInt, sampleBoolean, sampleString = 30, true, "Hello" 189 | 190 | fmt.Println(a, b , sampleInt, sampleBoolean, sampleString) 191 | } 192 | ``` 193 | *https://play.golang.org/p/TnRrIC43-NR* 194 | 195 | ### Initialization Values 196 | If no initial value is assigned to a declared variable, it will get a `zero` value: 197 | 198 | - `0` for numeric types (int, float, etc.). 199 | - `false` for the boolean type. 200 | - `""` (the empty string) for strings. 201 | 202 | 203 | ### Short variable declarations 204 | Inside a function (including `main`), the `:=` short assignment statement can be used in place of a `var` declaration with implicit type. 205 | 206 | Outside a function, every statement begins with a keyword (`var`, `func`) so the `:=` construct is not available. 207 | 208 | ``` go 209 | // 02.1-05-short-declaration.go 210 | package main 211 | 212 | import "fmt" 213 | 214 | func main() { 215 | sampleInt, sampleBoolean, sampleString := 30, true, "Hello" 216 | 217 | fmt.Println(sampleInt, sampleBoolean, sampleString) 218 | } 219 | ``` 220 | *https://play.golang.org/p/RMC-9h4eBLD* 221 | 222 | `var` statements can be put in different lines (increases readability): 223 | 224 | ``` go 225 | var ( 226 | sampleInt = 30 227 | sampleBoolean = true 228 | sampleString = "Hello" 229 | ) 230 | ``` 231 | 232 | Several other Go constructs use the same format. For example `import` and `const`. 233 | 234 | 235 | ## Basic types 236 | 237 | ``` go 238 | bool 239 | 240 | string 241 | 242 | int int8 int16 int32 int64 // use int unless you want a specific size 243 | uint uint8 uint16 uint32 uint64 uintptr // ditto, use uint 244 | 245 | byte // alias for uint8 246 | 247 | rune // alias for int32 248 | // represents a Unicode char 249 | 250 | float32 float64 251 | 252 | complex64 complex128 253 | ``` 254 | 255 | 256 | ## Casting 257 | Casting needs to be explicit, unlike C where some castings worked out of the box. 258 | 259 | ``` go 260 | // 02.1-06-casting.go 261 | package main 262 | 263 | import ( 264 | "fmt" 265 | ) 266 | 267 | func main() { 268 | var a, b int = 20, 30 269 | // Need to convert a and b to float32 before the division 270 | var div float32 = float32(a) / float32(b) 271 | // Cast float32 to int 272 | var divInt = int(div) 273 | fmt.Println(div, divInt) 274 | } 275 | ``` 276 | *https://play.golang.org/p/wKtudyE9f8q* 277 | 278 | ## Constants 279 | Declared with `const` keyword. Can be character, string, boolean or numeric. Cannot use `:=`. Coding standard requires constants to start with a capital letter. 280 | 281 | ``` go 282 | // 02.1-07-const.go 283 | package main 284 | 285 | import "fmt" 286 | 287 | const Whatever = "whatever" 288 | 289 | func main() { 290 | fmt.Println(Whatever) 291 | 292 | const One = 1 293 | fmt.Println(One) 294 | } 295 | ``` 296 | *https://play.golang.org/p/RaNzEnRlFZ4* 297 | 298 | Multiple constants can be declared together: 299 | 300 | ``` go 301 | const ( 302 | Const1 = "Constant String" 303 | Int1 = 12345 304 | True = true 305 | ) 306 | ``` 307 | 308 | 309 | ## Raw strings 310 | Go has two types of strings: 311 | 312 | - Interpreted strings: The typical `string` type created with `"`. Can contain anything except new line and unescaped `"`. 313 | - Raw strings: Encoded between "`" (backticks) can contain new lines and other artifacts. 314 | 315 | ``` go 316 | // 02.1-08-rawstring.go 317 | package main 318 | 319 | import "fmt" 320 | 321 | func main() { 322 | 323 | rawstr := 324 | 325 | `First line 326 | 327 | some new lines 328 | 329 | more new lines 330 | 331 | "double quotes" 332 | ` 333 | 334 | fmt.Print(rawstr) 335 | } 336 | ``` 337 | *https://play.golang.org/p/D8TwnBhwM0o* 338 | 339 | #### Continue reading ⇒ [02.2 - for, if, else, switch and defer](02.2.md) 340 | 341 | 342 | 343 | [raw-string]: https://golang.org/ref/spec#String_literals 344 | [internal-flag]: 03.1.md 345 | -------------------------------------------------------------------------------- /content/03.2.md: -------------------------------------------------------------------------------- 1 | # log package 2 | [log package][log-pkg] is used for logging. The examples (unlike some other packages) are not very helpful. It's very bare bones and has only two logging levels. 3 | 4 | For anything complicated use Google's [glog][glog-pkg] package. 5 | 6 | 7 | 8 | - [Basic logging](#basic-logging) 9 | - [Custom logger](#custom-logger) 10 | - [Log to file](#log-to-file) 11 | - [Logging to multiple files/streams](#logging-to-multiple-filesstreams) 12 | - [Flag](#flag) 13 | - [Prefix](#prefix) 14 | - [Logging levels](#logging-levels) 15 | 16 | 17 | 18 | 19 | 20 | ## Basic logging 21 | Basic logging is similar to other languages. 22 | 23 | ``` go 24 | // 03.2-01-basic-logging.go 25 | package main 26 | 27 | import ( 28 | "log" 29 | ) 30 | 31 | func main() { 32 | 33 | a, b := 10, 20 34 | 35 | log.Print("Use Print to log.") 36 | log.Println("Ditto for Println.") 37 | log.Printf("Use Printf and format strings. %d + %d = %d", a, b, a+b) 38 | } 39 | ``` 40 | 41 | Each log is on a new line: 42 | 43 | ``` 44 | $ go run 03.2-01-basic-logging.go 45 | 2017/12/25 22:18:38 Use Print to log. 46 | 2017/12/25 22:18:38 Ditto for Println. 47 | 2017/12/25 22:18:38 Use Printf and format strings. 10 + 20 = 30 48 | ``` 49 | 50 | We can also forward the output to a file (or any number of `io.Writer`s) with [log.SetOutput][setoutput1-log-pkg]. 51 | 52 | ``` go 53 | 54 | logFile, err := os.Create("log1.txt") 55 | if err != nil { 56 | panic("Could not open file") 57 | } 58 | 59 | log.SetOutput(logFile) 60 | ``` 61 | 62 | ## Custom logger 63 | We can setup a custom logger with [logger.New][new-log-pkg]. 64 | 65 | ``` go 66 | func New(out io.Writer, prefix string, flag int) *Logger 67 | ``` 68 | 69 | - `out`: Log destination. Any `io.Writer` like files. 70 | - `prefix`: Appears before each log entry. Think `Warning/Info/Error`. 71 | - `flag`: Defines logging properties (e.g. the date time format). 72 | 73 | 74 | ### Log to file 75 | Using `out` we can log to files. 76 | 77 | ``` go 78 | // 03.2-02-log-file.go 79 | package main 80 | 81 | import ( 82 | "log" 83 | "os" 84 | ) 85 | 86 | func main() { 87 | 88 | // Create a file 89 | logFile, err := os.Create("log1.txt") 90 | if err != nil { 91 | panic("Could not open file") 92 | } 93 | 94 | // Close the file after main returns 95 | defer logFile.Close() 96 | 97 | a, b := 10, 20 98 | 99 | // We will not use the other options 100 | myLog := log.New(logFile, "", 0) 101 | 102 | myLog.Print("Use Print to log.") 103 | myLog.Println("Ditto for Println.") 104 | myLog.Printf("Use Printf and format strings. %d + %d = %d", a, b, a+b) 105 | } 106 | ``` 107 | 108 | `log1.txt` will contain: 109 | 110 | ``` 111 | Use Print to log. 112 | Ditto for Println. 113 | Use Printf and format strings. 10 + 20 = 30 114 | ``` 115 | 116 | After `New`, `mylog.SetOutput(w io.Writer)` can redirect the logger. 117 | 118 | 119 | #### Logging to multiple files/streams 120 | It's also possible to log to multiple files (or `io.Writer`s) with [io.MultiWriter][multiwriter-io-pkg]. This is useful when we want to both output to stdout and to files. 121 | 122 | ``` go 123 | // 03.2-03-log-multiple-files.go 124 | package main 125 | 126 | import ( 127 | "bytes" 128 | "fmt" 129 | "io" 130 | "log" 131 | "os" 132 | ) 133 | 134 | func main() { 135 | 136 | // Create a file 137 | logFile, err := os.Create("log1.txt") 138 | if err != nil { 139 | panic("Could not open file") 140 | } 141 | 142 | // Close the file after main returns 143 | defer logFile.Close() 144 | 145 | // Create a second file 146 | logFile2, err := os.Create("log2.txt") 147 | if err != nil { 148 | panic("Could not open file2") 149 | } 150 | 151 | defer logFile2.Close() 152 | 153 | // Create a buffer 154 | var buflog bytes.Buffer 155 | 156 | multiW := io.MultiWriter(logFile, logFile2, &buflog, os.Stdout) 157 | 158 | a, b := 10, 20 159 | 160 | // Log to multiW 161 | myLog := log.New(multiW, "", 0) 162 | 163 | myLog.Print("Use Print to log.") 164 | myLog.Println("Ditto for Println.") 165 | myLog.Printf("Use Printf and format strings. %d + %d = %d", a, b, a+b) 166 | 167 | // Print buffer 168 | fmt.Println("Buffer:") 169 | fmt.Println(buflog.String()) 170 | } 171 | ``` 172 | 173 | We can see what we logged in both stdout and buffer: 174 | 175 | ``` 176 | $ go run 03.2-03-log-multiple-files.go 177 | Use Print to log. 178 | Ditto for Println. 179 | Use Printf and format strings. 10 + 20 = 30 180 | Buffer: 181 | Use Print to log. 182 | Ditto for Println. 183 | Use Printf and format strings. 10 + 20 = 30 184 | ``` 185 | 186 | 187 | ### Flag 188 | Prefix should be next but by discussing flag we can see if it appears before flag format or not. `flag` is an integer and is a collection of bits (like `FLAGS` CPU register). The flags are defined as constants: 189 | 190 | ``` go 191 | // https://godoc.org/log#pkg-constants 192 | 193 | const ( 194 | // Bits or'ed together to control what's printed. 195 | // There is no control over the order they appear (the order listed 196 | // here) or the format they present (as described in the comments). 197 | // The prefix is followed by a colon only when Llongfile or Lshortfile 198 | // is specified. 199 | // For example, flags Ldate | Ltime (or LstdFlags) produce, 200 | // 2009/01/23 01:23:23 message 201 | // while flags Ldate | Ltime | Lmicroseconds | Llongfile produce, 202 | // 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message 203 | Ldate = 1 << iota // the date in the local time zone: 2009/01/23 204 | Ltime // the time in the local time zone: 01:23:23 205 | Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime. 206 | Llongfile // full file name and line number: /a/b/c/d.go:23 207 | Lshortfile // final file name element and line number: d.go:23. overrides Llongfile 208 | LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone 209 | LstdFlags = Ldate | Ltime // initial values for the standard logger 210 | ) 211 | ``` 212 | 213 | There's only room for a few bits of customization (see what I did there?). 214 | 215 | ``` go 216 | // 03.2-04-log-flags.go 217 | package main 218 | 219 | import ( 220 | "log" 221 | "os" 222 | ) 223 | 224 | func main() { 225 | 226 | a, b := 10, 20 227 | 228 | // New logger will output to stdout with flags 229 | // Only log date and file 230 | myLog := log.New(os.Stdout, "", log.Ldate|log.Lshortfile) 231 | 232 | myLog.Print("Use Print to log.") 233 | myLog.Println("Ditto for Println.") 234 | myLog.Printf("Use Printf and format strings. %d + %d = %d", a, b, a+b) 235 | } 236 | ``` 237 | 238 | We log date and filename: 239 | 240 | 241 | ``` 242 | $ go run 03.2-04-log-flags.go 243 | 2017/12/26 03.2-04-log-flags.go:25: Use Print to log. 244 | 2017/12/26 03.2-04-log-flags.go:26: Ditto for Println. 245 | 2017/12/26 03.2-04-log-flags.go:27: Use Printf and format strings. 10 + 20 = 30 246 | ``` 247 | 248 | 249 | ### Prefix 250 | `prefix` adds a string to the beginning of each log line. 251 | 252 | ``` go 253 | // 03.2-05-log-prefix.go 254 | package main 255 | 256 | import ( 257 | "log" 258 | "os" 259 | ) 260 | 261 | func main() { 262 | 263 | a, b := 10, 20 264 | 265 | // New logger will output to stdout with prefix "Log1: " and flags 266 | // Note the space in prefix 267 | myLog := log.New(os.Stdout, "Log1: ", log.Ldate|log.Lshortfile) 268 | 269 | myLog.Print("Use Print to log.") 270 | myLog.Println("Ditto for Println.") 271 | myLog.Printf("Use Printf and format strings. %d + %d = %d", a, b, a+b) 272 | } 273 | ``` 274 | 275 | Prefix is printed before flags: 276 | 277 | ``` 278 | $ go run 03.2-05-log-prefix.go 279 | Log1: 2017/12/26 03.2-05-log-prefix.go:16: Use Print to log. 280 | Log1: 2017/12/26 03.2-05-log-prefix.go:17: Ditto for Println. 281 | Log1: 2017/12/26 03.2-05-log-prefix.go:18: Use Printf and format strings. 10 + 20 = 30 282 | ``` 283 | 284 | 285 | ## Logging levels 286 | `log` only supports two logging levels: 287 | 288 | - [Fatal][fatal-log-pkg]: `log.Print` and calls `os.Exit(1)`. 289 | - [Panic][panic-log-pkg]: `log.Print` and calls `panic()`. 290 | 291 | Both of these support `ln` and `f` variants (e.g. `Fatalf`, `Panicln`). 292 | 293 | #### Continue reading ⇒ [04 - Go networking](04.0.md) 294 | 295 | 296 | 297 | [log-pkg]: https://godoc.org/log 298 | [glog-pkg]: https://godoc.org/github.com/golang/glog 299 | [new-log-pkg]: https://godoc.org/log#New 300 | [multiwriter-io-pkg]: https://godoc.org/io#MultiWriter 301 | [setoutpu1-log-pkg]: https://godoc.org/log#SetOutput 302 | [setoutput2-log-pkg]: https://godoc.org/log#Logger.SetOutput 303 | [fatal-log-pkg]: https://godoc.org/log#Fatal 304 | [panic-log-pkg]: https://godoc.org/log#Panic -------------------------------------------------------------------------------- /content/02.3.md: -------------------------------------------------------------------------------- 1 | # 02.3 - Pointers, structs, arrays, slices and range 2 | 3 | 4 | 5 | - [Pointers](#pointers) 6 | - [Function arguments: variables vs. pointers](#function-arguments-variables-vs-pointers) 7 | - [Structs](#structs) 8 | - [Arrays](#arrays) 9 | - [Slices](#slices) 10 | - [Slice length and capacity](#slice-length-and-capacity) 11 | - [make](#make) 12 | - [append](#append) 13 | - [range](#range) 14 | 15 | 16 | 17 | 18 | ## Pointers 19 | Similar to C: 20 | 21 | - Point with `*`: `var p *int` == `int *p;` 22 | - Generate pointer (get address of) with `&`: `i := 1` and `p = &i` 23 | 24 | **No pointer arithmetic.** 25 | 26 | 27 | ### Function arguments: variables vs. pointers 28 | Functions/methods accept both variables and pointers. The golden rule is: 29 | 30 | - **Pass pointers when function/method needs to modify the parameter.** 31 | 32 | When a variable is passed, the function/method gets a copy and the original copy is not modified. With pointers the underlying value is modified. 33 | 34 | 35 | ## Structs 36 | Go does not have classes. It has structs like C. 37 | 38 | Exported field names need to be uppercase to be visible outside the defining package. 39 | 40 | ``` go 41 | // 02.3-01-structs.go 42 | package main 43 | 44 | import "fmt" 45 | 46 | type Student struct { 47 | FirstName string 48 | LastName string 49 | } 50 | 51 | func main() { 52 | // Make an instance 53 | studentOne := Student{"Ender", "Wiggin"} 54 | 55 | // Now we can access fields 56 | fmt.Println(studentOne.FirstName) 57 | 58 | // We can just assign fields using names, anything not assigned will be 59 | // initialized with "zero" as we have seen before 60 | studentTwo := Student{FirstName: "Petra"} 61 | 62 | // We will print "{Petra }" notice the space after Petra which is supposed 63 | // to be the delimiter between the fields, LastName is nil because it is not 64 | // given a value 65 | fmt.Println(studentTwo) 66 | 67 | // Can also make a pointer to a struct 68 | p := &studentOne 69 | 70 | // Now instead of *p.LastName (doesn't work) we can just use p.LastName 71 | // fmt.Println((*p).LastName) will not work with error message: invalid indirect of p (type Student) 72 | fmt.Println(p.LastName) 73 | 74 | // Which is the same as 75 | fmt.Println(studentOne.LastName) 76 | 77 | // We can just create a pointer out of the blue 78 | p2 := &Student{"Hercule", "Poirot"} 79 | fmt.Println(p2) 80 | } 81 | ``` 82 | 83 | Tour of Go says, we have to create a pointer to a struct to access fields while we can just do it directly as we saw in the code. 84 | 85 | 86 | ## Arrays 87 | 88 | `var a [10]int` == `int a[10];`. 89 | 90 | **Arrays cannot be resized.** 91 | 92 | ``` go 93 | // 02.3-02-array.go 94 | package main 95 | 96 | import "fmt" 97 | 98 | func main() { 99 | 100 | var a [5]int 101 | a[0] = 10 102 | a[4] = 20 103 | 104 | fmt.Println(a) // [10 0 0 0 20] 105 | 106 | // Array can be initialized during creation 107 | // characters[2] is empty 108 | characters := [3]string{"Ender", "Pentra"} 109 | 110 | fmt.Println(characters) // [Ender Pentra ] 111 | } 112 | ``` 113 | 114 | 115 | ## Slices 116 | Slice is a dynamic view of an array. Slices _don't store anything_ by themselves, they reference an array. If we change something via the slice, the array is modified. 117 | 118 | Think of slices as dynamic arrays. When a slice is created out of the blue, an underlying array is also initialized and can be modified by the slice. 119 | 120 | ``` go 121 | // 02.3-03-slice1.go 122 | package main 123 | 124 | import "fmt" 125 | 126 | func main() { 127 | 128 | // Create an array of strings with 3 members 129 | characters := [3]string{"Ender", "Petra", "Mazer"} 130 | 131 | // Last index is exclusive 132 | // allMembers []string := characters[0:3] 133 | var allMembers []string = characters[0:3] 134 | fmt.Println("All members", allMembers) 135 | 136 | var lastTwo []string = characters[1:3] 137 | fmt.Println("Last two members", lastTwo) 138 | 139 | // Replace Mazer with Bean 140 | fmt.Println("Replacing Mazer with Bean") 141 | allMembers[2] = "Bean" 142 | 143 | fmt.Println("All members after Bean swap", characters) 144 | 145 | fmt.Println("Last two members after Bean swap", lastTwo) 146 | } 147 | ``` 148 | 149 | We can create array and slice literals. Meaning we can just initialize them by their members instead of assigning a length and then add more members. If a slice literal is created, the underlying array is also created. 150 | 151 | ``` go 152 | // 02.3-04-slice2.go 153 | package main 154 | 155 | import "fmt" 156 | 157 | func main() { 158 | 159 | // Slice literal of type struct, the underlying array is created automatically 160 | sliceStruct := []struct { 161 | a, b int 162 | }{ 163 | {1, 2}, 164 | {3, 4}, 165 | {5, 6}, // need this comma in the end otherwise it will not work 166 | } 167 | 168 | fmt.Println(sliceStruct) 169 | } 170 | ``` 171 | 172 | If a length is not specified during array creation, the result is a slice literal as seen above. 173 | 174 | If we do not want to specify a length we can use `[...]`. 175 | 176 | ``` go 177 | // 02.3-05-slice3.go 178 | package main 179 | 180 | import "fmt" 181 | 182 | func main() { 183 | 184 | characters := [...]string{"Ender", "Petra", "Mazer"} 185 | 186 | fmt.Println(characters) 187 | } 188 | ``` 189 | 190 | 191 | ### Slice length and capacity 192 | Slices have length and capacity. 193 | 194 | - **Length** is the current number of items in the slice. Returned by `len(slice)`. 195 | - **Capacity** is the maximum number of items in the slice. Returned by `cap(slice)`. Capacity is determined by the number of items in the original array from the start of the slice and *not the size of array*. For example if the slice starts from the second item (index 1) of an array, slice capacity is `len(array)-1`. This ensures that the slice cannot go past the array. 196 | 197 | **In most cases, we do not care about capacity. Create slices and append to them.** 198 | 199 | ``` go 200 | // 02.3-06-slice4.go 201 | package main 202 | 203 | import "fmt" 204 | 205 | func main() { 206 | 207 | ints := [...]int{0, 1, 2, 3, 4, 5} 208 | fmt.Println(ints) 209 | 210 | slice1 := ints[2:6] 211 | 212 | // len=4 and cap=4 (from 3rd item of the array until the end) 213 | printSlice(slice1) 214 | 215 | slice1 = ints[2:4] 216 | 217 | // len=2 but cap will remain 4 218 | printSlice(slice1) 219 | } 220 | 221 | // Copied from the tour 222 | func printSlice(s []int) { 223 | fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s) 224 | } 225 | ``` 226 | 227 | 228 | ### make 229 | To create dynamically-sized arrays use `make`. `make` creates a zero-ed array and returns a slice pointing to it. 230 | 231 | - `slice1 := make([]int, 10)` creates an int array of length 10. 232 | - `slice2 := make([]int, 5, 10)` creates an int array of length 5 and capacity of 10. 233 | 234 | We can **append** stuff to slices and it grows as needed: 235 | 236 | - `slice1 = append(slice1, 1)` 237 | 238 | We can append multiple elements: 239 | 240 | - `slice1 = append(slice1, 1, 2, 3)` 241 | 242 | 243 | ### append 244 | In order to append one slice to another (obviously they should be of the same type), we have to use `...` as follows: 245 | 246 | - `slice1 = append(slice1, slice2...)` 247 | 248 | `append` is a variadic function, meaning it can an arbitrary number of arguments. By passing `slice2...`, we are essentially passing each member of `slice2` one by one to `append`. 249 | 250 | **This is pretty useful later on when we want to append two byte slices together.** 251 | 252 | ``` go 253 | // 02.3-07-slice-append.go 254 | package main 255 | 256 | import "fmt" 257 | 258 | func main() { 259 | 260 | // Create a slice pointing to an int array 261 | s1 := make([]int, 5) 262 | 263 | fmt.Println(s1) // [0 0 0 0 0] 264 | 265 | for i := 0; i < len(s1); i++ { 266 | s1[i] = i 267 | } 268 | 269 | fmt.Println(s1) // [0 1 2 3 4] 270 | 271 | s2 := make([]int, 3) 272 | 273 | for i := 0; i < len(s2); i++ { 274 | s2[i] = i 275 | } 276 | 277 | fmt.Println(s2) // [0 1 2] 278 | 279 | s3 := append(s1, s2...) 280 | 281 | fmt.Println(s3) // [0 1 2 3 4 0 1 2] 282 | } 283 | ``` 284 | 285 | 286 | ## range 287 | `range` iterates over slices. It returns an index and *a copy of the item* stored at that index. 288 | 289 | - `for index, value := range slice` 290 | 291 | `value` is optional but `index` is not. Ignore either with `_`. 292 | 293 | ``` go 294 | // 02.3-08-range.go 295 | package main 296 | 297 | import "fmt" 298 | 299 | func main() { 300 | characters := [3]string{"Ender", "Petra", "Mazer"} 301 | for i, v := range characters { 302 | fmt.Println(i, v) 303 | } 304 | 305 | // 0 Ender 306 | // 1 Petra 307 | // 2 Mazer 308 | 309 | fmt.Println("-----------") 310 | 311 | // Only using index 312 | for i := range characters { 313 | fmt.Println(i, characters[i]) 314 | } 315 | 316 | fmt.Println("-----------") 317 | 318 | // Ignoring index 319 | for _, v := range characters { 320 | // No non-elaborate way to get index here 321 | fmt.Println(v) 322 | } 323 | 324 | // Ender 325 | // Petra 326 | // Mazer 327 | } 328 | ``` 329 | 330 | #### Continue reading ⇒ [02.4 - Methods and interfaces](02.4.md) -------------------------------------------------------------------------------- /content/04.1.md: -------------------------------------------------------------------------------- 1 | # 04.1 - Basic TCP and UDP clients 2 | 3 | 4 | 5 | - [TCP client](#tcp-client) 6 | - [net.Dial - TCP](#netdial---tcp) 7 | - [net.DialTCP](#netdialtcp) 8 | - [UDP client](#udp-client) 9 | - [net.Dial - UDP](#netdial---udp) 10 | - [net.DialUDP](#netdialudp) 11 | - [Lessons learned](#lessons-learned) 12 | 13 | 14 | 15 | 16 | ## TCP client 17 | The building blocks for the basic TCP client is explained in the [net package overview][net-pkg-overview]. 18 | 19 | 20 | ### net.Dial - TCP 21 | [net.Dial][net-dial] is the general-purpose connect command. 22 | 23 | - First parameter is a string specifying the network. In this case we are using `tcp`. 24 | - Second parameter is a string with the address of the endpoint in format of `host:port`. 25 | 26 | ``` go 27 | // 04.1-01-basic-tcp1.go 28 | package main 29 | 30 | import ( 31 | "bufio" 32 | "flag" 33 | "fmt" 34 | "net" 35 | ) 36 | 37 | var ( 38 | host, port string 39 | ) 40 | 41 | func init() { 42 | flag.StringVar(&port, "port", "80", "target port") 43 | flag.StringVar(&host, "host", "example.com", "target host") 44 | } 45 | 46 | func main() { 47 | 48 | flag.Parse() 49 | 50 | // Converting host and port to host:port 51 | t := net.JoinHostPort(host, port) 52 | 53 | // Create a connection to server 54 | conn, err := net.Dial("tcp", t) 55 | if err != nil { 56 | panic(err) 57 | } 58 | 59 | // Write the GET request to connection 60 | // Note we are closing the HTTP connection with the Connection: close header 61 | // Fprintf writes to an io.writer 62 | req := "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n" 63 | fmt.Fprintf(conn, req) 64 | 65 | // Another way to do it to directly write bytes to conn with conn.Write 66 | // However we must first convert the string to bytes with []byte("string") 67 | // reqBytes := []byte(req) 68 | // conn.Write(reqBytes) 69 | 70 | // Reading the response 71 | 72 | // Create a new reader from connection 73 | connReader := bufio.NewReader(conn) 74 | 75 | // Create a scanner 76 | scanner := bufio.NewScanner(connReader) 77 | 78 | // Combined into one line 79 | // scanner := bufio.NewScanner(bufio.NewReader(conn)) 80 | 81 | // Read from the scanner and print 82 | // Scanner reads until an I/O error 83 | for scanner.Scan() { 84 | fmt.Printf("%s\n", scanner.Text()) 85 | } 86 | 87 | // Check if scanner has quit with an error 88 | if err := scanner.Err(); err != nil { 89 | fmt.Println("Scanner error", err) 90 | } 91 | } 92 | ``` 93 | 94 | The only drawback with `scanner` is having to close the HTTP connection with the `Connection: close` header. Otherwise we have to manually kill the application. 95 | 96 | ``` go 97 | $ go run 04.1-01-basic-tcp1.go -host example.com -port 80 98 | HTTP/1.1 200 OK 99 | Cache-Control: max-age=604800 100 | Content-Type: text/html 101 | Date: Sat, 16 Dec 2017 05:21:33 GMT 102 | Etag: "359670651+gzip+ident" 103 | Expires: Sat, 23 Dec 2017 05:21:33 GMT 104 | Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT 105 | Server: ECS (dca/53DB) 106 | Vary: Accept-Encoding 107 | X-Cache: HIT 108 | Content-Length: 1270 109 | Connection: close 110 | 111 | 112 | 113 | 114 | Example Domain 115 | 116 | 117 | ... 118 | ``` 119 | 120 | Instead of using a `scanner` we can use `ReadString(0x00)` and stop when we reach an error (in this case `EOF`): 121 | 122 | ``` go 123 | // 04.1-02-basic-tcp2.go 124 | 125 | ... 126 | // Read until a null byte (not safe in general) 127 | // Response will not be completely read if it has a null byte 128 | if status, err := connReader.ReadString(byte(0x00)); err != nil { 129 | fmt.Println(err) 130 | fmt.Println(status) 131 | } 132 | ... 133 | ``` 134 | 135 | Using `0x00` as delimiter is not ideal. If the response payload contains NULL bytes, we are not reading everything. But it works in this case. 136 | 137 | 138 | ### net.DialTCP 139 | [net.DialTCP][net-dialtcp] is the TCP specific version of `Dial`. It's a bit more complicated to call: 140 | 141 | - `func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error)` 142 | - `network` is the same as `net.Dial` but can only be `tcp`, `tcp4` and `tcp6`. 143 | - `laddr` is local address and can be chosen. If `nil`, a local address is automatically chosen. 144 | - `raddr` is remote address and is the endpoint. 145 | 146 | The type for both local and remote address is `*TCPAddr`: 147 | 148 | ``` go 149 | type TCPAddr struct { 150 | IP IP 151 | Port int 152 | Zone string // IPv6 scoped addressing zone 153 | } 154 | ``` 155 | 156 | We can pass the `network` (e.g. "tcp") along with `host:port` or `ip:port` string to [net.ResolveTCPAddr][net-resolvetcpaddr] to get a `*TCPAddr`. 157 | 158 | `DialTCP` returns a [*TCPConn][net-tcpconn-src]. It's a normal connection but with extra methods like `SetLinger`, `SetKeepAlive` or `SetKeepAlivePeriod`. 159 | 160 | Let's re-write the TCP client with TCP-specific methods: 161 | 162 | ``` go 163 | // 04.1-03-basic-tcp-dialtcp.go 164 | // Basic TCP client using TCPDial and TCP specific methods 165 | package main 166 | 167 | import ( 168 | "bufio" 169 | "flag" 170 | "fmt" 171 | "net" 172 | ) 173 | 174 | var ( 175 | host, port string 176 | ) 177 | 178 | func init() { 179 | flag.StringVar(&port, "port", "80", "target port") 180 | flag.StringVar(&host, "host", "example.com", "target host") 181 | } 182 | 183 | // CreateTCPAddr converts host and port to *TCPAddr 184 | func CreateTCPAddr(target, port string) (*net.TCPAddr, error) { 185 | return net.ResolveTCPAddr("tcp", net.JoinHostPort(host, port)) 186 | } 187 | 188 | func main() { 189 | 190 | // Converting host and port 191 | a, err := CreateTCPAddr(host, port) 192 | if err != nil { 193 | panic(err) 194 | } 195 | 196 | // Passing nil for local address 197 | tcpConn, err := net.DialTCP("tcp", nil, a) 198 | if err != nil { 199 | panic(err) 200 | } 201 | 202 | // Write the GET request to connection 203 | // Note we are closing the HTTP connection with the Connection: close header 204 | // Fprintf writes to an io.writer 205 | req := "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n" 206 | fmt.Fprintf(tcpConn, req) 207 | 208 | // Reading the response 209 | 210 | // Create a scanner 211 | scanner := bufio.NewScanner(bufio.NewReader(tcpConn)) 212 | 213 | // Read from the scanner and print 214 | // Scanner reads until an I/O error 215 | for scanner.Scan() { 216 | fmt.Printf("%s\n", scanner.Text()) 217 | } 218 | 219 | // Check if scanner has quit with an error 220 | if err := scanner.Err(); err != nil { 221 | fmt.Println("Scanner error", err) 222 | } 223 | } 224 | ``` 225 | 226 | This is a bit better. 227 | 228 | 229 | ## UDP client 230 | Similar to TCP, we can make a UDP client with both `net.Dial` and `net.DialUDP`. 231 | 232 | 233 | ### net.Dial - UDP 234 | Creating a UDP client is very similar. We will just call `net.Dial("udp", t)`. Being UDP, we will use `net.DialTimeout` to pass a timeout value. 235 | 236 | ``` go 237 | // 04.1-04-basic-udp.go 238 | 239 | // Create a connection to server with 5 second timeout 240 | conn, err := net.DialTimeout("udp", t, 5*time.Second) 241 | if err != nil { 242 | panic(err) 243 | } 244 | ``` 245 | 246 | Each second is one `time.Second` (remember to import the `time` package). 247 | 248 | 249 | ### net.DialUDP 250 | [net.DialUDP][net-dialudp] is similar to the TCP equivalent: 251 | 252 | - `func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error)` 253 | - `*UDPAddr` is acquired through [net.ResolveUDPAddr][net-resolveudpaddr]. 254 | - `network` should be `udp`. 255 | 256 | ``` go 257 | // 04.1-05-udp-dialudp.go 258 | package main 259 | 260 | import ( 261 | "bufio" 262 | "flag" 263 | "fmt" 264 | "net" 265 | ) 266 | 267 | var ( 268 | host, port string 269 | ) 270 | 271 | func init() { 272 | flag.StringVar(&port, "port", "80", "target port") 273 | flag.StringVar(&host, "host", "example.com", "target host") 274 | } 275 | 276 | // CreateUDPAddr converts host and port to *UDPAddr 277 | func CreateUDPAddr(target, port string) (*net.UDPAddr, error) { 278 | return net.ResolveUDPAddr("udp", net.JoinHostPort(host, port)) 279 | } 280 | 281 | func main() { 282 | 283 | // Converting host and port to host:port 284 | a, err := CreateUDPAddr(host, port) 285 | if err != nil { 286 | panic(err) 287 | } 288 | 289 | // Create a connection with DialUDP 290 | connUDP, err := net.DialUDP("udp", nil, a) 291 | if err != nil { 292 | panic(err) 293 | } 294 | 295 | // Write the GET request to connection 296 | // Note we are closing the HTTP connection with the Connection: close header 297 | // Fprintf writes to an io.writer 298 | req := "UDP PAYLOAD" 299 | fmt.Fprintf(connUDP, req) 300 | 301 | // Reading the response 302 | 303 | // Create a scanner 304 | scanner := bufio.NewScanner(bufio.NewReader(connUDP)) 305 | 306 | // Read from the scanner and print 307 | // Scanner reads until an I/O error 308 | for scanner.Scan() { 309 | fmt.Printf("%s\n", scanner.Text()) 310 | } 311 | 312 | // Check if scanner has quit with an error 313 | if err := scanner.Err(); err != nil { 314 | fmt.Println("Scanner error", err) 315 | } 316 | } 317 | ``` 318 | 319 | 320 | ## Lessons learned 321 | 322 | 1. Convert `int` to `string` using [strconv.Itoa][strconv-itoa]. [strconv.Atoi][strconv-atoi] does the opposite (note `Atoi` also returns an `err` so check for errors after using it. 323 | 2. `String(int)` converts the integer to corresponding Unicode character. 324 | 3. Create TCP connections with [net.Dial][net-dial]. 325 | 4. We can read/write bytes directly to connections returned by `net.Dial` or create a `Scanner`. 326 | 5. Convert a string to bytes with `[]byte("12345")`. 327 | 6. Get seconds of type `Duration` with `time.Second`. 328 | 7. `net` package has TCP and UDP specific methods. 329 | 330 | #### Continue reading ⇒ [04.2 - TCP servers](04.2.md) 331 | 332 | 333 | 334 | [net-pkg-overview]: https://golang.org/pkg/net/#pkg-overview 335 | [strconv-itoa]: https://golang.org/pkg/strconv/#Itoa 336 | [strconv-atoi]: https://golang.org/pkg/strconv/#Atoi 337 | [net-dial]: https://golang.org/pkg/net/#Dial 338 | [net-dialtcp]: https://golang.org/pkg/net/#DialTCP 339 | [net-resolvetcpaddr]: https://golang.org/pkg/net/#ResolveTCPAddr 340 | [net-tcpconn-src]: https://golang.org/src/net/tcpsock.go?s=2093:2122#L75 341 | [net-dialudp]: https://golang.org/pkg/net/#DialUDP 342 | [net-resolveudpaddr]: https://golang.org/pkg/net/#ResolveUDPAddr 343 | -------------------------------------------------------------------------------- /content/05.1.md: -------------------------------------------------------------------------------- 1 | # 05.1 - Extracting PNG Chunks 2 | This is a copy of my [blog post][png-chunk]. 3 | 4 | I wrote some quick code that parses a PNG file, extracts some information, identifies chunks and finally extracts chunk data. The code has minimal error handling (if chunks are not formatted properly). We also do not care about parsing `PLTE` and `tRNS` chunks although we will extract them. 5 | 6 | Code is in the `05/05.1` directory. 7 | 8 | Golang's https://golang.org/src/image/png/reader.go does a decent job of explaining the rendering. But we are not interested in rendering. 9 | 10 | Instead we look at `libpng` documentation at http://www.libpng.org/pub/png/spec/1.2/PNG-Contents.html. I am going to use a simple example (just a black rectangle which was supposed to be a square lol) to demonstrate: 11 | 12 | ![](images/05/05.1-01-black-rectangle.png) 13 | 14 | ``` 15 | 00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR| 16 | 00000010 00 00 00 6f 00 00 00 73 08 02 00 00 00 19 b3 cb |...o...s......³Ë| 17 | 00000020 d7 00 00 00 01 73 52 47 42 00 ae ce 1c e9 00 00 |×....sRGB.®Î.é..| 18 | 00000030 00 04 67 41 4d 41 00 00 b1 8f 0b fc 61 05 00 00 |..gAMA..±..üa...| 19 | 00000040 00 09 70 48 59 73 00 00 0e c3 00 00 0e c3 01 c7 |..pHYs...Ã...Ã.Ç| 20 | 00000050 6f a8 64 00 00 00 3c 49 44 41 54 78 5e ed c1 01 |o¨d... 4 { 69 | return 0, errors.New("invalid buffer") 70 | } 71 | return int(binary.BigEndian.Uint32(buf)), nil 72 | } 73 | ``` 74 | 75 | **Trick #1**: When reading chunks, I did something I had not done before. I passed in an `io.Reader`. This let me pass anything that implements that interface to the method. As each chunk is populated, reader pointer moves forward and gets to the start of next chunk. Note this assumes chunks are formatted correctly and does not check the CRC32 hash. 76 | 77 | ``` go 78 | // Populate will read bytes from the reader and populate a chunk. 79 | func (c *Chunk) Populate(r io.Reader) error { 80 | 81 | // Four byte buffer. 82 | buf := make([]byte, 4) 83 | 84 | // Read first four bytes == chunk length. 85 | if _, err := io.ReadFull(r, buf); err != nil { 86 | return err 87 | } 88 | // Convert bytes to int. 89 | // c.length = int(binary.BigEndian.Uint32(buf)) 90 | var err error 91 | c.Length, err = uInt32ToInt(buf) 92 | if err != nil { 93 | return errors.New("cannot convert length to int") 94 | } 95 | 96 | // Read second four bytes == chunk type. 97 | if _, err := io.ReadFull(r, buf); err != nil { 98 | return err 99 | } 100 | c.CType = string(buf) 101 | 102 | // Read chunk data. 103 | tmp := make([]byte, c.Length) 104 | if _, err := io.ReadFull(r, tmp); err != nil { 105 | return err 106 | } 107 | c.Data = tmp 108 | 109 | // Read CRC32 hash 110 | if _, err := io.ReadFull(r, buf); err != nil { 111 | return err 112 | } 113 | // We don't really care about checking the hash. 114 | c.Crc32 = buf 115 | 116 | return nil 117 | } 118 | ``` 119 | 120 | ## IHDR Chunk 121 | IHDR is a special chunk that contains [file information][C.IHDR]. It's always 13 bytes and has: 122 | 123 | ``` 124 | // Width: 4 bytes 125 | // Height: 4 bytes 126 | // Bit depth: 1 byte 127 | // Color type: 1 byte 128 | // Compression method: 1 byte 129 | // Filter method: 1 byte 130 | // Interlace method: 1 byte 131 | ``` 132 | 133 | These will go directly into the `PNG` struct: 134 | 135 | ``` go 136 | type PNG struct { 137 | Width int 138 | Height int 139 | BitDepth int 140 | ColorType int 141 | CompressionMethod int 142 | FilterMethod int 143 | InterlaceMethod int 144 | chunks []*Chunk // Not exported == won't appear in JSON string. 145 | NumberOfChunks int 146 | } 147 | ``` 148 | 149 | **Trick #2**: `chunks` does not start with a capital letter. It's not exported, so it is not parsed when we convert the struct to JSON. 150 | 151 | Parsing the header pretty easy: 152 | 153 | ``` go 154 | // Parse IHDR chunk. 155 | // https://golang.org/src/image/png/reader.go?#L142 is your friend. 156 | func (png *PNG) parseIHDR(iHDR *Chunk) error { 157 | if iHDR.Length != iHDRlength { 158 | errString := fmt.Sprintf("invalid IHDR length: got %d - expected %d", 159 | iHDR.Length, iHDRlength) 160 | return errors.New(errString) 161 | } 162 | 163 | tmp := iHDR.Data 164 | var err error 165 | 166 | png.Width, err = uInt32ToInt(tmp[0:4]) 167 | if err != nil || png.Width <= 0 { 168 | errString := fmt.Sprintf("invalid width in iHDR - got %x", tmp[0:4]) 169 | return errors.New(errString) 170 | } 171 | 172 | png.Height, err = uInt32ToInt(tmp[4:8]) 173 | if err != nil || png.Height <= 0 { 174 | errString := fmt.Sprintf("invalid height in iHDR - got %x", tmp[4:8]) 175 | return errors.New(errString) 176 | } 177 | 178 | png.BitDepth = int(tmp[8]) 179 | png.ColorType = int(tmp[9]) 180 | 181 | // Only compression method 0 is supported 182 | if int(tmp[10]) != 0 { 183 | errString := fmt.Sprintf("invalid compression method - expected 0 - got %x", 184 | tmp[10]) 185 | return errors.New(errString) 186 | } 187 | png.CompressionMethod = int(tmp[10]) 188 | 189 | // Only filter method 0 is supported 190 | if int(tmp[11]) != 0 { 191 | errString := fmt.Sprintf("invalid filter method - expected 0 - got %x", 192 | tmp[11]) 193 | return errors.New(errString) 194 | } 195 | png.FilterMethod = int(tmp[11]) 196 | 197 | // Only interlace methods 0 and 1 are supported 198 | if int(tmp[12]) != 0 { 199 | errString := fmt.Sprintf("invalid interlace method - expected 0 or 1 - got %x", 200 | tmp[12]) 201 | return errors.New(errString) 202 | } 203 | png.InterlaceMethod = int(tmp[12]) 204 | 205 | return nil 206 | } 207 | ``` 208 | 209 | Our example's `IHDR` is: 210 | 211 | ``` js 212 | { 213 | "Width": 111, 214 | "Height": 115, 215 | "BitDepth": 8, 216 | "ColorType": 2, 217 | "CompressionMethod": 0, 218 | "FilterMethod": 0, 219 | "InterlaceMethod": 0, 220 | "NumberOfChunks": 6 221 | } 222 | ``` 223 | 224 | ## IDAT Chunks 225 | IDAT chunks contain the image data. They are compressed using [deflate][png-compression]. If you look at the first chunk, you will see the `zlib` magic header. This [stackoverflow answer][stack-zlib] lists them: 226 | 227 | - `78 01` - No Compression/low 228 | - `78 9C` - Default Compression 229 | - `78 DA` - Best Compression 230 | 231 | [Another answer][stack-zlib2] has more info: 232 | 233 | ``` 234 | Level | ZLIB | GZIP 235 | 1 | 78 01 | 1F 8B 236 | 2 | 78 5E | 1F 8B 237 | 3 | 78 5E | 1F 8B 238 | 4 | 78 5E | 1F 8B 239 | 5 | 78 5E | 1F 8B 240 | 6 | 78 9C | 1F 8B 241 | 7 | 78 DA | 1F 8B 242 | 8 | 78 DA | 1F 8B 243 | 9 | 78 DA | 1F 8B 244 | ``` 245 | 246 | I have seen a lot of random looking blobs starting with `78 9C` when reversing custom protocols at work. I have never seen the other two headers. 247 | 248 | In Go we can `inflate` the blob (decompress them) with [zlib.NewReader][zlib-newreader]: 249 | 250 | ``` js 251 | package main 252 | 253 | import ( 254 | "compress/zlib" 255 | "io" 256 | "os" 257 | ) 258 | 259 | func main() { 260 | zlibFile, err := os.Open("test.zlib") 261 | if err != nil { 262 | panic(err) 263 | } 264 | defer zlibFile.Close() 265 | 266 | r, err := zlib.NewReader(zlibFile) 267 | if err != nil { 268 | panic(err) 269 | } 270 | defer r.Close() 271 | 272 | outFile, err := os.Create("out-zlib") 273 | if err != nil { 274 | panic(err) 275 | } 276 | defer outFile.Close() 277 | 278 | io.Copy(outFile, r) 279 | } 280 | ``` 281 | 282 | Note that each chunk is not compressed individually. All IDAT chunks need to be extracted, concatenated and decompressed together. 283 | 284 | In our case, `IDAT` chunk has the `78 5E` header: 285 | 286 | ``` 287 | 00000000 78 5e ed c1 01 0d 00 00 00 c2 a0 f7 4f 6d 0f 07 |x^íÁ..... ÷Om..| 288 | 00000010 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 289 | 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 290 | 00000030 00 00 00 00 00 70 ae 06 96 0a 00 01 |.....p®.....| 291 | ``` 292 | 293 | Everything else is straightforward after this. 294 | 295 | # Tool Operation 296 | Operation is pretty simple. PNG is passed by `-file`. Tool will display the PNG info like height and width. `-c` flag will display the chunks and their first 20 bytes. Chunks can be saved to file individually. Modifying the program to collect, decompress and store the IDAT chunks is also simple. 297 | 298 | ![](images/05/05.1-04-tool-operation.png) 299 | 300 | 301 | [Footnote 1]: We can hide data in random chunks. The hidden chunk must be added before/after IDAT chunks. The standard expects the chain of IDAT chunks to be uninterrupted. 302 | 303 | 304 | 305 | 306 | [chunk-layout]: http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html#Chunk-layout 307 | [C.IHDR]: http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.IHDR 308 | [png-compression]: http://www.libpng.org/pub/png/spec/1.2/PNG-Compression.html 309 | [stack-zlib]: https://stackoverflow.com/a/17176881 310 | [stack-zlib2]: https://stackoverflow.com/a/43170354 311 | [zlib-newreader]: https://golang.org/pkg/compress/zlib/#NewReader 312 | [png-chunk]: https://parsiya.net/blog/2018-02-25-extracting-png-chunks-with-go/ --------------------------------------------------------------------------------