diff --git a/CHANGELOG.md b/CHANGELOG.md index f0dfe8c8..1ec8e95a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,9 +37,11 @@ and this project adheres to ### Fixed +- A JSON parsing error in query log [#2345]. - Incorrect detection of the IPv6 address of an interface as well as another infinite loop in the `/dhcp/find_active_dhcp` HTTP API [#2355]. +[#2345]: https://github.com/AdguardTeam/AdGuardHome/issues/2345 [#2355]: https://github.com/AdguardTeam/AdGuardHome/issues/2355 diff --git a/internal/querylog/decode.go b/internal/querylog/decode.go index cb798aa7..b385f3eb 100644 --- a/internal/querylog/decode.go +++ b/internal/querylog/decode.go @@ -3,7 +3,7 @@ package querylog import ( "encoding/base64" "encoding/json" - "strconv" + "io" "strings" "time" @@ -84,15 +84,11 @@ var logEntryHandlers = map[string](func(t json.Token, ent *logEntry) error){ return err }, "IsFiltered": func(t json.Token, ent *logEntry) error { - v, ok := t.(string) + v, ok := t.(bool) if !ok { return nil } - b, err := strconv.ParseBool(v) - if err != nil { - return err - } - ent.Result.IsFiltered = b + ent.Result.IsFiltered = v return nil }, "Rule": func(t json.Token, ent *logEntry) error { @@ -104,23 +100,23 @@ var logEntryHandlers = map[string](func(t json.Token, ent *logEntry) error){ return nil }, "FilterID": func(t json.Token, ent *logEntry) error { - v, ok := t.(string) + v, ok := t.(json.Number) if !ok { return nil } - i, err := strconv.Atoi(v) + i, err := v.Int64() if err != nil { return err } - ent.Result.FilterID = int64(i) + ent.Result.FilterID = i return nil }, "Reason": func(t json.Token, ent *logEntry) error { - v, ok := t.(string) + v, ok := t.(json.Number) if !ok { return nil } - i, err := strconv.Atoi(v) + i, err := v.Int64() if err != nil { return err } @@ -144,17 +140,20 @@ var logEntryHandlers = map[string](func(t json.Token, ent *logEntry) error){ return nil }, "Elapsed": func(t json.Token, ent *logEntry) error { - v, ok := t.(string) + v, ok := t.(json.Number) if !ok { return nil } - i, err := strconv.Atoi(v) + i, err := v.Int64() if err != nil { return err } ent.Elapsed = time.Duration(i) return nil }, + "Result": func(json.Token, *logEntry) error { + return nil + }, "Question": func(t json.Token, ent *logEntry) error { v, ok := t.(string) if !ok { @@ -192,10 +191,13 @@ var logEntryHandlers = map[string](func(t json.Token, ent *logEntry) error){ func decodeLogEntry(ent *logEntry, str string) { dec := json.NewDecoder(strings.NewReader(str)) - for dec.More() { + dec.UseNumber() + for { keyToken, err := dec.Token() if err != nil { - log.Debug("decodeLogEntry err: %s", err) + if err != io.EOF { + log.Debug("decodeLogEntry err: %s", err) + } return } if _, ok := keyToken.(json.Delim); ok { @@ -203,15 +205,16 @@ func decodeLogEntry(ent *logEntry, str string) { } key := keyToken.(string) handler, ok := logEntryHandlers[key] - value, err := dec.Token() + if !ok { + continue + } + val, err := dec.Token() if err != nil { return } - if ok { - if err := handler(value, ent); err != nil { - log.Debug("decodeLogEntry err: %s", err) - return - } + if err := handler(val, ent); err != nil { + log.Debug("decodeLogEntry err: %s", err) + return } } } diff --git a/internal/querylog/decode_test.go b/internal/querylog/decode_test.go index e9c7c9f7..02ee77df 100644 --- a/internal/querylog/decode_test.go +++ b/internal/querylog/decode_test.go @@ -32,6 +32,66 @@ func TestDecode_decodeQueryLog(t *testing.T) { name: "back_compatibility_bad_decoding", log: `{"Question":"LgBAAABAAAAAAAAC2FkZ3VhcmR0ZWFtBmdpdGh1YgJpbwAAHAAB","Answer":"ULiBgAABAAAAAQAAC2FkZ3VhcmR0ZWFtBmdpdGh1YgJpbwAAHAABwBgABgABAAADQgBLB25zLTE2MjIJYXdzZG5zLTEwAmNvAnVrABFhd3NkbnMtaG9zdG1hc3RlcgZhbWF6b24DY29tAAAAAAEAABwgAAADhAASdQAAAVGA","Result":{},"Time":"2020-11-13T12:41:25.970861+03:00","Elapsed":244066501,"IP":"127.0.0.1","Upstream":"https://1.1.1.1:443/dns-query"}`, want: "decodeLogEntry err: illegal base64 data at input byte 48\n", + }, { + name: "modern_all_right", + log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`, + want: "default", + }, { + name: "bad_filter_id", + log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1.5},"Elapsed":837429}`, + want: "decodeLogEntry err: strconv.ParseInt: parsing \"1.5\": invalid syntax\n", + }, { + name: "bad_is_filtered", + log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":trooe,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`, + want: "default", + }, { + name: "bad_elapsed", + log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":-1}`, + want: "default", + }, { + name: "bad_ip", + log: `{"IP":127001,"T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`, + want: "default", + }, { + name: "bad_time", + log: `{"IP":"127.0.0.1","T":"12/09/1998T15:00:00.000000+05:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`, + want: "decodeLogEntry err: parsing time \"12/09/1998T15:00:00.000000+05:00\" as \"2006-01-02T15:04:05Z07:00\": cannot parse \"9/1998T15:00:00.000000+05:00\" as \"2006\"\n", + }, { + name: "bad_host", + log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":6,"QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`, + want: "default", + }, { + name: "bad_type", + log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":true,"QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`, + want: "default", + }, { + name: "bad_class", + log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":false,"CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`, + want: "default", + }, { + name: "bad_client_proto", + log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":8,"Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`, + want: "default", + }, { + name: "very_bad_client_proto", + log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"dog","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`, + want: "decodeLogEntry err: invalid client proto: \"dog\"\n", + }, { + name: "bad_answer", + log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":0.9,"Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`, + want: "default", + }, { + name: "very_bad_answer", + log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`, + want: "decodeLogEntry err: illegal base64 data at input byte 61\n", + }, { + name: "bad_rule", + log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":false,"FilterID":1},"Elapsed":837429}`, + want: "default", + }, { + name: "bad_reason", + log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":true,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`, + want: "default", }} for _, tc := range testCases {