100% found this document useful (1 vote)
107 views

Go Programming

Programming in go language

Uploaded by

doj12516
Copyright
© © All Rights Reserved
Available Formats
Download as PDF or read online on Scribd
100% found this document useful (1 vote)
107 views

Go Programming

Programming in go language

Uploaded by

doj12516
Copyright
© © All Rights Reserved
Available Formats
Download as PDF or read online on Scribd
You are on page 1/ 139
25/0372025, 05:21 Hacking with Go Hacking with Go This is my attempt at filling the gap in Go security tooling. When starting to learn Go, | learned from a lot of tutorials but | could find nothing that is geared towards security professionals. These documents are based on the Gray/Black Hat Python/C# series of books. | like their style. Join me as | learn more about Go and attempt to introduce Go to security denizens without fluff and through practical applications. Table of Contents * 01 - Setting up a Go development environment + 02- + 03- Basics 02.1 - Packages, functions, variables, basic types, casting and constants 02.2 - for, if, else, switch and defer 02.3 - Pointers, structs, arrays, slices and range 02.4 - Methods and interfaces 02.5 - Printf, Scanf, bufio readers and maps 02.6 - Goroutines and channels 027 - Error handling Useful Go packages - WIP 03.1 - flag package 2seams,os.21 aching wih Go © 04.1- Basic TCP and UDP clients © 04.2 - TCP servers © 04.3 - TCP proxy ° 04.4- SSH clients © 04.5 - SSH Harvester * 05 - Parsing Files © 06.1 - Extracting PNG Chunks + 06 - Go-Fuzz © 06.1 - Go-Fuzz Quickstart © 06.2 - Fuzzing iprange with Go-Fuzz © 06.2 - Fuzzing goexif2 with Go-Fuzz FAQ Why not use Python? 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, | think Go has its merits and can occupy a niche. Why not use other tutorials? 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. Why not just use Black Hat Go? There's a book named Black Hat Go by No Starch in production. Looking at the author list, | cannot compete with them in terms of experience and knowledge. That is a proper book with editors and a publisher while | am just some rando learning as | go. It does not take a lot of CPU power to decide the book will be better. 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. Update February 2020: Black Hat Go has been released. Please see the code samples at https://github.com/blackhat-go/bhg. Rewrite in Rust/Haskell Honestly | will be very much interested in a similar guide for Rust/Haskell geared for security people. Please let me know if you create one. Feedback 2sevams,os21 aching wih Go | 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 Other resources There are tons of Go resources online. | 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. The following links helped me get started: * GoDoe: https://godoc.org/ * A Tour of Go: https://tour.golang.org/ * Go by Example: https://gobyexample.com/ * Go playground: https://play.golang.org/ * Effective Go: https://golang.org/docjeftective_go.html Similar resources to Hacking with Go : * Security with Go published by Packt: https://github.com/PacktPublishing/Security-with-Go © goHackTools : https://github.com/dreddsa5dies/goHackTools * Go programming language secure coding practices guide License * Code in this repository is licensed under GPLv3. * Non-code content is licensed under Creative Commons Attribution-NonCommercial 4.0 (CC BY-NC 4.0). 01 - Setting up a Go development environment | 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 section on Go website has how-tos for most popular platforms. You can find binaries and building instructions. You can get free Windows VMs from modern.ie. Make a snapshot after you everything is set up. They expire in 90 ays and you can only re-arm them multiple times. * Installation on Windows 10 VM * GOPATH 2sevams,os21 aching wih Go * Editor © Go playground ° Offline coding * gofmt * Starting curly brace Installation on Windows 10 VM 1. Go to https://golang.org/doc/install and download the MSI binary. 2. Install the MSI, choose the default location 3. Choose a development directory. | have created a shared directory in my VM. This way | can code in host and run the guest. In my case it's Z:\Go where Zs the shared drive/directory. 4. Set the following environmental variables (installer might have already set some up): © GOROOT : C:\Go © GOPATH : Z:\Go or the directory from step 3. 5. Add C:\Go\Bin to PATH. 6. Open anew cmd and run go env . You should see what you have setup. Output of go env in my Windows 10 VM is: $ go env set GOARC! set GOBIN: set GOEXE=.exe set GOHOSTARCH=amd64 set GOHOSTOS=windows set G00S=windows set GOPATH=Z:\Go\ set GORACI set GOROO set GOTOOLDI set GCCG0=gccgo set CC=gcc set GOGCCFLAGS=-m64 -mthreads —fnessage-Length=0 —fdebug-prefix-map=C: \Users\IEUser\AppData\Local\Temp\go-bui ld352203231=/tmp/go-build “gno-record-gcc-switches set OxX=g++ set CGO_| set CGO_CFLAGS=-g -02 set CGO_CPPFLAGS= set CGO_CXXFLAGS=-g -02 set CGO_FFLAGS=-g -02 set CGO_LDFLAGS=-g -02 set PKG_CONFIG=pkg-conf ig .Go\pkg\tool\windows_amd64 25/0372025, 05:21 GOPATH Hacking with Go You can write Go code anywhere but only code ina GOPATH directory can be executed with go 2 run 2, Go to the development path in step 3 of last section and create three directories inside it: * sre: Source code. * bin : Compiled files. * pkg : Executables. You can clone this repository in src and then run everything in code The directory structure looks like in the Windows 10 VM: Z:\Gotree /F z. [bin -pko sre L_-Hacking-with-Go Lcode Le @[email protected] Test application Let's write a quick "Hello World" application and run it. package main import "fmt" func main() £ fmt.PrintIn("Hello World! ‘And we can runit with go run @1-01-HeLloWorld.go Z:\Go\sre\hacking-with-go\code\@1>go run @1-@1-HelloWorld. go Hello World! 2sevams,os21 aching wih Go Choose whatever you like. There are many editors with Go support (you will see below) to choose from. Some in no particular order are: SublimeText using GoSublime package. Atom via go-plus package. Visual Studio Code with Go extension. * Vim-go. Emacs go-mode. | personally use Sublime Text 3 and GoSublime. Go playground The online go playground at https://play.golang.org/ is good for prototyping/testing and sharing auick scripts. It's pretty useful when Go is not installed on the machine. For more information read Inside the Go Playground. Offline coding It's possible to run both the playground and documentation server offline. + godoc ~http :1234 will run the the documentation server at localthost:1234 * go tool tour will start an offline version of Tour of Go at Locathost:3999 . This allows coding offline in browser in Go playground. gofmt gotmt 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. | personally do not agree with gofmt . For example it uses tabs (( like spaces). Tab-width is fixed at four (| ike two). But it's better if our code adheres to language standards. For more information read go fmt your code. For usage see Command gofmt. Starting curly brace 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. This is wrong: func main() t 25/0372025, 05:21 Hacking with Go fmt.PrintIn("Hello World!) + This is correct: func main() ¢ fmt.PrintIn("Hello World!") + Continue reading = 02 - Basics 02 - Basics 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. These notes were originally created during the tutorials at Tour of Go and some other sources. Then more were added to make it a reference/cheat sheet. Table of Contents * 02.1- Packages, functions, variables, basic types, casting and constants * 02.2 - for, if, else, switch and defer + 02.3 - Pointers, structs, arrays, slices and range + 02.4 - Methods and interfaces * 02.5 - Printf, Scanf, bufio readers and maps + 02.6 - Goroutines and channels * 02.7 - Error handling 02.1 - Packages, functions, variables, basic types, casting and constants * Packages © Exported names * Functions © Functions can return multiple values © Named return values © init function 25/0372025, 05:21 Hacking with Go © Initialization © Initialization Values © Short variable declarations Basic types Casting Constants Raw strings Packages Go is divided into packages. Packages are the equivalent of modules in Python. Only the main package can be executed with go run We cai import packages with import . The Hello World application imported the fmt package. Multiple imports are similar: import ( "fmt" “math/ rand “otherimport" Exported names In Go, a name is exported if it begins with a capital letter. When importing a package, you can refer only to its exported names. Unexported names are not accessible from outside the package. Functions Unlike C, type comes after variable name except for pointers. // 02.1-01-multiply.go package main import "fmt" func multiply(x int, y int) int { return x * y + func main() { 25/0372025, 05:21 Hacking with Go fmt .PrintIn(multiply(10,20)) https://play.golang.org/p/iZrNpGAEWds Functions can return multiple values 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. // 02.1-02-addTwo. go package main import "fmt" func addTwo(x int, y int) (int, int) { return x+2, y+2 y func main() { fmt. Print Un(addTwo(10,20)) y https://play.golang.org/p/sHOLeYIBPOM If multiple variables have the same type we can declare them like this: func addTwo(x, y int) (int, int) { return x#2, y+2 + Attps://play.golang.org/p/Dwi94tWwetks Named return values Return values can be named. If so, they are treated as variables defined in the function. Areturn 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. // 02.1-03-addTwo2.go package main import "fmt" func addTwo2(x int. v int) (xPlusTwo int. yPlusTwo int) { 259092023, 0521 Hocking with Go yPlusTwo = y +2 return xPlusTwo, yPlusTwo + func main() { fmt. PrintIn(addTwo2(20, 38) ) + https://play.golang.org/p/wiC9HJOuxDN function init function is used to set up the state. A common practice is to declare flags in it. 1. Imported packages are initialized. 2. Variable declarations evaluate their initializers. 3. init function executes. // @2.1-09-init.go package main import "fmt" func init() { fmt.Println("Executing init function!") y func main() { fmt.PrintUn("Executing main!") + https://play.golang.org/p/HfL8YjGMsmw Resulting in: $ go run 02.1-09-init.go Executing init function! Executing main! Like any other function, v ‘ables declared in init are only valid there. Variables Use var. 25/0372025, 05:21 Hacking with Go © var x int Can be combined for multiple variables: © var xy int var x int, y int Initialization Variables can be initialized. * vara, b int = 10, 20 or * vara int = 10 + var b int = 20 If initialized value is present during declaration, type can be omitted: * var sampleInt, sampleBoolean, sampleString = 30, true, "Hello" or * var sampleInt = 30 * var sampleBoolean = true © var sampleString “Hello” // 02.1-04-variables.go package main import "fmt" func main() { var a, b int = 10, 20 var sampleInt, sampleBoolean, sampleString = 30, true, "Hello" fmt.PrintIn(a, b , sampleInt, sampleBoolean, sampleString) https://play.golang.org/p/TnRrIC43-NR Initialization Values If no initial value is assigned to a declared variable, it will get a zero value: * @ for numeric types (int, float, etc.). * false for the boolean type. 25/0372025, 05:21 Hacking with Go Short variable declarations Inside a function (including main ), the := short assignment statement can be used in place of a var declaration with implicit type. Outside a function, every statement begins with a keyword ( var, func) so the := construct is not available. // 02.1-05-short-declaration.go package main import "fmt" func main() { sampleInt, sampleBoolean, sampleString 3@, true, "Hello" fmt.PrintIn(sampleInt, sampleBoolean, sampleString) Attps://play.golang.org/p/RMC-9h4eBLD var statements can be put in different lines (increases readability): var ( sampleInt 30 sampleBoolean = true sampleString "Hello" Several other Go constructs use the same format. For example import and const Basic types bool string int int8 int16 int32 intéd // use int unless you want a specific size uint uint® uint16 uint32 uint64 uintptr // ditto, use uint byte // alias for ints rune // alias for int32 // represents a Unicode char float32 floated 25/0372025, 05:21 Hacking with Go complex64 complex128 Casting Casting needs to be explicit, unlike C where some castings worked out of the box. // 02,1-06-casting.go package main import ( nent ) func main() { var a, b int = 20, 30 // Need to convert a and b to float32 before the division var div float32 = float32(a) / float32(b) // Cast float32 to int var divint = int(div) fmt.PrintIn(div, divint) https://play.golang.org/p/wKtudyE918q Constants Declared with const keyword. Can be character, string, boolean or numeric. Cannot use := . Coding standard requires constants to start with a capital letter. // 02.1-07-const.go package main import "fmt" const Whatever = "whatever" func main() { fmt. Print In (Whatever) const One = 1 fmt. PrintIn (One) https://play.golang.org/p/RaNzEnRIFZ4 25/0372025, 05:21 Hacking with Go Multiple constants can be declared together: const ( Const1 = "Constant String" Inti = 12345 True = true Raw strings Go has two types of strings: + Interpreted strings: The typical string type created with " . Can contain anything except new line and unescaped " * Raw strings: Encoded between "*" (backticks) can contain new lines and other artifacts. // 02.1-08-rawstring. go package main import "fmt" func main() < rawstr i= “First Line some new Lines more new Lines “double quotes” fmt. Print(rawstr) https://play.golang.org/p/D8TWwnBhwM0o Continue reading = 02.2 - for, if, else, switch and defer 02.2 - for, if, else, switch and defer + For 25/0372025, 05:21 Hacking with Go * if * Short statements * else * switch * defer For Similar to C with two differences: + No parenthesis around the three components. Having parenthesis will give result in an error. * Curly braces { } are always required and the first one needs to be in the same line as for, if ete, It has three components: * for init; condition; post { } /1 02,2-01-for1.go package main import "fmt" func main() { // var sum int e sum for i := 0; i < 20; itt { sum += i fmt. Print tn (sum) Init and post (first and last) components are optional and turn for into white : // 02.2-02-for2.go package main import "fmt" func main() { // var sum int sum, i := 0 for i< 20 { // while (i<20) sum += i jee 25/0372025, 05:21 Hacking with Go fmt. PrintIn(sum) Without the condition it turns into for(;;) or while(1) for { // while(1) ++ and - Don't be fooled by their use in for examples. According to the FAQ, 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. This will not work: 11 02,2-03-incdec. go package main import "fmt" func main() { // var sum int sum, i := 0 // This will not work sum = itt fmt. PrintIn(sum) 2:\Go\src\Hacking-with-Go\code\@2>go run 02.2-03-incdec.go # conmand-Line-arguments +\02.2-03-incdec.go syntax error: unexpected ++ at end of statement if Does not need parenthesis but needs curly braces. // 02.2-04-if1, 90 package main import "fmt" 25/0372025, 05:21 Hacking with Go func main() { ats 10 20 ifb>a{ fmt.Printin(b, } > Short statements 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. They can be used in if s. // 02.2-05-if2, 90 package main import "fmt" func main() £ if varl := 20; var > 16 ¢ fmt.PrintIn("Inside it » varl) + // Cannot use the variable vari here In this code vari := 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. Short statements are usually used for executing a function and checking the return value with an if. else else is similar to C else. If the corresponding if has a short statement then any variables declared in the short statement are also in scope in the else block. // 02.2-06-else.go 2siox2m23,08:21 Hocking with Go import "fmt" fune main() £ if varl := 20; varl > 100 { fmt.PrintIn("Inside it } else { // Can use var here fmt.Printtn("Inside else:", varl) varl) + // Cannot use varl here + switch Similar to C switch with some differences: + Doesn't automatically go to the next switch statement unless you have falithrough in the end. The fallthrough only works if it's the last statement in the case. * Can have a short statement like if . // 02.2-07-switch1.go package main import ( me jath/rand" // This is not cryptographically secure! “time func main() { // Seeding rand rand.Seed(time.Now() .UnixNano()) fmt.PrintIn("Choosing a random numbe: switch num := rand.Intn(3); num { case fmt. Printtn("1") case fmt. Printin( default: fmt. Printtn( Cases can have if conditions if we use a switch with an empty value: 25/0372025, 05:21 Hacking with Go // 02.2-08-switch2..90 package main import ( “emt! “math/rand" // This is not cryptographically secure! "time" func main() ¢ // Seeding rand rand. Seed(time.Now().UnixNano()) fmt.Printtn("Choosing a random number: switch num := rand.Intn(100); { case num < 50: fmt.Printtn("Less than 50") default: fmt.Printtn("More than 50") The short statement does not have to be part of the switch: // 02.2-09-switch3.go package main import ( me" ath/rand" // This is not cryptographically secure! "time" func main() ¢ // Seeding rand rand. Seed(time.Now().UnixNano()) fmt.PrintIn("Choosing a random number: num := rand. Intn(100) switch { case num < 50: fmt.Printin("Less than 50") default: fmt.Printtn("More than 50") defer 25/03/2025, 05:21 Hacking with Go defer is another interesting feature in Go. It defers the execution of a function until the calling function returns. 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. /1 02,2-10-defer1.go package main import "fmt" func main() { defer fmt.PrintIn("This runs after main") fmt.PrintIn("Main ended") Results in: Z:\Go\sre\Hacking-with-Go\code\@2>go run @2.2-10-defer1.go Main ended This runs after main Argument values are saved when the defer statement is reached but it is executed later. // 02.2-11-defer2.go package main import "fmt" func main() ¢ num := 1 defer fmt.PrintIn("After main returns", num) nume+ fmt.PrintIn("Inside main", num) $ go run 02,2-11-defer2.go Inside main 2 After main returns 1 The value of num was 1 when the print was deferred. 25/0372025, 05:21 Hacking with Go 02.3 - Pointers, structs, arrays, slices and range Pointers © Function arguments: variables vs. pointers Structs Arrays Slices @ Slice length and capacity ° make © append range Pointers Similar to C: * Point with *: var p xint == int *p; * Generate pointer (get address of) with &: i := 1 and p = &i No pointer arithmetic. Function arguments: variables vs. pointers Functions/methods accept both variables and pointers. The golden rule is: + Pass pointers when function/method needs to modify the parameter. When a variable is passed, the function/method gets a copy and the original copy is not modified. pointers the underlying value is modified Structs Go does not have classes. It has structs like C. Exported field names need to be uppercase to be visible outside the defining package. // 02.3-01-structs.go package main import "fmt" 2siox2m23,08:21 Hocking with Go FirstName string LastName string func main() { // Make an instance studentOne := Student{” nder", "Wiggin"} // Now we can access fields fmt.PrintIn(studentOne. FirstName) 7/ We can just assign fields using names, anything not assigned will be // initialized with "zero" as we have seen before studentTwo := Student{FirstName: "Petra" // We will print "{Petra }" notice the space after Petra which is supposed 7/ to be the delimiter between the fields, LastName is nil because it is not // given a value fmt. PrintIn(studentTwo) // Can also make a pointer to a struct p t= &studentOne // Now instead of p.LastName (doesn't work) we can just use p.LastName // fwt.PrintUn((p).LastName) will not work with error message: invalid indirect o fmt. PrintIn(p. LastName) // which is the same as fmt. Print \n(studentOne. LastName) // We can just create a pointer out of the blue p2 i= &Student{"Hercule", "Poirot"} fmt. Printtn(p2) 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. Arrays var a [10]int int a[10]; . Arrays cannot be resized. // 02.3-02-array.go package main import "fmt" 25/0372025, 05:21 Hacking with Go var a [5]int alo] = 10 ala] = 20 fmt.PrintIn(a) // [10 0 @ 0 20] // Array can be initialized during creation // characters(2] is empty characters := [3]string{"Ender", "Pentra"} fmt.PrintIn(characters) // [Ender Pentra ] Slices 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 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. // 02,3-03-slicel. go package main import "fmt" func main() { // Create an array of strings with 3 members characters := [3]string{"Ender", "Petra", "Mazer"} // Last index is exclusive // aliMenbers [Istring := characters [0:3] var allWembers []string = characters [0:3] fmt.PrintIn("ALl members", allMembers) var lastTwo []string = characters [1:3] fmt.PrintIn("Last two members", lastTwo) // Replace Mazer with Bean fmt.PrintIn("Replacing Mazer with Bean") aliMenbers[2] = "Bean" fmt.PrintIn("All members after Bean swap", characters) fmt.PrintIn("Last two members after Bean swap", lastTwo) 2sevams,os21 asking wih Go 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. J 02,3-04-sLice2. go package main import "fmt" func main() { // Slice literal of type struct, the underlying array is created automatically sliceStruct := [Jstruct { a, b int H {1, 23, 3, 4}, {5, 6}, // need this comma in the end otherwise it will not work fmt.PrintIn(sliceStruct) If a length is not specified during array creation, the result is a slice literal as seen above. If we do not want to specify a length we can use [...]. /1 02.3-05-slice3. go package main import "fmt" func main() { characters Istring{"Ender", "Petra", "Mazer" fmt.Println(characters) Slice length and capacity Slices have length and capacity. + Length is the current number of items in the slice. Returned by len(slice) « + 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 2sevams,os21 asking wih Go 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. In most cases, we do not care about capacity. Create slices and append to them. // 02,3-06-sLice4. go package main import "fmt" func main() { ints := [...]int{@, 1, 2, 3, 4, 5} fmt.PrintIn(ints) slicel := ints [2:6] // \en=4 and cap=4 (from 3rd item of the array until the end) printSlice(slice1) slicel = ints [2:4] // \en=2 but cap will remain 4 printSlice(slice1) // Copied from the tour func printSlice(s [Jint) { firt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s) } make To create dynamically-sized arrays use make . make creates a zero-ed array and returns a slice pointing to it. © slicel : make({]int, 10) creates a it array of length 10. * slice2 := make([]int, 5, 10) creates an int array of length 5 and capacity of 10. We can append stuff to slices and it grows as needed: * slicel = append(slicel, 1) We can append multiple elements: © slicel = append(slicel, 1, 2, 3) append aeams,os21 asking wih Go In order to append one slice to another (obviously they should be of the same type), we have to use «++ as follows: © slicel append(slicel, slice2...) append is a variadic function, meaning it can an arbitrary number of arguments. By passing slice2... , we are essentially passing each member of slice? one by one to append This is pretty useful later on when we want to append two byte slices together. // 02.3-07-sLice-append. go package main import "fmt" func main() { // Create a slice pointing to an int array s1 i= make({Jint, 5) fmt.PrintIn(si) // [0 0 @ 6 0] for i t= 0; i < len(si); i++ { sili] =i + fmt.PrintIn(s1) // (0123 4] make([]int, 3) fori s2li] =a @; i < Len(s2); itt { + fmt.Printtn(s2) // [8 1 2) 33 ) append(s1, s2. fmt.Printtn(s3) // (01234012) range range iterates over slices. It returns an index and a copy of the item stored at that index. * for index, value := range slice value is optional but index is not. Ignore either with 25/0372025, 05:21 Hacking with Go // 02.3-08-range.go package main import "fmt" func main() { characters := [3]string{"Ender", "Petra", "Mazer"} for i, v i= range characters { fmt.Printtn(i, v) ¥ // @ Ender // 1 Petra // 2 Mazer fmt. Printin(" // Only using index for i := range characters { fmt.PrintIn(i, characters [il) fmt.Printin // Ignoring index for _, v t= range characters { 7/ No non-elaborate way to get index here fmt. Printtn(v) // Ender // Petra // Mazer Continue reading = 02.4 - Methods and interfaces# 02.4 - Methods and interfaces * Methods © Create methods for slices © Value vs. pointer receivers © Pointer Receivers © When to use methods vs. functions * Interfaces + Type switch * Stringers © Solution to the Stringers exercise 25/0372025, 05:21 Hacking with Go Methods Methods can be defined for types (e.g. structs). A method is a function with a special receiver, receiver is the type that a method is defined for. Create methods for slices Let's say want to create a method for a string array that prints the members. First problem is that we cannot create a method for type []string because it's an unnamed type and they cannot be method receivers. The trick is to declare a new type for [1string and then define the method for that type. /1 02.4-01-method1. go package main import "fmt" // Create a new type for []string type StringSlice []string // define the method for StringSlice func (x StringSlice) PrintSlice() { for _, v := range x fmt.Printin(v) } + func main() { // Create an array of strings with 3 members characters := [3]string{"Ender", "Petra", "Mazer"} // Create a StringSlice var allMembers StringSlice = characters [0:3] // Now we can call the method on it allMenbers.PrintSlice() // Ender // Petra // Mazer // aliMembers.PrintStice() // aliMenbers,PrintSlice undefined (type [Jstring has no field or method PrintSlic Note that we cannot call PrintSlice() on {Jstring although they are essentially the same type. 25/0372025, 05:21 Hacking with Go Value vs. pointer receivers In the previous example we created a value receiver. In methods with value receivers, the method gets a copy of the object and the initial object is not modified We can also designate a pointer as receiver. In this case, any changes on the pointer inside the method are reflected on the referenced object. Pointer receivers are usually used when a method changes the object or when it's called on a large struct. Because value receivers copy the whole object, a large struct will consume a lot of memory but pointer receivers do not have this overhead. Pointer Receivers Pointer receivers get a pointer instead of a value but can modify the referenced object. In the following code, Tuple's fields will be modified by ModifyTuplePointer() but not by ModifyTupleValue() . However, this is not the case for slices (e.g. IntSlice in the code). Both value and pointer receivers modify the slice. Pointer receivers are more efficient because they do not copy the original object. All methods for one type should either have value receivers or pointer receivers, do not mix and match like the code below :). // 02.4-02-method2.go package main import "fmt" // Tuple type type Tuple struct { A, B int y // Should not change the value of the object as it works on a copy of it func (x Tuple) ModifyTupleValue() { xA= 2 x.B=2 + // Should change the value of the object func (x #Tuple) ModifyTuplePointer() { XA = 3 X.B = 3 25/0372025, 05:21 Hacking with Go func (x IntSlice) PrintSlice() { fmt. Printin(x) + // Modifies the IntSlice although it's by value func (x IntSlice) ModifySliceVatue() { x[@] = 1 } // Modifies the IntSlice func (x #IntSlice) ModifySlicePointer() { (+x) [0] = 2 + func main() { tup := Tuple{1, 1} tup.ModifyTup leva lue() fmt.PrintIn(tup) // {1 1} - Does not change tup-ModifyTuplePointer() fmt.PrintIn(tup) // {3 3} - Modified by pointer receiver var slicel IntSlice = make({Jint, 5) slicel.PrintSlice() // [@ 0 @ 0 0] slicel.ModifysliceValue() slicel.PrintSlice() // [1 0 00 0] slicel.ModifySlicePointer() slicel.PrintSlice() // [2 0 @ 0 0] When to use methods vs. functions Methods are special functions. In general use methods when: + The output is based on the state of the receiver. Functions do not care about states. + The receiver must to be modified. + The method and receiver are logically connected. Interfaces An interface is not Generics! An interface can be one type of a set of types that implement a set of specific methods. 25/03/2025, 05:21 Hacking with Go For example we will define an interface which has the method MyPrint() . If we define and implement MyPrint() for type B, a variable of type B can be assigned to an interface of that type. // 02.4-03-interfacel. go package main import "fmt" // Define new interface type MyPrinter interface { MyPrint() } // Define a type for int type MyInt int // Define MyPrint() for MyInt func (i MyInt) MyPrint() { fmt. Println(i) } // Define a type for floate4 type MyFloat floated // Define MyPrint() for MyFloat func (f MyFloat) MyPrint() { fmt-Printtn(f) func main() { // Define interface var interfacel MyPrinter f1 r= MyFloat (1.2345) // Assign a float to interface interfacel = f1 // Call MyPrint() on interface interfacel.MyPrint() // 1.2345 i1 := MyInt(10) // Assign an int to interface interfacel = i1 // Call MyPrint() on interface interfacel.myPrint() // 10 Empty Interface is interface {} and can hold any type. We are going to use empty interfaces a lot in functions that handle unknown types. 25/0372025, 05:21 Hacking with Go // 02.4-04-interface2..9o package main import "fmt" var emptyInterface interface{} type Tuple struct { A, B int y func main() { // Use int intl := 10 emptyInterface = inti fmt.PrintIn(emptyinterface) // 10 // Use float floati := 1.2345 emptyInterface = float1 fmt.PrintIn(emptyInterface) // 1.2345 // Use custom struct tuplel := Tuple(s, 5} emptyInterface = tuplel fnt.PrintIn(emptyInterface) // {5 5} We can access the value inside the interface after casting. But if the interface does not contain a float, it will trigger a panic: © myFloat := myInterface(floaté4) In order to prevent panic we can check the error returned by casting and handle the error. + myFloat, ok := myInterface(floaté4) If the interface has a float, ok willbe true and otherwise false // 02.4-05-interface3.go package main import "fmt" func main() { var interfacel interface{} = 1234.5 // Only print f1 if cast was successful 25/0372025, 05:21 Hacking with Go fmt. Printin("Float") fmt.Printtn(f1) // 1234.5 2 := interfacel. (floated) fmt.Println(f2) // 1234.5 No panic but not recommended // This will trigger a panic // 31 = interfacel. (int) 42, ok := interfacel.(int) // No panic fmt.Printtn(i2, ok) // @ false , Type switch Type switches are usually used inside functions that accept empty interfaces. They are used to determine the type of data that inside the interface and act accordingly. A type switch is a switch on interface. (type) and some cases. // 02.4-06-typeswitch. go package main import "fmt" func printType(i interfacet}) { + // Do a type switch on interface switch val := i.(type) { // Tf an int is passed case int: fmt. Println("int") case string: fmt. Printin("string") case floated: fmt. Printtn("floaté4") default: fmt.Printin("Other:", val) } func main() { printType(10) M7 int printType("Hello") // string printType(156.32) // floate4 printType(nil) —// Other: printType(false) // Other: false 25/0372025, 05:21 Hacking with Go Stringers Stringers overload print methods. A Stringer is a method named String() that returns a string and is defined with a specific type as receiver (usually a struct). type Stringer interface { String() string , After the definition, if any Print function is called on the struct, the Stringer will be invoked instead. For example if a struct is printed with %v format string verb (we will see later that this verb prints the value of an object), Stringer is invoked. // 02.4-07-stringer1.go package main import "fmt" // Define a struct type Tuple struct { A, B int + // Create a Stringer for Tuple func (t Tuple) String() string { // Sprintf is similar to the equivalent in C return fmt.Sprintf("A: %d, B: %d", t.A, t.B) } func main() { tuplel := Tuple(10, 10} tuple2 := Tuple(20, 20} fmt.PrintIn(tuplel) // A: 10, B: 10 fmt.PrintUn(tuple2) // Ai 20, Bi 20 Solution to the Stringers exercise Make the IPAddr type implement fmt.Stringer to print the address as a dotted quad. For instance, IpAddr{1, 2, 3, 4} should printas 1.2.3.4. // 02.4-08-stringer2..go package main ‘import "fmt" 2siox2m23,08:21 Hocking with Go type IPAddr [4]byte // T0D0: Add a "String() string" method to IPAddr. func (ip IPAddr) String() string { return fmt.Sprintf("sv.%v.%v.%v", ipf@], ip{1], ip(21, ip{3]) y func main() { hosts := map[string] IPAddr{ "loopback": {127, 0, @, 1}, "googleDNs": {8, 8, 8, 8}, + for name, ip := range hosts { fmt.Printf("v: %v\n", name, ip) } Continue reading = 02.5 - Printf, Scanf, bufio readers and maps 02.5 - Printf, Scanf, bufio readers and maps © Print © Print verbs = Decimals = Floats = Value = Strings = Others = Print verbs in action * Scan ° Scan verbs = Reading user input with Scanin = What's wrong with Scanin? * bufio.Reader * Maps Print The fmt package contains printf . It's very similar to the C equivalent. These three need a format string: © fmt.Sprintf returns a string. 2siov2m23,08:21, Hocking with Go + fmt.Fprintf takes any objects that implements io.Writer for example os.Stdout and os.Stderr * fmt.Printf prints to stdout. The following are similar but do not need a format string: + fmt.Print and fnt-Printtn © fmt. Fprint © fmt. Sprint Print verbs To format strings we can use verbs (also known as switches). For more information on switches, see the fmt package source. Decimals 4d : digits = numbers. * and :1n = width of number. Right justified and padded with spaces. To left justify use - like %- nd . If n is less than the number of digits nothing happens. +b : number in binary. + %c: chr(int) , prints the character corresponding to the number. © %x :hex. Floats + f= float. + %n.mf :n = decimal width, m = float width. Right justified. To left justify use - like %-n.mf . If nis less than the number of digits nothing happens. + %e and %€ : scientific notation (output is a bit different from each other) Value + sy or value: catch all format. Will print based on value. + %¢v : will print struct's field names if we are printing a struct. Has no effect on anything else. + ‘stv : prints code that will generate that output. For example for a struct instance it will give code that creates such a struct instance and initializes it with the current values of the struct instance. Strings + %q : double-quotes the strings before printing and also prints any invisible characters. © 4s: string, 2sevams,os21 aching wih Go + ans : control width of string, Right justified, padded with spaces. To left justify use - like ns . If nis less than the length of the string, nothing happens Others + %t : boolean. * %T : prints the type of a value. For example int or main.myType . Print verbs in action // 02.5-01-print-verbs gos package main import "fmt" type myType struct { Field1 int field2 string field3 floated } func main() ¢ Vint fmt.Printin("int:") intl := 123 fmt.Printf("%v\n", int1) If 123 fmt.Print#("%d\n", int1) M1 123 fmt.Printf("|6d|\n", intl) // | 123] fmt.Printf("|%-6d|\n", intl) // |123 | fmt-Printf("%T\n", int) M7 int fmt. Print#("%sx\n", int1) WT fmt.Print#("%b\n", int1) yf 11i10811 fmt.Print#("%e\n", int1) // %1e(int=123) fmt. Print#("%c\n" fmt.Printin() int1) H { ~ Ox7B = 123 // Float fmt.Printin("float floatl := 1234.56 fmt.Printf("%f\n", float1) /1 1234.560000 fmt.Printf("|%3.2F|\n", float1) // |1234.56| firt.Printf(""|%-3.2F|\n", floatl) // |1234.56| fnt.Printf("%e\n", floatl) 11 1,2345600+03 fmt .Printf("%E\n", float1) 11 1.234560E+03 ft.Printtn() ) // string fmt.Printin("strin etrinal «= "Petra 25/0372025, 05:21 Hacking with Go fmt.Print#("|S10s|\n", stringl) // | Petra| fmrt.Print#(""|%-105|\n", stringl) // |Petra | fmt.Printf("%T\n", stringl) 7 string fmt.PrinttnQ) // boolean fnt.PrintIn("boolean:") booleant := true fmt.Printf("%t\n", booleanl) // true fmt.Printf("%T\n", boolean1) // bool fmt.Printin() 7/ struct type fmt. Printtn("struct:" struct1 := myType{10, "Ender", -10.2} fmt.Printf("%v\n", struct) // {10 Ender -10.2) fmt.Printf("%+v\n", struct1) // {field1:10 field2:Ender field3:-10.2} fmt.Printf("%#v\n", struct1) // main.myType{field1:10, field2:"Ender", field3:—10, fmt.Printf("T\n", structl) // main.myType Scan As expected Go has scan functions for reading input. Like printf the package description is comprehensive. The functions read from standard input ( os.Stdin ): These read from io.Reader Scan : treats new lines as spaces. Scanf : parses arguments according to a format string. Scanln : reads one line. Fscan Fscant Fscanln These read from an argument string: Sscan Sscant Sscantn As you can guess, the following stop at the first new line or EOF: ecanin 25/0372025, 05:21 Hacking with Go © Fscantn © Sscantn While these treat new lines as spaces: © Scan © Fscan * Sscan Similar to Printf we can use format strings with these functions: * Scant * Fscanf © Sscanf Scan verbs Scan verbs are the same as Print . sp, %T and # + flags are not implemented. Apart from %c every other verb discards leading whitespace (except new lines). Reading user input with Scanin Let's start by something simple like reading a line from input: // 02.5-02-scan1.go package main import "fmt" func main() { var s string n, err := fmt,Scantn(Ss) if err t= nil { panic(err) fmt-Printf("Entered %d word(s): %s", ny s) All is well when input does not have any whitespace (e.g. space): $ go run @2.5-02-scan1.go HeLloHelto Entered 1 word(s): HelloHello 25/0372025, 05:21 Hacking with Go But When input has whitespace: $ go run @2.5-02-scan1.go Hello Hello panic: expected newline goroutine 1 [running]: main. main() 2: /Go/src/Hacking-with-Go/code/02/02.5/02-5-02-scanl.go:10 +0xlae exit status 2 What's wrong with Scanin? 1. Scan does not return the number of characters as we expect from the C equivalent. It returns the number of words entered. 2. Scan and friends separate words by whitespace. Meaning when we entered Hello Hello , they are counted as two words. Scanin stored the first Hello in s and was expecting anew line or EOF to finish that. Instead it got a new word and panicked If we wanted to just read a number or anything without whitespace, it would have worked. If we replace Scantn with Scan in the code, the program will not panic but will ignore anything after the first whitespace. Lesson learned Don't use Scan for reading user input with whitespace. bufio.Reader An easier way to read user input is through bufio readers. We are looking for the quickest ways to get things done after all // 02.5-03-bufioreadstring.go package main import ( “pufio" emt gst ) func main() ¢ reader := bufio.NewReader(os.Stdin) // ReadString will read until first new line input, err := reader.ReadString("\n') // Need to pass '\n' as char (byte) if-err t= nil { 25/0372025, 05:21 Hacking with Go ¥ fmt.Printf("Entered %s", input) ReadString reads until the first occurrence of its argument (delimiter). The delimiter should be a byte hence we need to pass a char using single quotes ( \n ). "\n" isa string and will not work. bufio.Reader has more methods for reading different types. For example we can directly read user input and convert it to bytes with [ReadBytes] (bufio-readbytes}. // 02,5-04-buf ioreadbytes.go package main import ( ‘buf io" emt gs ) func main() { reader := bufio.NewReader(os.Stdin) // Read bytes until the new Line input, err := reader.ReadBytes("\n') // Need to pass '\n' as char (byte) iferr t= nit { panic(err) ¥ 7/ Print type of "input" and its value fnt.Printf("Entered type ST, ®v\n", input, input) // Print bytes as string fmt.Printf("Print bytes as string with %%s %s", input) We are printing the type of input and its value as-is first. Then we print the bytes as string with as. $ go run 02,5-04-bufioreadbytes.go Hello 0123456789 Entered type [Juint8, [72 101 108 108 111 32 48 49 5@ 51 52 53 5455 56 57 13 10] Print bytes as string with %s Hello 0123456789 ‘As you can see bytes are just uint8 (unsigned ints) and printing them yields decimal values and not hex. Don't worry about bytes and strings now. We will have a byte manipulation chapter. 25/0372025, 05:21 Hacking with Go Fast lookup/add/delete. Each key is associated with a value (similar to Python dict). Declare an initialized map: + mapName := make(map [KeyType] ValueType) KeyType needs to bea comparable type. ValueType can be anything. If a key does not exist, the result is a zero value. For example 0 for int . To check if a key exists or not (0 might be a valid value in the map) use: * value, ok := mapName [key] If ok is true then the key exists (and false if the key is not there). To test for a key without getting the value drop the value like this _, ok : then just check ok mapName(key] and range iterates over the contents of a map like arrays/slices. But we get keys instead of indexes. Typically we use the range witha for : * for key, value := range mapName We can create a map using data: © m r= map[string] int{"keyo": @, "key1": 1} Delete a key/value pair with delete : * delete(n, "keyo") // 02.5-05-maps.90 package main import “fnt" type intMap map [int] int // Create a Stringer for this map type func (i intMap) String() string { var s string s += fmt.Sprintf("Map type %T\ s ¢= fmt.Sprint#("Length: %d\n", len(i)) // Iterate through all key/value pairs for k, v := range i { s += fmt.Sprintf("[%v] = %v\n", k, v) } 25/0372025, 05:21 Hacking with Go y func main() { // Create a map mapi := make(intMap) // Add key/value pairs map1[@] = 10 mapi(5] = 20 7/ Print map - Stringer will be called fmt.Printtn(map1) // Map type main. intMap // Length: 2 // (0) = 10 i 5] = 20 // Delete a key/value pair delete(map1, 0) fmt.PrintIn(map1) 7/ Map type main. intMap 7/ Length: 1 // (5) = 20 // Create a map on the spot using members map2 i= map[string]string{"key1": "valuel", "key2" "value2"} fmt. Println(map2) // map {keyl:valuel key2:value2] Continue reading = 02.6 - Goroutines and channels [bufio-readbytes]: https://golang.org/pkg/bufio/#Reader.ReadBytes# 02.6 - Goroutines and channels * Goroutines © Spawning anonymous goroutines + Channels © Buffered channels © Closing channels © Checking channel status © Reading from channels with range © select © Directed channels 25/0372025, 05:21 Hacking with Go © sync WaitGroup Goroutines Concurrency is not parallelism * Rob "Commander" Pike With that said, let's look at one of Go's main selling points, the goroutine . go function(a, b) runs the function in parallel and continues with the rest of the program. // 02.6-01-goroutinel.go package main import "fmt" func PrintMe(t int, count int) { for i t= 0; i < count; it { fmt.Printf("Printing from %d\\ func main() { go PrintMe(@, 100) fmt.PrintIn("Main finished!") But we never see anything printed. main returns before goroutine is spun up and start printing: $ go run 02.6-01-goroutine1.go Main finished! Lesson learned: Always wait for goroutines to finish! (if applicable). Continuing the C tradition, we can wait for a key-press before ending main. // 02,6-02-goroutine2.go package main import "tnt" func PrintMe(t int, count int) { for i z= 0; i < count; it+ { fmt.Printf("Printing from %d\n", t) 25/0372025, 05:21 Hacking with Go } func main() { go PrintMe(@, 10) 7/ Wait for a keypress fmt.Scanin() fmt.Printin("Main finishe This time we can see the goroutine's output: $ go run 02,6-02-goroutine2.go Printing from 0 Printing from Printing from Printing from Printing from Printing from Printing from Printing from Printing from Printing from e Main finished! Spawning anonymous goroutines We can also spawn new goroutines on the spot: // 02.6-03-gorout ine3.go package main import "fmt" func main() £ go func() { for i i < 10; int { fmt.Printf("Printing from %d\n", @) +O // Wait for a keypress fmt.Scanin() fmt.Printin("Main finished!") 25/0372025, 05:21 Hacking with Go Channels Channels go hand-in-hand with gorotuines. They are typed. For example if we create a channel of type int , we can only use it to transfer int s. Values are transfered using <- . Channels must be created before use. Let's make a channel in honor of famous hacker 4chan and use it to transfer some numbers around: // 02.6-04-channel1.go 71 This will not run package main import "tnt" func main() { fourChan := make(chan int) il := 10 // Send i1 to channel fourChan <- it fmt.Printf ("Sent %d to channel\n", i1) // Receive int from channel i2 := <-fourChan fmt.Printf ("Received %d from channel\n", 2) But it doesn't work: $ go run 02.6-04-channel1.go fatal error: all goroutines are asleep - deadlock! goroutine 1 [chan send] main,main() Z:/Go/src/Hacking-with-Go/code/02/02.6/02.6-04—channel1.go:12 +0x75 exit status 2 [Unbuffered] Channels will not start until the other side is ready. Our channel's “other side" is also in main and the channel is unbuffered (we will talk about it in a bit). Meaning there's nothing on the other side listening. We can either send or receive the data in a goroutine (or both): 25/0372025, 05:21 Hacking with Go // 02.6-05-channel2.90 package main import "fmt" func main() { fourChan := make(chan int) go func() ¢ // Send il to channel il 10 fourChan <- il // fourChan <- 10 fmt.Printf("Sent %d to channel\n", i1) +0 go func() ¢ // Receive int from channel i2 := <-fourChan fmt. Printin(i2) fmt.Printf("Received %d from channel\n", i2) +0 // Wait for goroutines to finish fmt.Scanin() fmt.PrintIn("Main Finished!") This time we have another goroutine listening on the other side: $ go run 02.6-05-channel2..9o 10 Received 10 from channel Sent 10 to channel e Main Finished! Buffered channels Buffered channels have capacity and only block when the butter is full. Buffer size (as far as | know) is specified during declaration: * be := make(chan int, 18) makes an int channel with size 10 . Using buffered channels we can send and receive in main: J/ 02.6-06-channel3.go 2siox2m23,08:21 Hocking with Go import "fmt" func main() £ fourChan := make(chan int, 2) 7/ Send 1@ to channel fourChan <- 10 fnt-Printf("Sent %d to channel\n", 10) 7/ Receive int from channel // We can also receive directly fmt.Printf ("Received %d from channel\n", <-fourChan) If the channel goes over capacity, we get the same fatal runtime error as before. Closing channels Channels can be closed. To close a channel we can use close(fourChan) . Sending items to a closed channel will cause a panic. Checking channel status When reading from channels, we can also get a second return value: © il, ok t= < fourchan if channel is open ok willbe true. false means channel is closed. Reading from a closed channel will return a zero value (e.g. © for most number types). See this example. i2 is 10 before reading something from a closed channel. After it's 0. // 02.6-07-channel4.go package main import "fmt" func main() { fourChan := make(chan int, 2) close(fourChan) 42 := 10 fmt.PrintIn("i2 before reading from closed channel", i2) // 10 42. ok := <-fourChan 25/0372025, 05:21 Hacking with Go fmt.Print#("i2: %d - ok: St", i2, ok) // i2: 10 ~ ok: false Reading from channels with range Usea range ina for to receive values from the channel in a loop until it closes like for i:= range fourChan - // 02-8=channel5.go package main import "fmt" func main() { fourChan := make(chan int, 10) go func() ¢ // Send @-9 to channel for i t= 0; i < 10; is { fourChan

You might also like