diff --git a/d4-goclient.go b/d4-goclient.go new file mode 100644 index 0000000..176bd43 --- /dev/null +++ b/d4-goclient.go @@ -0,0 +1,294 @@ +package main + +import ( + "bytes" + "encoding/binary" + "flag" + "fmt" + "io" + "log" + "os" + "strconv" + "time" + + //BSD 3 + uuid "github.com/satori/go.uuid" +) + +const ( + // Version Size + VERSION_SIZE = 1 + // Type Size + TYPE_SIZE = 1 + // UUID v4 Size + UUID_SIZE = 16 + // Timestamp Size + TIMESTAMP_SIZE = 8 + // HMAC-SHA256 MAC Size + HMAC_SIZE = 32 + // Payload size Size + SSIZE = 4 + + HDR_SIZE = VERSION_SIZE + TYPE_SIZE + UUID_SIZE + HMAC_SIZE + TIMESTAMP_SIZE + SSIZE +) + +type ( + + // A d4 writer implements the io.Writer Interface by implementing Write() and Close() + // it accepts an io.Writer as sink + d4Writer struct { + w io.Writer + } + + d4S struct { + src io.Reader + dst io.Writer + confdir string + d4error uint8 + errnoCopy uint8 + conf d4params + //header d4Header + d4header []byte + payload []byte + } + + d4params struct { + uuid []byte + snaplen uint32 + key string + version uint8 + source string + destination string + ttype uint8 + } +) + +var ( + // verbose + buf bytes.Buffer + logger = log.New(&buf, "INFO: ", log.Lshortfile) + infof = func(info string) { + logger.Output(2, info) + } + + confdir = flag.String("c", "", "configuration directory") + debug = flag.Bool("v", false, "Set to True, true, TRUE, 1, or t to enable verbose output on stdout") +) + +func newD4Writer(writer io.Writer) *d4Writer { + return &d4Writer{w: writer} +} + +func main() { + + var d4 d4S + d4p := &d4 + + flag.Usage = func() { + fmt.Printf("d4 - d4 client\n") + fmt.Printf("Read data from the configured and send it to \n") + fmt.Printf("\n") + fmt.Printf("Usage: d4 -c config_directory\n") + fmt.Printf("\n") + fmt.Printf("Configuration\n\n") + fmt.Printf("The configuration settings are stored in files in the configuration directory\n") + fmt.Printf("specified with the -c command line switch.\n\n") + fmt.Printf("Files in the configuration directory\n") + fmt.Printf("\n") + fmt.Printf("key - is the private HMAC-SHA-256-128 key.\n") + fmt.Printf(" The HMAC is computed on the header with a HMAC value set to 0\n") + fmt.Printf(" which is updated later.\n") + fmt.Printf("snaplen - the length of bytes that is read from the \n") + fmt.Printf("version - the version of the d4 client\n") + fmt.Printf("type - the type of data that is send. pcap, netflow, ...\n") + fmt.Printf("source - the source where the data is read from\n") + fmt.Printf("destination - the destination where the data is written to\n") + fmt.Printf("\n") + fmt.Printf("-v [TRUE] for verbose output on stdout") + flag.PrintDefaults() + } + + flag.Parse() + if flag.NFlag() == 0 || *confdir == "" { + flag.Usage() + os.Exit(1) + } + + // Output logging before closing if debug is enabled + if *debug == true { + defer fmt.Print(&buf) + } + + d4.confdir = *confdir + //var d4 = d4loadConfig(confdir) + if d4loadConfig(d4p) == true { + initHeader(d4p) + //d4transfer(d4p) + } +} + +func readConfFile(d4 *d4S, fileName string) []byte { + f, err := os.Open((*d4).confdir + "/" + fileName) + if err != nil { + log.Fatal(err) + } + data := make([]byte, 100) + count, err := f.Read(data) + if err != nil { + if err != io.EOF { + log.Fatal(err) + } + } + infof(fmt.Sprintf("read %d bytes: %q\n", count, data[:count])) + if err := f.Close(); err != nil { + log.Fatal(err) + } + // removes 1 for \n + return data[:count-1] +} + +func d4loadConfig(d4 *d4S) bool { + // populate the map + (*d4).conf = d4params{} + (*d4).conf.source = string(readConfFile(d4, "source")) + (*d4).conf.destination = string(readConfFile(d4, "destination")) + (*d4).conf.uuid = readConfFile(d4, "uuid") + // parse snaplen to uint32 + tmp, _ := strconv.ParseUint(string(readConfFile(d4, "snaplen")), 10, 32) + (*d4).conf.snaplen = uint32(tmp) + (*d4).conf.key = string(readConfFile(d4, "key")) + // parse version to uint8 + tmp, _ = strconv.ParseUint(string(readConfFile(d4, "version")), 10, 8) + (*d4).conf.version = uint8(tmp) + // parse type to uint8 + tmp, _ = strconv.ParseUint(string(readConfFile(d4, "type")), 10, 8) + (*d4).conf.ttype = uint8(tmp) + return d4checkConfig(d4) +} + +// QUICK IMPLEM, REVISE +func d4checkConfig(d4 *d4S) bool { + + //TODO implement other destination file, fifo unix_socket ... + switch (*d4).conf.source { + case "stdin": + (*d4).src = os.Stdin + case "pcap": + f, _ := os.Open("capture.pcap") + (*d4).src = f + } + + switch (*d4).conf.destination { + case "stdout": + (*d4).dst = newD4Writer(os.Stdout) + case "file": + f, _ := os.Create("test.txt") + (*d4).dst = newD4Writer(f) + } + + if len((*d4).conf.uuid) == 0 { + // UUID not set, generate a new one + (*d4).conf.uuid = generateUUIDv4() + // And push it into the conf file + f, err := os.OpenFile((*d4).confdir+"/uuid", os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + log.Fatal(err) + } + f.WriteString(string((*d4).conf.uuid) + "\n") + f.Close() + } + + return true +} + +func generateUUIDv4() []byte { + uuid, err := uuid.NewV4() + if err != nil { + log.Fatal(err) + } + infof(fmt.Sprintf("UUIDv4: %s\n", uuid)) + return []byte(uuid[:]) +} + +//TODO turn this ugly mess into a Framer +func initHeader(d4 *d4S) bool { + // zero out the header + (*d4).d4header = make([]byte, HDR_SIZE) + // put version a type into the header + (*d4).d4header[0] = (*d4).conf.version + (*d4).d4header[1] = (*d4).conf.ttype + // put uuid into the header + ps := 2 + pe := ps + UUID_SIZE + copy((*d4).d4header[ps:pe], (*d4).conf.uuid) + // timestamp + timeUnix := time.Now().UnixNano() + ps = pe + pe = ps + TIMESTAMP_SIZE + binary.LittleEndian.PutUint64((*d4).d4header[ps:pe], uint64(timeUnix)) + // hmac is set to zero during hmac operations, so leave it alone + // still, we move the pointers + ps = pe + pe = ps + HMAC_SIZE + ps = pe + pe = ps + 4 + // TODO put nread size instead of snaplen + binary.LittleEndian.PutUint32((*d4).d4header[ps:pe], (*d4).conf.snaplen) + + // LittleEndian + // toto := append(make([]byte, 0), byte((*d4).conf.snaplen), byte((*d4).conf.snaplen>>8), byte((*d4).conf.snaplen>>16), byte((*d4).conf.snaplen>>24)) + // BigEndian + // toto := append(make([]byte, 0), byte((*d4).conf.snaplen>>24), byte((*d4).conf.snaplen>>16), byte((*d4).conf.snaplen>>8), byte((*d4).conf.snaplen)) + // tmpi := binary.BigEndian.Uint32(toto) + // tmpt := binary.LittleEndian.Uint32(others) + // fmt.Println(tmpi) + fmt.Println((*d4).d4header) + + return true +} + +func d4transfer(d4 *d4S) { + src := (*d4).src + dst := (*d4).dst + buf := make([]byte, (*d4).conf.snaplen) + + //for { + //n, _ := src.Read(buf) + _, _ = src.Read(buf) + + dst.Write(buf) + + // if n > 0 { + // update the header + // timestamp + + /* h := hmac.New(sha256.New, []byte((*d4).conf.key)) + h.Write((*d4).header.version) + h.Write((*d4).header.ttype) + h.Write((*d4).header.uuid) + h.Write((*d4).header.timestamp) + h.Write((*d4).header.hhmac) + h.Write((*d4).header.size) + h.Write([]byte(buf)) + + */ //Add it to the header + + // fmt.Println(base64.StdEncoding.EncodeToString(h.Sum(nil))) + + // Write the packet in the sink + //} + //fmt.Println(n) + //fmt.Println(string(buf)) + //} + //io.Copy((*d4).dst, (*d4).src) +} + +func (d4w *d4Writer) Write(bs []byte) (int, error) { + d4w.w.Write(bs) + return len(bs), nil +} + +func (d4w *d4Writer) Close() error { + // nothing ATM + return nil +}