From b1c5a2452ea7d22532543c5d02131ff495b65c0a Mon Sep 17 00:00:00 2001 From: dbardbar Date: Tue, 3 Jan 2023 10:24:59 +0200 Subject: [PATCH] Fix marshaling of string fields with special characters in nf9 and ipfix --- ipfix/marshal.go | 6 +- ipfix/marshal_test.go | 1 + netflow/v9/marshal.go | 6 +- netflow/v9/mashal_test.go | 159 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 6 deletions(-) create mode 100644 netflow/v9/mashal_test.go diff --git a/ipfix/marshal.go b/ipfix/marshal.go index c2e9bef0..eb154cbc 100644 --- a/ipfix/marshal.go +++ b/ipfix/marshal.go @@ -25,6 +25,7 @@ package ipfix import ( "bytes" "encoding/hex" + "encoding/json" "errors" "net" "strconv" @@ -184,9 +185,8 @@ func (m *Message) writeValue(b *bytes.Buffer, i, j int) error { case float64: b.WriteString(strconv.FormatFloat(m.DataSets[i][j].Value.(float64), 'E', -1, 64)) case string: - b.WriteByte('"') - b.WriteString(m.DataSets[i][j].Value.(string)) - b.WriteByte('"') + var asJson, _ = json.Marshal(m.DataSets[i][j].Value.(string)) + b.Write(asJson) case net.IP: b.WriteByte('"') b.WriteString(m.DataSets[i][j].Value.(net.IP).String()) diff --git a/ipfix/marshal_test.go b/ipfix/marshal_test.go index 7bbc78af..accc0854 100644 --- a/ipfix/marshal_test.go +++ b/ipfix/marshal_test.go @@ -76,6 +76,7 @@ var mockDecodedMsg = Message{ {ID: 0x88, Value: 0x1}, {ID: 0xf3, Value: 0x0}, {ID: 0xf5, Value: 0x0}, + {ID: 0x53, Value: "escape \" this"}, }, }, } diff --git a/netflow/v9/marshal.go b/netflow/v9/marshal.go index da7c8479..ba26e830 100644 --- a/netflow/v9/marshal.go +++ b/netflow/v9/marshal.go @@ -25,6 +25,7 @@ package netflow9 import ( "bytes" "encoding/hex" + "encoding/json" "errors" "net" "strconv" @@ -181,9 +182,8 @@ func (m *Message) writeValue(b *bytes.Buffer, i, j int) error { case float64: b.WriteString(strconv.FormatFloat(m.DataSets[i][j].Value.(float64), 'E', -1, 64)) case string: - b.WriteByte('"') - b.WriteString(m.DataSets[i][j].Value.(string)) - b.WriteByte('"') + var asJson, _ = json.Marshal(m.DataSets[i][j].Value.(string)) + b.Write(asJson) case net.IP: b.WriteByte('"') b.WriteString(m.DataSets[i][j].Value.(net.IP).String()) diff --git a/netflow/v9/mashal_test.go b/netflow/v9/mashal_test.go new file mode 100644 index 00000000..f1745a11 --- /dev/null +++ b/netflow/v9/mashal_test.go @@ -0,0 +1,159 @@ +//: ---------------------------------------------------------------------------- +//: Copyright (C) 2017 Verizon. All Rights Reserved. +//: All Rights Reserved +//: +//: file: marshal_test.go +//: details: provides support for automated testing of marshal methods +//: author: Mehrdad Arshad Rad +//: date: 02/01/2017 +//: +//: Licensed under the Apache License, Version 2.0 (the "License"); +//: you may not use this file except in compliance with the License. +//: You may obtain a copy of the License at +//: +//: http://www.apache.org/licenses/LICENSE-2.0 +//: +//: Unless required by applicable law or agreed to in writing, software +//: distributed under the License is distributed on an "AS IS" BASIS, +//: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//: See the License for the specific language governing permissions and +//: limitations under the License. +//: ---------------------------------------------------------------------------- + +package netflow9 + +import ( + "bytes" + "encoding/json" + "net" + "testing" +) + +type TestMessage struct { + AgentID string + Header PacketHeader + DataSets [][]TestDecodedField +} + +type TestDecodedField struct { + I uint16 + V interface{} +} + +var mockDecodedMsg = Message{ + AgentID: "10.10.10.10", + Header: PacketHeader{ + Version: 9, + Count: 420, + SysUpTime: 1483484756, + SeqNum: 2563920489, + SrcID: 34560, + }, + DataSets: [][]DecodedField{ + { + {ID: 0x8, Value: net.IP{0x5b, 0x7d, 0x82, 0x79}}, + {ID: 0xc, Value: net.IP{0xc0, 0xe5, 0xdc, 0x85}}, + {ID: 0x5, Value: 0x0}, + {ID: 0x4, Value: 0x6}, + {ID: 0x7, Value: 0xecba}, + {ID: 0xb, Value: 0x1bb}, + {ID: 0x20, Value: 0x0}, + {ID: 0xa, Value: 0x503}, + {ID: 0x3a, Value: 0x0}, + {ID: 0x9, Value: 0x10}, + {ID: 0xd, Value: 0x18}, + {ID: 0x10, Value: 0x1ad7}, + {ID: 0x11, Value: 0x3b1d}, + {ID: 0xf, Value: net.IP{0xc0, 0x10, 0x1c, 0x58}}, + {ID: 0x6, Value: []uint8{0x10}}, + {ID: 0xe, Value: 0x4f6}, + {ID: 0x1, Value: 0x28}, + {ID: 0x2, Value: 0x1}, + {ID: 0x34, Value: 0x3a}, + {ID: 0x35, Value: 0x3a}, + {ID: 0x98, Value: 1483484685331}, + {ID: 0x99, Value: 1483484685331}, + {ID: 0x88, Value: 0x1}, + {ID: 0xf3, Value: 0x0}, + {ID: 0xf5, Value: 0x0}, + {ID: 0x53, Value: "escape \" this"}, + }, + }, +} + +func TestJSONMarshal(t *testing.T) { + buf := new(bytes.Buffer) + msg := TestMessage{} + + b, err := mockDecodedMsg.JSONMarshal(buf) + if err != nil { + t.Error("unexpected error", err) + } + + err = json.Unmarshal(b, &msg) + if err != nil { + t.Error("unexpected error", err) + } + if msg.AgentID != "10.10.10.10" { + t.Error("expect AgentID 10.10.10.10, got", msg.AgentID) + } + if msg.Header.Version != 9 { + t.Error("expect Version 9, got", msg.Header.Version) + } +} + +func TestJSONMarshalDataSets(t *testing.T) { + buf := new(bytes.Buffer) + msg := TestMessage{} + + b, _ := mockDecodedMsg.JSONMarshal(buf) + json.Unmarshal(b, &msg) + + for _, ds := range msg.DataSets { + for _, f := range ds { + switch f.I { + case 1: + chkFloat64(t, f, 40) + case 2: + chkFloat64(t, f, 1) + case 4: + chkFloat64(t, f, 6) + case 5: + chkFloat64(t, f, 0) + case 6: + chkString(t, f, "0x10") + case 8: + chkString(t, f, "91.125.130.121") + case 12: + chkString(t, f, "192.229.220.133") + case 13: + chkFloat64(t, f, 24) + case 14: + chkFloat64(t, f, 1270) + case 152: + chkFloat64(t, f, 1483484685331) + } + } + } +} + +func BenchmarkJSONMarshal(b *testing.B) { + buf := new(bytes.Buffer) + + for i := 0; i < b.N; i++ { + mockDecodedMsg.JSONMarshal(buf) + } + +} + +func chkFloat64(t *testing.T, f TestDecodedField, expect float64) { + if f.V.(float64) != expect { + t.Errorf("expect ID %d value %f, got %f", f.I, expect, f.V) + } +} + +func chkString(t *testing.T, f TestDecodedField, expect string) { + if f.V.(string) != expect { + t.Errorf("expect ID %d value %s, got %s", f.I, expect, f.V.(string)) + } +}