diff --git a/ssm.go b/ssm.go new file mode 100644 index 0000000000000000000000000000000000000000..6d06ed87284589d3fb94283f7b3e143e27325416 --- /dev/null +++ b/ssm.go @@ -0,0 +1,153 @@ +package main + +import ( + //"encoding/binary" + "fmt" + "net" + "os" + "encoding/binary" + //"runtime" + "time" + "regexp" + "golang.org/x/net/ipv4" + +) + +func usage() { + fmt.Println("USAGE: ssm <source>%<group>:<port>\n") + os.Exit(0) +} + +func main() { + arguments := os.Args + if len(arguments) < 2 { + usage() + } + + /* Process the SSM group join argument */ + + ssmarg := arguments[1] + re := regexp.MustCompile("^(.*)%(.*):([0-9]+)$") + + if re.MatchString(ssmarg) == false { + usage() + } + + ssm := re.FindAllStringSubmatch(ssmarg, -1)[0] + source := ssm[1] + group := ssm[2] + port := ssm[3] + + var bindif string + allnets, _ := net.Interfaces() + + if(len(allnets) == 0) { + fmt.Printf("Did not find any network interfaces\n") + return + } else if (len(allnets) == 1) { + // Only one interface available so use that. + bindif = allnets[0].Name + } else { + bindif = allnets[1].Name + } + + if len(arguments) == 4 { + switch(arguments[2]) { + case "-i": + bindif = arguments[3] + default: + fmt.Printf("INVALID ARG: %v\n", arguments[2]) + usage() + } + + } + + ssmif, err := net.InterfaceByName(bindif) + if err != nil { + fmt.Printf("Could not bind to interface '%v'\n", bindif) + return + } + + c, err := net.ListenPacket("udp4", "0.0.0.0:" + port) + + if err != nil { + fmt.Println(err) + return + } + + fmt.Printf("Server listening on UDP port %v\nJoining multicast (S,G)=%v,%v w/iface %v\n", port, source, group, bindif) + + defer c.Close() + + p := ipv4.NewPacketConn(c) + + ssmsource := net.UDPAddr{IP: net.ParseIP(source)} + ssmgroup := net.UDPAddr{IP: net.ParseIP(group)} + + if err := p.JoinSourceSpecificGroup(ssmif, &ssmgroup, &ssmsource); err != nil { + // error handling + fmt.Println(err) + return + } + + b := make([]byte, 1500) + var index int32 + var last_index int32 + index = 0 + last_index = 0 + first_packet := true + packet_count := 0 + var stream_start time.Time + + + for { + n, _, _, err := p.ReadFrom(b) + if err != nil { + // error handling + fmt.Println(err) + return + } + if n != 0 { + packet_count++ + index = int32(binary.BigEndian.Uint32(b[0:4])) + if index < 0 { + // end of stream + fmt.Printf("!EOS [i:%v]\n", index) + first_packet = true + packet_count = 0 + last_index = 0 + index = 0 + continue + } + + if first_packet { + fmt.Printf("S: %v", index) + first_packet = false + stream_start = time.Now() + } else if index == (last_index + 1){ + fmt.Printf("\033[32m!\033[0m") + } else if index < last_index { + fmt.Printf("\033[34m<\033[0m") + } else if index > (last_index + 1) { + fmt.Printf("\033[36m>\033[0m") + } else { + fmt.Printf("\033[31m.\033[0m") + } + last_index = index + + if time.Since(stream_start)/time.Second >= 5 { + fmt.Printf("[%vpps]", packet_count/5) + stream_start = time.Now() + packet_count = 0 + } + } else { + break + } + } + + if err := p.LeaveSourceSpecificGroup(ssmif, &ssmgroup, &ssmsource); err != nil { + // error handling + fmt.Println(err) + return + } +} \ No newline at end of file