Skip to content
This repository has been archived by the owner on Jan 26, 2024. It is now read-only.

Commit

Permalink
-added background nbns refresh loop
Browse files Browse the repository at this point in the history
-fixed broadcast packet
  • Loading branch information
irai committed May 5, 2019
1 parent 354e68e commit 490a7e0
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 123 deletions.
14 changes: 12 additions & 2 deletions cmd/nbnslistener/nbnslistener.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,27 @@ import (
"net"
"os"
"strings"
"time"

"github.com/irai/nbns"
log "github.com/sirupsen/logrus"
)

var (
netFlag = flag.String("cidr", "192.168.1.1/24", "network to broadcast nbns to")
)

func main() {
flag.Parse()

setLogLevel("info")

handler, err := nbns.NewHandler()
_, network, err := net.ParseCIDR(*netFlag)
if err != nil {
log.Fatal("invalid CIDR ", err)
}

handler, err := nbns.NewHandler(*network)
if err != nil {
log.Fatal("error in nbns", err)
}
Expand All @@ -35,7 +45,7 @@ func main() {

handler.AddNotificationChannel(notify)

go handler.ListenAndServe()
go handler.ListenAndServe(time.Minute * 1)

cmd(handler)

Expand Down
7 changes: 7 additions & 0 deletions goroutine.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ func (h *goroutinePool) Begin(name string) *goroutine {
return &g
}

func (h *goroutinePool) Stopping() bool {
if atomic.LoadInt32(&h.stopping) != 0 {
return true
}
return false
}

func (g *goroutine) End() {
atomic.AddInt32(&g.pool.n, -1)
stopping := atomic.LoadInt32(&g.pool.stopping)
Expand Down
142 changes: 45 additions & 97 deletions nbns.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
package nbns

import (
"bytes"
"encoding/binary"
"fmt"
"net"
"strings"
"syscall"
"time"

log "github.com/sirupsen/logrus"
)

// Handler create a new NBNS handler
type Handler struct {
conn *net.UDPConn
notification chan<- Entry
conn *net.UDPConn
broadcastAddr net.IP
notification chan<- Entry
}

// Entry holds a NBNS name entry
Expand All @@ -24,41 +23,29 @@ type Entry struct {
}

// NewHandler create a NBNS handler
func NewHandler() (handler *Handler, err error) {
func NewHandler(network net.IPNet) (handler *Handler, err error) {
// srcAddr, err := net.ResolveUDPAddr("udp4", "127.0.0.1:0")

handler = &Handler{}
if handler.conn, err = net.ListenUDP("udp4", &net.UDPAddr{IP: nil, Port: 137}); err != nil {
log.Error("NBNS failed to bind UDP port 137 ", err)
return nil, err
}

// calculate broadcast addr
handler.broadcastAddr = net.IP(make([]byte, 4))
for i := range network.IP {
handler.broadcastAddr[i] = network.IP[i] | ^network.Mask[i]
}
return handler, nil

}

// SendQuery send a NBNS query
// 4.2.12. NAME QUERY REQUEST
//
// 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | NAME_TRN_ID |0| 0x0 |0|0|1|0|0 0|B| 0x0 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | 0x0001 | 0x0000 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | 0x0000 | 0x0000 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// / QUESTION_NAME /
// / /
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | NB (0x0020) | IN (0x0001) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// SendQuery send NBNS node status request query
func (h *Handler) SendQuery(ip net.IP) (err error) {

packet := nodeStatusRequestWireFormat(`* `)
packet.printHeader()
// packet.printHeader()

if ip == nil || ip.Equal(net.IPv4zero) {
return fmt.Errorf("invalid IP nil %v", ip)
Expand All @@ -68,7 +55,9 @@ func (h *Handler) SendQuery(ip net.IP) (err error) {
// To broadcast, use network broadcast i.e 192.168.0.255 for example.
targetAddr := &net.UDPAddr{IP: ip, Port: 137}
if _, err = h.conn.WriteToUDP(packet, targetAddr); err != nil {
log.Error("NPNS failed to send nbns packet ", err)
if !GoroutinePool.Stopping() {
log.Error("NPNS failed to send nbns packet ", err)
}
return err
}
return nil
Expand All @@ -85,85 +74,31 @@ func (h *Handler) Stop() {
GoroutinePool.Stop() // will stop all goroutines
}

// processNodeStatusResponse
// 4.2.18. NODE STATUS RESPONSE
// 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Header |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// / RR_NAME (variable len) /
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | NBSTAT (0x0021) | IN (0x0001) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | 0x00000000 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | RDLENGTH | NUM_NAMES | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
// / NODE_NAME ARRAY (variable len) /
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// / STATISTICS (variable len) /
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
func (h *Handler) processNameQueryResponse(packet packet, ip net.IP) error {

log.Debug("nbns received name query packet")
packet.printHeader()
// Assume no questions
if packet.qdCount() > 0 {
log.Printf("unexpected qdcount %v ", packet.qdCount())
}
func (h *Handler) broadcastLoop(interval time.Duration) {
g := GoroutinePool.Begin("nbns broadcastLoop")
defer g.End()

// Assume a single Response name
if packet.anCount() <= 0 {
return fmt.Errorf("unexpected ancount %v ", packet.anCount())
}
for {
h.SendQuery(h.broadcastAddr)
select {
case <-GoroutinePool.StopChannel:
return

// variable len reading
buf := bytes.NewBuffer(packet.payload())
name := decodeNBNSName(buf)
log.Printf("name: |%s|\n", name)

var tmp16 uint16
var numNames uint8
binary.Read(buf, binary.BigEndian, &tmp16) // type
binary.Read(buf, binary.BigEndian, &tmp16) // internet
binary.Read(buf, binary.BigEndian, &tmp16) // TTL is 32 bits
binary.Read(buf, binary.BigEndian, &tmp16) // TTL
binary.Read(buf, binary.BigEndian, &tmp16) // RDLength
binary.Read(buf, binary.BigEndian, &numNames) // numNames

tmpName := make([]byte, 16)
table := []string{}
for i := 0; i < int(numNames); i++ {
binary.Read(buf, binary.BigEndian, &tmpName)
binary.Read(buf, binary.BigEndian, &tmp16) // nameFlags
// log.Infof("names %q nFlags %02x", tmpName, tmp16)
if (tmp16 & 0x8000) == 0x00 { // don't add to the table if this is group name
t := strings.TrimRight(string(tmpName), " \x00")
t = strings.TrimRight(t, " \x03") // not sure why some have 03 at the end
t = strings.TrimRight(t, " \x1d") // not sure why some have 1d at the end
table = append(table, t)
case <-time.After(interval):
}
}

entry := Entry{IP: ip, Name: table[0]} // first entry
log.Info("nodes ", entry.Name, entry.IP, table)
if h.notification != nil {
log.Debugf("nbns send notification name %s ip %s", entry.Name, entry.IP)
h.notification <- entry
}

return nil
}

// ListenAndServe main listening loop
func (h *Handler) ListenAndServe() error {
func (h *Handler) ListenAndServe(interval time.Duration) error {
g := GoroutinePool.Begin("nbns ListenAndServe")
defer g.End()

go h.broadcastLoop(interval)

readBuffer := make([]byte, 1024)

// h.conn.SetDeadline(time.Now().Add(200 * time.Millisecond))
for !g.Stopping() {
_, udpAddr, err := h.conn.ReadFromUDP(readBuffer)
if g.Stopping() {
Expand Down Expand Up @@ -197,16 +132,29 @@ func (h *Handler) ListenAndServe() error {
return err
}

log.Info("nbns received nbns packet from IP ", *udpAddr)
packet := packet(readBuffer)
switch {
case packet.opcode() == opcodeQuery && packet.response() == 1:
if err := h.processNameQueryResponse(packet, udpAddr.IP); err != nil {
log.Error(err)
log.Info("nbns received nbns nodeStatusResponse from IP ", *udpAddr)
entry, err := parseNodeStatusResponsePacket(packet, udpAddr.IP)
if err != nil {
log.Error("error processing nodeStatusResponse ", err)
return err
}

if h.notification != nil {
log.Debugf("nbns send notification name %s ip %s", entry.Name, entry.IP)
h.notification <- entry
}

case packet.response() == 0:
if packet.trnID() != sequence { // ignore our own request
log.Info("nbns not implemented - recvd nbns request from IP ", *udpAddr)
packet.printHeader()
}

default:
log.Infof("nbns packet opcode=%v not implemented ", packet.opcode())
log.Infof("nbns not implemented opcode=%v ", packet.opcode())
packet.printHeader()
}

Expand Down
Loading

0 comments on commit 490a7e0

Please sign in to comment.