Time：2021-10-17

# origin

Recently read < < my first algorithm book > > ([Japan] Ishida Baohui; Miyazaki Xiuyi)
This series of notes is intended to use golang exercises

# Graph search

``````Graph search refers to starting from a vertex of the graph,
Reaching different vertices through edges,
The process of finally finding the target vertex.

Depending on the order of search,
The search algorithm of graph can be divided into "breadth first search" and "depth first search".

Although breadth first search and depth first search differ greatly in search order,
But there is only one difference in the operation steps,
That is, which candidate vertex is selected as the benchmark of the next vertex is different.

Breadth first search selects the first candidate vertex (FIFO),
The depth first search selects the latest candidate vertex (LIFO).

From < < my first algorithm book > > [Japan] Ishida Baohui; Miyazaki Xiuyi``````

# target

• By replacing the selection methods of candidate nodes (LIFO, FIFO), depth first search and breadth first search are realized and verified respectively

# Design

• Inode: vertex interface
• Igraphvisitor: traversal interface of graph
• Tnode: implementation of vertex
• Inodequeue: candidate node queue. Different selection methods of candidate nodes determine whether depth first or breadth first
• Tlifoqueue: LIFO stack, which implements the inodequeue interface
• Tfifoqeue: FIFO queue, which implements the inodequeue interface
• Tgraphvisitor: ergodic, implementing igraphvisitor interface,

• Starting from the specified vertex, access all reachable vertices, and then return the vertex array
• Use a hash table to record visited nodes to prevent repeated access

# unit testing

graph_visit_test.go

``````package graph

import (
"fmt"
"learning/gooop/graph"
"strings"
"testing"
)

func Test_GraphVisit(t *testing.T) {
fnAssertTrue := func(b bool, msg string) {
if !b {
t.Fatal(msg)
}
}

root := graph.NewNode("ROOT")
a := graph.NewNode("a")
b := graph.NewNode("b")
root.Append(a)
root.Append(b)

ac := graph.NewNode("ac")
a.Append(ac)

be := graph.NewNode("be")
bf := graph.NewNode("bf")
b.Append(be)
b.Append(bf)

acg := graph.NewNode("acg")
ach := graph.NewNode("ach")
ac.Append(acg)
ac.Append(ach)

bfi := graph.NewNode("bfi")
bfj := graph.NewNode("bfj")
bf.Append(bfi)
bf.Append(bfj)

fnVisitGraph := func(policy graph.VisitPolicy) string {
lines := make([]string, 0)
nodes := graph.GraphVisitor.Visit(root, policy)
for _,it := range nodes {
lines = append(lines, fmt.Sprintf("%s", it))
}
return strings.Join(lines, " ")
}

t.Log("testing dfs visitor")
dfs := fnVisitGraph(graph.DFSPolicy)
t.Log(dfs)
fnAssertTrue(dfs == "ROOT-[a,b] b-[be,bf] bf-[bfi,bfj] bfj-[] bfi-[] be-[] a-[ac,ad] ad-[] ac-[acg,ach] ach-[] acg-[]", "expecting dfs result")

t.Log("testing bfs visitor")
bfs := fnVisitGraph(graph.BFSPolicy)
t.Log(bfs)
fnAssertTrue(bfs == "ROOT-[a,b] a-[ac,ad] b-[be,bf] ac-[acg,ach] ad-[] be-[] bf-[bfi,bfj] acg-[] ach-[] bfi-[] bfj-[]", "expecting bfs result")
}
``````

# Test output

``````\$ go test -v graph_visit_test.go
=== RUN   Test_GraphVisit
graph_visit_test.go:53: testing dfs visitor
graph_visit_test.go:55: ROOT-[a,b] b-[be,bf] bf-[bfi,bfj] bfj-[] bfi-[] be-[] a-[ac,ad] ad-[] ac-[acg,ach] ach-[] acg-[]
graph_visit_test.go:58: testing bfs visitor
graph_visit_test.go:60: ROOT-[a,b] a-[ac,ad] b-[be,bf] ac-[acg,ach] ad-[] be-[] bf-[bfi,bfj] acg-[] ach-[] bfi-[] bfj-[]
--- PASS: Test_GraphVisit (0.00s)
PASS
ok      command-line-arguments  0.002s``````

# INode.go

Vertex interface

``````package graph

type INode interface {
ID() string
Append(child INode)
Children() []INode
}``````

# IGraphVisitor.go

Graph traverser interface

``````package graph

type IGraphVisitor interface {
Visit(root INode, policy VisitPolicy) []INode
}

type VisitPolicy int
const DFSPolicy VisitPolicy = 1
const BFSPolicy VisitPolicy = 2``````

# tNode.go

Implementation of vertex

``````package graph

import (
"fmt"
"strings"
)

type tNode struct {
id string
children []INode
}

func NewNode(id string) INode {
return &tNode{
id: id,
children: make([]INode, 0),
}
}

func (me *tNode) ID() string {
return me.id
}

func (me *tNode) Append(child INode) {
me.children = append(me.children, child)
}

func (me *tNode) Children() []INode {
return me.children
}

func (me *tNode) String() string {
items := make([]string, len(me.children))
for i,it := range me.children {
items[i] = it.ID()
}
return fmt.Sprintf("%v-[%s]", me.id, strings.Join(items, ","))
}``````

# iNodeQueue.go

Candidate node queue interface. Different selection methods of candidate nodes determine whether depth first or breadth first

``````package graph

type iNodeQueue interface {
Clear()
Empty() bool
Push(node INode)
Poll() (bool, INode)
}``````

# tLIFOQueue.go

LIFO stack to implement inodequeue interface

``````package graph

type tLIFOQueue struct {
nodes []INode
capacity int
size int
}

func newLIFOQueue() iNodeQueue {
it := &tLIFOQueue{}
it.Clear()
return it
}

func (me *tLIFOQueue) Clear() {
me.nodes = make([]INode, 0)
me.capacity = 0
me.size = 0
}

func (me *tLIFOQueue) Push(node INode) {
me.ensureSpace(1)
me.nodes[me.size] = node
me.size++
}

func (me *tLIFOQueue) ensureSpace(space int) {
for me.capacity < me.size + space {
me.nodes = append(me.nodes, nil)
me.capacity++
}
}

func (me *tLIFOQueue) Empty() bool {
return me.size <= 0
}

func (me *tLIFOQueue) Poll() (bool, INode) {
if me.Empty() {
return false, nil
}

me.size--
it := me.nodes[me.size]
me.nodes[me.size] = nil
return true, it
}``````

# tFIFOQeuue.go

FIFO queue, which implements the inodequeue interface

``````package graph

type tFIFOQueue struct {
nodes []INode
capacity int
rindex int
windex int
}

func newFIFOQueue() iNodeQueue {
it := &tFIFOQueue{}
it.Clear()
return it
}

func (me *tFIFOQueue) Clear() {
me.nodes = make([]INode, 0)
me.capacity = 0
me.rindex = -1
me.windex = -1
}

func (me *tFIFOQueue) size() int {
return me.windex - me.rindex
}

func (me *tFIFOQueue) Empty() bool {
return me.size() <= 0
}

func (me *tFIFOQueue) Push(node INode) {
me.ensureSpace(1)
me.windex++
me.nodes[me.windex] = node
}

func (me *tFIFOQueue) ensureSpace(size int) {
for me.capacity < me.windex + size + 1 {
me.nodes = append(me.nodes, nil)
me.capacity++
}
}

func (me *tFIFOQueue) Poll() (bool, INode) {
if me.Empty() {
return false, nil
}

me.rindex++
it := me.nodes[me.rindex]
me.nodes[me.rindex] = nil

if me.rindex > me.capacity / 2 {
size := me.size()
offset := me.rindex + 1
for i := 0;i < size;i++ {
me.nodes[i], me.nodes[i + offset] = me.nodes[i + offset], nil
}

me.rindex -= offset
me.windex -= offset
}

return true, it
}``````

# tGraphVisitor.go

Ergodic implement igraphvisitor interface

• Starting from the specified vertex, access all reachable vertices, and then return the vertex array
• Use a hash table to record visited nodes to prevent repeated access
``````package graph

type tGraphVisitor struct {
}

func newGraphVisitor() IGraphVisitor {
return &tGraphVisitor{
}
}

func (me *tGraphVisitor) createQueue(policy VisitPolicy) iNodeQueue {
switch policy {
case BFSPolicy:
return newFIFOQueue()

case DFSPolicy:
return newLIFOQueue()

default:
panic("unsupported policy")
}
}

func (me *tGraphVisitor) Visit(root INode, policy VisitPolicy) []INode {
queue := me.createQueue(policy)
queue.Push(root)

visited := make(map[string]bool, 0)
result := make([]INode, 0)
for !queue.Empty() {
_,node := queue.Poll()
visited[node.ID()] = true
result = append(result, node)

children := node.Children()
if children != nil {
for _,it := range children {
ok,_ := visited[it.ID()]
if ok {
continue
}
queue.Push(it)
}
}
}

return result
}

var GraphVisitor = newGraphVisitor()``````

(end)

## [vulfocus range reproduction] Nostromo remote command execution (cve-2019-16278)

Vulnerability introduction Nostromo nhttpd is an open source web server. Nostromo has defects in verifying URL security, resulting in directory traversal. Anyone can traverse any file in the system. Therefore, a remote unauthenticated attacker can force the server to point to a shell file such as / bin / sh, thereby executing arbitrary commands. Impact […]