From 4ee9881270abd8939111b184896d9fa32a0b4a7a Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 7 Oct 2015 16:18:58 -0400 Subject: [PATCH] Add Nut Dependencies for NTLM --- Nut.toml | 1 - lfs/ntlm.go | 2 +- vendor/_nuts/code.google.com/p/log4go/.hgtags | 4 + vendor/_nuts/code.google.com/p/log4go/LICENSE | 13 + vendor/_nuts/code.google.com/p/log4go/README | 12 + .../_nuts/code.google.com/p/log4go/config.go | 288 ++++++++++ .../examples/ConsoleLogWriter_Manual.go | 13 + .../p/log4go/examples/FileLogWriter_Manual.go | 57 ++ .../p/log4go/examples/SimpleNetLogServer.go | 42 ++ .../log4go/examples/SocketLogWriter_Manual.go | 18 + .../examples/XMLConfigurationExample.go | 13 + .../p/log4go/examples/example.xml | 47 ++ .../_nuts/code.google.com/p/log4go/filelog.go | 239 ++++++++ .../_nuts/code.google.com/p/log4go/log4go.go | 484 ++++++++++++++++ .../code.google.com/p/log4go/log4go_test.go | 534 +++++++++++++++++ .../_nuts/code.google.com/p/log4go/pattlog.go | 122 ++++ .../_nuts/code.google.com/p/log4go/socklog.go | 57 ++ .../_nuts/code.google.com/p/log4go/termlog.go | 45 ++ .../_nuts/code.google.com/p/log4go/wrapper.go | 278 +++++++++ .../ThomsonReutersEikon/go-ntlm/.gitignore | 5 + .../ThomsonReutersEikon/go-ntlm/License | 27 + .../ThomsonReutersEikon/go-ntlm/README.md | 61 ++ .../go-ntlm/ntlm/av_pairs.go | 187 ++++++ .../go-ntlm/ntlm/challenge_responses.go | 154 +++++ .../go-ntlm/ntlm/crypto.go | 135 +++++ .../go-ntlm/ntlm/crypto_test.go | 64 +++ .../go-ntlm/ntlm/helpers.go | 89 +++ .../go-ntlm/ntlm/helpers_test.go | 35 ++ .../ThomsonReutersEikon/go-ntlm/ntlm/keys.go | 70 +++ .../go-ntlm/ntlm/md4/LICENSE | 27 + .../go-ntlm/ntlm/md4/md4.go | 118 ++++ .../go-ntlm/ntlm/md4/md4_test.go | 71 +++ .../go-ntlm/ntlm/md4/md4block.go | 89 +++ .../go-ntlm/ntlm/message_authenticate.go | 291 ++++++++++ .../go-ntlm/ntlm/message_authenticate_test.go | 102 ++++ .../go-ntlm/ntlm/message_challenge.go | 171 ++++++ .../go-ntlm/ntlm/message_challenge_test.go | 65 +++ .../go-ntlm/ntlm/message_negotiate.go | 27 + .../go-ntlm/ntlm/negotiate_flags.go | 202 +++++++ .../go-ntlm/ntlm/negotiate_flags_test.go | 31 + .../ThomsonReutersEikon/go-ntlm/ntlm/ntlm.go | 127 +++++ .../go-ntlm/ntlm/ntlmv1.go | 392 +++++++++++++ .../go-ntlm/ntlm/ntlmv1_test.go | 238 ++++++++ .../go-ntlm/ntlm/ntlmv2.go | 408 +++++++++++++ .../go-ntlm/ntlm/ntlmv2_test.go | 189 ++++++ .../go-ntlm/ntlm/payload.go | 94 +++ .../go-ntlm/ntlm/signature.go | 120 ++++ .../go-ntlm/ntlm/signature_test.go | 66 +++ .../go-ntlm/ntlm/version.go | 46 ++ .../go-ntlm/utils/decode_auth.go | 18 + .../go-ntlm/utils/test_auth.go | 87 +++ .../ThomsonReutersEikon/log4go/.gitignore | 1 + .../ThomsonReutersEikon/log4go/LICENSE | 13 + .../ThomsonReutersEikon/log4go/README | 12 + .../ThomsonReutersEikon/log4go/config.go | 283 +++++++++ .../examples/ConsoleLogWriter_Manual.go | 13 + .../log4go/examples/FileLogWriter_Manual.go | 57 ++ .../log4go/examples/SimpleNetLogServer.go | 42 ++ .../log4go/examples/SocketLogWriter_Manual.go | 18 + .../examples/XMLConfigurationExample.go | 13 + .../log4go/examples/example.xml | 47 ++ .../ThomsonReutersEikon/log4go/filelog.go | 239 ++++++++ .../ThomsonReutersEikon/log4go/log4go.go | 484 ++++++++++++++++ .../ThomsonReutersEikon/log4go/log4go_test.go | 537 ++++++++++++++++++ .../ThomsonReutersEikon/log4go/pattlog.go | 131 +++++ .../ThomsonReutersEikon/log4go/socklog.go | 57 ++ .../ThomsonReutersEikon/log4go/termlog.go | 31 + .../ThomsonReutersEikon/log4go/wrapper.go | 278 +++++++++ 68 files changed, 8329 insertions(+), 2 deletions(-) create mode 100644 vendor/_nuts/code.google.com/p/log4go/.hgtags create mode 100644 vendor/_nuts/code.google.com/p/log4go/LICENSE create mode 100644 vendor/_nuts/code.google.com/p/log4go/README create mode 100644 vendor/_nuts/code.google.com/p/log4go/config.go create mode 100644 vendor/_nuts/code.google.com/p/log4go/examples/ConsoleLogWriter_Manual.go create mode 100644 vendor/_nuts/code.google.com/p/log4go/examples/FileLogWriter_Manual.go create mode 100644 vendor/_nuts/code.google.com/p/log4go/examples/SimpleNetLogServer.go create mode 100644 vendor/_nuts/code.google.com/p/log4go/examples/SocketLogWriter_Manual.go create mode 100644 vendor/_nuts/code.google.com/p/log4go/examples/XMLConfigurationExample.go create mode 100644 vendor/_nuts/code.google.com/p/log4go/examples/example.xml create mode 100644 vendor/_nuts/code.google.com/p/log4go/filelog.go create mode 100644 vendor/_nuts/code.google.com/p/log4go/log4go.go create mode 100644 vendor/_nuts/code.google.com/p/log4go/log4go_test.go create mode 100644 vendor/_nuts/code.google.com/p/log4go/pattlog.go create mode 100644 vendor/_nuts/code.google.com/p/log4go/socklog.go create mode 100644 vendor/_nuts/code.google.com/p/log4go/termlog.go create mode 100644 vendor/_nuts/code.google.com/p/log4go/wrapper.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/.gitignore create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/License create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/README.md create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/av_pairs.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/challenge_responses.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/crypto.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/crypto_test.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/helpers.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/helpers_test.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/keys.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4/LICENSE create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4/md4.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4/md4_test.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4/md4block.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_authenticate.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_authenticate_test.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_challenge.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_challenge_test.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_negotiate.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/negotiate_flags.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/negotiate_flags_test.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlm.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlmv1.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlmv1_test.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlmv2.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlmv2_test.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/payload.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/signature.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/signature_test.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/version.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/utils/decode_auth.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/utils/test_auth.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/.gitignore create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/LICENSE create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/README create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/config.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/ConsoleLogWriter_Manual.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/FileLogWriter_Manual.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/SimpleNetLogServer.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/SocketLogWriter_Manual.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/XMLConfigurationExample.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/example.xml create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/filelog.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/log4go.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/log4go_test.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/pattlog.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/socklog.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/termlog.go create mode 100644 vendor/_nuts/github.com/ThomsonReutersEikon/log4go/wrapper.go diff --git a/Nut.toml b/Nut.toml index 815e3f38..03962fde 100644 --- a/Nut.toml +++ b/Nut.toml @@ -21,4 +21,3 @@ authors = [ "github.com/technoweenie/assert" = "b25ea301d127043ffacf3b2545726e79b6632139" "github.com/technoweenie/go-contentaddressable" = "38171def3cd15e3b76eb156219b3d48704643899" "github.com/ThomsonReutersEikon/go-ntlm/ntlm" = "52b7efa603f1b809167b528b8bbaa467e36fdc02" -"github.com/ThomsonReutersEikon/log4go" = "173466d4d37ffd4ca9c400bc0b67d57b4a73cdae" diff --git a/lfs/ntlm.go b/lfs/ntlm.go index 384346ba..02fde22e 100644 --- a/lfs/ntlm.go +++ b/lfs/ntlm.go @@ -208,4 +208,4 @@ type myReader struct { } // So that myReader implements the io.ReadCloser interface -func (m myReader) Close() error { return nil } \ No newline at end of file +func (m myReader) Close() error { return nil } diff --git a/vendor/_nuts/code.google.com/p/log4go/.hgtags b/vendor/_nuts/code.google.com/p/log4go/.hgtags new file mode 100644 index 00000000..72a2eea2 --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/.hgtags @@ -0,0 +1,4 @@ +4fbe6aadba231e838a449d340e43bdaab0bf85bd go.weekly.2012-02-07 +56168fd53249d639c25c74ced881fffb20d27be9 go.weekly.2012-02-22 +56168fd53249d639c25c74ced881fffb20d27be9 go.weekly.2012-02-22 +5c22fbd77d91f54d76cdbdee05318699754c44cc go.weekly.2012-02-22 diff --git a/vendor/_nuts/code.google.com/p/log4go/LICENSE b/vendor/_nuts/code.google.com/p/log4go/LICENSE new file mode 100644 index 00000000..7093402b --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2010, Kyle Lemons . All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/_nuts/code.google.com/p/log4go/README b/vendor/_nuts/code.google.com/p/log4go/README new file mode 100644 index 00000000..16d80ecb --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/README @@ -0,0 +1,12 @@ +Please see http://log4go.googlecode.com/ + +Installation: +- Run `goinstall log4go.googlecode.com/hg` + +Usage: +- Add the following import: +import l4g "log4go.googlecode.com/hg" + +Acknowledgements: +- pomack + For providing awesome patches to bring log4go up to the latest Go spec diff --git a/vendor/_nuts/code.google.com/p/log4go/config.go b/vendor/_nuts/code.google.com/p/log4go/config.go new file mode 100644 index 00000000..f048b69f --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/config.go @@ -0,0 +1,288 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +package log4go + +import ( + "encoding/xml" + "fmt" + "io/ioutil" + "os" + "strconv" + "strings" +) + +type xmlProperty struct { + Name string `xml:"name,attr"` + Value string `xml:",chardata"` +} + +type xmlFilter struct { + Enabled string `xml:"enabled,attr"` + Tag string `xml:"tag"` + Level string `xml:"level"` + Type string `xml:"type"` + Property []xmlProperty `xml:"property"` +} + +type xmlLoggerConfig struct { + Filter []xmlFilter `xml:"filter"` +} + +// Load XML configuration; see examples/example.xml for documentation +func (log Logger) LoadConfiguration(filename string) { + log.Close() + + // Open the configuration file + fd, err := os.Open(filename) + if err != nil { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not open %q for reading: %s\n", filename, err) + os.Exit(1) + } + + contents, err := ioutil.ReadAll(fd) + if err != nil { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not read %q: %s\n", filename, err) + os.Exit(1) + } + + xc := new(xmlLoggerConfig) + if err := xml.Unmarshal(contents, xc); err != nil { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not parse XML configuration in %q: %s\n", filename, err) + os.Exit(1) + } + + for _, xmlfilt := range xc.Filter { + var filt LogWriter + var lvl level + bad, good, enabled := false, true, false + + // Check required children + if len(xmlfilt.Enabled) == 0 { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required attribute %s for filter missing in %s\n", "enabled", filename) + bad = true + } else { + enabled = xmlfilt.Enabled != "false" + } + if len(xmlfilt.Tag) == 0 { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "tag", filename) + bad = true + } + if len(xmlfilt.Type) == 0 { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "type", filename) + bad = true + } + if len(xmlfilt.Level) == 0 { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "level", filename) + bad = true + } + + switch xmlfilt.Level { + case "FINEST": + lvl = FINEST + case "FINE": + lvl = FINE + case "DEBUG": + lvl = DEBUG + case "TRACE": + lvl = TRACE + case "INFO": + lvl = INFO + case "WARNING": + lvl = WARNING + case "ERROR": + lvl = ERROR + case "CRITICAL": + lvl = CRITICAL + default: + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter has unknown value in %s: %s\n", "level", filename, xmlfilt.Level) + bad = true + } + + // Just so all of the required attributes are errored at the same time if missing + if bad { + os.Exit(1) + } + + switch xmlfilt.Type { + case "console": + filt, good = xmlToConsoleLogWriter(filename, xmlfilt.Property, enabled) + case "file": + filt, good = xmlToFileLogWriter(filename, xmlfilt.Property, enabled) + case "xml": + filt, good = xmlToXMLLogWriter(filename, xmlfilt.Property, enabled) + case "socket": + filt, good = xmlToSocketLogWriter(filename, xmlfilt.Property, enabled) + default: + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not load XML configuration in %s: unknown filter type \"%s\"\n", filename, xmlfilt.Type) + os.Exit(1) + } + + // Just so all of the required params are errored at the same time if wrong + if !good { + os.Exit(1) + } + + // If we're disabled (syntax and correctness checks only), don't add to logger + if !enabled { + continue + } + + log[xmlfilt.Tag] = &Filter{lvl, filt} + } +} + +func xmlToConsoleLogWriter(filename string, props []xmlProperty, enabled bool) (ConsoleLogWriter, bool) { + // Parse properties + for _, prop := range props { + switch prop.Name { + default: + fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for console filter in %s\n", prop.Name, filename) + } + } + + // If it's disabled, we're just checking syntax + if !enabled { + return nil, true + } + + return NewConsoleLogWriter(), true +} + +// Parse a number with K/M/G suffixes based on thousands (1000) or 2^10 (1024) +func strToNumSuffix(str string, mult int) int { + num := 1 + if len(str) > 1 { + switch str[len(str)-1] { + case 'G', 'g': + num *= mult + fallthrough + case 'M', 'm': + num *= mult + fallthrough + case 'K', 'k': + num *= mult + str = str[0 : len(str)-1] + } + } + parsed, _ := strconv.Atoi(str) + return parsed * num +} +func xmlToFileLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) { + file := "" + format := "[%D %T] [%L] (%S) %M" + maxlines := 0 + maxsize := 0 + daily := false + rotate := false + + // Parse properties + for _, prop := range props { + switch prop.Name { + case "filename": + file = strings.Trim(prop.Value, " \r\n") + case "format": + format = strings.Trim(prop.Value, " \r\n") + case "maxlines": + maxlines = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000) + case "maxsize": + maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024) + case "daily": + daily = strings.Trim(prop.Value, " \r\n") != "false" + case "rotate": + rotate = strings.Trim(prop.Value, " \r\n") != "false" + default: + fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename) + } + } + + // Check properties + if len(file) == 0 { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "filename", filename) + return nil, false + } + + // If it's disabled, we're just checking syntax + if !enabled { + return nil, true + } + + flw := NewFileLogWriter(file, rotate) + flw.SetFormat(format) + flw.SetRotateLines(maxlines) + flw.SetRotateSize(maxsize) + flw.SetRotateDaily(daily) + return flw, true +} + +func xmlToXMLLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) { + file := "" + maxrecords := 0 + maxsize := 0 + daily := false + rotate := false + + // Parse properties + for _, prop := range props { + switch prop.Name { + case "filename": + file = strings.Trim(prop.Value, " \r\n") + case "maxrecords": + maxrecords = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000) + case "maxsize": + maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024) + case "daily": + daily = strings.Trim(prop.Value, " \r\n") != "false" + case "rotate": + rotate = strings.Trim(prop.Value, " \r\n") != "false" + default: + fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for xml filter in %s\n", prop.Name, filename) + } + } + + // Check properties + if len(file) == 0 { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for xml filter missing in %s\n", "filename", filename) + return nil, false + } + + // If it's disabled, we're just checking syntax + if !enabled { + return nil, true + } + + xlw := NewXMLLogWriter(file, rotate) + xlw.SetRotateLines(maxrecords) + xlw.SetRotateSize(maxsize) + xlw.SetRotateDaily(daily) + return xlw, true +} + +func xmlToSocketLogWriter(filename string, props []xmlProperty, enabled bool) (SocketLogWriter, bool) { + endpoint := "" + protocol := "udp" + + // Parse properties + for _, prop := range props { + switch prop.Name { + case "endpoint": + endpoint = strings.Trim(prop.Value, " \r\n") + case "protocol": + protocol = strings.Trim(prop.Value, " \r\n") + default: + fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename) + } + } + + // Check properties + if len(endpoint) == 0 { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "endpoint", filename) + return nil, false + } + + // If it's disabled, we're just checking syntax + if !enabled { + return nil, true + } + + return NewSocketLogWriter(protocol, endpoint), true +} diff --git a/vendor/_nuts/code.google.com/p/log4go/examples/ConsoleLogWriter_Manual.go b/vendor/_nuts/code.google.com/p/log4go/examples/ConsoleLogWriter_Manual.go new file mode 100644 index 00000000..7169ec97 --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/examples/ConsoleLogWriter_Manual.go @@ -0,0 +1,13 @@ +package main + +import ( + "time" +) + +import l4g "github.com/github/git-lfs/vendor/_nuts/code.google.com/p/log4go" + +func main() { + log := l4g.NewLogger() + log.AddFilter("stdout", l4g.DEBUG, l4g.NewConsoleLogWriter()) + log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) +} diff --git a/vendor/_nuts/code.google.com/p/log4go/examples/FileLogWriter_Manual.go b/vendor/_nuts/code.google.com/p/log4go/examples/FileLogWriter_Manual.go new file mode 100644 index 00000000..872bb45b --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/examples/FileLogWriter_Manual.go @@ -0,0 +1,57 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "os" + "time" +) + +import l4g "github.com/github/git-lfs/vendor/_nuts/code.google.com/p/log4go" + +const ( + filename = "flw.log" +) + +func main() { + // Get a new logger instance + log := l4g.NewLogger() + + // Create a default logger that is logging messages of FINE or higher + log.AddFilter("file", l4g.FINE, l4g.NewFileLogWriter(filename, false)) + log.Close() + + /* Can also specify manually via the following: (these are the defaults) */ + flw := l4g.NewFileLogWriter(filename, false) + flw.SetFormat("[%D %T] [%L] (%S) %M") + flw.SetRotate(false) + flw.SetRotateSize(0) + flw.SetRotateLines(0) + flw.SetRotateDaily(false) + log.AddFilter("file", l4g.FINE, flw) + + // Log some experimental messages + log.Finest("Everything is created now (notice that I will not be printing to the file)") + log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) + log.Critical("Time to close out!") + + // Close the log + log.Close() + + // Print what was logged to the file (yes, I know I'm skipping error checking) + fd, _ := os.Open(filename) + in := bufio.NewReader(fd) + fmt.Print("Messages logged to file were: (line numbers not included)\n") + for lineno := 1; ; lineno++ { + line, err := in.ReadString('\n') + if err == io.EOF { + break + } + fmt.Printf("%3d:\t%s", lineno, line) + } + fd.Close() + + // Remove the file so it's not lying around + os.Remove(filename) +} diff --git a/vendor/_nuts/code.google.com/p/log4go/examples/SimpleNetLogServer.go b/vendor/_nuts/code.google.com/p/log4go/examples/SimpleNetLogServer.go new file mode 100644 index 00000000..83c80ad1 --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/examples/SimpleNetLogServer.go @@ -0,0 +1,42 @@ +package main + +import ( + "flag" + "fmt" + "net" + "os" +) + +var ( + port = flag.String("p", "12124", "Port number to listen on") +) + +func e(err error) { + if err != nil { + fmt.Printf("Erroring out: %s\n", err) + os.Exit(1) + } +} + +func main() { + flag.Parse() + + // Bind to the port + bind, err := net.ResolveUDPAddr("0.0.0.0:" + *port) + e(err) + + // Create listener + listener, err := net.ListenUDP("udp", bind) + e(err) + + fmt.Printf("Listening to port %s...\n", *port) + for { + // read into a new buffer + buffer := make([]byte, 1024) + _, _, err := listener.ReadFrom(buffer) + e(err) + + // log to standard output + fmt.Println(string(buffer)) + } +} diff --git a/vendor/_nuts/code.google.com/p/log4go/examples/SocketLogWriter_Manual.go b/vendor/_nuts/code.google.com/p/log4go/examples/SocketLogWriter_Manual.go new file mode 100644 index 00000000..d553699f --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/examples/SocketLogWriter_Manual.go @@ -0,0 +1,18 @@ +package main + +import ( + "time" +) + +import l4g "github.com/github/git-lfs/vendor/_nuts/code.google.com/p/log4go" + +func main() { + log := l4g.NewLogger() + log.AddFilter("network", l4g.FINEST, l4g.NewSocketLogWriter("udp", "192.168.1.255:12124")) + + // Run `nc -u -l -p 12124` or similar before you run this to see the following message + log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) + + // This makes sure the output stream buffer is written + log.Close() +} diff --git a/vendor/_nuts/code.google.com/p/log4go/examples/XMLConfigurationExample.go b/vendor/_nuts/code.google.com/p/log4go/examples/XMLConfigurationExample.go new file mode 100644 index 00000000..d6485753 --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/examples/XMLConfigurationExample.go @@ -0,0 +1,13 @@ +package main + +import l4g "github.com/github/git-lfs/vendor/_nuts/code.google.com/p/log4go" + +func main() { + // Load the configuration (isn't this easy?) + l4g.LoadConfiguration("example.xml") + + // And now we're ready! + l4g.Finest("This will only go to those of you really cool UDP kids! If you change enabled=true.") + l4g.Debug("Oh no! %d + %d = %d!", 2, 2, 2+2) + l4g.Info("About that time, eh chaps?") +} diff --git a/vendor/_nuts/code.google.com/p/log4go/examples/example.xml b/vendor/_nuts/code.google.com/p/log4go/examples/example.xml new file mode 100644 index 00000000..e791278c --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/examples/example.xml @@ -0,0 +1,47 @@ + + + stdout + console + + DEBUG + + + file + file + FINEST + test.log + + [%D %T] [%L] (%S) %M + false + 0M + 0K + true + + + xmllog + xml + TRACE + trace.xml + true + 100M + 6K + false + + + donotopen + socket + FINEST + 192.168.1.255:12124 + udp + + diff --git a/vendor/_nuts/code.google.com/p/log4go/filelog.go b/vendor/_nuts/code.google.com/p/log4go/filelog.go new file mode 100644 index 00000000..9cbd815d --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/filelog.go @@ -0,0 +1,239 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +package log4go + +import ( + "os" + "fmt" + "time" +) + +// This log writer sends output to a file +type FileLogWriter struct { + rec chan *LogRecord + rot chan bool + + // The opened file + filename string + file *os.File + + // The logging format + format string + + // File header/trailer + header, trailer string + + // Rotate at linecount + maxlines int + maxlines_curlines int + + // Rotate at size + maxsize int + maxsize_cursize int + + // Rotate daily + daily bool + daily_opendate int + + // Keep old logfiles (.001, .002, etc) + rotate bool +} + +// This is the FileLogWriter's output method +func (w *FileLogWriter) LogWrite(rec *LogRecord) { + w.rec <- rec +} + +func (w *FileLogWriter) Close() { + close(w.rec) +} + +// NewFileLogWriter creates a new LogWriter which writes to the given file and +// has rotation enabled if rotate is true. +// +// If rotate is true, any time a new log file is opened, the old one is renamed +// with a .### extension to preserve it. The various Set* methods can be used +// to configure log rotation based on lines, size, and daily. +// +// The standard log-line format is: +// [%D %T] [%L] (%S) %M +func NewFileLogWriter(fname string, rotate bool) *FileLogWriter { + w := &FileLogWriter{ + rec: make(chan *LogRecord, LogBufferLength), + rot: make(chan bool), + filename: fname, + format: "[%D %T] [%L] (%S) %M", + rotate: rotate, + } + + // open the file for the first time + if err := w.intRotate(); err != nil { + fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) + return nil + } + + go func() { + defer func() { + if w.file != nil { + fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()})) + w.file.Close() + } + }() + + for { + select { + case <-w.rot: + if err := w.intRotate(); err != nil { + fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) + return + } + case rec, ok := <-w.rec: + if !ok { + return + } + now := time.Now() + if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) || + (w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) || + (w.daily && now.Day() != w.daily_opendate) { + if err := w.intRotate(); err != nil { + fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) + return + } + } + + // Perform the write + n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec)) + if err != nil { + fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) + return + } + + // Update the counts + w.maxlines_curlines++ + w.maxsize_cursize += n + } + } + }() + + return w +} + +// Request that the logs rotate +func (w *FileLogWriter) Rotate() { + w.rot <- true +} + +// If this is called in a threaded context, it MUST be synchronized +func (w *FileLogWriter) intRotate() error { + // Close any log file that may be open + if w.file != nil { + fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()})) + w.file.Close() + } + + // If we are keeping log files, move it to the next available number + if w.rotate { + _, err := os.Lstat(w.filename) + if err == nil { // file exists + // Find the next available number + num := 1 + fname := "" + for ; err == nil && num <= 999; num++ { + fname = w.filename + fmt.Sprintf(".%03d", num) + _, err = os.Lstat(fname) + } + // return error if the last file checked still existed + if err == nil { + return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename) + } + + // Rename the file to its newfound home + err = os.Rename(w.filename, fname) + if err != nil { + return fmt.Errorf("Rotate: %s\n", err) + } + } + } + + // Open the log file + fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) + if err != nil { + return err + } + w.file = fd + + now := time.Now() + fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now})) + + // Set the daily open date to the current date + w.daily_opendate = now.Day() + + // initialize rotation values + w.maxlines_curlines = 0 + w.maxsize_cursize = 0 + + return nil +} + +// Set the logging format (chainable). Must be called before the first log +// message is written. +func (w *FileLogWriter) SetFormat(format string) *FileLogWriter { + w.format = format + return w +} + +// Set the logfile header and footer (chainable). Must be called before the first log +// message is written. These are formatted similar to the FormatLogRecord (e.g. +// you can use %D and %T in your header/footer for date and time). +func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter { + w.header, w.trailer = head, foot + if w.maxlines_curlines == 0 { + fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()})) + } + return w +} + +// Set rotate at linecount (chainable). Must be called before the first log +// message is written. +func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter { + //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines) + w.maxlines = maxlines + return w +} + +// Set rotate at size (chainable). Must be called before the first log message +// is written. +func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter { + //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize) + w.maxsize = maxsize + return w +} + +// Set rotate daily (chainable). Must be called before the first log message is +// written. +func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter { + //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily) + w.daily = daily + return w +} + +// SetRotate changes whether or not the old logs are kept. (chainable) Must be +// called before the first log message is written. If rotate is false, the +// files are overwritten; otherwise, they are rotated to another file before the +// new log is opened. +func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter { + //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate) + w.rotate = rotate + return w +} + +// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to +// output XML record log messages instead of line-based ones. +func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter { + return NewFileLogWriter(fname, rotate).SetFormat( + ` + %D %T + %S + %M + `).SetHeadFoot("", "") +} diff --git a/vendor/_nuts/code.google.com/p/log4go/log4go.go b/vendor/_nuts/code.google.com/p/log4go/log4go.go new file mode 100644 index 00000000..ab4e857f --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/log4go.go @@ -0,0 +1,484 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +// Package log4go provides level-based and highly configurable logging. +// +// Enhanced Logging +// +// This is inspired by the logging functionality in Java. Essentially, you create a Logger +// object and create output filters for it. You can send whatever you want to the Logger, +// and it will filter that based on your settings and send it to the outputs. This way, you +// can put as much debug code in your program as you want, and when you're done you can filter +// out the mundane messages so only the important ones show up. +// +// Utility functions are provided to make life easier. Here is some example code to get started: +// +// log := log4go.NewLogger() +// log.AddFilter("stdout", log4go.DEBUG, log4go.NewConsoleLogWriter()) +// log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true)) +// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02")) +// +// The first two lines can be combined with the utility NewDefaultLogger: +// +// log := log4go.NewDefaultLogger(log4go.DEBUG) +// log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true)) +// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02")) +// +// Usage notes: +// - The ConsoleLogWriter does not display the source of the message to standard +// output, but the FileLogWriter does. +// - The utility functions (Info, Debug, Warn, etc) derive their source from the +// calling function, and this incurs extra overhead. +// +// Changes from 2.0: +// - The external interface has remained mostly stable, but a lot of the +// internals have been changed, so if you depended on any of this or created +// your own LogWriter, then you will probably have to update your code. In +// particular, Logger is now a map and ConsoleLogWriter is now a channel +// behind-the-scenes, and the LogWrite method no longer has return values. +// +// Future work: (please let me know if you think I should work on any of these particularly) +// - Log file rotation +// - Logging configuration files ala log4j +// - Have the ability to remove filters? +// - Have GetInfoChannel, GetDebugChannel, etc return a chan string that allows +// for another method of logging +// - Add an XML filter type +package log4go + +import ( + "errors" + "os" + "fmt" + "time" + "strings" + "runtime" +) + +// Version information +const ( + L4G_VERSION = "log4go-v3.0.1" + L4G_MAJOR = 3 + L4G_MINOR = 0 + L4G_BUILD = 1 +) + +/****** Constants ******/ + +// These are the integer logging levels used by the logger +type level int + +const ( + FINEST level = iota + FINE + DEBUG + TRACE + INFO + WARNING + ERROR + CRITICAL +) + +// Logging level strings +var ( + levelStrings = [...]string{"FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"} +) + +func (l level) String() string { + if l < 0 || int(l) > len(levelStrings) { + return "UNKNOWN" + } + return levelStrings[int(l)] +} + +/****** Variables ******/ +var ( + // LogBufferLength specifies how many log messages a particular log4go + // logger can buffer at a time before writing them. + LogBufferLength = 32 +) + +/****** LogRecord ******/ + +// A LogRecord contains all of the pertinent information for each message +type LogRecord struct { + Level level // The log level + Created time.Time // The time at which the log message was created (nanoseconds) + Source string // The message source + Message string // The log message +} + +/****** LogWriter ******/ + +// This is an interface for anything that should be able to write logs +type LogWriter interface { + // This will be called to log a LogRecord message. + LogWrite(rec *LogRecord) + + // This should clean up anything lingering about the LogWriter, as it is called before + // the LogWriter is removed. LogWrite should not be called after Close. + Close() +} + +/****** Logger ******/ + +// A Filter represents the log level below which no log records are written to +// the associated LogWriter. +type Filter struct { + Level level + LogWriter +} + +// A Logger represents a collection of Filters through which log messages are +// written. +type Logger map[string]*Filter + +// Create a new logger. +// +// DEPRECATED: Use make(Logger) instead. +func NewLogger() Logger { + os.Stderr.WriteString("warning: use of deprecated NewLogger\n") + return make(Logger) +} + +// Create a new logger with a "stdout" filter configured to send log messages at +// or above lvl to standard output. +// +// DEPRECATED: use NewDefaultLogger instead. +func NewConsoleLogger(lvl level) Logger { + os.Stderr.WriteString("warning: use of deprecated NewConsoleLogger\n") + return Logger{ + "stdout": &Filter{lvl, NewConsoleLogWriter()}, + } +} + +// Create a new logger with a "stdout" filter configured to send log messages at +// or above lvl to standard output. +func NewDefaultLogger(lvl level) Logger { + return Logger{ + "stdout": &Filter{lvl, NewConsoleLogWriter()}, + } +} + +// Closes all log writers in preparation for exiting the program or a +// reconfiguration of logging. Calling this is not really imperative, unless +// you want to guarantee that all log messages are written. Close removes +// all filters (and thus all LogWriters) from the logger. +func (log Logger) Close() { + // Close all open loggers + for name, filt := range log { + filt.Close() + delete(log, name) + } +} + +// Add a new LogWriter to the Logger which will only log messages at lvl or +// higher. This function should not be called from multiple goroutines. +// Returns the logger for chaining. +func (log Logger) AddFilter(name string, lvl level, writer LogWriter) Logger { + log[name] = &Filter{lvl, writer} + return log +} + +/******* Logging *******/ +// Send a formatted log message internally +func (log Logger) intLogf(lvl level, format string, args ...interface{}) { + skip := true + + // Determine if any logging will be done + for _, filt := range log { + if lvl >= filt.Level { + skip = false + break + } + } + if skip { + return + } + + // Determine caller func + pc, _, lineno, ok := runtime.Caller(2) + src := "" + if ok { + src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno) + } + + msg := format + if len(args) > 0 { + msg = fmt.Sprintf(format, args...) + } + + // Make the log record + rec := &LogRecord{ + Level: lvl, + Created: time.Now(), + Source: src, + Message: msg, + } + + // Dispatch the logs + for _, filt := range log { + if lvl < filt.Level { + continue + } + filt.LogWrite(rec) + } +} + +// Send a closure log message internally +func (log Logger) intLogc(lvl level, closure func() string) { + skip := true + + // Determine if any logging will be done + for _, filt := range log { + if lvl >= filt.Level { + skip = false + break + } + } + if skip { + return + } + + // Determine caller func + pc, _, lineno, ok := runtime.Caller(2) + src := "" + if ok { + src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno) + } + + // Make the log record + rec := &LogRecord{ + Level: lvl, + Created: time.Now(), + Source: src, + Message: closure(), + } + + // Dispatch the logs + for _, filt := range log { + if lvl < filt.Level { + continue + } + filt.LogWrite(rec) + } +} + +// Send a log message with manual level, source, and message. +func (log Logger) Log(lvl level, source, message string) { + skip := true + + // Determine if any logging will be done + for _, filt := range log { + if lvl >= filt.Level { + skip = false + break + } + } + if skip { + return + } + + // Make the log record + rec := &LogRecord{ + Level: lvl, + Created: time.Now(), + Source: source, + Message: message, + } + + // Dispatch the logs + for _, filt := range log { + if lvl < filt.Level { + continue + } + filt.LogWrite(rec) + } +} + +// Logf logs a formatted log message at the given log level, using the caller as +// its source. +func (log Logger) Logf(lvl level, format string, args ...interface{}) { + log.intLogf(lvl, format, args...) +} + +// Logc logs a string returned by the closure at the given log level, using the caller as +// its source. If no log message would be written, the closure is never called. +func (log Logger) Logc(lvl level, closure func() string) { + log.intLogc(lvl, closure) +} + +// Finest logs a message at the finest log level. +// See Debug for an explanation of the arguments. +func (log Logger) Finest(arg0 interface{}, args ...interface{}) { + const ( + lvl = FINEST + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + log.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + log.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Fine logs a message at the fine log level. +// See Debug for an explanation of the arguments. +func (log Logger) Fine(arg0 interface{}, args ...interface{}) { + const ( + lvl = FINE + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + log.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + log.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Debug is a utility method for debug log messages. +// The behavior of Debug depends on the first argument: +// - arg0 is a string +// When given a string as the first argument, this behaves like Logf but with +// the DEBUG log level: the first argument is interpreted as a format for the +// latter arguments. +// - arg0 is a func()string +// When given a closure of type func()string, this logs the string returned by +// the closure iff it will be logged. The closure runs at most one time. +// - arg0 is interface{} +// When given anything else, the log message will be each of the arguments +// formatted with %v and separated by spaces (ala Sprint). +func (log Logger) Debug(arg0 interface{}, args ...interface{}) { + const ( + lvl = DEBUG + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + log.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + log.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Trace logs a message at the trace log level. +// See Debug for an explanation of the arguments. +func (log Logger) Trace(arg0 interface{}, args ...interface{}) { + const ( + lvl = TRACE + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + log.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + log.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Info logs a message at the info log level. +// See Debug for an explanation of the arguments. +func (log Logger) Info(arg0 interface{}, args ...interface{}) { + const ( + lvl = INFO + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + log.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + log.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Warn logs a message at the warning log level and returns the formatted error. +// At the warning level and higher, there is no performance benefit if the +// message is not actually logged, because all formats are processed and all +// closures are executed to format the error message. +// See Debug for further explanation of the arguments. +func (log Logger) Warn(arg0 interface{}, args ...interface{}) error { + const ( + lvl = WARNING + ) + var msg string + switch first := arg0.(type) { + case string: + // Use the string as a format string + msg = fmt.Sprintf(first, args...) + case func() string: + // Log the closure (no other arguments used) + msg = first() + default: + // Build a format string so that it will be similar to Sprint + msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) + } + log.intLogf(lvl, msg) + return errors.New(msg) +} + +// Error logs a message at the error log level and returns the formatted error, +// See Warn for an explanation of the performance and Debug for an explanation +// of the parameters. +func (log Logger) Error(arg0 interface{}, args ...interface{}) error { + const ( + lvl = ERROR + ) + var msg string + switch first := arg0.(type) { + case string: + // Use the string as a format string + msg = fmt.Sprintf(first, args...) + case func() string: + // Log the closure (no other arguments used) + msg = first() + default: + // Build a format string so that it will be similar to Sprint + msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) + } + log.intLogf(lvl, msg) + return errors.New(msg) +} + +// Critical logs a message at the critical log level and returns the formatted error, +// See Warn for an explanation of the performance and Debug for an explanation +// of the parameters. +func (log Logger) Critical(arg0 interface{}, args ...interface{}) error { + const ( + lvl = CRITICAL + ) + var msg string + switch first := arg0.(type) { + case string: + // Use the string as a format string + msg = fmt.Sprintf(first, args...) + case func() string: + // Log the closure (no other arguments used) + msg = first() + default: + // Build a format string so that it will be similar to Sprint + msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) + } + log.intLogf(lvl, msg) + return errors.New(msg) +} diff --git a/vendor/_nuts/code.google.com/p/log4go/log4go_test.go b/vendor/_nuts/code.google.com/p/log4go/log4go_test.go new file mode 100644 index 00000000..90c62997 --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/log4go_test.go @@ -0,0 +1,534 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +package log4go + +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "io" + "io/ioutil" + "os" + "runtime" + "testing" + "time" +) + +const testLogFile = "_logtest.log" + +var now time.Time = time.Unix(0, 1234567890123456789).In(time.UTC) + +func newLogRecord(lvl level, src string, msg string) *LogRecord { + return &LogRecord{ + Level: lvl, + Source: src, + Created: now, + Message: msg, + } +} + +func TestELog(t *testing.T) { + fmt.Printf("Testing %s\n", L4G_VERSION) + lr := newLogRecord(CRITICAL, "source", "message") + if lr.Level != CRITICAL { + t.Errorf("Incorrect level: %d should be %d", lr.Level, CRITICAL) + } + if lr.Source != "source" { + t.Errorf("Incorrect source: %s should be %s", lr.Source, "source") + } + if lr.Message != "message" { + t.Errorf("Incorrect message: %s should be %s", lr.Source, "message") + } +} + +var formatTests = []struct { + Test string + Record *LogRecord + Formats map[string]string +}{ + { + Test: "Standard formats", + Record: &LogRecord{ + Level: ERROR, + Source: "source", + Message: "message", + Created: now, + }, + Formats: map[string]string{ + // TODO(kevlar): How can I do this so it'll work outside of PST? + FORMAT_DEFAULT: "[2009/02/13 23:31:30 UTC] [EROR] (source) message\n", + FORMAT_SHORT: "[23:31 02/13/09] [EROR] message\n", + FORMAT_ABBREV: "[EROR] message\n", + }, + }, +} + +func TestFormatLogRecord(t *testing.T) { + for _, test := range formatTests { + name := test.Test + for fmt, want := range test.Formats { + if got := FormatLogRecord(fmt, test.Record); got != want { + t.Errorf("%s - %s:", name, fmt) + t.Errorf(" got %q", got) + t.Errorf(" want %q", want) + } + } + } +} + +var logRecordWriteTests = []struct { + Test string + Record *LogRecord + Console string +}{ + { + Test: "Normal message", + Record: &LogRecord{ + Level: CRITICAL, + Source: "source", + Message: "message", + Created: now, + }, + Console: "[02/13/09 23:31:30] [CRIT] message\n", + }, +} + +func TestConsoleLogWriter(t *testing.T) { + console := make(ConsoleLogWriter) + + r, w := io.Pipe() + go console.run(w) + defer console.Close() + + buf := make([]byte, 1024) + + for _, test := range logRecordWriteTests { + name := test.Test + + console.LogWrite(test.Record) + n, _ := r.Read(buf) + + if got, want := string(buf[:n]), test.Console; got != want { + t.Errorf("%s: got %q", name, got) + t.Errorf("%s: want %q", name, want) + } + } +} + +func TestFileLogWriter(t *testing.T) { + defer func(buflen int) { + LogBufferLength = buflen + }(LogBufferLength) + LogBufferLength = 0 + + w := NewFileLogWriter(testLogFile, false) + if w == nil { + t.Fatalf("Invalid return: w should not be nil") + } + defer os.Remove(testLogFile) + + w.LogWrite(newLogRecord(CRITICAL, "source", "message")) + w.Close() + runtime.Gosched() + + if contents, err := ioutil.ReadFile(testLogFile); err != nil { + t.Errorf("read(%q): %s", testLogFile, err) + } else if len(contents) != 50 { + t.Errorf("malformed filelog: %q (%d bytes)", string(contents), len(contents)) + } +} + +func TestXMLLogWriter(t *testing.T) { + defer func(buflen int) { + LogBufferLength = buflen + }(LogBufferLength) + LogBufferLength = 0 + + w := NewXMLLogWriter(testLogFile, false) + if w == nil { + t.Fatalf("Invalid return: w should not be nil") + } + defer os.Remove(testLogFile) + + w.LogWrite(newLogRecord(CRITICAL, "source", "message")) + w.Close() + runtime.Gosched() + + if contents, err := ioutil.ReadFile(testLogFile); err != nil { + t.Errorf("read(%q): %s", testLogFile, err) + } else if len(contents) != 185 { + t.Errorf("malformed xmllog: %q (%d bytes)", string(contents), len(contents)) + } +} + +func TestLogger(t *testing.T) { + sl := NewDefaultLogger(WARNING) + if sl == nil { + t.Fatalf("NewDefaultLogger should never return nil") + } + if lw, exist := sl["stdout"]; lw == nil || exist != true { + t.Fatalf("NewDefaultLogger produced invalid logger (DNE or nil)") + } + if sl["stdout"].Level != WARNING { + t.Fatalf("NewDefaultLogger produced invalid logger (incorrect level)") + } + if len(sl) != 1 { + t.Fatalf("NewDefaultLogger produced invalid logger (incorrect map count)") + } + + //func (l *Logger) AddFilter(name string, level int, writer LogWriter) {} + l := make(Logger) + l.AddFilter("stdout", DEBUG, NewConsoleLogWriter()) + if lw, exist := l["stdout"]; lw == nil || exist != true { + t.Fatalf("AddFilter produced invalid logger (DNE or nil)") + } + if l["stdout"].Level != DEBUG { + t.Fatalf("AddFilter produced invalid logger (incorrect level)") + } + if len(l) != 1 { + t.Fatalf("AddFilter produced invalid logger (incorrect map count)") + } + + //func (l *Logger) Warn(format string, args ...interface{}) error {} + if err := l.Warn("%s %d %#v", "Warning:", 1, []int{}); err.Error() != "Warning: 1 []int{}" { + t.Errorf("Warn returned invalid error: %s", err) + } + + //func (l *Logger) Error(format string, args ...interface{}) error {} + if err := l.Error("%s %d %#v", "Error:", 10, []string{}); err.Error() != "Error: 10 []string{}" { + t.Errorf("Error returned invalid error: %s", err) + } + + //func (l *Logger) Critical(format string, args ...interface{}) error {} + if err := l.Critical("%s %d %#v", "Critical:", 100, []int64{}); err.Error() != "Critical: 100 []int64{}" { + t.Errorf("Critical returned invalid error: %s", err) + } + + // Already tested or basically untestable + //func (l *Logger) Log(level int, source, message string) {} + //func (l *Logger) Logf(level int, format string, args ...interface{}) {} + //func (l *Logger) intLogf(level int, format string, args ...interface{}) string {} + //func (l *Logger) Finest(format string, args ...interface{}) {} + //func (l *Logger) Fine(format string, args ...interface{}) {} + //func (l *Logger) Debug(format string, args ...interface{}) {} + //func (l *Logger) Trace(format string, args ...interface{}) {} + //func (l *Logger) Info(format string, args ...interface{}) {} +} + +func TestLogOutput(t *testing.T) { + const ( + expected = "fdf3e51e444da56b4cb400f30bc47424" + ) + + // Unbuffered output + defer func(buflen int) { + LogBufferLength = buflen + }(LogBufferLength) + LogBufferLength = 0 + + l := make(Logger) + + // Delete and open the output log without a timestamp (for a constant md5sum) + l.AddFilter("file", FINEST, NewFileLogWriter(testLogFile, false).SetFormat("[%L] %M")) + defer os.Remove(testLogFile) + + // Send some log messages + l.Log(CRITICAL, "testsrc1", fmt.Sprintf("This message is level %d", int(CRITICAL))) + l.Logf(ERROR, "This message is level %v", ERROR) + l.Logf(WARNING, "This message is level %s", WARNING) + l.Logc(INFO, func() string { return "This message is level INFO" }) + l.Trace("This message is level %d", int(TRACE)) + l.Debug("This message is level %s", DEBUG) + l.Fine(func() string { return fmt.Sprintf("This message is level %v", FINE) }) + l.Finest("This message is level %v", FINEST) + l.Finest(FINEST, "is also this message's level") + + l.Close() + + contents, err := ioutil.ReadFile(testLogFile) + if err != nil { + t.Fatalf("Could not read output log: %s", err) + } + + sum := md5.New() + sum.Write(contents) + if sumstr := hex.EncodeToString(sum.Sum(nil)); sumstr != expected { + t.Errorf("--- Log Contents:\n%s---", string(contents)) + t.Fatalf("Checksum does not match: %s (expecting %s)", sumstr, expected) + } +} + +func TestCountMallocs(t *testing.T) { + const N = 1 + var m runtime.MemStats + getMallocs := func() uint64 { + runtime.ReadMemStats(&m) + return m.Mallocs + } + + // Console logger + sl := NewDefaultLogger(INFO) + mallocs := 0 - getMallocs() + for i := 0; i < N; i++ { + sl.Log(WARNING, "here", "This is a WARNING message") + } + mallocs += getMallocs() + fmt.Printf("mallocs per sl.Log((WARNING, \"here\", \"This is a log message\"): %d\n", mallocs/N) + + // Console logger formatted + mallocs = 0 - getMallocs() + for i := 0; i < N; i++ { + sl.Logf(WARNING, "%s is a log message with level %d", "This", WARNING) + } + mallocs += getMallocs() + fmt.Printf("mallocs per sl.Logf(WARNING, \"%%s is a log message with level %%d\", \"This\", WARNING): %d\n", mallocs/N) + + // Console logger (not logged) + sl = NewDefaultLogger(INFO) + mallocs = 0 - getMallocs() + for i := 0; i < N; i++ { + sl.Log(DEBUG, "here", "This is a DEBUG log message") + } + mallocs += getMallocs() + fmt.Printf("mallocs per unlogged sl.Log((WARNING, \"here\", \"This is a log message\"): %d\n", mallocs/N) + + // Console logger formatted (not logged) + mallocs = 0 - getMallocs() + for i := 0; i < N; i++ { + sl.Logf(DEBUG, "%s is a log message with level %d", "This", DEBUG) + } + mallocs += getMallocs() + fmt.Printf("mallocs per unlogged sl.Logf(WARNING, \"%%s is a log message with level %%d\", \"This\", WARNING): %d\n", mallocs/N) +} + +func TestXMLConfig(t *testing.T) { + const ( + configfile = "example.xml" + ) + + fd, err := os.Create(configfile) + if err != nil { + t.Fatalf("Could not open %s for writing: %s", configfile, err) + } + + fmt.Fprintln(fd, "") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " stdout") + fmt.Fprintln(fd, " console") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " DEBUG") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " file") + fmt.Fprintln(fd, " file") + fmt.Fprintln(fd, " FINEST") + fmt.Fprintln(fd, " test.log") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " [%D %T] [%L] (%S) %M") + fmt.Fprintln(fd, " false ") + fmt.Fprintln(fd, " 0M ") + fmt.Fprintln(fd, " 0K ") + fmt.Fprintln(fd, " true ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " xmllog") + fmt.Fprintln(fd, " xml") + fmt.Fprintln(fd, " TRACE") + fmt.Fprintln(fd, " trace.xml") + fmt.Fprintln(fd, " true ") + fmt.Fprintln(fd, " 100M ") + fmt.Fprintln(fd, " 6K ") + fmt.Fprintln(fd, " false ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " donotopen") + fmt.Fprintln(fd, " socket") + fmt.Fprintln(fd, " FINEST") + fmt.Fprintln(fd, " 192.168.1.255:12124 ") + fmt.Fprintln(fd, " udp ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, "") + fd.Close() + + log := make(Logger) + log.LoadConfiguration(configfile) + defer os.Remove("trace.xml") + defer os.Remove("test.log") + defer log.Close() + + // Make sure we got all loggers + if len(log) != 3 { + t.Fatalf("XMLConfig: Expected 3 filters, found %d", len(log)) + } + + // Make sure they're the right keys + if _, ok := log["stdout"]; !ok { + t.Errorf("XMLConfig: Expected stdout logger") + } + if _, ok := log["file"]; !ok { + t.Fatalf("XMLConfig: Expected file logger") + } + if _, ok := log["xmllog"]; !ok { + t.Fatalf("XMLConfig: Expected xmllog logger") + } + + // Make sure they're the right type + if _, ok := log["stdout"].LogWriter.(ConsoleLogWriter); !ok { + t.Fatalf("XMLConfig: Expected stdout to be ConsoleLogWriter, found %T", log["stdout"].LogWriter) + } + if _, ok := log["file"].LogWriter.(*FileLogWriter); !ok { + t.Fatalf("XMLConfig: Expected file to be *FileLogWriter, found %T", log["file"].LogWriter) + } + if _, ok := log["xmllog"].LogWriter.(*FileLogWriter); !ok { + t.Fatalf("XMLConfig: Expected xmllog to be *FileLogWriter, found %T", log["xmllog"].LogWriter) + } + + // Make sure levels are set + if lvl := log["stdout"].Level; lvl != DEBUG { + t.Errorf("XMLConfig: Expected stdout to be set to level %d, found %d", DEBUG, lvl) + } + if lvl := log["file"].Level; lvl != FINEST { + t.Errorf("XMLConfig: Expected file to be set to level %d, found %d", FINEST, lvl) + } + if lvl := log["xmllog"].Level; lvl != TRACE { + t.Errorf("XMLConfig: Expected xmllog to be set to level %d, found %d", TRACE, lvl) + } + + // Make sure the w is open and points to the right file + if fname := log["file"].LogWriter.(*FileLogWriter).file.Name(); fname != "test.log" { + t.Errorf("XMLConfig: Expected file to have opened %s, found %s", "test.log", fname) + } + + // Make sure the XLW is open and points to the right file + if fname := log["xmllog"].LogWriter.(*FileLogWriter).file.Name(); fname != "trace.xml" { + t.Errorf("XMLConfig: Expected xmllog to have opened %s, found %s", "trace.xml", fname) + } + + // Move XML log file + os.Rename(configfile, "examples/"+configfile) // Keep this so that an example with the documentation is available +} + +func BenchmarkFormatLogRecord(b *testing.B) { + const updateEvery = 1 + rec := &LogRecord{ + Level: CRITICAL, + Created: now, + Source: "source", + Message: "message", + } + for i := 0; i < b.N; i++ { + rec.Created = rec.Created.Add(1 * time.Second / updateEvery) + if i%2 == 0 { + FormatLogRecord(FORMAT_DEFAULT, rec) + } else { + FormatLogRecord(FORMAT_SHORT, rec) + } + } +} + +func BenchmarkConsoleLog(b *testing.B) { + /* This doesn't seem to work on OS X + sink, err := os.Open(os.DevNull) + if err != nil { + panic(err) + } + if err := syscall.Dup2(int(sink.Fd()), syscall.Stdout); err != nil { + panic(err) + } + */ + + stdout = ioutil.Discard + sl := NewDefaultLogger(INFO) + for i := 0; i < b.N; i++ { + sl.Log(WARNING, "here", "This is a log message") + } +} + +func BenchmarkConsoleNotLogged(b *testing.B) { + sl := NewDefaultLogger(INFO) + for i := 0; i < b.N; i++ { + sl.Log(DEBUG, "here", "This is a log message") + } +} + +func BenchmarkConsoleUtilLog(b *testing.B) { + sl := NewDefaultLogger(INFO) + for i := 0; i < b.N; i++ { + sl.Info("%s is a log message", "This") + } +} + +func BenchmarkConsoleUtilNotLog(b *testing.B) { + sl := NewDefaultLogger(INFO) + for i := 0; i < b.N; i++ { + sl.Debug("%s is a log message", "This") + } +} + +func BenchmarkFileLog(b *testing.B) { + sl := make(Logger) + b.StopTimer() + sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false)) + b.StartTimer() + for i := 0; i < b.N; i++ { + sl.Log(WARNING, "here", "This is a log message") + } + b.StopTimer() + os.Remove("benchlog.log") +} + +func BenchmarkFileNotLogged(b *testing.B) { + sl := make(Logger) + b.StopTimer() + sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false)) + b.StartTimer() + for i := 0; i < b.N; i++ { + sl.Log(DEBUG, "here", "This is a log message") + } + b.StopTimer() + os.Remove("benchlog.log") +} + +func BenchmarkFileUtilLog(b *testing.B) { + sl := make(Logger) + b.StopTimer() + sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false)) + b.StartTimer() + for i := 0; i < b.N; i++ { + sl.Info("%s is a log message", "This") + } + b.StopTimer() + os.Remove("benchlog.log") +} + +func BenchmarkFileUtilNotLog(b *testing.B) { + sl := make(Logger) + b.StopTimer() + sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false)) + b.StartTimer() + for i := 0; i < b.N; i++ { + sl.Debug("%s is a log message", "This") + } + b.StopTimer() + os.Remove("benchlog.log") +} + +// Benchmark results (darwin amd64 6g) +//elog.BenchmarkConsoleLog 100000 22819 ns/op +//elog.BenchmarkConsoleNotLogged 2000000 879 ns/op +//elog.BenchmarkConsoleUtilLog 50000 34380 ns/op +//elog.BenchmarkConsoleUtilNotLog 1000000 1339 ns/op +//elog.BenchmarkFileLog 100000 26497 ns/op +//elog.BenchmarkFileNotLogged 2000000 821 ns/op +//elog.BenchmarkFileUtilLog 50000 33945 ns/op +//elog.BenchmarkFileUtilNotLog 1000000 1258 ns/op diff --git a/vendor/_nuts/code.google.com/p/log4go/pattlog.go b/vendor/_nuts/code.google.com/p/log4go/pattlog.go new file mode 100644 index 00000000..8224302b --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/pattlog.go @@ -0,0 +1,122 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +package log4go + +import ( + "fmt" + "bytes" + "io" +) + +const ( + FORMAT_DEFAULT = "[%D %T] [%L] (%S) %M" + FORMAT_SHORT = "[%t %d] [%L] %M" + FORMAT_ABBREV = "[%L] %M" +) + +type formatCacheType struct { + LastUpdateSeconds int64 + shortTime, shortDate string + longTime, longDate string +} + +var formatCache = &formatCacheType{} + +// Known format codes: +// %T - Time (15:04:05 MST) +// %t - Time (15:04) +// %D - Date (2006/01/02) +// %d - Date (01/02/06) +// %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT) +// %S - Source +// %M - Message +// Ignores unknown formats +// Recommended: "[%D %T] [%L] (%S) %M" +func FormatLogRecord(format string, rec *LogRecord) string { + if rec == nil { + return "" + } + if len(format) == 0 { + return "" + } + + out := bytes.NewBuffer(make([]byte, 0, 64)) + secs := rec.Created.UnixNano() / 1e9 + + cache := *formatCache + if cache.LastUpdateSeconds != secs { + month, day, year := rec.Created.Month(), rec.Created.Day(), rec.Created.Year() + hour, minute, second := rec.Created.Hour(), rec.Created.Minute(), rec.Created.Second() + zone, _ := rec.Created.Zone() + updated := &formatCacheType{ + LastUpdateSeconds: secs, + shortTime: fmt.Sprintf("%02d:%02d", hour, minute), + shortDate: fmt.Sprintf("%02d/%02d/%02d", month, day, year%100), + longTime: fmt.Sprintf("%02d:%02d:%02d %s", hour, minute, second, zone), + longDate: fmt.Sprintf("%04d/%02d/%02d", year, month, day), + } + cache = *updated + formatCache = updated + } + + // Split the string into pieces by % signs + pieces := bytes.Split([]byte(format), []byte{'%'}) + + // Iterate over the pieces, replacing known formats + for i, piece := range pieces { + if i > 0 && len(piece) > 0 { + switch piece[0] { + case 'T': + out.WriteString(cache.longTime) + case 't': + out.WriteString(cache.shortTime) + case 'D': + out.WriteString(cache.longDate) + case 'd': + out.WriteString(cache.shortDate) + case 'L': + out.WriteString(levelStrings[rec.Level]) + case 'S': + out.WriteString(rec.Source) + case 'M': + out.WriteString(rec.Message) + } + if len(piece) > 1 { + out.Write(piece[1:]) + } + } else if len(piece) > 0 { + out.Write(piece) + } + } + out.WriteByte('\n') + + return out.String() +} + +// This is the standard writer that prints to standard output. +type FormatLogWriter chan *LogRecord + +// This creates a new FormatLogWriter +func NewFormatLogWriter(out io.Writer, format string) FormatLogWriter { + records := make(FormatLogWriter, LogBufferLength) + go records.run(out, format) + return records +} + +func (w FormatLogWriter) run(out io.Writer, format string) { + for rec := range w { + fmt.Fprint(out, FormatLogRecord(format, rec)) + } +} + +// This is the FormatLogWriter's output method. This will block if the output +// buffer is full. +func (w FormatLogWriter) LogWrite(rec *LogRecord) { + w <- rec +} + +// Close stops the logger from sending messages to standard output. Attempts to +// send log messages to this logger after a Close have undefined behavior. +func (w FormatLogWriter) Close() { + close(w) +} diff --git a/vendor/_nuts/code.google.com/p/log4go/socklog.go b/vendor/_nuts/code.google.com/p/log4go/socklog.go new file mode 100644 index 00000000..1d224a99 --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/socklog.go @@ -0,0 +1,57 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +package log4go + +import ( + "encoding/json" + "fmt" + "net" + "os" +) + +// This log writer sends output to a socket +type SocketLogWriter chan *LogRecord + +// This is the SocketLogWriter's output method +func (w SocketLogWriter) LogWrite(rec *LogRecord) { + w <- rec +} + +func (w SocketLogWriter) Close() { + close(w) +} + +func NewSocketLogWriter(proto, hostport string) SocketLogWriter { + sock, err := net.Dial(proto, hostport) + if err != nil { + fmt.Fprintf(os.Stderr, "NewSocketLogWriter(%q): %s\n", hostport, err) + return nil + } + + w := SocketLogWriter(make(chan *LogRecord, LogBufferLength)) + + go func() { + defer func() { + if sock != nil && proto == "tcp" { + sock.Close() + } + }() + + for rec := range w { + // Marshall into JSON + js, err := json.Marshal(rec) + if err != nil { + fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err) + return + } + + _, err = sock.Write(js) + if err != nil { + fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err) + return + } + } + }() + + return w +} diff --git a/vendor/_nuts/code.google.com/p/log4go/termlog.go b/vendor/_nuts/code.google.com/p/log4go/termlog.go new file mode 100644 index 00000000..1ed2e4e0 --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/termlog.go @@ -0,0 +1,45 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +package log4go + +import ( + "io" + "os" + "fmt" +) + +var stdout io.Writer = os.Stdout + +// This is the standard writer that prints to standard output. +type ConsoleLogWriter chan *LogRecord + +// This creates a new ConsoleLogWriter +func NewConsoleLogWriter() ConsoleLogWriter { + records := make(ConsoleLogWriter, LogBufferLength) + go records.run(stdout) + return records +} + +func (w ConsoleLogWriter) run(out io.Writer) { + var timestr string + var timestrAt int64 + + for rec := range w { + if at := rec.Created.UnixNano() / 1e9; at != timestrAt { + timestr, timestrAt = rec.Created.Format("01/02/06 15:04:05"), at + } + fmt.Fprint(out, "[", timestr, "] [", levelStrings[rec.Level], "] ", rec.Message, "\n") + } +} + +// This is the ConsoleLogWriter's output method. This will block if the output +// buffer is full. +func (w ConsoleLogWriter) LogWrite(rec *LogRecord) { + w <- rec +} + +// Close stops the logger from sending messages to standard output. Attempts to +// send log messages to this logger after a Close have undefined behavior. +func (w ConsoleLogWriter) Close() { + close(w) +} diff --git a/vendor/_nuts/code.google.com/p/log4go/wrapper.go b/vendor/_nuts/code.google.com/p/log4go/wrapper.go new file mode 100644 index 00000000..10ecd88e --- /dev/null +++ b/vendor/_nuts/code.google.com/p/log4go/wrapper.go @@ -0,0 +1,278 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +package log4go + +import ( + "errors" + "os" + "fmt" + "strings" +) + +var ( + Global Logger +) + +func init() { + Global = NewDefaultLogger(DEBUG) +} + +// Wrapper for (*Logger).LoadConfiguration +func LoadConfiguration(filename string) { + Global.LoadConfiguration(filename) +} + +// Wrapper for (*Logger).AddFilter +func AddFilter(name string, lvl level, writer LogWriter) { + Global.AddFilter(name, lvl, writer) +} + +// Wrapper for (*Logger).Close (closes and removes all logwriters) +func Close() { + Global.Close() +} + +func Crash(args ...interface{}) { + if len(args) > 0 { + Global.intLogf(CRITICAL, strings.Repeat(" %v", len(args))[1:], args...) + } + panic(args) +} + +// Logs the given message and crashes the program +func Crashf(format string, args ...interface{}) { + Global.intLogf(CRITICAL, format, args...) + Global.Close() // so that hopefully the messages get logged + panic(fmt.Sprintf(format, args...)) +} + +// Compatibility with `log` +func Exit(args ...interface{}) { + if len(args) > 0 { + Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...) + } + Global.Close() // so that hopefully the messages get logged + os.Exit(0) +} + +// Compatibility with `log` +func Exitf(format string, args ...interface{}) { + Global.intLogf(ERROR, format, args...) + Global.Close() // so that hopefully the messages get logged + os.Exit(0) +} + +// Compatibility with `log` +func Stderr(args ...interface{}) { + if len(args) > 0 { + Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...) + } +} + +// Compatibility with `log` +func Stderrf(format string, args ...interface{}) { + Global.intLogf(ERROR, format, args...) +} + +// Compatibility with `log` +func Stdout(args ...interface{}) { + if len(args) > 0 { + Global.intLogf(INFO, strings.Repeat(" %v", len(args))[1:], args...) + } +} + +// Compatibility with `log` +func Stdoutf(format string, args ...interface{}) { + Global.intLogf(INFO, format, args...) +} + +// Send a log message manually +// Wrapper for (*Logger).Log +func Log(lvl level, source, message string) { + Global.Log(lvl, source, message) +} + +// Send a formatted log message easily +// Wrapper for (*Logger).Logf +func Logf(lvl level, format string, args ...interface{}) { + Global.intLogf(lvl, format, args...) +} + +// Send a closure log message +// Wrapper for (*Logger).Logc +func Logc(lvl level, closure func() string) { + Global.intLogc(lvl, closure) +} + +// Utility for finest log messages (see Debug() for parameter explanation) +// Wrapper for (*Logger).Finest +func Finest(arg0 interface{}, args ...interface{}) { + const ( + lvl = FINEST + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + Global.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + Global.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Utility for fine log messages (see Debug() for parameter explanation) +// Wrapper for (*Logger).Fine +func Fine(arg0 interface{}, args ...interface{}) { + const ( + lvl = FINE + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + Global.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + Global.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Utility for debug log messages +// When given a string as the first argument, this behaves like Logf but with the DEBUG log level (e.g. the first argument is interpreted as a format for the latter arguments) +// When given a closure of type func()string, this logs the string returned by the closure iff it will be logged. The closure runs at most one time. +// When given anything else, the log message will be each of the arguments formatted with %v and separated by spaces (ala Sprint). +// Wrapper for (*Logger).Debug +func Debug(arg0 interface{}, args ...interface{}) { + const ( + lvl = DEBUG + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + Global.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + Global.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Utility for trace log messages (see Debug() for parameter explanation) +// Wrapper for (*Logger).Trace +func Trace(arg0 interface{}, args ...interface{}) { + const ( + lvl = TRACE + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + Global.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + Global.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Utility for info log messages (see Debug() for parameter explanation) +// Wrapper for (*Logger).Info +func Info(arg0 interface{}, args ...interface{}) { + const ( + lvl = INFO + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + Global.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + Global.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Utility for warn log messages (returns an error for easy function returns) (see Debug() for parameter explanation) +// These functions will execute a closure exactly once, to build the error message for the return +// Wrapper for (*Logger).Warn +func Warn(arg0 interface{}, args ...interface{}) error { + const ( + lvl = WARNING + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + Global.intLogf(lvl, first, args...) + return errors.New(fmt.Sprintf(first, args...)) + case func() string: + // Log the closure (no other arguments used) + str := first() + Global.intLogf(lvl, "%s", str) + return errors.New(str) + default: + // Build a format string so that it will be similar to Sprint + Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) + return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...)) + } + return nil +} + +// Utility for error log messages (returns an error for easy function returns) (see Debug() for parameter explanation) +// These functions will execute a closure exactly once, to build the error message for the return +// Wrapper for (*Logger).Error +func Error(arg0 interface{}, args ...interface{}) error { + const ( + lvl = ERROR + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + Global.intLogf(lvl, first, args...) + return errors.New(fmt.Sprintf(first, args...)) + case func() string: + // Log the closure (no other arguments used) + str := first() + Global.intLogf(lvl, "%s", str) + return errors.New(str) + default: + // Build a format string so that it will be similar to Sprint + Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) + return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...)) + } + return nil +} + +// Utility for critical log messages (returns an error for easy function returns) (see Debug() for parameter explanation) +// These functions will execute a closure exactly once, to build the error message for the return +// Wrapper for (*Logger).Critical +func Critical(arg0 interface{}, args ...interface{}) error { + const ( + lvl = CRITICAL + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + Global.intLogf(lvl, first, args...) + return errors.New(fmt.Sprintf(first, args...)) + case func() string: + // Log the closure (no other arguments used) + str := first() + Global.intLogf(lvl, "%s", str) + return errors.New(str) + default: + // Build a format string so that it will be similar to Sprint + Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) + return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...)) + } + return nil +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/.gitignore b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/.gitignore new file mode 100644 index 00000000..a7628300 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/.gitignore @@ -0,0 +1,5 @@ +<<<<<<< HEAD +pkg/ +======= +pkg +>>>>>>> adding License information and go format diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/License b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/License new file mode 100644 index 00000000..4a559a35 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/License @@ -0,0 +1,27 @@ +Copyright (c) 2013, Thomson Reuters Global Resources +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + This product includes software developed by the Thomson Reuters Global Resources. +4. Neither the name of the Thomson Reuters Global Resources nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY Thomson Reuters Global Resources ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL Thomson Reuters Global Resources BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/README.md b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/README.md new file mode 100644 index 00000000..12f1b95e --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/README.md @@ -0,0 +1,61 @@ +# NTLM Implementation for Go + +This is a native implementation of NTLM for Go that was implemented using the Microsoft MS-NLMP documentation available at http://msdn.microsoft.com/en-us/library/cc236621.aspx. +The library is currently in use and has been tested with connectionless NTLMv1 and v2 with and without extended session security. + +## Usage Notes + +Currently the implementation only supports connectionless (datagram) oriented NTLM. We did not need connection oriented NTLM for our usage +and so it is not implemented. However it should be extremely straightforward to implement connection oriented NTLM as all +the operations required are present in the library. The major missing piece is the negotiation of capabilities between +the client and the server, for our use we hardcoded a supported set of negotiation flags. + +## Sample Usage as NTLM Client + +```go +import "github.com/ThomsonReutersEikon/go-ntlm/ntlm" + +session, err = ntlm.CreateClientSession(ntlm.Version2, ntlm.ConnectionlessMode) +session.SetUserInfo("someuser","somepassword","somedomain") + +negotiate := session.GenerateNegotiateMessage() + + + +challenge, err := ntlm.ParseChallengeMessage(challengeBytes) +session.ProcessChallengeMessage(challenge) + +authenticate := session.GenerateAuthenticateMessage() + + +``` + +## Sample Usage as NTLM Server + +```go +session, err := ntlm.CreateServerSession(ntlm.Version1, ntlm.ConnectionlessMode) +session.SetUserInfo("someuser","somepassword","somedomain") + +challenge := session.GenerateChallengeMessage() + + + + + +auth, err := ntlm.ParseAuthenticateMessage(authenticateBytes) +session.ProcessAuthenticateMessage(auth) +``` + +## Generating a message MAC + +Once a session is created you can generate the Mac for a message using: + +```go +message := "this is some message to sign" +sequenceNumber := 100 +signature, err := session.Mac([]byte(message), sequenceNumber) +``` + +## License +Copyright Thomson Reuters Global Resources 2013 +Apache License diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/av_pairs.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/av_pairs.go new file mode 100644 index 00000000..dc362eed --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/av_pairs.go @@ -0,0 +1,187 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "fmt" +) + +type AvPairType uint16 + +// MS-NLMP - 2.2.2.1 AV_PAIR +const ( + // Indicates that this is the last AV_PAIR in the list. AvLen MUST be 0. This type of information MUST be present in the AV pair list. + MsvAvEOL AvPairType = iota + // The server's NetBIOS computer name. The name MUST be in Unicode, and is not null-terminated. This type of information MUST be present in the AV_pair list. + MsvAvNbComputerName + // The server's NetBIOS domain name. The name MUST be in Unicode, and is not null-terminated. This type of information MUST be present in the AV_pair list. + MsvAvNbDomainName + // The fully qualified domain name (FQDN (1)) of the computer. The name MUST be in Unicode, and is not null-terminated. + MsvAvDnsComputerName + // The FQDN (2) of the domain. The name MUST be in Unicode, and is not null-terminate. + MsvAvDnsDomainName + // The FQDN (2) of the forest. The name MUST be in Unicode, and is not null-terminated.<11> + MsvAvDnsTreeName + // A 32-bit value indicating server or client configuration. + // 0x00000001: indicates to the client that the account authentication is constrained. + // 0x00000002: indicates that the client is providing message integrity in the MIC field (section 2.2.1.3) in the AUTHENTICATE_MESSAGE.<12> + // 0x00000004: indicates that the client is providing a target SPN generated from an untrusted source.<13> + MsvAvFlags + // A FILETIME structure ([MS-DTYP] section 2.3.1) in little-endian byte order that contains the server local time.<14> + MsvAvTimestamp + //A Restriction_Encoding (section 2.2.2.2) structure. The Value field contains a structure representing the integrity level of the security principal, as well as a MachineID created at computer startup to identify the calling machine.<15> + MsAvRestrictions + // The SPN of the target server. The name MUST be in Unicode and is not null-terminated.<16> + MsvAvTargetName + // annel bindings hash. The Value field contains an MD5 hash ([RFC4121] section 4.1.1.2) of a gss_channel_bindings_struct ([RFC2744] section 3.11). + // An all-zero value of the hash is used to indicate absence of channel bindings.<17> + MsvChannelBindings +) + +// Helper struct that contains a list of AvPairs with helper methods for running through them +type AvPairs struct { + List []AvPair +} + +func (p *AvPairs) AddAvPair(avId AvPairType, bytes []byte) { + a := &AvPair{AvId: avId, AvLen: uint16(len(bytes)), Value: bytes} + p.List = append(p.List, *a) +} + +func ReadAvPairs(data []byte) *AvPairs { + pairs := new(AvPairs) + + // Get the number of AvPairs and allocate enough AvPair structures to hold them + offset := 0 + for i := 0; len(data) > 0 && i < 11; i++ { + pair := ReadAvPair(data, offset) + offset = offset + 4 + int(pair.AvLen) + pairs.List = append(pairs.List, *pair) + if pair.AvId == MsvAvEOL { + break + } + } + + return pairs +} + +func (p *AvPairs) Bytes() (result []byte) { + totalLength := 0 + for i := range p.List { + a := p.List[i] + totalLength = totalLength + int(a.AvLen) + 4 + } + + result = make([]byte, 0, totalLength) + for i := range p.List { + a := p.List[i] + result = append(result, a.Bytes()...) + } + + return result +} + +func (p *AvPairs) String() string { + var buffer bytes.Buffer + + buffer.WriteString(fmt.Sprintf("Av Pairs (Total %d pairs)\n", len(p.List))) + + for i := range p.List { + buffer.WriteString(p.List[i].String()) + buffer.WriteString("\n") + } + + return buffer.String() +} + +func (p *AvPairs) Find(avType AvPairType) (result *AvPair) { + for i := range p.List { + pair := p.List[i] + if avType == pair.AvId { + result = &pair + break + } + } + return +} + +func (p *AvPairs) ByteValue(avType AvPairType) (result []byte) { + pair := p.Find(avType) + if pair != nil { + result = pair.Value + } + return +} + +func (p *AvPairs) StringValue(avType AvPairType) (result string) { + pair := p.Find(avType) + if pair != nil { + result = pair.UnicodeStringValue() + } + return +} + +// AvPair as described by MS-NLMP +type AvPair struct { + AvId AvPairType + AvLen uint16 + Value []byte +} + +func ReadAvPair(data []byte, offset int) *AvPair { + pair := new(AvPair) + pair.AvId = AvPairType(binary.LittleEndian.Uint16(data[offset : offset+2])) + pair.AvLen = binary.LittleEndian.Uint16(data[offset+2 : offset+4]) + pair.Value = data[offset+4 : offset+4+int(pair.AvLen)] + return pair +} + +func (a *AvPair) UnicodeStringValue() string { + return utf16ToString(a.Value) +} + +func (a *AvPair) Bytes() (result []byte) { + result = make([]byte, 4, a.AvLen+4) + result[0] = byte(a.AvId) + result[1] = byte(a.AvId >> 8) + result[2] = byte(a.AvLen) + result[3] = byte(a.AvLen >> 8) + result = append(result, a.Value...) + return +} + +func (a *AvPair) String() string { + var outString string + + switch a.AvId { + case MsvAvEOL: + outString = "MsvAvEOL" + case MsvAvNbComputerName: + outString = "MsAvNbComputerName: " + a.UnicodeStringValue() + case MsvAvNbDomainName: + outString = "MsvAvNbDomainName: " + a.UnicodeStringValue() + case MsvAvDnsComputerName: + outString = "MsvAvDnsComputerName: " + a.UnicodeStringValue() + case MsvAvDnsDomainName: + outString = "MsvAvDnsDomainName: " + a.UnicodeStringValue() + case MsvAvDnsTreeName: + outString = "MsvAvDnsTreeName: " + a.UnicodeStringValue() + case MsvAvFlags: + outString = "MsvAvFlags: " + hex.EncodeToString(a.Value) + case MsvAvTimestamp: + outString = "MsvAvTimestamp: " + hex.EncodeToString(a.Value) + case MsAvRestrictions: + outString = "MsAvRestrictions: " + hex.EncodeToString(a.Value) + case MsvAvTargetName: + outString = "MsvAvTargetName: " + a.UnicodeStringValue() + case MsvChannelBindings: + outString = "MsvChannelBindings: " + hex.EncodeToString(a.Value) + default: + outString = fmt.Sprintf("unknown pair type: '%d'", a.AvId) + } + + return outString +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/challenge_responses.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/challenge_responses.go new file mode 100644 index 00000000..c69793d7 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/challenge_responses.go @@ -0,0 +1,154 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "bytes" + "encoding/hex" + "errors" + "fmt" +) + +// NTLMv1 +// ****** +type NtlmV1Response struct { + // 24 byte array + Response []byte +} + +func (n *NtlmV1Response) String() string { + return fmt.Sprintf("NtlmV1Response: %s", hex.EncodeToString(n.Response)) +} + +func ReadNtlmV1Response(bytes []byte) (*NtlmV1Response, error) { + r := new(NtlmV1Response) + r.Response = bytes[0:24] + return r, nil +} + +// *** NTLMv2 +// The NTLMv2_CLIENT_CHALLENGE structure defines the client challenge in the AUTHENTICATE_MESSAGE. +// This structure is used only when NTLM v2 authentication is configured. +type NtlmV2ClientChallenge struct { + // An 8-bit unsigned char that contains the current version of the challenge response type. + // This field MUST be 0x01. + RespType byte + // An 8-bit unsigned char that contains the maximum supported version of the challenge response type. + // This field MUST be 0x01. + HiRespType byte + // A 16-bit unsigned integer that SHOULD be 0x0000 and MUST be ignored on receipt. + Reserved1 uint16 + // A 32-bit unsigned integer that SHOULD be 0x00000000 and MUST be ignored on receipt. + Reserved2 uint32 + // A 64-bit unsigned integer that contains the current system time, represented as the number of 100 nanosecond + // ticks elapsed since midnight of January 1, 1601 (UTC). + TimeStamp []byte + // An 8-byte array of unsigned char that contains the client's ClientChallenge (section 3.1.5.1.2). + ChallengeFromClient []byte + // A 32-bit unsigned integer that SHOULD be 0x00000000 and MUST be ignored on receipt. + Reserved3 uint32 + AvPairs *AvPairs +} + +func (n *NtlmV2ClientChallenge) String() string { + var buffer bytes.Buffer + + buffer.WriteString("NTLM v2 ClientChallenge\n") + buffer.WriteString(fmt.Sprintf("Timestamp: %s\n", hex.EncodeToString(n.TimeStamp))) + buffer.WriteString(fmt.Sprintf("ChallengeFromClient: %s\n", hex.EncodeToString(n.ChallengeFromClient))) + buffer.WriteString("AvPairs\n") + buffer.WriteString(n.AvPairs.String()) + + return buffer.String() +} + +// The NTLMv2_RESPONSE structure defines the NTLMv2 authentication NtChallengeResponse in the AUTHENTICATE_MESSAGE. +// This response is used only when NTLMv2 authentication is configured. +type NtlmV2Response struct { + // A 16-byte array of unsigned char that contains the client's NT challenge- response as defined in section 3.3.2. + // Response corresponds to the NTProofStr variable from section 3.3.2. + Response []byte + // A variable-length byte array that contains the ClientChallenge as defined in section 3.3.2. + // ChallengeFromClient corresponds to the temp variable from section 3.3.2. + NtlmV2ClientChallenge *NtlmV2ClientChallenge +} + +func (n *NtlmV2Response) String() string { + var buffer bytes.Buffer + + buffer.WriteString("NTLM v2 Response\n") + buffer.WriteString(fmt.Sprintf("Response: %s\n", hex.EncodeToString(n.Response))) + buffer.WriteString(n.NtlmV2ClientChallenge.String()) + + return buffer.String() +} + +func ReadNtlmV2Response(bytes []byte) (*NtlmV2Response, error) { + r := new(NtlmV2Response) + r.Response = bytes[0:16] + r.NtlmV2ClientChallenge = new(NtlmV2ClientChallenge) + c := r.NtlmV2ClientChallenge + c.RespType = bytes[16] + c.HiRespType = bytes[17] + + if c.RespType != 1 || c.HiRespType != 1 { + return nil, errors.New("Does not contain a valid NTLM v2 client challenge - could be NTLMv1.") + } + + // Ignoring - 2 bytes reserved + // c.Reserved1 + // Ignoring - 4 bytes reserved + // c.Reserved2 + c.TimeStamp = bytes[24:32] + c.ChallengeFromClient = bytes[32:40] + // Ignoring - 4 bytes reserved + // c.Reserved3 + c.AvPairs = ReadAvPairs(bytes[44:]) + return r, nil +} + +// LMv1 +// **** +type LmV1Response struct { + // 24 bytes + Response []byte +} + +func ReadLmV1Response(bytes []byte) *LmV1Response { + r := new(LmV1Response) + r.Response = bytes[0:24] + return r +} + +func (l *LmV1Response) String() string { + return fmt.Sprintf("LmV1Response: %s", hex.EncodeToString(l.Response)) +} + +// *** LMv2 +type LmV2Response struct { + // A 16-byte array of unsigned char that contains the client's LM challenge-response. + // This is the portion of the LmChallengeResponse field to which the HMAC_MD5 algorithm + /// has been applied, as defined in section 3.3.2. Specifically, Response corresponds + // to the result of applying the HMAC_MD5 algorithm, using the key ResponseKeyLM, to a + // message consisting of the concatenation of the ResponseKeyLM, ServerChallenge and ClientChallenge. + Response []byte + // An 8-byte array of unsigned char that contains the client's ClientChallenge, as defined in section 3.1.5.1.2. + ChallengeFromClient []byte +} + +func ReadLmV2Response(bytes []byte) *LmV2Response { + r := new(LmV2Response) + r.Response = bytes[0:16] + r.ChallengeFromClient = bytes[16:24] + return r +} + +func (l *LmV2Response) String() string { + var buffer bytes.Buffer + + buffer.WriteString("LM v2 Response\n") + buffer.WriteString(fmt.Sprintf("Response: %s\n", hex.EncodeToString(l.Response))) + buffer.WriteString(fmt.Sprintf("ChallengeFromClient: %s\n", hex.EncodeToString(l.ChallengeFromClient))) + + return buffer.String() +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/crypto.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/crypto.go new file mode 100644 index 00000000..1f7c41ca --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/crypto.go @@ -0,0 +1,135 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + desP "crypto/des" + hmacP "crypto/hmac" + md5P "crypto/md5" + "crypto/rand" + rc4P "crypto/rc4" + md4P "github.com/github/git-lfs/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4" + crc32P "hash/crc32" +) + +func md4(data []byte) []byte { + md4 := md4P.New() + md4.Write(data) + return md4.Sum(nil) +} + +func md5(data []byte) []byte { + md5 := md5P.New() + md5.Write(data) + return md5.Sum(nil) +} + +// Indicates the computation of a 16-byte HMAC-keyed MD5 message digest of the byte string M using the key K. +func hmacMd5(key []byte, data []byte) []byte { + mac := hmacP.New(md5P.New, key) + mac.Write(data) + return mac.Sum(nil) +} + +// Indicates the computation of an N-byte cryptographic- strength random number. +func nonce(length int) []byte { + result := make([]byte, length) + rand.Read(result) + return result +} + +func crc32(bytes []byte) uint32 { + crc := crc32P.New(crc32P.IEEETable) + crc.Write(bytes) + return crc.Sum32() +} + +// Indicates the encryption of data item D with the key K using the RC4 algorithm. +func rc4K(key []byte, ciphertext []byte) ([]byte, error) { + cipher, err := rc4P.NewCipher(key) + if err != nil { + return nil, err + } + result := make([]byte, len(ciphertext)) + cipher.XORKeyStream(result, ciphertext) + return result, nil +} + +func rc4Init(key []byte) (cipher *rc4P.Cipher, err error) { + cipher, err = rc4P.NewCipher(key) + if err != nil { + return nil, err + } + return cipher, nil +} + +func rc4(cipher *rc4P.Cipher, ciphertext []byte) []byte { + result := make([]byte, len(ciphertext)) + cipher.XORKeyStream(result, ciphertext) + return result +} + +// Indicates the encryption of an 8-byte data item D with the 7-byte key K using the Data Encryption Standard (DES) +// algorithm in Electronic Codebook (ECB) mode. The result is 8 bytes in length ([FIPS46-2]). +func des(key []byte, ciphertext []byte) ([]byte, error) { + calcKey := createDesKey(key) + cipher, err := desP.NewCipher(calcKey) + if err != nil { + return nil, err + } + + result := make([]byte, len(ciphertext)) + cipher.Encrypt(result, ciphertext) + + return result, nil +} + +// Indicates the encryption of an 8-byte data item D with the 16-byte key K using the Data Encryption Standard Long (DESL) algorithm. +// The result is 24 bytes in length. DESL(K, D) is computed as follows. +// Note K[] implies a key represented as a character array. +func desL(key []byte, cipherText []byte) ([]byte, error) { + out1, err := des(zeroPaddedBytes(key, 0, 7), cipherText) + if err != nil { + return nil, err + } + + out2, err := des(zeroPaddedBytes(key, 7, 7), cipherText) + if err != nil { + return nil, err + } + + out3, err := des(zeroPaddedBytes(key, 14, 7), cipherText) + if err != nil { + return nil, err + } + + return concat(out1, out2, out3), nil +} + +// Creates a DES encryption key from the given 7 byte key material. +func createDesKey(keyBytes []byte) []byte { + material := zeroBytes(8) + material[0] = keyBytes[0] + material[1] = (byte)(keyBytes[0]<<7 | (keyBytes[1]&0xff)>>1) + material[2] = (byte)(keyBytes[1]<<6 | (keyBytes[2]&0xff)>>2) + material[3] = (byte)(keyBytes[2]<<5 | (keyBytes[3]&0xff)>>3) + material[4] = (byte)(keyBytes[3]<<4 | (keyBytes[4]&0xff)>>4) + material[5] = (byte)(keyBytes[4]<<3 | (keyBytes[5]&0xff)>>5) + material[6] = (byte)(keyBytes[5]<<2 | (keyBytes[6]&0xff)>>6) + material[7] = (byte)(keyBytes[6] << 1) + oddParity(material) + return material +} + +// Applies odd parity to the given byte array. +func oddParity(bytes []byte) { + for i := 0; i < len(bytes); i++ { + b := bytes[i] + needsParity := (((b >> 7) ^ (b >> 6) ^ (b >> 5) ^ (b >> 4) ^ (b >> 3) ^ (b >> 2) ^ (b >> 1)) & 0x01) == 0 + if needsParity { + bytes[i] = bytes[i] | byte(0x01) + } else { + bytes[i] = bytes[i] & byte(0xfe) + } + } +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/crypto_test.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/crypto_test.go new file mode 100644 index 00000000..35c4bf01 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/crypto_test.go @@ -0,0 +1,64 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "bytes" + "encoding/hex" + "testing" +) + +func TestMd4(t *testing.T) { + data := []byte{1, 2, 3, 4, 5} + byteData, _ := hex.DecodeString("93ebafdfedd1994e8018cc295cc1a8ee") + if !bytes.Equal(md4(data), byteData) { + t.Error("MD4 result not correct") + } +} + +func TestHmacMd5(t *testing.T) { + data := []byte{1, 2, 3, 4, 5} + byteData, _ := hex.DecodeString("9155578efbf3810a2adb4dee232a5fee") + if !bytes.Equal(hmacMd5(data, data), byteData) { + t.Error("HmacMd5 result not correct") + } +} + +func TestNonce(t *testing.T) { + data := nonce(10) + if len(data) != 10 { + t.Error("Nonce is incorrect length") + } +} + +func TestRc4K(t *testing.T) { + data := []byte{1, 2, 3, 4, 5} + key := []byte{1, 2, 3, 4, 5} + result, err := rc4K(key, data) + if err != nil { + // TODO: Need some sample data to test RC4K + // t.Error("Error returned for RC4K") + } + if !bytes.Equal(result, data) { + // t.Error("RC4K result not correct") + } +} + +func TestDesL(t *testing.T) { + key, _ := hex.DecodeString("e52cac67419a9a224a3b108f3fa6cb6d") + message := []byte("12345678") + result, _ := desL(key, message) + expected, _ := hex.DecodeString("1192855D461A9754D189D8AE94D82488E3707C0662C0476A") + if !bytes.Equal(result, expected) { + t.Errorf("DesL did not produce correct result, got %s expected %s", hex.EncodeToString(result), hex.EncodeToString(expected)) + } +} + +func TestCRC32(t *testing.T) { + bytes := []byte("Discard medicine more than two years old.") + result := crc32(bytes) + expected := uint32(0x6b9cdfe7) + if expected != result { + t.Errorf("CRC 32 data is not correct got %d expected %d", result, expected) + } +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/helpers.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/helpers.go new file mode 100644 index 00000000..1cc78d52 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/helpers.go @@ -0,0 +1,89 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "bytes" + "crypto/rand" + "unicode/utf16" + "encoding/binary" +) + +// Concatenate two byte slices into a new slice +func concat(ar ...[]byte) []byte { + return bytes.Join(ar, nil) +} + +// Create a 0 initialized slice of bytes +func zeroBytes(length int) []byte { + return make([]byte, length, length) +} + +func randomBytes(length int) []byte { + randombytes := make([]byte, length) + _, err := rand.Read(randombytes) + if err != nil { + } // TODO: What to do with err here + return randombytes +} + +// Zero pad the input byte slice to the given size +// bytes - input byte slice +// offset - where to start taking the bytes from the input slice +// size - size of the output byte slize +func zeroPaddedBytes(bytes []byte, offset int, size int) []byte { + newSlice := zeroBytes(size) + for i := 0; i < size && i+offset < len(bytes); i++ { + newSlice[i] = bytes[i+offset] + } + return newSlice +} + +func MacsEqual(slice1, slice2 []byte) bool { + if len(slice1) != len(slice2) { + return false + } + for i := 0; i < len(slice1); i++ { + // bytes between 4 and 7 (inclusive) contains random + // data that should be ignored while comparing the + // macs + if (i < 4 || i > 7) && slice1[i] != slice2[i] { + return false + } + } + return true +} + +func utf16FromString(s string) []byte { + encoded := utf16.Encode([]rune(s)) + // TODO: I'm sure there is an easier way to do the conversion from utf16 to bytes + result := zeroBytes(len(encoded) * 2) + for i := 0; i < len(encoded); i++ { + result[i*2] = byte(encoded[i]) + result[i*2+1] = byte(encoded[i] << 8) + } + return result +} + +// Convert a UTF16 string to UTF8 string for Go usage +func utf16ToString(bytes []byte) string { + var data []uint16 + + // NOTE: This is definitely not the best way to do this, but when I tried using a buffer.Read I could not get it to work + for offset := 0; offset < len(bytes); offset = offset + 2 { + i := binary.LittleEndian.Uint16(bytes[offset : offset+2]) + data = append(data, i) + } + + return string(utf16.Decode(data)) +} + +func uint32ToBytes(v uint32) []byte { + bytes := make([]byte, 4) + bytes[0] = byte(v & 0xff) + bytes[1] = byte((v >> 8) & 0xff) + bytes[2] = byte((v >> 16) & 0xff) + bytes[3] = byte((v >> 24) & 0xff) + return bytes +} + diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/helpers_test.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/helpers_test.go new file mode 100644 index 00000000..9d445bbb --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/helpers_test.go @@ -0,0 +1,35 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "bytes" + "encoding/hex" + "testing" +) + +func TestUTf16ToString(t *testing.T) { + expected, _ := hex.DecodeString("5500730065007200") + result := utf16FromString("User") + if !bytes.Equal(expected, result) { + t.Errorf("UTF16ToString failed got %s expected %s", hex.EncodeToString(result), "5500730065007200") + } +} + +func TestMacsEquals(t *testing.T) { + // the MacsEqual should ignore the values in the second 4 bytes + firstSlice := []byte{0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xf0, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff} + secondSlice := []byte{0xf1, 0xf2, 0xf3, 0xf4, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf0, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff} + if !MacsEqual(firstSlice, secondSlice) { + t.Errorf("Expected MacsEqual(%v, %v) to be true", firstSlice, secondSlice) + } +} + +func TestMacsEqualsFail(t *testing.T) { + // the last bytes in the following test case should cause MacsEqual to return false + firstSlice := []byte{0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xf0, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff} + secondSlice := []byte{0xf1, 0xf2, 0xf3, 0xf4, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf0, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xfe} + if MacsEqual(firstSlice, secondSlice) { + t.Errorf("Expected MacsEqual(%v, %v) to be false", firstSlice, secondSlice) + } +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/keys.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/keys.go new file mode 100644 index 00000000..b6b83d96 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/keys.go @@ -0,0 +1,70 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +// Define KXKEY(SessionBaseKey, LmChallengeResponse, ServerChallenge) as +func kxKey(flags uint32, sessionBaseKey []byte, lmChallengeResponse []byte, serverChallenge []byte, lmnowf []byte) (keyExchangeKey []byte, err error) { + if NTLMSSP_NEGOTIATE_LM_KEY.IsSet(flags) { + var part1, part2 []byte + part1, err = des(lmnowf[0:7], lmChallengeResponse[0:8]) + if err != nil { + return nil, err + } + + key := append([]byte{lmnowf[7]}, []byte{0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD}...) + part2, err = des(key, lmChallengeResponse[0:8]) + if err != nil { + return nil, err + } + + keyExchangeKey = concat(part1, part2) + } else if NTLMSSP_REQUEST_NON_NT_SESSION_KEY.IsSet(flags) { + keyExchangeKey = concat(lmnowf[0:8], zeroBytes(8)) + } else { + keyExchangeKey = sessionBaseKey + } + + return +} + +// Define SIGNKEY(NegFlg, RandomSessionKey, Mode) as +func signKey(flags uint32, randomSessionKey []byte, mode string) (signKey []byte) { + if NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.IsSet(flags) { + if mode == "Client" { + signKey = md5(concat(randomSessionKey, []byte("session key to client-to-server signing key magic constant\x00"))) + } else { + signKey = md5(concat(randomSessionKey, []byte("session key to server-to-client signing key magic constant\x00"))) + } + } else { + signKey = nil + } + return +} + +// Define SEALKEY(NegotiateFlags, RandomSessionKey, Mode) as +func sealKey(flags uint32, randomSessionKey []byte, mode string) (sealKey []byte) { + if NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.IsSet(flags) { + if NTLMSSP_NEGOTIATE_128.IsSet(flags) { + sealKey = randomSessionKey + } else if NTLMSSP_NEGOTIATE_56.IsSet(flags) { + sealKey = randomSessionKey[0:7] + } else { + sealKey = randomSessionKey[0:5] + } + if mode == "Client" { + sealKey = md5(concat(sealKey, []byte("session key to client-to-server sealing key magic constant\x00"))) + } else { + sealKey = md5(concat(sealKey, []byte("session key to server-to-client sealing key magic constant\x00"))) + } + } else if NTLMSSP_NEGOTIATE_LM_KEY.IsSet(flags) { + if NTLMSSP_NEGOTIATE_56.IsSet(flags) { + sealKey = concat(randomSessionKey[0:7], []byte{0xA0}) + } else { + sealKey = concat(randomSessionKey[0:5], []byte{0xE5, 0x38, 0xB0}) + } + } else { + sealKey = randomSessionKey + } + + return +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4/LICENSE b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4/LICENSE new file mode 100644 index 00000000..6a66aea5 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4/md4.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4/md4.go new file mode 100644 index 00000000..c5f7c57d --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4/md4.go @@ -0,0 +1,118 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package md4 implements the MD4 hash algorithm as defined in RFC 1320. +package md4 + +import ( + "crypto" + "hash" +) + +func init() { + crypto.RegisterHash(crypto.MD4, New) +} + +// The size of an MD4 checksum in bytes. +const Size = 16 + +// The blocksize of MD4 in bytes. +const BlockSize = 64 + +const ( + _Chunk = 64 + _Init0 = 0x67452301 + _Init1 = 0xEFCDAB89 + _Init2 = 0x98BADCFE + _Init3 = 0x10325476 +) + +// digest represents the partial evaluation of a checksum. +type digest struct { + s [4]uint32 + x [_Chunk]byte + nx int + len uint64 +} + +func (d *digest) Reset() { + d.s[0] = _Init0 + d.s[1] = _Init1 + d.s[2] = _Init2 + d.s[3] = _Init3 + d.nx = 0 + d.len = 0 +} + +// New returns a new hash.Hash computing the MD4 checksum. +func New() hash.Hash { + d := new(digest) + d.Reset() + return d +} + +func (d *digest) Size() int { return Size } + +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Write(p []byte) (nn int, err error) { + nn = len(p) + d.len += uint64(nn) + if d.nx > 0 { + n := len(p) + if n > _Chunk-d.nx { + n = _Chunk - d.nx + } + for i := 0; i < n; i++ { + d.x[d.nx+i] = p[i] + } + d.nx += n + if d.nx == _Chunk { + _Block(d, d.x[0:]) + d.nx = 0 + } + p = p[n:] + } + n := _Block(d, p) + p = p[n:] + if len(p) > 0 { + d.nx = copy(d.x[:], p) + } + return +} + +func (d0 *digest) Sum(in []byte) []byte { + // Make a copy of d0, so that caller can keep writing and summing. + d := new(digest) + *d = *d0 + + // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. + len := d.len + var tmp [64]byte + tmp[0] = 0x80 + if len%64 < 56 { + d.Write(tmp[0 : 56-len%64]) + } else { + d.Write(tmp[0 : 64+56-len%64]) + } + + // Length in bits. + len <<= 3 + for i := uint(0); i < 8; i++ { + tmp[i] = byte(len >> (8 * i)) + } + d.Write(tmp[0:8]) + + if d.nx != 0 { + panic("d.nx != 0") + } + + for _, s := range d.s { + in = append(in, byte(s>>0)) + in = append(in, byte(s>>8)) + in = append(in, byte(s>>16)) + in = append(in, byte(s>>24)) + } + return in +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4/md4_test.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4/md4_test.go new file mode 100644 index 00000000..b56edd78 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4/md4_test.go @@ -0,0 +1,71 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package md4 + +import ( + "fmt" + "io" + "testing" +) + +type md4Test struct { + out string + in string +} + +var golden = []md4Test{ + {"31d6cfe0d16ae931b73c59d7e0c089c0", ""}, + {"bde52cb31de33e46245e05fbdbd6fb24", "a"}, + {"ec388dd78999dfc7cf4632465693b6bf", "ab"}, + {"a448017aaf21d8525fc10ae87aa6729d", "abc"}, + {"41decd8f579255c5200f86a4bb3ba740", "abcd"}, + {"9803f4a34e8eb14f96adba49064a0c41", "abcde"}, + {"804e7f1c2586e50b49ac65db5b645131", "abcdef"}, + {"752f4adfe53d1da0241b5bc216d098fc", "abcdefg"}, + {"ad9daf8d49d81988590a6f0e745d15dd", "abcdefgh"}, + {"1e4e28b05464316b56402b3815ed2dfd", "abcdefghi"}, + {"dc959c6f5d6f9e04e4380777cc964b3d", "abcdefghij"}, + {"1b5701e265778898ef7de5623bbe7cc0", "Discard medicine more than two years old."}, + {"d7f087e090fe7ad4a01cb59dacc9a572", "He who has a shady past knows that nice guys finish last."}, + {"a6f8fd6df617c72837592fc3570595c9", "I wouldn't marry him with a ten foot pole."}, + {"c92a84a9526da8abc240c05d6b1a1ce0", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, + {"f6013160c4dcb00847069fee3bb09803", "The days of the digital watch are numbered. -Tom Stoppard"}, + {"2c3bb64f50b9107ed57640fe94bec09f", "Nepal premier won't resign."}, + {"45b7d8a32c7806f2f7f897332774d6e4", "For every action there is an equal and opposite government program."}, + {"b5b4f9026b175c62d7654bdc3a1cd438", "His money is twice tainted: 'taint yours and 'taint mine."}, + {"caf44e80f2c20ce19b5ba1cab766e7bd", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, + {"191fae6707f496aa54a6bce9f2ecf74d", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, + {"9ddc753e7a4ccee6081cd1b45b23a834", "size: a.out: bad magic"}, + {"8d050f55b1cadb9323474564be08a521", "The major problem is with sendmail. -Mark Horton"}, + {"ad6e2587f74c3e3cc19146f6127fa2e3", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, + {"1d616d60a5fabe85589c3f1566ca7fca", "If the enemy is within range, then so are you."}, + {"aec3326a4f496a2ced65a1963f84577f", "It's well we cannot hear the screams/That we create in others' dreams."}, + {"77b4fd762d6b9245e61c50bf6ebf118b", "You remind me of a TV show, but that's all right: I watch it anyway."}, + {"e8f48c726bae5e516f6ddb1a4fe62438", "C is as portable as Stonehedge!!"}, + {"a3a84366e7219e887423b01f9be7166e", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, + {"a6b7aa35157e984ef5d9b7f32e5fbb52", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, + {"75661f0545955f8f9abeeb17845f3fd6", "How can you write a big system without C++? -Paul Glick"}, +} + +func TestGolden(t *testing.T) { + for i := 0; i < len(golden); i++ { + g := golden[i] + c := New() + for j := 0; j < 3; j++ { + if j < 2 { + io.WriteString(c, g.in) + } else { + io.WriteString(c, g.in[0:len(g.in)/2]) + c.Sum(nil) + io.WriteString(c, g.in[len(g.in)/2:]) + } + s := fmt.Sprintf("%x", c.Sum(nil)) + if s != g.out { + t.Fatalf("md4[%d](%s) = %s want %s", j, g.in, s, g.out) + } + c.Reset() + } + } +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4/md4block.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4/md4block.go new file mode 100644 index 00000000..3fed475f --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4/md4block.go @@ -0,0 +1,89 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// MD4 block step. +// In its own file so that a faster assembly or C version +// can be substituted easily. + +package md4 + +var shift1 = []uint{3, 7, 11, 19} +var shift2 = []uint{3, 5, 9, 13} +var shift3 = []uint{3, 9, 11, 15} + +var xIndex2 = []uint{0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15} +var xIndex3 = []uint{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15} + +func _Block(dig *digest, p []byte) int { + a := dig.s[0] + b := dig.s[1] + c := dig.s[2] + d := dig.s[3] + n := 0 + var X [16]uint32 + for len(p) >= _Chunk { + aa, bb, cc, dd := a, b, c, d + + j := 0 + for i := 0; i < 16; i++ { + X[i] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24 + j += 4 + } + + // If this needs to be made faster in the future, + // the usual trick is to unroll each of these + // loops by a factor of 4; that lets you replace + // the shift[] lookups with constants and, + // with suitable variable renaming in each + // unrolled body, delete the a, b, c, d = d, a, b, c + // (or you can let the optimizer do the renaming). + // + // The index variables are uint so that % by a power + // of two can be optimized easily by a compiler. + + // Round 1. + for i := uint(0); i < 16; i++ { + x := i + s := shift1[i%4] + f := ((c ^ d) & b) ^ d + a += f + X[x] + a = a<>(32-s) + a, b, c, d = d, a, b, c + } + + // Round 2. + for i := uint(0); i < 16; i++ { + x := xIndex2[i] + s := shift2[i%4] + g := (b & c) | (b & d) | (c & d) + a += g + X[x] + 0x5a827999 + a = a<>(32-s) + a, b, c, d = d, a, b, c + } + + // Round 3. + for i := uint(0); i < 16; i++ { + x := xIndex3[i] + s := shift3[i%4] + h := b ^ c ^ d + a += h + X[x] + 0x6ed9eba1 + a = a<>(32-s) + a, b, c, d = d, a, b, c + } + + a += aa + b += bb + c += cc + d += dd + + p = p[_Chunk:] + n += _Chunk + } + + dig.s[0] = a + dig.s[1] = b + dig.s[2] = c + dig.s[3] = d + return n +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_authenticate.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_authenticate.go new file mode 100644 index 00000000..5c2448ef --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_authenticate.go @@ -0,0 +1,291 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "errors" + "fmt" +) + +type AuthenticateMessage struct { + // sig - 8 bytes + Signature []byte + // message type - 4 bytes + MessageType uint32 + + // The LmChallenge Response can be v1 or v2 + LmChallengeResponse *PayloadStruct // 8 bytes + LmV1Response *LmV1Response + LmV2Response *LmV2Response + + // The NtChallengeResponse can be v1 or v2 + NtChallengeResponseFields *PayloadStruct // 8 bytes + NtlmV1Response *NtlmV1Response + NtlmV2Response *NtlmV2Response + + DomainName *PayloadStruct // 8 bytes + UserName *PayloadStruct // 8 bytes + Workstation *PayloadStruct // 8 bytes + + // If the NTLMSSP_NEGOTIATE_KEY_EXCH flag is set in the neogitate flags then this will point to the offset in the payload + // with the key, otherwise it will have Len = 0. According to Davenport these bytes are optional (see Type3 message). + // The MS-NLMP docs do not mention this. + EncryptedRandomSessionKey *PayloadStruct // 8 bytes + + /// MS-NLMP 2.2.1.3 - In connectionless mode, a NEGOTIATE structure that contains a set of bit flags (section 2.2.2.5) and represents the + // conclusion of negotiation—the choices the client has made from the options the server offered in the CHALLENGE_MESSAGE. + // In connection-oriented mode, a NEGOTIATE structure that contains the set of bit flags (section 2.2.2.5) negotiated in + // the previous + NegotiateFlags uint32 // 4 bytes + + // Version (8 bytes): A VERSION structure (section 2.2.2.10) that is present only when the NTLMSSP_NEGOTIATE_VERSION + // flag is set in the NegotiateFlags field. This structure is used for debugging purposes only. In normal protocol + // messages, it is ignored and does not affect the NTLM message processing.<9> + Version *VersionStruct + + // The message integrity for the NTLM NEGOTIATE_MESSAGE, CHALLENGE_MESSAGE, and AUTHENTICATE_MESSAGE.<10> + Mic []byte // 16 bytes + + // payload - variable + Payload []byte +} + +func ParseAuthenticateMessage(body []byte, ntlmVersion int) (*AuthenticateMessage, error) { + am := new(AuthenticateMessage) + + am.Signature = body[0:8] + if !bytes.Equal(am.Signature, []byte("NTLMSSP\x00")) { + return nil, errors.New("Invalid NTLM message signature") + } + + am.MessageType = binary.LittleEndian.Uint32(body[8:12]) + if am.MessageType != 3 { + return nil, errors.New("Invalid NTLM message type should be 0x00000003 for authenticate message") + } + + var err error + + am.LmChallengeResponse, err = ReadBytePayload(12, body) + if err != nil { + return nil, err + } + + if ntlmVersion == 2 { + am.LmV2Response = ReadLmV2Response(am.LmChallengeResponse.Payload) + } else { + am.LmV1Response = ReadLmV1Response(am.LmChallengeResponse.Payload) + } + + am.NtChallengeResponseFields, err = ReadBytePayload(20, body) + if err != nil { + return nil, err + } + + // Check to see if this is a v1 or v2 response + if ntlmVersion == 2 { + am.NtlmV2Response, err = ReadNtlmV2Response(am.NtChallengeResponseFields.Payload) + } else { + am.NtlmV1Response, err = ReadNtlmV1Response(am.NtChallengeResponseFields.Payload) + } + + if err != nil { + return nil, err + } + + am.DomainName, err = ReadStringPayload(28, body) + if err != nil { + return nil, err + } + + am.UserName, err = ReadStringPayload(36, body) + if err != nil { + return nil, err + } + + am.Workstation, err = ReadStringPayload(44, body) + if err != nil { + return nil, err + } + + lowestOffset := am.getLowestPayloadOffset() + offset := 52 + + // If the lowest payload offset is 52 then: + // The Session Key, flags, and OS Version structure are omitted. The data (payload) block in this case starts after the Workstation Name + // security buffer header, at offset 52. This form is seen in older Win9x-based systems. This is from the davenport notes about Type 3 + // messages and this information does not seem to be present in the MS-NLMP document + if lowestOffset > 52 { + am.EncryptedRandomSessionKey, err = ReadBytePayload(offset, body) + if err != nil { + return nil, err + } + offset = offset + 8 + + am.NegotiateFlags = binary.LittleEndian.Uint32(body[offset : offset+4]) + offset = offset + 4 + + // Version (8 bytes): A VERSION structure (section 2.2.2.10) that is present only when the NTLMSSP_NEGOTIATE_VERSION flag is set in the NegotiateFlags field. This structure is used for debugging purposes only. In normal protocol messages, it is ignored and does not affect the NTLM message processing.<9> + if NTLMSSP_NEGOTIATE_VERSION.IsSet(am.NegotiateFlags) { + am.Version, err = ReadVersionStruct(body[offset : offset+8]) + if err != nil { + return nil, err + } + offset = offset + 8 + } + + // The MS-NLMP has this to say about the MIC + // "An AUTHENTICATE_MESSAGE indicates the presence of a MIC field if the TargetInfo field has an AV_PAIR structure whose two fields are: + // AvId == MsvAvFlags Value bit 0x2 == 1" + // However there is no TargetInfo structure in the Authenticate Message! There is one in the Challenge Message though. So I'm using + // a hack to check to see if there is a MIC. I look to see if there is room for the MIC before the payload starts. If so I assume + // there is a MIC and read it out. + var lowestOffset = am.getLowestPayloadOffset() + if lowestOffset > offset { + // MIC - 16 bytes + am.Mic = body[offset : offset+16] + offset = offset + 16 + } + } + + am.Payload = body[offset:] + + return am, nil +} + +func (a *AuthenticateMessage) ClientChallenge() (response []byte) { + if a.NtlmV2Response != nil { + response = a.NtlmV2Response.NtlmV2ClientChallenge.ChallengeFromClient + } else if a.NtlmV1Response != nil && NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.IsSet(a.NegotiateFlags) { + response = a.LmV1Response.Response[0:8] + } + + return response +} + +func (a *AuthenticateMessage) getLowestPayloadOffset() int { + payloadStructs := [...]*PayloadStruct{a.LmChallengeResponse, a.NtChallengeResponseFields, a.DomainName, a.UserName, a.Workstation, a.EncryptedRandomSessionKey} + + // Find the lowest offset value + lowest := 9999 + for i := range payloadStructs { + p := payloadStructs[i] + if p != nil && p.Offset > 0 && int(p.Offset) < lowest { + lowest = int(p.Offset) + } + } + + return lowest +} + +func (a *AuthenticateMessage) Bytes() []byte { + payloadLen := int(a.LmChallengeResponse.Len + a.NtChallengeResponseFields.Len + a.DomainName.Len + a.UserName.Len + a.Workstation.Len + a.EncryptedRandomSessionKey.Len) + messageLen := 8 + 4 + 6*8 + 4 + 8 + 16 + payloadOffset := uint32(messageLen) + + messageBytes := make([]byte, 0, messageLen+payloadLen) + buffer := bytes.NewBuffer(messageBytes) + + buffer.Write(a.Signature) + + binary.Write(buffer, binary.LittleEndian, a.MessageType) + + a.LmChallengeResponse.Offset = payloadOffset + payloadOffset += uint32(a.LmChallengeResponse.Len) + buffer.Write(a.LmChallengeResponse.Bytes()) + + a.NtChallengeResponseFields.Offset = payloadOffset + payloadOffset += uint32(a.NtChallengeResponseFields.Len) + buffer.Write(a.NtChallengeResponseFields.Bytes()) + + a.DomainName.Offset = payloadOffset + payloadOffset += uint32(a.DomainName.Len) + buffer.Write(a.DomainName.Bytes()) + + a.UserName.Offset = payloadOffset + payloadOffset += uint32(a.UserName.Len) + buffer.Write(a.UserName.Bytes()) + + a.Workstation.Offset = payloadOffset + payloadOffset += uint32(a.Workstation.Len) + buffer.Write(a.Workstation.Bytes()) + + a.EncryptedRandomSessionKey.Offset = payloadOffset + payloadOffset += uint32(a.EncryptedRandomSessionKey.Len) + buffer.Write(a.EncryptedRandomSessionKey.Bytes()) + + buffer.Write(uint32ToBytes(a.NegotiateFlags)) + + if a.Version != nil { + buffer.Write(a.Version.Bytes()) + } else { + buffer.Write(make([]byte, 8)) + } + + if a.Mic != nil { + buffer.Write(a.Mic) + } else { + buffer.Write(make([]byte, 16)) + } + + // Write out the payloads + buffer.Write(a.LmChallengeResponse.Payload) + buffer.Write(a.NtChallengeResponseFields.Payload) + buffer.Write(a.DomainName.Payload) + buffer.Write(a.UserName.Payload) + buffer.Write(a.Workstation.Payload) + buffer.Write(a.EncryptedRandomSessionKey.Payload) + + return buffer.Bytes() +} + +func (a *AuthenticateMessage) String() string { + var buffer bytes.Buffer + + buffer.WriteString("Authenticate NTLM Message\n") + buffer.WriteString(fmt.Sprintf("Payload Offset: %d Length: %d\n", a.getLowestPayloadOffset(), len(a.Payload))) + + if a.LmV2Response != nil { + buffer.WriteString(a.LmV2Response.String()) + buffer.WriteString("\n") + } + + if a.LmV1Response != nil { + buffer.WriteString(a.LmV1Response.String()) + buffer.WriteString("\n") + } + + if a.NtlmV2Response != nil { + buffer.WriteString(a.NtlmV2Response.String()) + buffer.WriteString("\n") + } + + if a.NtlmV1Response != nil { + buffer.WriteString(fmt.Sprintf("NtlmResponse Length: %d\n", a.NtChallengeResponseFields.Len)) + buffer.WriteString(a.NtlmV1Response.String()) + buffer.WriteString("\n") + } + + buffer.WriteString(fmt.Sprintf("UserName: %s\n", a.UserName.String())) + buffer.WriteString(fmt.Sprintf("DomainName: %s\n", a.DomainName.String())) + buffer.WriteString(fmt.Sprintf("Workstation: %s\n", a.Workstation.String())) + + if a.EncryptedRandomSessionKey != nil { + buffer.WriteString(fmt.Sprintf("EncryptedRandomSessionKey: %s\n", a.EncryptedRandomSessionKey.String())) + } + + if a.Version != nil { + buffer.WriteString(fmt.Sprintf("Version: %s\n", a.Version.String())) + } + + if a.Mic != nil { + buffer.WriteString(fmt.Sprintf("MIC: %s\n", hex.EncodeToString(a.Mic))) + } + + buffer.WriteString(fmt.Sprintf("Flags %d\n", a.NegotiateFlags)) + buffer.WriteString(FlagsToString(a.NegotiateFlags)) + + return buffer.String() +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_authenticate_test.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_authenticate_test.go new file mode 100644 index 00000000..af4ced30 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_authenticate_test.go @@ -0,0 +1,102 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "bytes" + "encoding/base64" + "encoding/hex" + "testing" +) + +func checkPayloadStruct(t *testing.T, payloadStruct *PayloadStruct, len uint16, offset uint32) { + if payloadStruct.Len != len || payloadStruct.Offset != offset { + t.Errorf("Failed to parse payload struct %d, %d", payloadStruct.Len, payloadStruct.Offset) + } +} + +func TestParseNTLMv1AsV2(t *testing.T) { + ntlmv1data := "TlRMTVNTUAADAAAAGAAYALYAAAAYABgAzgAAADQANABIAAAAIAAgAHwAAAAaABoAnAAAABAAEADmAAAAVYKQQgUCzg4AAAAPYQByAHIAYQB5ADEAMgAuAG0AcwBnAHQAcwB0AC4AcgBlAHUAdABlAHIAcwAuAGMAbwBtAHUAcwBlAHIAcwB0AHIAZQBzAHMAMQAwADAAMAAwADgATgBZAEMAVgBBADEAMgBTADIAQwBNAFMAQQDguXWdC2hLH+C5dZ0LaEsf4Ll1nQtoSx9nI+fkE73qtElnkDiSQbxfcDN9zbtO1qfyK3ZTI6CUhvjxmXnpZEjY" + authBytes, err := base64.StdEncoding.DecodeString(ntlmv1data) + _, err = ParseAuthenticateMessage(authBytes, 2) + if err == nil { + t.Error("Should have returned error when tring to parse an NTLMv1 authenticate message as NTLMv2") + } + _, err = ParseAuthenticateMessage(authBytes, 1) + if err != nil { + t.Error("Should not have returned error when tring to parse an NTLMv1 authenticate message") + } +} + +func TestAuthenticateNtlmV1(t *testing.T) { + authenticateMessage := "TlRMTVNTUAADAAAAGAAYAIgAAAAYABgAoAAAAAAAAABYAAAAIAAgAFgAAAAQABAAeAAAABAAEAC4AAAAVYKQYgYBsR0AAAAP2BgW++b14Dh6Z5B4Xs1DiHAAYQB1AGwAQABwAGEAdQBsAGQAaQB4AC4AbgBlAHQAVwBJAE4ANwBfAEkARQA4ACugxZFzvHB4P6LdKbbZpiYHo2ErZURLiSugxZFzvHB4P6LdKbbZpiYHo2ErZURLibmpCUlnbq2I4LAdEhLdg7I=" + authenticateData, err := base64.StdEncoding.DecodeString(authenticateMessage) + + if err != nil { + t.Error("Could not base64 decode message data") + } + + a, err := ParseAuthenticateMessage(authenticateData, 1) + if err != nil { + t.Error("Could not parse authenticate message") + } + + a.String() + + outBytes := a.Bytes() + + if len(outBytes) > 0 { + reparsed, err := ParseAuthenticateMessage(outBytes, 1) + if err != nil { + t.Error("Could not re-parse authenticate message") + } + if reparsed.String() != a.String() { + t.Error("Reparsed message is not the same") + } + } else { + t.Error("Invalid authenticate messsage bytes") + } +} + +func TestAuthenticateNtlmV2(t *testing.T) { + authenticateMessage := "TlRMTVNTUAADAAAAGAAYAI4AAAAGAQYBpgAAAAAAAABYAAAAIAAgAFgAAAAWABYAeAAAABAAEACsAQAAVYKQQgYAchcAAAAPpdhi9ItaLWwSGpFMT4VQbnAAYQB1AGwAQABwAGEAdQBsAGQAaQB4AC4AbgBlAHQASQBQAC0AMABBADAAQwAzAEEAMQBFAAE/QEbbIB1InAX5KMgp4s4wmpPZ9jp9T3EC95rRY01DhMSv1kei5wYBAQAAAAAAADM6xfahoM0BMJqT2fY6fU8AAAAAAgAOAFIARQBVAFQARQBSAFMAAQAcAFUASwBCAFAALQBDAEIAVABSAE0ARgBFADAANgAEABYAUgBlAHUAdABlAHIAcwAuAG4AZQB0AAMANAB1AGsAYgBwAC0AYwBiAHQAcgBtAGYAZQAwADYALgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQABQAWAFIAZQB1AHQAZQByAHMALgBuAGUAdAAIADAAMAAAAAAAAAAAAAAAADAAAFaspfI82pMCKSuN2L09orn37EQVvxCSqVqQhCloFhQeAAAAAAAAAADRgm1iKYwwmIF3axms/dIe" + authenticateData, err := base64.StdEncoding.DecodeString(authenticateMessage) + + if err != nil { + t.Error("Could not base64 decode message data") + } + + a, err := ParseAuthenticateMessage(authenticateData, 2) + + if err != nil || a == nil { + t.Error("Failed to parse authenticate message " + err.Error()) + } + + checkPayloadStruct(t, a.LmChallengeResponse, 24, 142) + checkPayloadStruct(t, a.NtChallengeResponseFields, 262, 166) + checkPayloadStruct(t, a.DomainName, 0, 88) + checkPayloadStruct(t, a.UserName, 32, 88) + checkPayloadStruct(t, a.Workstation, 22, 120) + checkPayloadStruct(t, a.EncryptedRandomSessionKey, 16, 428) + + if a.NegotiateFlags != uint32(1116766805) { + t.Errorf("Authenticate negotiate flags not correct should be %d got %d", uint32(1116766805), a.NegotiateFlags) + } + + mic, err := hex.DecodeString("a5d862f48b5a2d6c121a914c4f85506e") + if !bytes.Equal(a.Mic, mic) { + t.Errorf("Mic not correct, should be %s, got %s", "a5d862f48b5a2d6c121a914c4f85506e", hex.EncodeToString(a.Mic)) + } + + if len(a.Payload) != 356 { + t.Errorf("Length of payload is incorrect got: %d, should be %d", len(a.Payload), 356) + } + + a.String() + + // Generate the bytes from the message and reparse it and make sure that works + bytes := a.Bytes() + if len(bytes) == 0 { + + } +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_challenge.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_challenge.go new file mode 100644 index 00000000..a57c4549 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_challenge.go @@ -0,0 +1,171 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "errors" + "fmt" +) + +type ChallengeMessage struct { + // sig - 8 bytes + Signature []byte + // message type - 4 bytes + MessageType uint32 + // targetname - 12 bytes + TargetName *PayloadStruct + // negotiate flags - 4bytes + NegotiateFlags uint32 + // server challenge - 8 bytes + ServerChallenge []byte + + // MS-NLMP and Davenport disagree a little on the next few fields and how optional they are + // This is what Davenport has to say: + // As with the Type 1 message, there are a few versions of the Type 2 that have been observed: + // + // Version 1 -- The Context, Target Information, and OS Version structure are all omitted. The data block + // (containing only the contents of the Target Name security buffer) begins at offset 32. This form + // is seen in older Win9x-based systems, and is roughly documented in the Open Group's ActiveX reference + // documentation (Section 11.2.3). + // + // Version 2 -- The Context and Target Information fields are present, but the OS Version structure is not. + // The data block begins after the Target Information header, at offset 48. This form is seen in most out-of-box + // shipping versions of Windows. + // + // Version 3 -- The Context, Target Information, and OS Version structure are all present. The data block begins + // after the OS Version structure, at offset 56. Again, the buffers may be empty (yielding a zero-length data block). + // This form was introduced in a relatively recent Service Pack, and is seen on currently-patched versions of Windows 2000, + // Windows XP, and Windows 2003. + + // reserved - 8 bytes (set to 0). This field is also known as 'context' in the davenport documentation + Reserved []byte + + // targetinfo - 12 bytes + TargetInfoPayloadStruct *PayloadStruct + TargetInfo *AvPairs + + // version - 8 bytes + Version *VersionStruct + // payload - variable + Payload []byte +} + +func ParseChallengeMessage(body []byte) (*ChallengeMessage, error) { + challenge := new(ChallengeMessage) + + challenge.Signature = body[0:8] + if !bytes.Equal(challenge.Signature, []byte("NTLMSSP\x00")) { + return challenge, errors.New("Invalid NTLM message signature") + } + + challenge.MessageType = binary.LittleEndian.Uint32(body[8:12]) + if challenge.MessageType != 2 { + return challenge, errors.New("Invalid NTLM message type should be 0x00000002 for challenge message") + } + + var err error + + challenge.TargetName, err = ReadStringPayload(12, body) + if err != nil { + return nil, err + } + + challenge.NegotiateFlags = binary.LittleEndian.Uint32(body[20:24]) + + challenge.ServerChallenge = body[24:32] + + challenge.Reserved = body[32:40] + + challenge.TargetInfoPayloadStruct, err = ReadBytePayload(40, body) + if err != nil { + return nil, err + } + + challenge.TargetInfo = ReadAvPairs(challenge.TargetInfoPayloadStruct.Payload) + + offset := 48 + + if NTLMSSP_NEGOTIATE_VERSION.IsSet(challenge.NegotiateFlags) { + challenge.Version, err = ReadVersionStruct(body[offset : offset+8]) + if err != nil { + return nil, err + } + offset = offset + 8 + } + + challenge.Payload = body[offset:] + + return challenge, nil +} + +func (c *ChallengeMessage) Bytes() []byte { + payloadLen := int(c.TargetName.Len + c.TargetInfoPayloadStruct.Len) + messageLen := 8 + 4 + 8 + 4 + 8 + 8 + 8 + 8 + payloadOffset := uint32(messageLen) + + messageBytes := make([]byte, 0, messageLen+payloadLen) + buffer := bytes.NewBuffer(messageBytes) + + buffer.Write(c.Signature) + binary.Write(buffer, binary.LittleEndian, c.MessageType) + + c.TargetName.Offset = payloadOffset + buffer.Write(c.TargetName.Bytes()) + payloadOffset += uint32(c.TargetName.Len) + + binary.Write(buffer, binary.LittleEndian, c.NegotiateFlags) + buffer.Write(c.ServerChallenge) + buffer.Write(make([]byte, 8)) + + c.TargetInfoPayloadStruct.Offset = payloadOffset + buffer.Write(c.TargetInfoPayloadStruct.Bytes()) + payloadOffset += uint32(c.TargetInfoPayloadStruct.Len) + + // if(c.Version != nil) { + buffer.Write(c.Version.Bytes()) + // } else { + // buffer.Write(make([]byte, 8)) + //} + + // Write out the payloads + buffer.Write(c.TargetName.Payload) + buffer.Write(c.TargetInfoPayloadStruct.Payload) + + return buffer.Bytes() +} + +func (c *ChallengeMessage) getLowestPayloadOffset() int { + payloadStructs := [...]*PayloadStruct{c.TargetName, c.TargetInfoPayloadStruct} + + // Find the lowest offset value + lowest := 9999 + for i := range payloadStructs { + p := payloadStructs[i] + if p != nil && p.Offset > 0 && int(p.Offset) < lowest { + lowest = int(p.Offset) + } + } + + return lowest +} + +func (c *ChallengeMessage) String() string { + var buffer bytes.Buffer + + buffer.WriteString("Challenge NTLM Message") + buffer.WriteString(fmt.Sprintf("\nPayload Offset: %d Length: %d", c.getLowestPayloadOffset(), len(c.Payload))) + buffer.WriteString(fmt.Sprintf("\nTargetName: %s", c.TargetName.String())) + buffer.WriteString(fmt.Sprintf("\nServerChallenge: %s", hex.EncodeToString(c.ServerChallenge))) + if c.Version != nil { + buffer.WriteString(fmt.Sprintf("\nVersion: %s\n", c.Version.String())) + } + buffer.WriteString("\nTargetInfo") + buffer.WriteString(c.TargetInfo.String()) + buffer.WriteString(fmt.Sprintf("\nFlags %d\n", c.NegotiateFlags)) + buffer.WriteString(FlagsToString(c.NegotiateFlags)) + + return buffer.String() +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_challenge_test.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_challenge_test.go new file mode 100644 index 00000000..85b69670 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_challenge_test.go @@ -0,0 +1,65 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "bytes" + "encoding/base64" + "encoding/hex" + "fmt" + "testing" +) + +func TestDecodeChallenge(t *testing.T) { + challengeMessage := "TlRMTVNTUAACAAAAAAAAADgAAADzgpjiuaopAbx9ejQAAAAAAAAAAKIAogA4AAAABQLODgAAAA8CAA4AUgBFAFUAVABFAFIAUwABABwAVQBLAEIAUAAtAEMAQgBUAFIATQBGAEUAMAA2AAQAFgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQAAwA0AHUAawBiAHAALQBjAGIAdAByAG0AZgBlADAANgAuAFIAZQB1AHQAZQByAHMALgBuAGUAdAAFABYAUgBlAHUAdABlAHIAcwAuAG4AZQB0AAAAAAA=" + challengeData, err := base64.StdEncoding.DecodeString(challengeMessage) + + if err != nil { + t.Error("Could not base64 decode message data") + } + + challenge, err := ParseChallengeMessage(challengeData) + + if err != nil || challenge == nil { + t.Error("Failed to parse challenge message " + err.Error()) + } + + if challenge.TargetName.Len != 0 || challenge.TargetName.MaxLen != 0 || challenge.TargetName.Offset != 56 { + values := fmt.Sprintf("TargetName Len:%v MaxLen:%v Offset:%v", challenge.TargetName.Len, challenge.TargetName.MaxLen, challenge.TargetName.Offset) + t.Error("Failed to parse Target Name in challenge: " + values) + } + + if challenge.NegotiateFlags != uint32(3801645811) { + t.Errorf("Challenge negotiate flags not correct should be %v got %d", uint32(3801645811), challenge.NegotiateFlags) + } + + serverChallenge, err := hex.DecodeString("B9AA2901BC7D7A34") + if !bytes.Equal(challenge.ServerChallenge, serverChallenge) { + hex := hex.EncodeToString(challenge.ServerChallenge) + t.Error("Server challenge is not correct '" + hex + "'") + } + + if challenge.Version.ProductMajorVersion != 5 || challenge.Version.ProductMinorVersion != 2 || challenge.Version.ProductBuild != 3790 || challenge.Version.NTLMRevisionCurrent != 15 { + t.Error("Version information is not correct: '" + challenge.Version.String() + "'") + } + + if len(challenge.Payload) != int(challenge.TargetInfoPayloadStruct.Len) { + t.Error("Payload length is not long enough") + } + + challenge.String() + + outBytes := challenge.Bytes() + + if len(outBytes) > 0 { + reparsed, err := ParseChallengeMessage(outBytes) + if err != nil { + t.Error("Could not re-parse challenge message") + } + if reparsed.String() != challenge.String() { + t.Error("Reparsed message is not the same") + } + } else { + t.Error("Invalid challenge messsage bytes") + } +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_negotiate.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_negotiate.go new file mode 100644 index 00000000..c18374b4 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/message_negotiate.go @@ -0,0 +1,27 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +type NegotiateMessage struct { + // All bytes of the message + Bytes []byte + + // sig - 8 bytes + Signature []byte + // message type - 4 bytes + MessageType uint32 + // negotiate flags - 4bytes + NegotiateFlags uint32 + // If the NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED flag is not set in NegotiateFlags, + // indicating that no DomainName is supplied in Payload - then this should have Len 0 / MaxLen 0 + // this contains a domain name + DomainNameFields *PayloadStruct + // If the NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED flag is not set in NegotiateFlags, + // indicating that no WorkstationName is supplied in Payload - then this should have Len 0 / MaxLen 0 + WorkstationFields *PayloadStruct + // version - 8 bytes + Version *VersionStruct + // payload - variable + Payload []byte + PayloadOffset int +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/negotiate_flags.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/negotiate_flags.go new file mode 100644 index 00000000..a3c7b9f8 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/negotiate_flags.go @@ -0,0 +1,202 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +// During NTLM authentication, each of the following flags is a possible value of the NegotiateFlags field of the NEGOTIATE_MESSAGE, +// CHALLENGE_MESSAGE, and AUTHENTICATE_MESSAGE, unless otherwise noted. These flags define client or server NTLM capabilities +// ssupported by the sender. + +import ( + "bytes" + "fmt" + "reflect" +) + +type NegotiateFlag uint32 + +const ( + // A (1 bit): If set, requests Unicode character set encoding. An alternate name for this field is NTLMSSP_NEGOTIATE_UNICODE. + NTLMSSP_NEGOTIATE_UNICODE NegotiateFlag = 1 << iota + // B (1 bit): If set, requests OEM character set encoding. An alternate name for this field is NTLM_NEGOTIATE_OEM. See bit A for details. + NTLM_NEGOTIATE_OEM + // The A and B bits are evaluated together as follows: + // A==1: The choice of character set encoding MUST be Unicode. + // A==0 and B==1: The choice of character set encoding MUST be OEM. + // A==0 and B==0: The protocol MUST return SEC_E_INVALID_TOKEN. + // C (1 bit): If set, a TargetName field of the CHALLENGE_MESSAGE (section 2.2.1.2) MUST be supplied. An alternate name for this field is NTLMSSP_REQUEST_TARGET. + NTLMSSP_REQUEST_TARGET + // r10 (1 bit): This bit is unused and MUST be zero. + NTLMSSP_R10 + // D (1 bit): If set, requests session key negotiation for message signatures. If the client sends NTLMSSP_NEGOTIATE_SIGN to the server + // in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_SIGN to the client in the CHALLENGE_MESSAGE. An alternate name + // for this field is NTLMSSP_NEGOTIATE_SIGN. + NTLMSSP_NEGOTIATE_SIGN + // E (1 bit): If set, requests session key negotiation for message confidentiality. If the client sends NTLMSSP_NEGOTIATE_SEAL + // to the server in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_SEAL to the client in the CHALLENGE_MESSAGE. + // Clients and servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD always set NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128, + // if they are supported. An alternate name for this field is NTLMSSP_NEGOTIATE_SEAL. + NTLMSSP_NEGOTIATE_SEAL + // F (1 bit): If set, requests connectionless authentication. If NTLMSSP_NEGOTIATE_DATAGRAM is set, then NTLMSSP_NEGOTIATE_KEY_EXCH + // MUST always be set in the AUTHENTICATE_MESSAGE to the server and the CHALLENGE_MESSAGE to the client. An alternate name for + // this field is NTLMSSP_NEGOTIATE_DATAGRAM. + NTLMSSP_NEGOTIATE_DATAGRAM + // G (1 bit): If set, requests LAN Manager (LM) session key computation. NTLMSSP_NEGOTIATE_LM_KEY and NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY + // are mutually exclusive. If both NTLMSSP_NEGOTIATE_LM_KEY and NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY are requested, + // NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY alone MUST be returned to the client. NTLM v2 authentication session key generation + // MUST be supported by both the client and the DC in order to be used, and extended session security signing and sealing requires + // support from the client and the server to be used. An alternate name for this field is NTLMSSP_NEGOTIATE_LM_KEY. + NTLMSSP_NEGOTIATE_LM_KEY + // r9 (1 bit): This bit is unused and MUST be zero. + NTLMSSP_R9 + // H (1 bit): If set, requests usage of the NTLM v1 session security protocol. NTLMSSP_NEGOTIATE_NTLM MUST be set in the + // NEGOTIATE_MESSAGE to the server and the CHALLENGE_MESSAGE to the client. An alternate name for this field is NTLMSSP_NEGOTIATE_NTLM. + NTLMSSP_NEGOTIATE_NTLM + // r8 (1 bit): This bit is unused and MUST be zero. + NTLMSSP_R8 + // J (1 bit): If set, the connection SHOULD be anonymous.<26> r8 (1 bit): This bit is unused and SHOULD be zero.<27> + NTLMSSP_ANONYMOUS + // K (1 bit): If set, the domain name is provided (section 2.2.1.1).<25> An alternate name for this field is NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED. + NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED + // L (1 bit): This flag indicates whether the Workstation field is present. If this flag is not set, the Workstation field + // MUST be ignored. If this flag is set, the length field of the Workstation field specifies whether the workstation name + // is nonempty or not.<24> An alternate name for this field is NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED. + NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED + // r7 (1 bit): This bit is unused and MUST be zero. + NTLMSSP_R7 + // M (1 bit): If set, requests the presence of a signature block on all NTLMSSP_NEGOTIATE_ALWAYS_SIGN MUST be + // set in the NEGOTIATE_MESSAGE to the server and the CHALLENGE_MESSAGE to the client. NTLMSSP_NEGOTIATE_ALWAYS_SIGN is + // overridden by NTLMSSP_NEGOTIATE_SIGN and NTLMSSP_NEGOTIATE_SEAL, if they are supported. An alternate name for this field + // is NTLMSSP_NEGOTIATE_ALWAYS_SIGN. + NTLMSSP_NEGOTIATE_ALWAYS_SIGN + // N (1 bit): If set, TargetName MUST be a domain name. The data corresponding to this flag is provided by the server in the + // TargetName field of the CHALLENGE_MESSAGE. If set, then NTLMSSP_TARGET_TYPE_SERVER MUST NOT be set. This flag MUST be ignored + // in the NEGOTIATE_MESSAGE and the AUTHENTICATE_MESSAGE. An alternate name for this field is NTLMSSP_TARGET_TYPE_DOMAIN. + NTLMSSP_TARGET_TYPE_DOMAIN + // O (1 bit): If set, TargetName MUST be a server name. The data corresponding to this flag is provided by the server in the + // TargetName field of the CHALLENGE_MESSAGE. If this bit is set, then NTLMSSP_TARGET_TYPE_DOMAIN MUST NOT be set. This flag MUST + // be ignored in the NEGOTIATE_MESSAGE and the AUTHENTICATE_MESSAGE. An alternate name for this field is NTLMSSP_TARGET_TYPE_SERVER. + NTLMSSP_TARGET_TYPE_SERVER + // r6 (1 bit): This bit is unused and MUST be zero. + NTLMSSP_R6 + // P (1 bit): If set, requests usage of the NTLM v2 session security. NTLM v2 session security is a misnomer because it is not + // NTLM v2. It is NTLM v1 using the extended session security that is also in NTLM v2. NTLMSSP_NEGOTIATE_LM_KEY and + // NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY are mutually exclusive. If both NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY and + // NTLMSSP_NEGOTIATE_LM_KEY are requested, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY alone MUST be returned to the client. + // NTLM v2 authentication session key generation MUST be supported by both the client and the DC in order to be used, and extended + // session security signing and sealing requires support from the client and the server in order to be used.<23> An alternate name + // for this field is NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY. + NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY + // Q (1 bit): If set, requests an identify level token. An alternate name for this field is NTLMSSP_NEGOTIATE_IDENTIFY. + NTLMSSP_NEGOTIATE_IDENTIFY + // r5 (1 bit): This bit is unused and MUST be zero. + NTLMSSP_R5 + // R (1 bit): If set, requests the usage of the LMOWF (section 3.3). An alternate name for this field is NTLMSSP_REQUEST_NON_NT_SESSION_KEY. + NTLMSSP_REQUEST_NON_NT_SESSION_KEY + // S (1 bit): If set, indicates that the TargetInfo fields in the CHALLENGE_MESSAGE (section 2.2.1.2) are populated. An alternate + // name for this field is NTLMSSP_NEGOTIATE_TARGET_INFO. + NTLMSSP_NEGOTIATE_TARGET_INFO + // r4 (1 bit): This bit is unused and MUST be zero. + NTLMSSP_R4 + // T (1 bit): If set, requests the protocol version number. The data corresponding to this flag is provided in the Version field of the + // NEGOTIATE_MESSAGE, the CHALLENGE_MESSAGE, and the AUTHENTICATE_MESSAGE.<22> An alternate name for this field is NTLMSSP_NEGOTIATE_VERSION. + NTLMSSP_NEGOTIATE_VERSION + // r3 (1 bit): This bit is unused and MUST be zero. + NTLMSSP_R3 + // r2 (1 bit): This bit is unused and MUST be zero. + NTLMSSP_R2 + // r1 (1 bit): This bit is unused and MUST be zero. + NTLMSSP_R1 + // U (1 bit): If set, requests 128-bit session key negotiation. An alternate name for this field is NTLMSSP_NEGOTIATE_128. If the client + // sends NTLMSSP_NEGOTIATE_128 to the server in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_128 to the client in the + // CHALLENGE_MESSAGE only if the client sets NTLMSSP_NEGOTIATE_SEAL or NTLMSSP_NEGOTIATE_SIGN. Otherwise it is ignored. If both + // NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 are requested and supported by the client and server, NTLMSSP_NEGOTIATE_56 and + // NTLMSSP_NEGOTIATE_128 will both be returned to the client. Clients and servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD set + // NTLMSSP_NEGOTIATE_128 if it is supported. An alternate name for this field is NTLMSSP_NEGOTIATE_128.<21> + NTLMSSP_NEGOTIATE_128 + // V (1 bit): If set, requests an explicit key exchange. This capability SHOULD be used because it improves security for message integrity or + // confidentiality. See sections 3.2.5.1.2, 3.2.5.2.1, and 3.2.5.2.2 for details. An alternate name for this field is NTLMSSP_NEGOTIATE_KEY_EXCH. + NTLMSSP_NEGOTIATE_KEY_EXCH + // If set, requests 56-bit encryption. If the client sends NTLMSSP_NEGOTIATE_SEAL or NTLMSSP_NEGOTIATE_SIGN with NTLMSSP_NEGOTIATE_56 to the + // server in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_56 to the client in the CHALLENGE_MESSAGE. Otherwise it is ignored. + // If both NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 are requested and supported by the client and server, NTLMSSP_NEGOTIATE_56 and + // NTLMSSP_NEGOTIATE_128 will both be returned to the client. Clients and servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD set NTLMSSP_NEGOTIATE_56 + // if it is supported. An alternate name for this field is NTLMSSP_NEGOTIATE_56. + NTLMSSP_NEGOTIATE_56 +) + +func (f NegotiateFlag) Set(flags uint32) uint32 { + return flags | uint32(f) +} + +func (f NegotiateFlag) IsSet(flags uint32) bool { + return (flags & uint32(f)) != 0 +} + +func (f NegotiateFlag) Unset(flags uint32) uint32 { + return flags &^ uint32(f) +} + +func (f NegotiateFlag) String() string { + return reflect.TypeOf(f).Name() +} + +func GetFlagName(flag NegotiateFlag) string { + nameMap := map[NegotiateFlag]string{ + NTLMSSP_NEGOTIATE_56: "NTLMSSP_NEGOTIATE_56", + NTLMSSP_NEGOTIATE_KEY_EXCH: "NTLMSSP_NEGOTIATE_KEY_EXCH", + NTLMSSP_NEGOTIATE_128: "NTLMSSP_NEGOTIATE_128", + NTLMSSP_NEGOTIATE_VERSION: "NTLMSSP_NEGOTIATE_VERSION", + NTLMSSP_NEGOTIATE_TARGET_INFO: "NTLMSSP_NEGOTIATE_TARGET_INFO", + NTLMSSP_REQUEST_NON_NT_SESSION_KEY: "NTLMSSP_REQUEST_NON_NT_SESSION_KEY", + NTLMSSP_NEGOTIATE_IDENTIFY: "NTLMSSP_NEGOTIATE_IDENTIFY", + NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY: "NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY", + NTLMSSP_TARGET_TYPE_SERVER: "NTLMSSP_TARGET_TYPE_SERVER", + NTLMSSP_TARGET_TYPE_DOMAIN: "NTLMSSP_TARGET_TYPE_DOMAIN", + NTLMSSP_NEGOTIATE_ALWAYS_SIGN: "NTLMSSP_NEGOTIATE_ALWAYS_SIGN", + NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED: "NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED", + NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED: "NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED", + NTLMSSP_ANONYMOUS: "NTLMSSP_ANONYMOUS", + NTLMSSP_NEGOTIATE_NTLM: "NTLMSSP_NEGOTIATE_NTLM", + NTLMSSP_NEGOTIATE_LM_KEY: "NTLMSSP_NEGOTIATE_LM_KEY", + NTLMSSP_NEGOTIATE_DATAGRAM: "NTLMSSP_NEGOTIATE_DATAGRAM", + NTLMSSP_NEGOTIATE_SEAL: "NTLMSSP_NEGOTIATE_SEAL", + NTLMSSP_NEGOTIATE_SIGN: "NTLMSSP_NEGOTIATE_SIGN", + NTLMSSP_REQUEST_TARGET: "NTLMSSP_REQUEST_TARGET", + NTLM_NEGOTIATE_OEM: "NTLM_NEGOTIATE_OEM", + NTLMSSP_NEGOTIATE_UNICODE: "NTLMSSP_NEGOTIATE_UNICODE"} + + return nameMap[flag] +} + +func FlagsToString(flags uint32) string { + allFlags := [...]NegotiateFlag{ + NTLMSSP_NEGOTIATE_56, + NTLMSSP_NEGOTIATE_KEY_EXCH, + NTLMSSP_NEGOTIATE_128, + NTLMSSP_NEGOTIATE_VERSION, + NTLMSSP_NEGOTIATE_TARGET_INFO, + NTLMSSP_REQUEST_NON_NT_SESSION_KEY, + NTLMSSP_NEGOTIATE_IDENTIFY, + NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY, + NTLMSSP_TARGET_TYPE_SERVER, + NTLMSSP_TARGET_TYPE_DOMAIN, + NTLMSSP_NEGOTIATE_ALWAYS_SIGN, + NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED, + NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED, + NTLMSSP_ANONYMOUS, + NTLMSSP_NEGOTIATE_NTLM, + NTLMSSP_NEGOTIATE_LM_KEY, + NTLMSSP_NEGOTIATE_DATAGRAM, + NTLMSSP_NEGOTIATE_SEAL, + NTLMSSP_NEGOTIATE_SIGN, + NTLMSSP_REQUEST_TARGET, + NTLM_NEGOTIATE_OEM, + NTLMSSP_NEGOTIATE_UNICODE} + + var buffer bytes.Buffer + for i := range allFlags { + f := allFlags[i] + buffer.WriteString(fmt.Sprintf("%s: %v\n", GetFlagName(f), f.IsSet(flags))) + } + return buffer.String() +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/negotiate_flags_test.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/negotiate_flags_test.go new file mode 100644 index 00000000..7a1184a0 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/negotiate_flags_test.go @@ -0,0 +1,31 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "encoding/binary" + "encoding/hex" + "testing" +) + +func TestFlags(t *testing.T) { + // Sample value from 4.2.2 NTLM v1 Authentication + bytes, _ := hex.DecodeString("338202e2") + + flags := uint32(0) + flags = NTLMSSP_NEGOTIATE_KEY_EXCH.Set(flags) + flags = NTLMSSP_NEGOTIATE_56.Set(flags) + flags = NTLMSSP_NEGOTIATE_128.Set(flags) + flags = NTLMSSP_NEGOTIATE_VERSION.Set(flags) + flags = NTLMSSP_TARGET_TYPE_SERVER.Set(flags) + flags = NTLMSSP_NEGOTIATE_ALWAYS_SIGN.Set(flags) + flags = NTLMSSP_NEGOTIATE_NTLM.Set(flags) + flags = NTLMSSP_NEGOTIATE_SEAL.Set(flags) + flags = NTLMSSP_NEGOTIATE_SIGN.Set(flags) + flags = NTLM_NEGOTIATE_OEM.Set(flags) + flags = NTLMSSP_NEGOTIATE_UNICODE.Set(flags) + + if flags != binary.LittleEndian.Uint32(bytes) { + t.Error("NTLM Flags are not correct") + } +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlm.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlm.go new file mode 100644 index 00000000..1bc014af --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlm.go @@ -0,0 +1,127 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +// Package NTLM implements the interfaces used for interacting with NTLMv1 and NTLMv2. +// To create NTLM v1 or v2 sessions you would use CreateClientSession and create ClientServerSession. +package ntlm + +import ( + rc4P "crypto/rc4" + "errors" +) + +type Version int + +const ( + Version1 Version = 1 + Version2 Version = 2 +) + +type Mode int + +const ( + ConnectionlessMode Mode = iota + ConnectionOrientedMode +) + +// Creates an NTLM v1 or v2 client +// mode - This must be ConnectionlessMode or ConnectionOrientedMode depending on what type of NTLM is used +// version - This must be Version1 or Version2 depending on the version of NTLM used +func CreateClientSession(version Version, mode Mode) (n ClientSession, err error) { + switch version { + case Version1: + n = new(V1ClientSession) + case Version2: + n = new(V2ClientSession) + default: + return nil, errors.New("Unknown NTLM Version, must be 1 or 2") + } + + return n, nil +} + +type ClientSession interface { + SetUserInfo(username string, password string, domain string) + SetMode(mode Mode) + + GenerateNegotiateMessage() (*NegotiateMessage, error) + ProcessChallengeMessage(*ChallengeMessage) error + GenerateAuthenticateMessage() (*AuthenticateMessage, error) + + Seal(message []byte) ([]byte, error) + Sign(message []byte) ([]byte, error) + Mac(message []byte, sequenceNumber int) ([]byte, error) + VerifyMac(message, expectedMac []byte, sequenceNumber int) (bool, error) +} + +// Creates an NTLM v1 or v2 server +// mode - This must be ConnectionlessMode or ConnectionOrientedMode depending on what type of NTLM is used +// version - This must be Version1 or Version2 depending on the version of NTLM used +func CreateServerSession(version Version, mode Mode) (n ServerSession, err error) { + switch version { + case Version1: + n = new(V1ServerSession) + case Version2: + n = new(V2ServerSession) + default: + return nil, errors.New("Unknown NTLM Version, must be 1 or 2") + } + + n.SetMode(mode) + return n, nil +} + +type ServerSession interface { + SetUserInfo(username string, password string, domain string) + GetUserInfo() (string, string, string) + + SetMode(mode Mode) + SetServerChallenge(challege []byte) + + ProcessNegotiateMessage(*NegotiateMessage) error + GenerateChallengeMessage() (*ChallengeMessage, error) + ProcessAuthenticateMessage(*AuthenticateMessage) error + + GetSessionData() *SessionData + + Version() int + Seal(message []byte) ([]byte, error) + Sign(message []byte) ([]byte, error) + Mac(message []byte, sequenceNumber int) ([]byte, error) + VerifyMac(message, expectedMac []byte, sequenceNumber int) (bool, error) +} + +// This struct collects NTLM data structures and keys that are used across all types of NTLM requests +type SessionData struct { + mode Mode + + user string + password string + userDomain string + + NegotiateFlags uint32 + + negotiateMessage *NegotiateMessage + challengeMessage *ChallengeMessage + authenticateMessage *AuthenticateMessage + + serverChallenge []byte + clientChallenge []byte + ntChallengeResponse []byte + lmChallengeResponse []byte + + responseKeyLM []byte + responseKeyNT []byte + exportedSessionKey []byte + encryptedRandomSessionKey []byte + keyExchangeKey []byte + sessionBaseKey []byte + mic []byte + + ClientSigningKey []byte + ServerSigningKey []byte + ClientSealingKey []byte + ServerSealingKey []byte + + clientHandle *rc4P.Cipher + serverHandle *rc4P.Cipher +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlmv1.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlmv1.go new file mode 100644 index 00000000..4536f955 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlmv1.go @@ -0,0 +1,392 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "bytes" + rc4P "crypto/rc4" + "errors" + l4g "github.com/github/git-lfs/vendor/_nuts/github.com/ThomsonReutersEikon/log4go" + "strings" +) + +/******************************* + Shared Session Data and Methods +*******************************/ + +type V1Session struct { + SessionData +} + +func (n *V1Session) SetUserInfo(username string, password string, domain string) { + n.user = username + n.password = password + n.userDomain = domain +} + +func (n *V1Session) GetUserInfo() (string, string, string) { + return n.user, n.password, n.userDomain +} + +func (n *V1Session) SetMode(mode Mode) { + n.mode = mode +} + +func (n *V1Session) Version() int { + return 1 +} + +func (n *V1Session) fetchResponseKeys() (err error) { + n.responseKeyLM, err = lmowfv1(n.password) + if err != nil { + return err + } + n.responseKeyNT = ntowfv1(n.password) + return +} + +func (n *V1Session) computeExpectedResponses() (err error) { + if NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.IsSet(n.NegotiateFlags) { + n.ntChallengeResponse, err = desL(n.responseKeyNT, md5(concat(n.serverChallenge, n.clientChallenge))[0:8]) + if err != nil { + return err + } + n.lmChallengeResponse = concat(n.clientChallenge, make([]byte, 16)) + } else { + n.ntChallengeResponse, err = desL(n.responseKeyNT, n.serverChallenge) + if err != nil { + return err + } + // NoLMResponseNTLMv1: A Boolean setting that controls using the NTLM response for the LM + // response to the server challenge when NTLMv1 authentication is used.<30> + // <30> Section 3.1.1.1: The default value of this state variable is TRUE. Windows NT Server 4.0 SP3 + // does not support providing NTLM instead of LM responses. + noLmResponseNtlmV1 := false + if noLmResponseNtlmV1 { + n.lmChallengeResponse = n.ntChallengeResponse + } else { + n.lmChallengeResponse, err = desL(n.responseKeyLM, n.serverChallenge) + if err != nil { + return err + } + } + } + + return nil +} + +func (n *V1Session) computeSessionBaseKey() (err error) { + n.sessionBaseKey = md4(n.responseKeyNT) + return +} + +func (n *V1Session) computeKeyExchangeKey() (err error) { + if NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.IsSet(n.NegotiateFlags) { + n.keyExchangeKey = hmacMd5(n.sessionBaseKey, concat(n.serverChallenge, n.lmChallengeResponse[0:8])) + } else { + n.keyExchangeKey, err = kxKey(n.NegotiateFlags, n.sessionBaseKey, n.lmChallengeResponse, n.serverChallenge, n.responseKeyLM) + } + return +} + +func (n *V1Session) calculateKeys(ntlmRevisionCurrent uint8) (err error) { + // This lovely piece of code comes courtesy of an the excellent Open Document support system from MSFT + // In order to calculate the keys correctly when the client has set the NTLMRevisionCurrent to 0xF (15) + // We must treat the flags as if NTLMSSP_NEGOTIATE_LM_KEY is set. + // This information is not contained (at least currently, until they correct it) in the MS-NLMP document + if ntlmRevisionCurrent == 15 { + n.NegotiateFlags = NTLMSSP_NEGOTIATE_LM_KEY.Set(n.NegotiateFlags) + } + + n.ClientSigningKey = signKey(n.NegotiateFlags, n.exportedSessionKey, "Client") + n.ServerSigningKey = signKey(n.NegotiateFlags, n.exportedSessionKey, "Server") + n.ClientSealingKey = sealKey(n.NegotiateFlags, n.exportedSessionKey, "Client") + n.ServerSealingKey = sealKey(n.NegotiateFlags, n.exportedSessionKey, "Server") + return +} + +func (n *V1Session) Seal(message []byte) ([]byte, error) { + return nil, nil +} + +func (n *V1Session) Sign(message []byte) ([]byte, error) { + return nil, nil +} + +func ntlmV1Mac(message []byte, sequenceNumber int, handle *rc4P.Cipher, sealingKey, signingKey []byte, NegotiateFlags uint32) []byte { + // TODO: Need to keep track of the sequence number for connection oriented NTLM + if NTLMSSP_NEGOTIATE_DATAGRAM.IsSet(NegotiateFlags) && NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.IsSet(NegotiateFlags) { + handle, _ = reinitSealingKey(sealingKey, sequenceNumber) + } else if NTLMSSP_NEGOTIATE_DATAGRAM.IsSet(NegotiateFlags) { + // CONOR: Reinitializing the rc4 cipher on every requst, but not using the + // algorithm as described in the MS-NTLM document. Just reinitialize it directly. + handle, _ = rc4Init(sealingKey) + } + sig := mac(NegotiateFlags, handle, signingKey, uint32(sequenceNumber), message) + return sig.Bytes() +} + +func (n *V1ServerSession) Mac(message []byte, sequenceNumber int) ([]byte, error) { + mac := ntlmV1Mac(message, sequenceNumber, n.serverHandle, n.ServerSealingKey, n.ServerSigningKey, n.NegotiateFlags) + return mac, nil +} + +func (n *V1ClientSession) Mac(message []byte, sequenceNumber int) ([]byte, error) { + mac := ntlmV1Mac(message, sequenceNumber, n.clientHandle, n.ClientSealingKey, n.ClientSigningKey, n.NegotiateFlags) + return mac, nil +} + +func (n *V1ServerSession) VerifyMac(message, expectedMac []byte, sequenceNumber int) (bool, error) { + mac := ntlmV1Mac(message, sequenceNumber, n.clientHandle, n.ClientSealingKey, n.ClientSigningKey, n.NegotiateFlags) + return MacsEqual(mac, expectedMac), nil +} + +func (n *V1ClientSession) VerifyMac(message, expectedMac []byte, sequenceNumber int) (bool, error) { + mac := ntlmV1Mac(message, sequenceNumber, n.serverHandle, n.ServerSealingKey, n.ServerSigningKey, n.NegotiateFlags) + return MacsEqual(mac, expectedMac), nil +} + +/************** + Server Session +**************/ + +type V1ServerSession struct { + V1Session +} + +func (n *V1ServerSession) ProcessNegotiateMessage(nm *NegotiateMessage) (err error) { + n.negotiateMessage = nm + return +} + +func (n *V1ServerSession) GenerateChallengeMessage() (cm *ChallengeMessage, err error) { + // TODO: Generate this challenge message + return +} + +func (n *V1ServerSession) SetServerChallenge(challenge []byte) { + n.serverChallenge = challenge +} + +func (n *V1ServerSession) GetSessionData() *SessionData { + return &n.SessionData +} + +func (n *V1ServerSession) ProcessAuthenticateMessage(am *AuthenticateMessage) (err error) { + n.authenticateMessage = am + n.NegotiateFlags = am.NegotiateFlags + n.clientChallenge = am.ClientChallenge() + n.encryptedRandomSessionKey = am.EncryptedRandomSessionKey.Payload + // Ignore the values used in SetUserInfo and use these instead from the authenticate message + // They should always be correct (I hope) + n.user = am.UserName.String() + n.userDomain = am.DomainName.String() + l4g.Info("(ProcessAuthenticateMessage)NTLM v1 User %s Domain %s", n.user, n.userDomain) + + err = n.fetchResponseKeys() + if err != nil { + return err + } + + err = n.computeExpectedResponses() + if err != nil { + return err + } + + err = n.computeSessionBaseKey() + if err != nil { + return err + } + + err = n.computeKeyExchangeKey() + if err != nil { + return err + } + + if !bytes.Equal(am.NtChallengeResponseFields.Payload, n.ntChallengeResponse) { + // There is a bug with the steps in MS-NLMP. In section 3.2.5.1.2 it says you should fall through + // to compare the lmChallengeResponse if the ntChallengeRepsonse fails, but with extended session security + // this would *always* pass because the lmChallengeResponse and expectedLmChallengeRepsonse will always + // be the same + if !bytes.Equal(am.LmChallengeResponse.Payload, n.lmChallengeResponse) || NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.IsSet(n.NegotiateFlags) { + return errors.New("Could not authenticate") + } + } + + n.mic = am.Mic + am.Mic = zeroBytes(16) + + err = n.computeExportedSessionKey() + if err != nil { + return err + } + + if am.Version == nil { + //UGH not entirely sure how this could possibly happen, going to put this in for now + //TODO investigate if this ever is really happening + am.Version = &VersionStruct{ProductMajorVersion: uint8(5), ProductMinorVersion: uint8(1), ProductBuild: uint16(2600), NTLMRevisionCurrent: uint8(15)} + l4g.Error("Nil version in ntlmv1") + } + + err = n.calculateKeys(am.Version.NTLMRevisionCurrent) + if err != nil { + return err + } + + n.clientHandle, err = rc4Init(n.ClientSealingKey) + if err != nil { + return err + } + n.serverHandle, err = rc4Init(n.ServerSealingKey) + if err != nil { + return err + } + + return nil +} + +func (n *V1ServerSession) computeExportedSessionKey() (err error) { + if NTLMSSP_NEGOTIATE_KEY_EXCH.IsSet(n.NegotiateFlags) { + n.exportedSessionKey, err = rc4K(n.keyExchangeKey, n.encryptedRandomSessionKey) + if err != nil { + return err + } + // TODO: Calculate mic correctly. This calculation is not producing the right results now + // n.calculatedMic = HmacMd5(n.exportedSessionKey, concat(n.challengeMessage.Payload, n.authenticateMessage.Bytes)) + } else { + n.exportedSessionKey = n.keyExchangeKey + // TODO: Calculate mic correctly. This calculation is not producing the right results now + // n.calculatedMic = HmacMd5(n.keyExchangeKey, concat(n.challengeMessage.Payload, n.authenticateMessage.Bytes)) + } + return nil +} + +/************* + Client Session +**************/ + +type V1ClientSession struct { + V1Session +} + +func (n *V1ClientSession) GenerateNegotiateMessage() (nm *NegotiateMessage, err error) { + return nil, nil +} + +func (n *V1ClientSession) ProcessChallengeMessage(cm *ChallengeMessage) (err error) { + n.challengeMessage = cm + n.serverChallenge = cm.ServerChallenge + n.clientChallenge = randomBytes(8) + + // Set up the default flags for processing the response. These are the flags that we will return + // in the authenticate message + flags := uint32(0) + flags = NTLMSSP_NEGOTIATE_KEY_EXCH.Set(flags) + // NOTE: Unsetting this flag in order to get the server to generate the signatures we can recognize + flags = NTLMSSP_NEGOTIATE_VERSION.Set(flags) + flags = NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.Set(flags) + flags = NTLMSSP_NEGOTIATE_TARGET_INFO.Set(flags) + flags = NTLMSSP_NEGOTIATE_IDENTIFY.Set(flags) + flags = NTLMSSP_NEGOTIATE_ALWAYS_SIGN.Set(flags) + flags = NTLMSSP_NEGOTIATE_NTLM.Set(flags) + flags = NTLMSSP_NEGOTIATE_DATAGRAM.Set(flags) + flags = NTLMSSP_NEGOTIATE_SIGN.Set(flags) + flags = NTLMSSP_REQUEST_TARGET.Set(flags) + flags = NTLMSSP_NEGOTIATE_UNICODE.Set(flags) + + n.NegotiateFlags = flags + + err = n.fetchResponseKeys() + if err != nil { + return err + } + + err = n.computeExpectedResponses() + if err != nil { + return err + } + + err = n.computeSessionBaseKey() + if err != nil { + return err + } + + err = n.computeKeyExchangeKey() + if err != nil { + return err + } + + err = n.computeEncryptedSessionKey() + if err != nil { + return err + } + + err = n.calculateKeys(cm.Version.NTLMRevisionCurrent) + if err != nil { + return err + } + + n.clientHandle, err = rc4Init(n.ClientSealingKey) + if err != nil { + return err + } + n.serverHandle, err = rc4Init(n.ServerSealingKey) + if err != nil { + return err + } + + return nil +} + +func (n *V1ClientSession) GenerateAuthenticateMessage() (am *AuthenticateMessage, err error) { + am = new(AuthenticateMessage) + am.Signature = []byte("NTLMSSP\x00") + am.MessageType = uint32(3) + am.LmChallengeResponse, _ = CreateBytePayload(n.lmChallengeResponse) + am.NtChallengeResponseFields, _ = CreateBytePayload(n.ntChallengeResponse) + am.DomainName, _ = CreateStringPayload(n.userDomain) + am.UserName, _ = CreateStringPayload(n.user) + am.Workstation, _ = CreateStringPayload("SQUAREMILL") + am.EncryptedRandomSessionKey, _ = CreateBytePayload(n.encryptedRandomSessionKey) + am.NegotiateFlags = n.NegotiateFlags + am.Version = &VersionStruct{ProductMajorVersion: uint8(5), ProductMinorVersion: uint8(1), ProductBuild: uint16(2600), NTLMRevisionCurrent: uint8(15)} + return am, nil +} + +func (n *V1ClientSession) computeEncryptedSessionKey() (err error) { + if NTLMSSP_NEGOTIATE_KEY_EXCH.IsSet(n.NegotiateFlags) { + n.exportedSessionKey = randomBytes(16) + n.encryptedRandomSessionKey, err = rc4K(n.keyExchangeKey, n.exportedSessionKey) + if err != nil { + return err + } + } else { + n.encryptedRandomSessionKey = n.keyExchangeKey + } + return nil +} + +/******************************** + NTLM V1 Password hash functions +*********************************/ + +func ntowfv1(passwd string) []byte { + return md4(utf16FromString(passwd)) +} + +// ConcatenationOf( DES( UpperCase( Passwd)[0..6],"KGS!@#$%"), DES( UpperCase( Passwd)[7..13],"KGS!@#$%")) +func lmowfv1(passwd string) ([]byte, error) { + asciiPassword := []byte(strings.ToUpper(passwd)) + keyBytes := zeroPaddedBytes(asciiPassword, 0, 14) + + first, err := des(keyBytes[0:7], []byte("KGS!@#$%")) + if err != nil { + return nil, err + } + second, err := des(keyBytes[7:14], []byte("KGS!@#$%")) + if err != nil { + return nil, err + } + + return append(first, second...), nil +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlmv1_test.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlmv1_test.go new file mode 100644 index 00000000..18e02cf4 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlmv1_test.go @@ -0,0 +1,238 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "bytes" + "encoding/base64" + "encoding/hex" + "testing" +) + +func TestLMOWFv1(t *testing.T) { + // Sample from MS-NLMP + result, err := lmowfv1("Password") + expected, _ := hex.DecodeString("e52cac67419a9a224a3b108f3fa6cb6d") + if err != nil || !bytes.Equal(result, expected) { + t.Errorf("LMNOWFv1 is not correct, got %s expected %s", hex.EncodeToString(result), "e52cac67419a9a224a3b108f3fa6cb6d") + } +} + +func TestNTOWFv1(t *testing.T) { + // Sample from MS-NLMP + result := ntowfv1("Password") + expected, _ := hex.DecodeString("a4f49c406510bdcab6824ee7c30fd852") + if !bytes.Equal(result, expected) { + t.Error("NTOWFv1 is not correct") + } +} + +func checkV1Value(t *testing.T, name string, value []byte, expected string, err error) { + if err != nil { + t.Errorf("NTLMv1 %s received error: %s", name, err) + } else { + expectedBytes, _ := hex.DecodeString(expected) + if !bytes.Equal(expectedBytes, value) { + t.Errorf("NTLMv1 %s is not correct got %s expected %s", name, hex.EncodeToString(value), expected) + } + } +} + +// There was an issue where all NTLMv1 authentications with extended session security +// would authenticate. This was due to a bug in the MS-NLMP docs. This tests for that issue +func TestNtlmV1ExtendedSessionSecurity(t *testing.T) { + // NTLMv1 with extended session security + challengeMessage := "TlRMTVNTUAACAAAAAAAAADgAAABVgphiRy3oSZvn1I4AAAAAAAAAAKIAogA4AAAABQEoCgAAAA8CAA4AUgBFAFUAVABFAFIAUwABABwAVQBLAEIAUAAtAEMAQgBUAFIATQBGAEUAMAA2AAQAFgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQAAwA0AHUAawBiAHAALQBjAGIAdAByAG0AZgBlADAANgAuAFIAZQB1AHQAZQByAHMALgBuAGUAdAAFABYAUgBlAHUAdABlAHIAcwAuAG4AZQB0AAAAAAA=" + authenticateMessage := "TlRMTVNTUAADAAAAGAAYAJgAAAAYABgAsAAAAAAAAABIAAAAOgA6AEgAAAAWABYAggAAABAAEADIAAAAVYKYYgUCzg4AAAAPMQAwADAAMAAwADEALgB3AGMAcABAAHQAaABvAG0AcwBvAG4AcgBlAHUAdABlAHIAcwAuAGMAbwBtAE4AWQBDAFMATQBTAEcAOQA5ADAAOQBRWAK3h/TIywAAAAAAAAAAAAAAAAAAAAA3tp89kZU1hs1XZp7KTyGm3XsFAT9stEDW9YXDaeYVBmBcBb//2FOu" + + challengeData, _ := base64.StdEncoding.DecodeString(challengeMessage) + c, _ := ParseChallengeMessage(challengeData) + + authenticateData, _ := base64.StdEncoding.DecodeString(authenticateMessage) + msg, err := ParseAuthenticateMessage(authenticateData, 1) + if err != nil { + t.Errorf("Could not process authenticate message: %s", err) + } + + context, err := CreateServerSession(Version1, ConnectionlessMode) + if err != nil { + t.Errorf("Could not create NTLMv1 session") + } + context.SetUserInfo("100001.wcp.thomsonreuters.com", "notmypass", "") + context.SetServerChallenge(c.ServerChallenge) + err = context.ProcessAuthenticateMessage(msg) + if err == nil { + t.Errorf("This message should have failed to authenticate, but it passed", err) + } +} + +func TestNtlmV1(t *testing.T) { + flags := uint32(0) + flags = NTLMSSP_NEGOTIATE_KEY_EXCH.Set(flags) + flags = NTLMSSP_NEGOTIATE_56.Set(flags) + flags = NTLMSSP_NEGOTIATE_128.Set(flags) + flags = NTLMSSP_NEGOTIATE_VERSION.Set(flags) + flags = NTLMSSP_TARGET_TYPE_SERVER.Set(flags) + flags = NTLMSSP_NEGOTIATE_ALWAYS_SIGN.Set(flags) + flags = NTLMSSP_NEGOTIATE_NTLM.Set(flags) + flags = NTLMSSP_NEGOTIATE_SEAL.Set(flags) + flags = NTLMSSP_NEGOTIATE_SIGN.Set(flags) + flags = NTLM_NEGOTIATE_OEM.Set(flags) + flags = NTLMSSP_NEGOTIATE_UNICODE.Set(flags) + + n := new(V1ClientSession) + n.SetUserInfo("User", "Password", "Domain") + n.NegotiateFlags = flags + n.responseKeyNT, _ = hex.DecodeString("a4f49c406510bdcab6824ee7c30fd852") + n.responseKeyLM, _ = hex.DecodeString("e52cac67419a9a224a3b108f3fa6cb6d") + n.clientChallenge, _ = hex.DecodeString("aaaaaaaaaaaaaaaa") + n.serverChallenge, _ = hex.DecodeString("0123456789abcdef") + + var err error + // 4.2.2.1.3 Session Base Key and Key Exchange Key + err = n.computeSessionBaseKey() + checkV1Value(t, "sessionBaseKey", n.sessionBaseKey, "d87262b0cde4b1cb7499becccdf10784", err) + err = n.computeKeyExchangeKey() + checkV1Value(t, "keyExchangeKey", n.keyExchangeKey, "d87262b0cde4b1cb7499becccdf10784", err) + + // 4.2.2.2.1 NTLMv1 Response + // NTChallengeResponse with With NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY not set + err = n.computeExpectedResponses() + checkV1Value(t, "NTChallengeResponse", n.ntChallengeResponse, "67c43011f30298a2ad35ece64f16331c44bdbed927841f94", err) + // 4.2.2.2.2 LMv1 Response + // The LmChallengeResponse is specified in section 3.3.1. With the NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY flag + // not set and with the NoLMResponseNTLMv1 flag not set + checkV1Value(t, "LMChallengeResponse", n.lmChallengeResponse, "98def7b87f88aa5dafe2df779688a172def11c7d5ccdef13", err) + + // If the NTLMSSP_NEGOTIATE_LM_KEY flag is set then the KeyExchangeKey is: + n.NegotiateFlags = NTLMSSP_NEGOTIATE_LM_KEY.Set(n.NegotiateFlags) + err = n.computeKeyExchangeKey() + checkV1Value(t, "keyExchangeKey with NTLMSSP_NEGOTIATE_LM_KEY", n.keyExchangeKey, "b09e379f7fbecb1eaf0afdcb0383c8a0", err) + n.NegotiateFlags = NTLMSSP_NEGOTIATE_LM_KEY.Unset(n.NegotiateFlags) + + // 4.2.2.2.3 Encrypted Session Key + //n.randomSessionKey, _ = hex.DecodeString("55555555555555555555555555555555") + + // RC4 decryption of the EncryptedRandomSessionKey with the KeyExchange key + //err = n.computeKeyExchangeKey() + //n.encryptedRandomSessionKey, err = hex.DecodeString("518822b1b3f350c8958682ecbb3e3cb7") + //err = n.computeExportedSessionKey() + //checkV1Value(t, "ExportedSessionKey", n.exportedSessionKey, "55555555555555555555555555555555", err) + + // NTLMSSP_REQUEST_NON_NT_SESSION_KEY is set: + n.NegotiateFlags = NTLMSSP_REQUEST_NON_NT_SESSION_KEY.Set(n.NegotiateFlags) + err = n.computeKeyExchangeKey() + // n.encryptedRandomSessionKey, err = hex.DecodeString("7452ca55c225a1ca04b48fae32cf56fc") + // err = n.computeExportedSessionKey() + // checkV1Value(t, "ExportedSessionKey - NTLMSSP_REQUEST_NON_NT_SESSION_KEY", n.exportedSessionKey, "55555555555555555555555555555555", err) + n.NegotiateFlags = NTLMSSP_REQUEST_NON_NT_SESSION_KEY.Unset(n.NegotiateFlags) + + // NTLMSSP_NEGOTIATE_LM_KEY is set: + n.NegotiateFlags = NTLMSSP_NEGOTIATE_LM_KEY.Set(n.NegotiateFlags) + err = n.computeKeyExchangeKey() + // n.encryptedRandomSessionKey, err = hex.DecodeString("4cd7bb57d697ef9b549f02b8f9b37864") + // err = n.computeExportedSessionKey() + // checkV1Value(t, "ExportedSessionKey - NTLMSSP_NEGOTIATE_LM_KEY", n.exportedSessionKey, "55555555555555555555555555555555", err) + n.NegotiateFlags = NTLMSSP_NEGOTIATE_LM_KEY.Unset(n.NegotiateFlags) + + // 4.2.2.3 Messages + challengeMessageBytes, _ := hex.DecodeString("4e544c4d53535000020000000c000c003800000033820a820123456789abcdef00000000000000000000000000000000060070170000000f530065007200760065007200") + challengeMessage, err := ParseChallengeMessage(challengeMessageBytes) + if err == nil { + challengeMessage.String() + } else { + t.Errorf("Could not parse challenge message: %s", err) + } + + client := new(V1ClientSession) + client.SetUserInfo("User", "Password", "Domain") + err = client.ProcessChallengeMessage(challengeMessage) + if err != nil { + t.Errorf("Could not process challenge message: %s", err) + } + + server := new(V1ServerSession) + server.SetUserInfo("User", "Password", "Domain") + authenticateMessageBytes, err := hex.DecodeString("4e544c4d5353500003000000180018006c00000018001800840000000c000c00480000000800080054000000100010005c000000100010009c000000358280e20501280a0000000f44006f006d00610069006e00550073006500720043004f004d005000550054004500520098def7b87f88aa5dafe2df779688a172def11c7d5ccdef1367c43011f30298a2ad35ece64f16331c44bdbed927841f94518822b1b3f350c8958682ecbb3e3cb7") + authenticateMessage, err := ParseAuthenticateMessage(authenticateMessageBytes, 1) + if err == nil { + authenticateMessage.String() + } else { + t.Errorf("Could not parse authenticate message: %s", err) + } + + server = new(V1ServerSession) + server.SetUserInfo("User", "Password", "Domain") + server.serverChallenge = challengeMessage.ServerChallenge + + err = server.ProcessAuthenticateMessage(authenticateMessage) + if err != nil { + t.Errorf("Could not process authenticate message: %s", err) + } +} + +func TestNTLMv1WithClientChallenge(t *testing.T) { + flags := uint32(0) + flags = NTLMSSP_NEGOTIATE_56.Set(flags) + flags = NTLMSSP_NEGOTIATE_VERSION.Set(flags) + flags = NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.Set(flags) + flags = NTLMSSP_TARGET_TYPE_SERVER.Set(flags) + flags = NTLMSSP_NEGOTIATE_ALWAYS_SIGN.Set(flags) + flags = NTLMSSP_NEGOTIATE_NTLM.Set(flags) + flags = NTLMSSP_NEGOTIATE_SEAL.Set(flags) + flags = NTLMSSP_NEGOTIATE_SIGN.Set(flags) + flags = NTLM_NEGOTIATE_OEM.Set(flags) + flags = NTLMSSP_NEGOTIATE_UNICODE.Set(flags) + + n := new(V1Session) + n.NegotiateFlags = flags + n.responseKeyNT, _ = hex.DecodeString("a4f49c406510bdcab6824ee7c30fd852") + n.responseKeyLM, _ = hex.DecodeString("e52cac67419a9a224a3b108f3fa6cb6d") + n.clientChallenge, _ = hex.DecodeString("aaaaaaaaaaaaaaaa") + n.serverChallenge, _ = hex.DecodeString("0123456789abcdef") + + var err error + // 4.2.2.1.3 Session Base Key and Key Exchange Key + err = n.computeExpectedResponses() + err = n.computeSessionBaseKey() + checkV1Value(t, "sessionBaseKey", n.sessionBaseKey, "d87262b0cde4b1cb7499becccdf10784", err) + checkV1Value(t, "LMv1Response", n.lmChallengeResponse, "aaaaaaaaaaaaaaaa00000000000000000000000000000000", err) + checkV1Value(t, "NTLMv1Response", n.ntChallengeResponse, "7537f803ae367128ca458204bde7caf81e97ed2683267232", err) + err = n.computeKeyExchangeKey() + checkV1Value(t, "keyExchangeKey", n.keyExchangeKey, "eb93429a8bd952f8b89c55b87f475edc", err) + + challengeMessageBytes, _ := hex.DecodeString("4e544c4d53535000020000000c000c003800000033820a820123456789abcdef00000000000000000000000000000000060070170000000f530065007200760065007200") + challengeMessage, err := ParseChallengeMessage(challengeMessageBytes) + if err == nil { + challengeMessage.String() + } else { + t.Errorf("Could not parse challenge message: %s", err) + } + + client := new(V1ClientSession) + client.SetUserInfo("User", "Password", "Domain") + err = client.ProcessChallengeMessage(challengeMessage) + if err != nil { + t.Errorf("Could not process challenge message: %s", err) + } + + server := new(V1ServerSession) + server.SetUserInfo("User", "Password", "Domain") + server.serverChallenge = challengeMessage.ServerChallenge + + authenticateMessageBytes, _ := hex.DecodeString("4e544c4d5353500003000000180018006c00000018001800840000000c000c00480000000800080054000000100010005c000000000000009c000000358208820501280a0000000f44006f006d00610069006e00550073006500720043004f004d0050005500540045005200aaaaaaaaaaaaaaaa000000000000000000000000000000007537f803ae367128ca458204bde7caf81e97ed2683267232") + authenticateMessage, err := ParseAuthenticateMessage(authenticateMessageBytes, 1) + if err == nil { + authenticateMessage.String() + } else { + t.Errorf("Could not parse authenticate message: %s", err) + } + + err = server.ProcessAuthenticateMessage(authenticateMessage) + if err != nil { + t.Errorf("Could not process authenticate message: %s", err) + } + + checkV1Value(t, "SealKey", server.ClientSealingKey, "04dd7f014d8504d265a25cc86a3a7c06", nil) + checkV1Value(t, "SignKey", server.ClientSigningKey, "60e799be5c72fc92922ae8ebe961fb8d", nil) +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlmv2.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlmv2.go new file mode 100644 index 00000000..cc319f3d --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlmv2.go @@ -0,0 +1,408 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "bytes" + rc4P "crypto/rc4" + "encoding/binary" + "errors" + l4g "github.com/github/git-lfs/vendor/_nuts/github.com/ThomsonReutersEikon/log4go" + "strings" + "time" +) + +/******************************* + Shared Session Data and Methods +*******************************/ + +type V2Session struct { + SessionData +} + +func (n *V2Session) SetUserInfo(username string, password string, domain string) { + n.user = username + n.password = password + n.userDomain = domain +} + +func (n *V2Session) GetUserInfo() (string, string, string) { + return n.user, n.password, n.userDomain +} + +func (n *V2Session) SetMode(mode Mode) { + n.mode = mode +} + +func (n *V2Session) Version() int { + return 2 +} + +func (n *V2Session) fetchResponseKeys() (err error) { + // Usually at this point we'd go out to Active Directory and get these keys + // Here we are assuming we have the information locally + n.responseKeyLM = lmowfv2(n.user, n.password, n.userDomain) + n.responseKeyNT = ntowfv2(n.user, n.password, n.userDomain) + return +} + +func (n *V2ServerSession) GetSessionData() *SessionData { + return &n.SessionData +} + +// Define ComputeResponse(NegFlg, ResponseKeyNT, ResponseKeyLM, CHALLENGE_MESSAGE.ServerChallenge, ClientChallenge, Time, ServerName) +// ServerNameBytes - The NtChallengeResponseFields.NTLMv2_RESPONSE.NTLMv2_CLIENT_CHALLENGE.AvPairs field structure of the AUTHENTICATE_MESSAGE payload. +func (n *V2Session) computeExpectedResponses(timestamp []byte, avPairBytes []byte) (err error) { + temp := concat([]byte{0x01}, []byte{0x01}, zeroBytes(6), timestamp, n.clientChallenge, zeroBytes(4), avPairBytes, zeroBytes(4)) + ntProofStr := hmacMd5(n.responseKeyNT, concat(n.serverChallenge, temp)) + n.ntChallengeResponse = concat(ntProofStr, temp) + n.lmChallengeResponse = concat(hmacMd5(n.responseKeyLM, concat(n.serverChallenge, n.clientChallenge)), n.clientChallenge) + n.sessionBaseKey = hmacMd5(n.responseKeyNT, ntProofStr) + return +} + +func (n *V2Session) computeKeyExchangeKey() (err error) { + n.keyExchangeKey = n.sessionBaseKey + return +} + +func (n *V2Session) calculateKeys(ntlmRevisionCurrent uint8) (err error) { + // This lovely piece of code comes courtesy of an the excellent Open Document support system from MSFT + // In order to calculate the keys correctly when the client has set the NTLMRevisionCurrent to 0xF (15) + // We must treat the flags as if NTLMSSP_NEGOTIATE_LM_KEY is set. + // This information is not contained (at least currently, until they correct it) in the MS-NLMP document + if ntlmRevisionCurrent == 15 { + n.NegotiateFlags = NTLMSSP_NEGOTIATE_LM_KEY.Set(n.NegotiateFlags) + } + + n.ClientSigningKey = signKey(n.NegotiateFlags, n.exportedSessionKey, "Client") + n.ServerSigningKey = signKey(n.NegotiateFlags, n.exportedSessionKey, "Server") + n.ClientSealingKey = sealKey(n.NegotiateFlags, n.exportedSessionKey, "Client") + n.ServerSealingKey = sealKey(n.NegotiateFlags, n.exportedSessionKey, "Server") + return +} + +func (n *V2Session) Seal(message []byte) ([]byte, error) { + return nil, nil +} +func (n *V2Session) Sign(message []byte) ([]byte, error) { + return nil, nil +} + +//Mildly ghetto that we expose this +func NtlmVCommonMac(message []byte, sequenceNumber int, sealingKey, signingKey []byte, NegotiateFlags uint32) []byte { + var handle *rc4P.Cipher + // TODO: Need to keep track of the sequence number for connection oriented NTLM + if NTLMSSP_NEGOTIATE_DATAGRAM.IsSet(NegotiateFlags) && NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.IsSet(NegotiateFlags) { + handle, _ = reinitSealingKey(sealingKey, sequenceNumber) + } else if NTLMSSP_NEGOTIATE_DATAGRAM.IsSet(NegotiateFlags) { + // CONOR: Reinitializing the rc4 cipher on every requst, but not using the + // algorithm as described in the MS-NTLM document. Just reinitialize it directly. + handle, _ = rc4Init(sealingKey) + } + sig := mac(NegotiateFlags, handle, signingKey, uint32(sequenceNumber), message) + return sig.Bytes() +} + +func NtlmV2Mac(message []byte, sequenceNumber int, handle *rc4P.Cipher, sealingKey, signingKey []byte, NegotiateFlags uint32) []byte { + // TODO: Need to keep track of the sequence number for connection oriented NTLM + if NTLMSSP_NEGOTIATE_DATAGRAM.IsSet(NegotiateFlags) && NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.IsSet(NegotiateFlags) { + handle, _ = reinitSealingKey(sealingKey, sequenceNumber) + } else if NTLMSSP_NEGOTIATE_DATAGRAM.IsSet(NegotiateFlags) { + // CONOR: Reinitializing the rc4 cipher on every requst, but not using the + // algorithm as described in the MS-NTLM document. Just reinitialize it directly. + handle, _ = rc4Init(sealingKey) + } + sig := mac(NegotiateFlags, handle, signingKey, uint32(sequenceNumber), message) + return sig.Bytes() +} + +func (n *V2ServerSession) Mac(message []byte, sequenceNumber int) ([]byte, error) { + mac := NtlmV2Mac(message, sequenceNumber, n.serverHandle, n.ServerSealingKey, n.ServerSigningKey, n.NegotiateFlags) + return mac, nil +} + +func (n *V2ServerSession) VerifyMac(message, expectedMac []byte, sequenceNumber int) (bool, error) { + mac := NtlmV2Mac(message, sequenceNumber, n.clientHandle, n.ClientSealingKey, n.ClientSigningKey, n.NegotiateFlags) + return MacsEqual(mac, expectedMac), nil +} + +func (n *V2ClientSession) Mac(message []byte, sequenceNumber int) ([]byte, error) { + mac := NtlmV2Mac(message, sequenceNumber, n.clientHandle, n.ClientSealingKey, n.ClientSigningKey, n.NegotiateFlags) + return mac, nil +} + +func (n *V2ClientSession) VerifyMac(message, expectedMac []byte, sequenceNumber int) (bool, error) { + mac := NtlmV2Mac(message, sequenceNumber, n.serverHandle, n.ServerSealingKey, n.ServerSigningKey, n.NegotiateFlags) + return MacsEqual(mac, expectedMac), nil +} + +/************** + Server Session +**************/ + +type V2ServerSession struct { + V2Session +} + +func (n *V2ServerSession) SetServerChallenge(challenge []byte) { + n.serverChallenge = challenge +} + +func (n *V2ServerSession) ProcessNegotiateMessage(nm *NegotiateMessage) (err error) { + n.negotiateMessage = nm + return +} + +func (n *V2ServerSession) GenerateChallengeMessage() (cm *ChallengeMessage, err error) { + cm = new(ChallengeMessage) + cm.Signature = []byte("NTLMSSP\x00") + cm.MessageType = uint32(2) + cm.TargetName, _ = CreateBytePayload(make([]byte, 0)) + + flags := uint32(0) + flags = NTLMSSP_NEGOTIATE_KEY_EXCH.Set(flags) + flags = NTLMSSP_NEGOTIATE_VERSION.Set(flags) + flags = NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.Set(flags) + flags = NTLMSSP_NEGOTIATE_TARGET_INFO.Set(flags) + flags = NTLMSSP_NEGOTIATE_IDENTIFY.Set(flags) + flags = NTLMSSP_NEGOTIATE_ALWAYS_SIGN.Set(flags) + flags = NTLMSSP_NEGOTIATE_NTLM.Set(flags) + flags = NTLMSSP_NEGOTIATE_DATAGRAM.Set(flags) + flags = NTLMSSP_NEGOTIATE_SIGN.Set(flags) + flags = NTLMSSP_REQUEST_TARGET.Set(flags) + flags = NTLMSSP_NEGOTIATE_UNICODE.Set(flags) + flags = NTLMSSP_NEGOTIATE_128.Set(flags) + + cm.NegotiateFlags = flags + + n.serverChallenge = randomBytes(8) + cm.ServerChallenge = n.serverChallenge + cm.Reserved = make([]byte, 8) + + // Create the AvPairs we need + pairs := new(AvPairs) + pairs.AddAvPair(MsvAvNbDomainName, utf16FromString("REUTERS")) + pairs.AddAvPair(MsvAvNbComputerName, utf16FromString("UKBP-CBTRMFE06")) + pairs.AddAvPair(MsvAvDnsDomainName, utf16FromString("Reuters.net")) + pairs.AddAvPair(MsvAvDnsComputerName, utf16FromString("ukbp-cbtrmfe06.Reuters.net")) + pairs.AddAvPair(MsvAvDnsTreeName, utf16FromString("Reuters.net")) + pairs.AddAvPair(MsvAvEOL, make([]byte, 0)) + cm.TargetInfo = pairs + cm.TargetInfoPayloadStruct, _ = CreateBytePayload(pairs.Bytes()) + + cm.Version = &VersionStruct{ProductMajorVersion: uint8(5), ProductMinorVersion: uint8(1), ProductBuild: uint16(2600), NTLMRevisionCurrent: uint8(15)} + return cm, nil +} + +func (n *V2ServerSession) ProcessAuthenticateMessage(am *AuthenticateMessage) (err error) { + n.authenticateMessage = am + n.NegotiateFlags = am.NegotiateFlags + n.clientChallenge = am.ClientChallenge() + n.encryptedRandomSessionKey = am.EncryptedRandomSessionKey.Payload + // Ignore the values used in SetUserInfo and use these instead from the authenticate message + // They should always be correct (I hope) + n.user = am.UserName.String() + n.userDomain = am.DomainName.String() + l4g.Info("(ProcessAuthenticateMessage)NTLM v2 User %s Domain %s", n.user, n.userDomain) + + err = n.fetchResponseKeys() + if err != nil { + return err + } + + timestamp := am.NtlmV2Response.NtlmV2ClientChallenge.TimeStamp + avPairsBytes := am.NtlmV2Response.NtlmV2ClientChallenge.AvPairs.Bytes() + + err = n.computeExpectedResponses(timestamp, avPairsBytes) + if err != nil { + return err + } + + if !bytes.Equal(am.NtChallengeResponseFields.Payload, n.ntChallengeResponse) { + if !bytes.Equal(am.LmChallengeResponse.Payload, n.lmChallengeResponse) { + return errors.New("Could not authenticate") + } + } + + err = n.computeKeyExchangeKey() + if err != nil { + return err + } + + n.mic = am.Mic + am.Mic = zeroBytes(16) + + err = n.computeExportedSessionKey() + if err != nil { + return err + } + + if am.Version == nil { + //UGH not entirely sure how this could possibly happen, going to put this in for now + //TODO investigate if this ever is really happening + am.Version = &VersionStruct{ProductMajorVersion: uint8(5), ProductMinorVersion: uint8(1), ProductBuild: uint16(2600), NTLMRevisionCurrent: uint8(15)} + + l4g.Error("Nil version in ntlmv2") + } + + err = n.calculateKeys(am.Version.NTLMRevisionCurrent) + if err != nil { + return err + } + + n.clientHandle, err = rc4Init(n.ClientSealingKey) + if err != nil { + return err + } + n.serverHandle, err = rc4Init(n.ServerSealingKey) + if err != nil { + return err + } + + return nil +} + +func (n *V2ServerSession) computeExportedSessionKey() (err error) { + if NTLMSSP_NEGOTIATE_KEY_EXCH.IsSet(n.NegotiateFlags) { + n.exportedSessionKey, err = rc4K(n.keyExchangeKey, n.encryptedRandomSessionKey) + if err != nil { + return err + } + // TODO: Calculate mic correctly. This calculation is not producing the right results now + // n.calculatedMic = HmacMd5(n.exportedSessionKey, concat(n.challengeMessage.Payload, n.authenticateMessage.Bytes)) + } else { + n.exportedSessionKey = n.keyExchangeKey + // TODO: Calculate mic correctly. This calculation is not producing the right results now + // n.calculatedMic = HmacMd5(n.keyExchangeKey, concat(n.challengeMessage.Payload, n.authenticateMessage.Bytes)) + } + return nil +} + +/************* + Client Session +**************/ + +type V2ClientSession struct { + V2Session +} + +func (n *V2ClientSession) GenerateNegotiateMessage() (nm *NegotiateMessage, err error) { + return nil, nil +} + +func (n *V2ClientSession) ProcessChallengeMessage(cm *ChallengeMessage) (err error) { + n.challengeMessage = cm + n.serverChallenge = cm.ServerChallenge + n.clientChallenge = randomBytes(8) + + // Set up the default flags for processing the response. These are the flags that we will return + // in the authenticate message + flags := uint32(0) + flags = NTLMSSP_NEGOTIATE_KEY_EXCH.Set(flags) + flags = NTLMSSP_NEGOTIATE_VERSION.Set(flags) + flags = NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.Set(flags) + flags = NTLMSSP_NEGOTIATE_TARGET_INFO.Set(flags) + flags = NTLMSSP_NEGOTIATE_IDENTIFY.Set(flags) + flags = NTLMSSP_NEGOTIATE_ALWAYS_SIGN.Set(flags) + flags = NTLMSSP_NEGOTIATE_NTLM.Set(flags) + flags = NTLMSSP_NEGOTIATE_DATAGRAM.Set(flags) + flags = NTLMSSP_NEGOTIATE_SIGN.Set(flags) + flags = NTLMSSP_REQUEST_TARGET.Set(flags) + flags = NTLMSSP_NEGOTIATE_UNICODE.Set(flags) + flags = NTLMSSP_NEGOTIATE_128.Set(flags) + + n.NegotiateFlags = flags + + err = n.fetchResponseKeys() + if err != nil { + return err + } + + timestamp := timeToWindowsFileTime(time.Now()) + err = n.computeExpectedResponses(timestamp, cm.TargetInfoPayloadStruct.Payload) + if err != nil { + return err + } + + err = n.computeKeyExchangeKey() + if err != nil { + return err + } + + err = n.computeEncryptedSessionKey() + if err != nil { + return err + } + + err = n.calculateKeys(cm.Version.NTLMRevisionCurrent) + if err != nil { + return err + } + + n.clientHandle, err = rc4Init(n.ClientSealingKey) + if err != nil { + return err + } + n.serverHandle, err = rc4Init(n.ServerSealingKey) + if err != nil { + return err + } + return nil +} + +func (n *V2ClientSession) GenerateAuthenticateMessage() (am *AuthenticateMessage, err error) { + am = new(AuthenticateMessage) + am.Signature = []byte("NTLMSSP\x00") + am.MessageType = uint32(3) + am.LmChallengeResponse, _ = CreateBytePayload(n.lmChallengeResponse) + am.NtChallengeResponseFields, _ = CreateBytePayload(n.ntChallengeResponse) + am.DomainName, _ = CreateStringPayload(n.userDomain) + am.UserName, _ = CreateStringPayload(n.user) + am.Workstation, _ = CreateStringPayload("SQUAREMILL") + am.EncryptedRandomSessionKey, _ = CreateBytePayload(n.encryptedRandomSessionKey) + am.NegotiateFlags = n.NegotiateFlags + am.Mic = make([]byte, 16) + am.Version = &VersionStruct{ProductMajorVersion: uint8(5), ProductMinorVersion: uint8(1), ProductBuild: uint16(2600), NTLMRevisionCurrent: 0x0F} + return am, nil +} + +func (n *V2ClientSession) computeEncryptedSessionKey() (err error) { + if NTLMSSP_NEGOTIATE_KEY_EXCH.IsSet(n.NegotiateFlags) { + n.exportedSessionKey = randomBytes(16) + n.encryptedRandomSessionKey, err = rc4K(n.keyExchangeKey, n.exportedSessionKey) + if err != nil { + return err + } + } else { + n.encryptedRandomSessionKey = n.keyExchangeKey + } + return nil +} + +/******************************** + NTLM V2 Password hash functions +*********************************/ + +// Define ntowfv2(Passwd, User, UserDom) as +func ntowfv2(user string, passwd string, userDom string) []byte { + concat := utf16FromString(strings.ToUpper(user) + userDom) + return hmacMd5(md4(utf16FromString(passwd)), concat) +} + +// Define lmowfv2(Passwd, User, UserDom) as +func lmowfv2(user string, passwd string, userDom string) []byte { + return ntowfv2(user, passwd, userDom) +} + +/******************************** + Helper functions +*********************************/ + +func timeToWindowsFileTime(t time.Time) []byte { + var ll int64 + ll = (int64(t.Unix()) * int64(10000000)) + int64(116444736000000000) + buffer := bytes.NewBuffer(make([]byte, 0, 8)) + binary.Write(buffer, binary.LittleEndian, ll) + return buffer.Bytes() +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlmv2_test.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlmv2_test.go new file mode 100644 index 00000000..0ec0532f --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/ntlmv2_test.go @@ -0,0 +1,189 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "bytes" + "encoding/base64" + "encoding/hex" + "strings" + "testing" + "time" +) + +func checkV2Value(t *testing.T, name string, value []byte, expected string, err error) { + if err != nil { + t.Errorf("NTLMv2 %s received error: %s", name, err) + } else { + expectedBytes, _ := hex.DecodeString(expected) + if !bytes.Equal(expectedBytes, value) { + t.Errorf("NTLMv2 %s is not correct got %s expected %s", name, hex.EncodeToString(value), expected) + } + } +} + +func TestNTOWFv2(t *testing.T) { + result := ntowfv2("User", "Password", "Domain") + // Sample value from 4.2.4.1.1 in MS-NLMP + expected, _ := hex.DecodeString("0c868a403bfd7a93a3001ef22ef02e3f") + if !bytes.Equal(result, expected) { + t.Errorf("NTOWFv2 is not correct got %s expected %s", hex.EncodeToString(result), "0c868a403bfd7a93a3001ef22ef02e3f") + } +} + +func TestNTLMv2(t *testing.T) { + flags := uint32(0) + flags = NTLMSSP_NEGOTIATE_KEY_EXCH.Set(flags) + flags = NTLMSSP_NEGOTIATE_56.Set(flags) + flags = NTLMSSP_NEGOTIATE_128.Set(flags) + flags = NTLMSSP_NEGOTIATE_VERSION.Set(flags) + flags = NTLMSSP_NEGOTIATE_TARGET_INFO.Set(flags) + flags = NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.Set(flags) + flags = NTLMSSP_TARGET_TYPE_SERVER.Set(flags) + flags = NTLMSSP_NEGOTIATE_ALWAYS_SIGN.Set(flags) + flags = NTLMSSP_NEGOTIATE_NTLM.Set(flags) + flags = NTLMSSP_NEGOTIATE_SEAL.Set(flags) + flags = NTLMSSP_NEGOTIATE_SIGN.Set(flags) + flags = NTLM_NEGOTIATE_OEM.Set(flags) + flags = NTLMSSP_NEGOTIATE_UNICODE.Set(flags) + + // n := new(V2Session) + // n.SetUserInfo("User","Password","Domain") + // n.NegotiateFlags = flags + // n.responseKeyNT, _ = hex.DecodeString("0c868a403bfd7a93a3001ef22ef02e3f") + // n.responseKeyLM = n.responseKeyNT + // n.clientChallenge, _ = hex.DecodeString("aaaaaaaaaaaaaaaa") + // n.serverChallenge, _ = hex.DecodeString("0123456789abcdef") + + // Encrypted Random Session key + //c5 da d2 54 4f c9 79 90 94 ce 1c e9 0b c9 d0 3e + + // Challenge message + client := new(V2ClientSession) + client.SetUserInfo("User", "Password", "Domain") + + challengeMessageBytes, _ := hex.DecodeString("4e544c4d53535000020000000c000c003800000033828ae20123456789abcdef00000000000000002400240044000000060070170000000f53006500720076006500720002000c0044006f006d00610069006e0001000c0053006500720076006500720000000000") + challengeMessage, err := ParseChallengeMessage(challengeMessageBytes) + if err == nil { + challengeMessage.String() + } else { + t.Errorf("Could not parse challenge message: %s", err) + } + + err = client.ProcessChallengeMessage(challengeMessage) + if err != nil { + t.Errorf("Could not process challenge message: %s", err) + } + + server := new(V2ServerSession) + server.SetUserInfo("User", "Password", "Domain") + server.serverChallenge = challengeMessage.ServerChallenge + + // Authenticate message + r := strings.NewReplacer("\n", "", "\t", "", " ", "") + authenticateMessageBytes, _ := hex.DecodeString(r.Replace(` + 4e544c4d535350000300000018001800 + 6c00000054005400840000000c000c00 + 48000000080008005400000010001000 + 5c00000010001000d8000000358288e2 + 0501280a0000000f44006f006d006100 + 69006e00550073006500720043004f00 + 4d005000550054004500520086c35097 + ac9cec102554764a57cccc19aaaaaaaa + aaaaaaaa68cd0ab851e51c96aabc927b + ebef6a1c010100000000000000000000 + 00000000aaaaaaaaaaaaaaaa00000000 + 02000c0044006f006d00610069006e00 + 01000c00530065007200760065007200 + 0000000000000000c5dad2544fc97990 + 94ce1ce90bc9d03e`)) + + authenticateMessage, err := ParseAuthenticateMessage(authenticateMessageBytes, 2) + if err == nil { + authenticateMessage.String() + } else { + t.Errorf("Could not parse authenticate message: %s", err) + } + + err = server.ProcessAuthenticateMessage(authenticateMessage) + if err != nil { + t.Errorf("Could not process authenticate message: %s", err) + } + + checkV2Value(t, "SessionBaseKey", server.sessionBaseKey, "8de40ccadbc14a82f15cb0ad0de95ca3", nil) + checkV2Value(t, "NTChallengeResponse", server.ntChallengeResponse[0:16], "68cd0ab851e51c96aabc927bebef6a1c", nil) + checkV2Value(t, "LMChallengeResponse", server.lmChallengeResponse, "86c35097ac9cec102554764a57cccc19aaaaaaaaaaaaaaaa", nil) + + checkV2Value(t, "client seal key", server.ClientSealingKey, "59f600973cc4960a25480a7c196e4c58", nil) + checkV2Value(t, "client signing key", server.ClientSigningKey, "4788dc861b4782f35d43fd98fe1a2d39", nil) + + // Have the server generate an initial challenge message + challenge, err := server.GenerateChallengeMessage() + challenge.String() + + // Have the client process this server challenge message + client = new(V2ClientSession) + client.SetUserInfo("User", "Password", "Domain") + err = client.ProcessChallengeMessage(challenge) + if err != nil { + t.Errorf("Could not process server generated challenge message: %s", err) + } + // TODO: we should be able to use the ntlm library end to end to make sure + // that Mac, VerifyMac + + // // the client should be able to verify the server's mac + // sig := "" + // mac, err := server.Mac([]byte(sig), 100) + // if err != nil { + // t.Errorf("Could not generate a mac for %s", sig) + // } + // matches, err := client.VerifyMac([]byte(sig), mac, 100) + // if err != nil { + // t.Errorf("Could not verify mac for %s (mac = %v)", sig, mac) + // } + // if !matches { + // t.Errorf("Server's Mac couldn't be verified by client") + // } + + // mac, err = client.Mac([]byte(sig), 100) + // if err != nil { + // t.Errorf("Could not generate a mac for %s", sig) + // } + // matches, err = server.VerifyMac([]byte(sig), mac, 100) + // if err != nil { + // t.Errorf("Could not verify mac for %s (mac = %v)", sig, mac) + // } + // if !matches { + // t.Errorf("Client's Mac couldn't be verified by server") + // } +} + +func TestNTLMv2WithDomain(t *testing.T) { + authenticateMessage := "TlRMTVNTUAADAAAAGAAYALYAAADSANIAzgAAADQANABIAAAAIAAgAHwAAAAaABoAnAAAABAAEACgAQAAVYKQQgUCzg4AAAAPYQByAHIAYQB5ADEAMgAuAG0AcwBnAHQAcwB0AC4AcgBlAHUAdABlAHIAcwAuAGMAbwBtAHUAcwBlAHIAcwB0AHIAZQBzAHMAMQAwADAAMAAwADgATgBZAEMAVgBBADEAMgBTADIAQwBNAFMAQQBPYrLjU4h0YlWZeEoNvTJtBQMnnJuAeUwsP+vGmAHNRBpgZ+4ChQLqAQEAAAAAAACPFEIFjx7OAQUDJ5ybgHlMAAAAAAIADgBSAEUAVQBUAEUAUgBTAAEAHABVAEsAQgBQAC0AQwBCAFQAUgBNAEYARQAwADYABAAWAFIAZQB1AHQAZQByAHMALgBuAGUAdAADADQAdQBrAGIAcAAtAGMAYgB0AHIAbQBmAGUAMAA2AC4AUgBlAHUAdABlAHIAcwAuAG4AZQB0AAUAFgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQAAAAAAAAAAAANuvnqD3K88ZpjkLleL0NW" + + server := new(V2ServerSession) + server.SetUserInfo("blahblah", "Welcome1", "blahblah") + + authenticateData, _ := base64.StdEncoding.DecodeString(authenticateMessage) + a, _ := ParseAuthenticateMessage(authenticateData, 2) + + serverChallenge, _ := hex.DecodeString("3d74b2d04ebe1eb3") + server.SetServerChallenge(serverChallenge) + + err := server.ProcessAuthenticateMessage(a) + if err != nil { + t.Error("Could not process authenticate message: %s\n", err) + } +} + +func TestWindowsTimeConversion(t *testing.T) { + // From http://davenport.sourceforge.net/ntlm.html#theType3Message + // Next, the blob is constructed. The timestamp is the most tedious part of this; looking at the clock on my desk, + // it's about 6:00 AM EDT on June 17th, 2003. In Unix time, that would be 1055844000 seconds after the Epoch. + // Adding 11644473600 will give us seconds after January 1, 1601 (12700317600). Multiplying by 107 (10000000) + // will give us tenths of a microsecond (127003176000000000). As a little-endian 64-bit value, this is + // "0x0090d336b734c301" (in hexadecimal). + unix := time.Unix(1055844000, 0) + result := timeToWindowsFileTime(unix) + checkV2Value(t, "Timestamp", result, "0090d336b734c301", nil) +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/payload.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/payload.go new file mode 100644 index 00000000..89b14910 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/payload.go @@ -0,0 +1,94 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "bytes" + "encoding/binary" + "encoding/hex" +) + +const ( + UnicodeStringPayload = iota + OemStringPayload + BytesPayload +) + +type PayloadStruct struct { + Type int + Len uint16 + MaxLen uint16 + Offset uint32 + Payload []byte +} + +func (p *PayloadStruct) Bytes() []byte { + dest := make([]byte, 0, 8) + buffer := bytes.NewBuffer(dest) + + binary.Write(buffer, binary.LittleEndian, p.Len) + binary.Write(buffer, binary.LittleEndian, p.MaxLen) + binary.Write(buffer, binary.LittleEndian, p.Offset) + + return buffer.Bytes() +} + +func (p *PayloadStruct) String() string { + var returnString string + + switch p.Type { + case UnicodeStringPayload: + returnString = utf16ToString(p.Payload) + case OemStringPayload: + returnString = string(p.Payload) + case BytesPayload: + returnString = hex.EncodeToString(p.Payload) + default: + returnString = "unknown type" + } + return returnString +} + +func CreateBytePayload(bytes []byte) (*PayloadStruct, error) { + p := new(PayloadStruct) + p.Type = BytesPayload + p.Len = uint16(len(bytes)) + p.MaxLen = uint16(len(bytes)) + p.Payload = bytes // TODO: Copy these bytes instead of keeping a reference + return p, nil +} + +func CreateStringPayload(value string) (*PayloadStruct, error) { + // Create UTF16 unicode bytes from string + bytes := utf16FromString(value) + p := new(PayloadStruct) + p.Type = UnicodeStringPayload + p.Len = uint16(len(bytes)) + p.MaxLen = uint16(len(bytes)) + p.Payload = bytes // TODO: Copy these bytes instead of keeping a reference + return p, nil +} + +func ReadStringPayload(startByte int, bytes []byte) (*PayloadStruct, error) { + return ReadPayloadStruct(startByte, bytes, UnicodeStringPayload) +} + +func ReadBytePayload(startByte int, bytes []byte) (*PayloadStruct, error) { + return ReadPayloadStruct(startByte, bytes, BytesPayload) +} + +func ReadPayloadStruct(startByte int, bytes []byte, PayloadType int) (*PayloadStruct, error) { + p := new(PayloadStruct) + + p.Type = PayloadType + p.Len = binary.LittleEndian.Uint16(bytes[startByte : startByte+2]) + p.MaxLen = binary.LittleEndian.Uint16(bytes[startByte+2 : startByte+4]) + p.Offset = binary.LittleEndian.Uint32(bytes[startByte+4 : startByte+8]) + + if p.Len > 0 { + endOffset := p.Offset + uint32(p.Len) + p.Payload = bytes[p.Offset:endOffset] + } + + return p, nil +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/signature.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/signature.go new file mode 100644 index 00000000..9ae07cc3 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/signature.go @@ -0,0 +1,120 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + rc4P "crypto/rc4" + "encoding/binary" + "encoding/hex" + "fmt" +) + +type NtlmsspMessageSignature struct { + ByteData []byte + // A 32-bit unsigned integer that contains the signature version. This field MUST be 0x00000001. + Version []byte + // A 4-byte array that contains the random pad for the message. + RandomPad []byte + // A 4-byte array that contains the checksum for the message. + CheckSum []byte + // A 32-bit unsigned integer that contains the NTLM sequence number for this application message. + SeqNum []byte +} + +func (n *NtlmsspMessageSignature) String() string { + return fmt.Sprintf("NtlmsspMessageSignature: %s", hex.EncodeToString(n.Bytes())) +} + +func (n *NtlmsspMessageSignature) Bytes() []byte { + if n.ByteData != nil { + return n.ByteData + } else { + return concat(n.Version, n.RandomPad, n.CheckSum, n.SeqNum) + } + return nil +} + +// Define SEAL(Handle, SigningKey, SeqNum, Message) as +func seal(negFlags uint32, handle *rc4P.Cipher, signingKey []byte, seqNum uint32, message []byte) (sealedMessage []byte, sig *NtlmsspMessageSignature) { + sealedMessage = rc4(handle, message) + sig = mac(negFlags, handle, signingKey, uint32(seqNum), message) + return +} + +// Define SIGN(Handle, SigningKey, SeqNum, Message) as +func sign(negFlags uint32, handle *rc4P.Cipher, signingKey []byte, seqNum uint32, message []byte) []byte { + return concat(message, mac(negFlags, handle, signingKey, uint32(seqNum), message).Bytes()) +} + +func mac(negFlags uint32, handle *rc4P.Cipher, signingKey []byte, seqNum uint32, message []byte) (result *NtlmsspMessageSignature) { + if NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.IsSet(negFlags) { + result = macWithExtendedSessionSecurity(negFlags, handle, signingKey, seqNum, message) + } else { + result = macWithoutExtendedSessionSecurity(handle, seqNum, message) + } + return result +} + +// Define MAC(Handle, SigningKey, SeqNum, Message) as +// Set NTLMSSP_MESSAGE_SIGNATURE.Version to 0x00000001 +// Set NTLMSSP_MESSAGE_SIGNATURE.Checksum to CRC32(Message) +// Set NTLMSSP_MESSAGE_SIGNATURE.RandomPad RC4(Handle, RandomPad) +// Set NTLMSSP_MESSAGE_SIGNATURE.Checksum to RC4(Handle, NTLMSSP_MESSAGE_SIGNATURE.Checksum) +// Set NTLMSSP_MESSAGE_SIGNATURE.SeqNum to RC4(Handle, 0x00000000) +// If (connection oriented) +// Set NTLMSSP_MESSAGE_SIGNATURE.SeqNum to NTLMSSP_MESSAGE_SIGNATURE.SeqNum XOR SeqNum +// Set SeqNum to SeqNum + 1 +// Else +// Set NTLMSSP_MESSAGE_SIGNATURE.SeqNum to NTLMSSP_MESSAGE_SIGNATURE.SeqNum XOR (application supplied SeqNum) +// EndIf +// Set NTLMSSP_MESSAGE_SIGNATURE.RandomPad to 0 +// End +func macWithoutExtendedSessionSecurity(handle *rc4P.Cipher, seqNum uint32, message []byte) *NtlmsspMessageSignature { + sig := new(NtlmsspMessageSignature) + + seqNumBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(seqNumBytes, seqNum) + + sig.Version = []byte{0x01, 0x00, 0x00, 0x00} + sig.CheckSum = make([]byte, 4) + binary.LittleEndian.PutUint32(sig.CheckSum, crc32(message)) + sig.RandomPad = rc4(handle, zeroBytes(4)) + sig.CheckSum = rc4(handle, sig.CheckSum) + sig.SeqNum = rc4(handle, zeroBytes(4)) + for i := 0; i < 4; i++ { + sig.SeqNum[i] = sig.SeqNum[i] ^ seqNumBytes[i] + } + sig.RandomPad = zeroBytes(4) + return sig +} + +// Define MAC(Handle, SigningKey, SeqNum, Message) as +// Set NTLMSSP_MESSAGE_SIGNATURE.Version to 0x00000001 +// if Key Exchange Key Negotiated +// Set NTLMSSP_MESSAGE_SIGNATURE.Checksum to RC4(Handle, HMAC_MD5(SigningKey, ConcatenationOf(SeqNum, Message))[0..7]) +// else +// Set NTLMSSP_MESSAGE_SIGNATURE.Checksum to HMAC_MD5(SigningKey, ConcatenationOf(SeqNum, Message))[0..7] +// end +// Set NTLMSSP_MESSAGE_SIGNATURE.SeqNum to SeqNum +// Set SeqNum to SeqNum + 1 +// EndDefine +func macWithExtendedSessionSecurity(negFlags uint32, handle *rc4P.Cipher, signingKey []byte, seqNum uint32, message []byte) *NtlmsspMessageSignature { + sig := new(NtlmsspMessageSignature) + sig.Version = []byte{0x01, 0x00, 0x00, 0x00} + seqNumBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(seqNumBytes, seqNum) + sig.CheckSum = hmacMd5(signingKey, concat(seqNumBytes, message))[0:8] + if NTLMSSP_NEGOTIATE_KEY_EXCH.IsSet(negFlags) { + sig.CheckSum = rc4(handle, sig.CheckSum) + } + sig.SeqNum = seqNumBytes + return sig +} + +func reinitSealingKey(key []byte, sequenceNumber int) (handle *rc4P.Cipher, err error) { + seqNumBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(seqNumBytes, uint32(sequenceNumber)) + newKey := md5(concat(key, seqNumBytes)) + handle, err = rc4Init(newKey) + return handle, err +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/signature_test.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/signature_test.go new file mode 100644 index 00000000..4f7792e7 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/signature_test.go @@ -0,0 +1,66 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "bytes" + "encoding/hex" + "testing" +) + +func checkSigValue(t *testing.T, name string, value []byte, expected string, err error) { + if err != nil { + t.Errorf("Signature %s received error: %s", name, err) + } else { + expectedBytes, _ := hex.DecodeString(expected) + if !bytes.Equal(expectedBytes, value) { + t.Errorf("Signature %s is not correct got %s expected %s", name, hex.EncodeToString(value), expected) + } + } +} + +// 4.2.2.4 GSS_WrapEx Examples +func TestSealWithoutExtendedSessionSecurity(t *testing.T) { + key, _ := hex.DecodeString("55555555555555555555555555555555") + handle, _ := rc4Init(key) + plaintext, _ := hex.DecodeString("50006c00610069006e007400650078007400") + seqNum := uint32(0) + flags := uint32(0) + + sealed, sig := seal(flags, handle, nil, seqNum, plaintext) + checkSigValue(t, "Sealed message", sealed, "56fe04d861f9319af0d7238a2e3b4d457fb8", nil) + checkSigValue(t, "Randompad", sig.RandomPad, "00000000", nil) + checkSigValue(t, "RC4 Checksum", sig.CheckSum, "09dcd1df", nil) + checkSigValue(t, "Xor Seq", sig.SeqNum, "2e459d36", nil) +} + +func TestSealSignWithExtendedSessionSecurity(t *testing.T) { + sealKey, _ := hex.DecodeString("04dd7f014d8504d265a25cc86a3a7c06") + signKey, _ := hex.DecodeString("60e799be5c72fc92922ae8ebe961fb8d") + handle, _ := rc4Init(sealKey) + plaintext, _ := hex.DecodeString("50006c00610069006e007400650078007400") + seqNum := uint32(0) + flags := uint32(0) + flags = NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.Set(flags) + + sealed, sig := seal(flags, handle, signKey, seqNum, plaintext) + checkSigValue(t, "Sealed Data", sealed, "a02372f6530273f3aa1eb90190ce5200c99d", nil) + checkSigValue(t, "CheckSum", sig.CheckSum, "ff2aeb52f681793a", nil) + checkSigValue(t, "Signature", sig.Bytes(), "01000000ff2aeb52f681793a00000000", nil) +} + +func TestSealSignWithExtendedSessionSecurityKeyEx(t *testing.T) { + sealKey, _ := hex.DecodeString("59f600973cc4960a25480a7c196e4c58") + signKey, _ := hex.DecodeString("4788dc861b4782f35d43fd98fe1a2d39") + handle, _ := rc4Init(sealKey) + plaintext, _ := hex.DecodeString("50006c00610069006e007400650078007400") + seqNum := uint32(0) + flags := uint32(0) + flags = NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.Set(flags) + flags = NTLMSSP_NEGOTIATE_KEY_EXCH.Set(flags) + + sealed, sig := seal(flags, handle, signKey, seqNum, plaintext) + checkSigValue(t, "Sealed Data", sealed, "54e50165bf1936dc996020c1811b0f06fb5f", nil) + checkSigValue(t, "RC4 CheckSum", sig.CheckSum, "7fb38ec5c55d4976", nil) + checkSigValue(t, "Signature", sig.Bytes(), "010000007fb38ec5c55d497600000000", nil) +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/version.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/version.go new file mode 100644 index 00000000..78026cf4 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm/version.go @@ -0,0 +1,46 @@ +//Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information + +package ntlm + +import ( + "bytes" + "encoding/binary" + "fmt" +) + +type VersionStruct struct { + ProductMajorVersion uint8 + ProductMinorVersion uint8 + ProductBuild uint16 + Reserved []byte + NTLMRevisionCurrent uint8 +} + +func ReadVersionStruct(structSource []byte) (*VersionStruct, error) { + versionStruct := new(VersionStruct) + + versionStruct.ProductMajorVersion = uint8(structSource[0]) + versionStruct.ProductMinorVersion = uint8(structSource[1]) + versionStruct.ProductBuild = binary.LittleEndian.Uint16(structSource[2:4]) + versionStruct.Reserved = structSource[4:7] + versionStruct.NTLMRevisionCurrent = uint8(structSource[7]) + + return versionStruct, nil +} + +func (v *VersionStruct) String() string { + return fmt.Sprintf("%d.%d.%d Ntlm %d", v.ProductMajorVersion, v.ProductMinorVersion, v.ProductBuild, v.NTLMRevisionCurrent) +} + +func (v *VersionStruct) Bytes() []byte { + dest := make([]byte, 0, 8) + buffer := bytes.NewBuffer(dest) + + binary.Write(buffer, binary.LittleEndian, v.ProductMajorVersion) + binary.Write(buffer, binary.LittleEndian, v.ProductMinorVersion) + binary.Write(buffer, binary.LittleEndian, v.ProductBuild) + buffer.Write(make([]byte, 3)) + binary.Write(buffer, binary.LittleEndian, uint8(v.NTLMRevisionCurrent)) + + return buffer.Bytes() +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/utils/decode_auth.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/utils/decode_auth.go new file mode 100644 index 00000000..d2d1d1f5 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/utils/decode_auth.go @@ -0,0 +1,18 @@ +package main + +import ( + "encoding/base64" + "flag" + "fmt" +) + +func main() { + var ntlmVersion = flag.Int("ntlm", 2, "NTLM version to try: 1 or 2") + flag.Parse() + var data string + fmt.Println("Paste the base64 encoded Authenticate message (with no line breaks):") + fmt.Scanf("%s", &data) + authenticateData, _ := base64.StdEncoding.DecodeString(data) + a, _ := ntlm.ParseAuthenticateMessage(authenticateData, *ntlmVersion) + fmt.Printf(a.String()) +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/utils/test_auth.go b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/utils/test_auth.go new file mode 100644 index 00000000..2338ba10 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/utils/test_auth.go @@ -0,0 +1,87 @@ +package main + +import ( + "encoding/base64" + "fmt" + "github.com/ThomsonReutersEikon/go-ntlm/ntlm" +) + +func main() { + // ntlm v2 + // challengeMessage := "TlRMTVNTUAACAAAAAAAAADgAAABVgphiPXSy0E6+HrMAAAAAAAAAAKIAogA4AAAABQEoCgAAAA8CAA4AUgBFAFUAVABFAFIAUwABABwAVQBLAEIAUAAtAEMAQgBUAFIATQBGAEUAMAA2AAQAFgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQAAwA0AHUAawBiAHAALQBjAGIAdAByAG0AZgBlADAANgAuAFIAZQB1AHQAZQByAHMALgBuAGUAdAAFABYAUgBlAHUAdABlAHIAcwAuAG4AZQB0AAAAAAA=" + // authenticateMessage := "TlRMTVNTUAADAAAAGAAYALYAAADSANIAzgAAADQANABIAAAAIAAgAHwAAAAaABoAnAAAABAAEACgAQAAVYKQQgUCzg4AAAAPYQByAHIAYQB5ADEAMgAuAG0AcwBnAHQAcwB0AC4AcgBlAHUAdABlAHIAcwAuAGMAbwBtAHUAcwBlAHIAcwB0AHIAZQBzAHMAMQAwADAAMAAwADgATgBZAEMAVgBBADEAMgBTADIAQwBNAFMAQQBPYrLjU4h0YlWZeEoNvTJtBQMnnJuAeUwsP+vGmAHNRBpgZ+4ChQLqAQEAAAAAAACPFEIFjx7OAQUDJ5ybgHlMAAAAAAIADgBSAEUAVQBUAEUAUgBTAAEAHABVAEsAQgBQAC0AQwBCAFQAUgBNAEYARQAwADYABAAWAFIAZQB1AHQAZQByAHMALgBuAGUAdAADADQAdQBrAGIAcAAtAGMAYgB0AHIAbQBmAGUAMAA2AC4AUgBlAHUAdABlAHIAcwAuAG4AZQB0AAUAFgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQAAAAAAAAAAAANuvnqD3K88ZpjkLleL0NW" + + //LCS v1 + //challengeMessage := "TlRMTVNTUAACAAAAAAAAADgAAADzgpjid08w9p89DLUAAAAAAAAAAPAA8AA4AAAABQLODgAAAA8CAA4AQQBSAFIAQQBZADEAMgABABYATgBZAEMAUwBNAFMARwA5ADkAMQAyAAQANABhAHIAcgBhAHkAMQAyAC4AbQBzAGcAdABzAHQALgByAGUAdQB0AGUAcgBzAC4AYwBvAG0AAwBMAE4AWQBDAFMATQBTAEcAOQA5ADEAMgAuAGEAcgByAGEAeQAxADIALgBtAHMAZwB0AHMAdAAuAHIAZQB1AHQAZQByAHMALgBjAG8AbQAFADQAYQByAHIAYQB5ADEAMgAuAG0AcwBnAHQAcwB0AC4AcgBlAHUAdABlAHIAcwAuAGMAbwBtAAAAAAA=" + //authenticateMessage := "TlRMTVNTUAADAAAAGAAYAKwAAAAYABgAxAAAAAAAAABYAAAANgA2AFgAAAAeAB4AjgAAABAAEADcAAAAVYKQYgYBsR0AAAAPUJSCwwcYcGpE0Zp9GsD3RDAANQAwADAANAA1AC4AcgBtAHcAYQB0AGUAcwB0AEAAcgBlAHUAdABlAHIAcwAuAGMAbwBtAFcASQBOAC0AMABEAEQAQQBCAEsAQwAxAFUASQA4ALIsDLYZktr3YlJDLyVT6GHgwNA+DFdM87IsDLYZktr3YlJDLyVT6GHgwNA+DFdM851g+vaa4CHvomwyYmjbB1M=" + + //US + //challengeMessage := "TlRMTVNTUAACAAAAAAAAADgAAABVgphisF5WgZrWn4MAAAAAAAAAAKIAogA4AAAABQEoCgAAAA8CAA4AUgBFAFUAVABFAFIAUwABABwAVQBLAEIAUAAtAEMAQgBUAFIATQBGAEUAMAA2AAQAFgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQAAwA0AHUAawBiAHAALQBjAGIAdAByAG0AZgBlADAANgAuAFIAZQB1AHQAZQByAHMALgBuAGUAdAAFABYAUgBlAHUAdABlAHIAcwAuAG4AZQB0AAAAAAA=" + //authenticateMessage := "TlRMTVNTUAADAAAAGAAYAKwAAAAYABgAxAAAAAAAAABYAAAANgA2AFgAAAAeAB4AjgAAABAAEADcAAAAVYKQYgYBsR0AAAAPJc+NGJ4qgACnkkGb9J8RezAANQAwADAANAA1AC4AcgBtAHcAYQB0AGUAcwB0AEAAcgBlAHUAdABlAHIAcwAuAGMAbwBtAFcASQBOAC0AMABEAEQAQQBCAEsAQwAxAFUASQA4AJLPhCq8UHZjb5sEjtoaJtWBY2ZwNZyujpLPhCq8UHZjb5sEjtoaJtWBY2ZwNZyujtW8TsZdZ6PMc1ipWbL7VgY=" + + //US again + challengeMessage := "TlRMTVNTUAACAAAAAAAAADgAAABVgphiMx43owKH33MAAAAAAAAAAKIAogA4AAAABQEoCgAAAA8CAA4AUgBFAFUAVABFAFIAUwABABwAVQBLAEIAUAAtAEMAQgBUAFIATQBGAEUAMAA2AAQAFgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQAAwA0AHUAawBiAHAALQBjAGIAdAByAG0AZgBlADAANgAuAFIAZQB1AHQAZQByAHMALgBuAGUAdAAFABYAUgBlAHUAdABlAHIAcwAuAG4AZQB0AAAAAAA=" + authenticateMessage := "TlRMTVNTUAADAAAAGAAYAKwAAAAYABgAxAAAAAAAAABYAAAANgA2AFgAAAAeAB4AjgAAABAAEADcAAAAVYKQYgYBsR0AAAAPukU9WmBJLdSLU2NvXjNgUzAANQAwADAANAA1AC4AcgBtAHcAYQB0AGUAcwB0AEAAcgBlAHUAdABlAHIAcwAuAGMAbwBtAFcASQBOAC0AMABEAEQAQQBCAEsAQwAxAFUASQA4AOLIAEYvI6zgw2+MBf8xHSTZhIfVaKIIFuLIAEYvI6zgw2+MBf8xHSTZhIfVaKIIFroZDwl770tY/oFQk38nnuI=" + + server, err := ntlm.CreateServerSession(ntlm.Version2, ntlm.ConnectionlessMode) + server.SetUserInfo("050045.rmwatest@reuters.com", "Welcome1", "") + + challengeData, _ := base64.StdEncoding.DecodeString(challengeMessage) + c, _ := ntlm.ParseChallengeMessage(challengeData) + + fmt.Println("----- Challenge Message ----- ") + fmt.Println(c.String()) + fmt.Println("----- END Challenge Message ----- ") + + authenticateData, _ := base64.StdEncoding.DecodeString(authenticateMessage) + var context ntlm.ServerSession + + msg, err := ntlm.ParseAuthenticateMessage(authenticateData, 2) + if err != nil { + msg2, newErr := ntlm.ParseAuthenticateMessage(authenticateData, 1) + if newErr != nil { + fmt.Printf("Error ParseAuthenticateMessage , %s", err) + return + } + + // Message parsed correctly as NTLMv1 so assume the session is v1 and reset the server session + newContext, err := ntlm.CreateServerSession(ntlm.Version1, ntlm.ConnectionlessMode) + newContext.SetUserInfo(server.GetUserInfo()) + if err != nil { + fmt.Println("Could not create NTLMv1 session") + return + } + + // Need the originally generated server challenge so we can process the response + newContext.SetServerChallenge(c.ServerChallenge) + // err = server.ProcessAuthenticateMessage(msg) + err = newContext.ProcessAuthenticateMessage(msg2) + if err != nil { + fmt.Printf("Could not process authenticate v1 message: %s\n", err) + return + } + // Set the security context to now be NTLMv1 + context = newContext + fmt.Println("----- Authenticate Message ----- ") + fmt.Println(msg2.String()) + fmt.Println("----- END Authenticate Message ----- ") + + } else { + context = server + // Need the server challenge to be set + server.SetServerChallenge(c.ServerChallenge) + + // err = server.ProcessAuthenticateMessage(msg) + err = context.ProcessAuthenticateMessage(msg) + if err != nil { + fmt.Printf("Could not process authenticate message: %s\n", err) + return + } + fmt.Println("----- Authenticate Message ----- ") + fmt.Println(msg.String()) + fmt.Println("----- END Authenticate Message ----- ") + + } + + fmt.Println("success") +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/.gitignore b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/.gitignore new file mode 100644 index 00000000..6e92f57d --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/.gitignore @@ -0,0 +1 @@ +tags diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/LICENSE b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/LICENSE new file mode 100644 index 00000000..7093402b --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2010, Kyle Lemons . All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/README b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/README new file mode 100644 index 00000000..16d80ecb --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/README @@ -0,0 +1,12 @@ +Please see http://log4go.googlecode.com/ + +Installation: +- Run `goinstall log4go.googlecode.com/hg` + +Usage: +- Add the following import: +import l4g "log4go.googlecode.com/hg" + +Acknowledgements: +- pomack + For providing awesome patches to bring log4go up to the latest Go spec diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/config.go b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/config.go new file mode 100644 index 00000000..324dc16f --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/config.go @@ -0,0 +1,283 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +package log4go + +import ( + "encoding/xml" + "fmt" + "io/ioutil" + "os" + "strconv" + "strings" +) + +type xmlProperty struct { + Name string `xml:"name,attr"` + Value string `xml:",chardata"` +} + +type xmlFilter struct { + Enabled string `xml:"enabled,attr"` + Tag string `xml:"tag"` + Level string `xml:"level"` + Type string `xml:"type"` + Property []xmlProperty `xml:"property"` +} + +type xmlLoggerConfig struct { + Filter []xmlFilter `xml:"filter"` +} + +// Load XML configuration; see examples/example.xml for documentation +func (log Logger) LoadConfiguration(filename string) { + log.Close() + + // Open the configuration file + fd, err := os.Open(filename) + if err != nil { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not open %q for reading: %s\n", filename, err) + os.Exit(1) + } + + contents, err := ioutil.ReadAll(fd) + if err != nil { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not read %q: %s\n", filename, err) + os.Exit(1) + } + + xc := new(xmlLoggerConfig) + if err := xml.Unmarshal(contents, xc); err != nil { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not parse XML configuration in %q: %s\n", filename, err) + os.Exit(1) + } + + for _, xmlfilt := range xc.Filter { + var filt LogWriter + var lvl level + bad, good, enabled := false, true, false + + // Check required children + if len(xmlfilt.Enabled) == 0 { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required attribute %s for filter missing in %s\n", "enabled", filename) + bad = true + } else { + enabled = xmlfilt.Enabled != "false" + } + if len(xmlfilt.Tag) == 0 { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "tag", filename) + bad = true + } + if len(xmlfilt.Type) == 0 { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "type", filename) + bad = true + } + if len(xmlfilt.Level) == 0 { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "level", filename) + bad = true + } + + switch xmlfilt.Level { + case "FINEST": + lvl = FINEST + case "FINE": + lvl = FINE + case "DEBUG": + lvl = DEBUG + case "TRACE": + lvl = TRACE + case "INFO": + lvl = INFO + case "WARNING": + lvl = WARNING + case "ERROR": + lvl = ERROR + case "CRITICAL": + lvl = CRITICAL + default: + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter has unknown value in %s: %s\n", "level", filename, xmlfilt.Level) + bad = true + } + + // Just so all of the required attributes are errored at the same time if missing + if bad { + os.Exit(1) + } + + switch xmlfilt.Type { + case "console": + filt, good = xmlToConsoleLogWriter(filename, xmlfilt.Property, enabled) + case "file": + filt, good = xmlToFileLogWriter(filename, xmlfilt.Property, enabled) + case "xml": + filt, good = xmlToXMLLogWriter(filename, xmlfilt.Property, enabled) + case "socket": + filt, good = xmlToSocketLogWriter(filename, xmlfilt.Property, enabled) + default: + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not load XML configuration in %s: unknown filter type \"%s\"\n", filename, xmlfilt.Type) + os.Exit(1) + } + + // Just so all of the required params are errored at the same time if wrong + if !good { + os.Exit(1) + } + + // If we're disabled (syntax and correctness checks only), don't add to logger + if !enabled { + continue + } + + log[xmlfilt.Tag] = &Filter{lvl, filt} + } +} + +func xmlToConsoleLogWriter(filename string, props []xmlProperty, enabled bool) (ConsoleLogWriter, bool) { + // Parse properties + for _, prop := range props { + switch prop.Name { + default: + fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for console filter in %s\n", prop.Name, filename) + } + } + + return NewConsoleLogWriter(), true +} + +// Parse a number with K/M/G suffixes based on thousands (1000) or 2^10 (1024) +func strToNumSuffix(str string, mult int) int { + num := 1 + if len(str) > 1 { + switch str[len(str)-1] { + case 'G', 'g': + num *= mult + fallthrough + case 'M', 'm': + num *= mult + fallthrough + case 'K', 'k': + num *= mult + str = str[0 : len(str)-1] + } + } + parsed, _ := strconv.Atoi(str) + return parsed * num +} +func xmlToFileLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) { + file := "" + format := "[%D %T] [%L] (%S) %M" + maxlines := 0 + maxsize := 0 + daily := false + rotate := false + + // Parse properties + for _, prop := range props { + switch prop.Name { + case "filename": + file = strings.Trim(prop.Value, " \r\n") + case "format": + format = strings.Trim(prop.Value, " \r\n") + case "maxlines": + maxlines = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000) + case "maxsize": + maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024) + case "daily": + daily = strings.Trim(prop.Value, " \r\n") != "false" + case "rotate": + rotate = strings.Trim(prop.Value, " \r\n") != "false" + default: + fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename) + } + } + + // Check properties + if len(file) == 0 { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "filename", filename) + return nil, false + } + + // If it's disabled, we're just checking syntax + if !enabled { + return nil, true + } + + flw := NewFileLogWriter(file, rotate) + flw.SetFormat(format) + flw.SetRotateLines(maxlines) + flw.SetRotateSize(maxsize) + flw.SetRotateDaily(daily) + return flw, true +} + +func xmlToXMLLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) { + file := "" + maxrecords := 0 + maxsize := 0 + daily := false + rotate := false + + // Parse properties + for _, prop := range props { + switch prop.Name { + case "filename": + file = strings.Trim(prop.Value, " \r\n") + case "maxrecords": + maxrecords = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000) + case "maxsize": + maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024) + case "daily": + daily = strings.Trim(prop.Value, " \r\n") != "false" + case "rotate": + rotate = strings.Trim(prop.Value, " \r\n") != "false" + default: + fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for xml filter in %s\n", prop.Name, filename) + } + } + + // Check properties + if len(file) == 0 { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for xml filter missing in %s\n", "filename", filename) + return nil, false + } + + // If it's disabled, we're just checking syntax + if !enabled { + return nil, true + } + + xlw := NewXMLLogWriter(file, rotate) + xlw.SetRotateLines(maxrecords) + xlw.SetRotateSize(maxsize) + xlw.SetRotateDaily(daily) + return xlw, true +} + +func xmlToSocketLogWriter(filename string, props []xmlProperty, enabled bool) (SocketLogWriter, bool) { + endpoint := "" + protocol := "udp" + + // Parse properties + for _, prop := range props { + switch prop.Name { + case "endpoint": + endpoint = strings.Trim(prop.Value, " \r\n") + case "protocol": + protocol = strings.Trim(prop.Value, " \r\n") + default: + fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename) + } + } + + // Check properties + if len(endpoint) == 0 { + fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "endpoint", filename) + return nil, false + } + + // If it's disabled, we're just checking syntax + if !enabled { + return nil, true + } + + return NewSocketLogWriter(protocol, endpoint), true +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/ConsoleLogWriter_Manual.go b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/ConsoleLogWriter_Manual.go new file mode 100644 index 00000000..7169ec97 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/ConsoleLogWriter_Manual.go @@ -0,0 +1,13 @@ +package main + +import ( + "time" +) + +import l4g "github.com/github/git-lfs/vendor/_nuts/code.google.com/p/log4go" + +func main() { + log := l4g.NewLogger() + log.AddFilter("stdout", l4g.DEBUG, l4g.NewConsoleLogWriter()) + log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/FileLogWriter_Manual.go b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/FileLogWriter_Manual.go new file mode 100644 index 00000000..872bb45b --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/FileLogWriter_Manual.go @@ -0,0 +1,57 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "os" + "time" +) + +import l4g "github.com/github/git-lfs/vendor/_nuts/code.google.com/p/log4go" + +const ( + filename = "flw.log" +) + +func main() { + // Get a new logger instance + log := l4g.NewLogger() + + // Create a default logger that is logging messages of FINE or higher + log.AddFilter("file", l4g.FINE, l4g.NewFileLogWriter(filename, false)) + log.Close() + + /* Can also specify manually via the following: (these are the defaults) */ + flw := l4g.NewFileLogWriter(filename, false) + flw.SetFormat("[%D %T] [%L] (%S) %M") + flw.SetRotate(false) + flw.SetRotateSize(0) + flw.SetRotateLines(0) + flw.SetRotateDaily(false) + log.AddFilter("file", l4g.FINE, flw) + + // Log some experimental messages + log.Finest("Everything is created now (notice that I will not be printing to the file)") + log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) + log.Critical("Time to close out!") + + // Close the log + log.Close() + + // Print what was logged to the file (yes, I know I'm skipping error checking) + fd, _ := os.Open(filename) + in := bufio.NewReader(fd) + fmt.Print("Messages logged to file were: (line numbers not included)\n") + for lineno := 1; ; lineno++ { + line, err := in.ReadString('\n') + if err == io.EOF { + break + } + fmt.Printf("%3d:\t%s", lineno, line) + } + fd.Close() + + // Remove the file so it's not lying around + os.Remove(filename) +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/SimpleNetLogServer.go b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/SimpleNetLogServer.go new file mode 100644 index 00000000..83c80ad1 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/SimpleNetLogServer.go @@ -0,0 +1,42 @@ +package main + +import ( + "flag" + "fmt" + "net" + "os" +) + +var ( + port = flag.String("p", "12124", "Port number to listen on") +) + +func e(err error) { + if err != nil { + fmt.Printf("Erroring out: %s\n", err) + os.Exit(1) + } +} + +func main() { + flag.Parse() + + // Bind to the port + bind, err := net.ResolveUDPAddr("0.0.0.0:" + *port) + e(err) + + // Create listener + listener, err := net.ListenUDP("udp", bind) + e(err) + + fmt.Printf("Listening to port %s...\n", *port) + for { + // read into a new buffer + buffer := make([]byte, 1024) + _, _, err := listener.ReadFrom(buffer) + e(err) + + // log to standard output + fmt.Println(string(buffer)) + } +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/SocketLogWriter_Manual.go b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/SocketLogWriter_Manual.go new file mode 100644 index 00000000..d553699f --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/SocketLogWriter_Manual.go @@ -0,0 +1,18 @@ +package main + +import ( + "time" +) + +import l4g "github.com/github/git-lfs/vendor/_nuts/code.google.com/p/log4go" + +func main() { + log := l4g.NewLogger() + log.AddFilter("network", l4g.FINEST, l4g.NewSocketLogWriter("udp", "192.168.1.255:12124")) + + // Run `nc -u -l -p 12124` or similar before you run this to see the following message + log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) + + // This makes sure the output stream buffer is written + log.Close() +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/XMLConfigurationExample.go b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/XMLConfigurationExample.go new file mode 100644 index 00000000..d6485753 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/XMLConfigurationExample.go @@ -0,0 +1,13 @@ +package main + +import l4g "github.com/github/git-lfs/vendor/_nuts/code.google.com/p/log4go" + +func main() { + // Load the configuration (isn't this easy?) + l4g.LoadConfiguration("example.xml") + + // And now we're ready! + l4g.Finest("This will only go to those of you really cool UDP kids! If you change enabled=true.") + l4g.Debug("Oh no! %d + %d = %d!", 2, 2, 2+2) + l4g.Info("About that time, eh chaps?") +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/example.xml b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/example.xml new file mode 100644 index 00000000..e791278c --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/examples/example.xml @@ -0,0 +1,47 @@ + + + stdout + console + + DEBUG + + + file + file + FINEST + test.log + + [%D %T] [%L] (%S) %M + false + 0M + 0K + true + + + xmllog + xml + TRACE + trace.xml + true + 100M + 6K + false + + + donotopen + socket + FINEST + 192.168.1.255:12124 + udp + + diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/filelog.go b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/filelog.go new file mode 100644 index 00000000..9cbd815d --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/filelog.go @@ -0,0 +1,239 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +package log4go + +import ( + "os" + "fmt" + "time" +) + +// This log writer sends output to a file +type FileLogWriter struct { + rec chan *LogRecord + rot chan bool + + // The opened file + filename string + file *os.File + + // The logging format + format string + + // File header/trailer + header, trailer string + + // Rotate at linecount + maxlines int + maxlines_curlines int + + // Rotate at size + maxsize int + maxsize_cursize int + + // Rotate daily + daily bool + daily_opendate int + + // Keep old logfiles (.001, .002, etc) + rotate bool +} + +// This is the FileLogWriter's output method +func (w *FileLogWriter) LogWrite(rec *LogRecord) { + w.rec <- rec +} + +func (w *FileLogWriter) Close() { + close(w.rec) +} + +// NewFileLogWriter creates a new LogWriter which writes to the given file and +// has rotation enabled if rotate is true. +// +// If rotate is true, any time a new log file is opened, the old one is renamed +// with a .### extension to preserve it. The various Set* methods can be used +// to configure log rotation based on lines, size, and daily. +// +// The standard log-line format is: +// [%D %T] [%L] (%S) %M +func NewFileLogWriter(fname string, rotate bool) *FileLogWriter { + w := &FileLogWriter{ + rec: make(chan *LogRecord, LogBufferLength), + rot: make(chan bool), + filename: fname, + format: "[%D %T] [%L] (%S) %M", + rotate: rotate, + } + + // open the file for the first time + if err := w.intRotate(); err != nil { + fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) + return nil + } + + go func() { + defer func() { + if w.file != nil { + fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()})) + w.file.Close() + } + }() + + for { + select { + case <-w.rot: + if err := w.intRotate(); err != nil { + fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) + return + } + case rec, ok := <-w.rec: + if !ok { + return + } + now := time.Now() + if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) || + (w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) || + (w.daily && now.Day() != w.daily_opendate) { + if err := w.intRotate(); err != nil { + fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) + return + } + } + + // Perform the write + n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec)) + if err != nil { + fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) + return + } + + // Update the counts + w.maxlines_curlines++ + w.maxsize_cursize += n + } + } + }() + + return w +} + +// Request that the logs rotate +func (w *FileLogWriter) Rotate() { + w.rot <- true +} + +// If this is called in a threaded context, it MUST be synchronized +func (w *FileLogWriter) intRotate() error { + // Close any log file that may be open + if w.file != nil { + fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()})) + w.file.Close() + } + + // If we are keeping log files, move it to the next available number + if w.rotate { + _, err := os.Lstat(w.filename) + if err == nil { // file exists + // Find the next available number + num := 1 + fname := "" + for ; err == nil && num <= 999; num++ { + fname = w.filename + fmt.Sprintf(".%03d", num) + _, err = os.Lstat(fname) + } + // return error if the last file checked still existed + if err == nil { + return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename) + } + + // Rename the file to its newfound home + err = os.Rename(w.filename, fname) + if err != nil { + return fmt.Errorf("Rotate: %s\n", err) + } + } + } + + // Open the log file + fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) + if err != nil { + return err + } + w.file = fd + + now := time.Now() + fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now})) + + // Set the daily open date to the current date + w.daily_opendate = now.Day() + + // initialize rotation values + w.maxlines_curlines = 0 + w.maxsize_cursize = 0 + + return nil +} + +// Set the logging format (chainable). Must be called before the first log +// message is written. +func (w *FileLogWriter) SetFormat(format string) *FileLogWriter { + w.format = format + return w +} + +// Set the logfile header and footer (chainable). Must be called before the first log +// message is written. These are formatted similar to the FormatLogRecord (e.g. +// you can use %D and %T in your header/footer for date and time). +func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter { + w.header, w.trailer = head, foot + if w.maxlines_curlines == 0 { + fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()})) + } + return w +} + +// Set rotate at linecount (chainable). Must be called before the first log +// message is written. +func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter { + //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines) + w.maxlines = maxlines + return w +} + +// Set rotate at size (chainable). Must be called before the first log message +// is written. +func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter { + //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize) + w.maxsize = maxsize + return w +} + +// Set rotate daily (chainable). Must be called before the first log message is +// written. +func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter { + //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily) + w.daily = daily + return w +} + +// SetRotate changes whether or not the old logs are kept. (chainable) Must be +// called before the first log message is written. If rotate is false, the +// files are overwritten; otherwise, they are rotated to another file before the +// new log is opened. +func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter { + //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate) + w.rotate = rotate + return w +} + +// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to +// output XML record log messages instead of line-based ones. +func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter { + return NewFileLogWriter(fname, rotate).SetFormat( + ` + %D %T + %S + %M + `).SetHeadFoot("", "") +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/log4go.go b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/log4go.go new file mode 100644 index 00000000..ab4e857f --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/log4go.go @@ -0,0 +1,484 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +// Package log4go provides level-based and highly configurable logging. +// +// Enhanced Logging +// +// This is inspired by the logging functionality in Java. Essentially, you create a Logger +// object and create output filters for it. You can send whatever you want to the Logger, +// and it will filter that based on your settings and send it to the outputs. This way, you +// can put as much debug code in your program as you want, and when you're done you can filter +// out the mundane messages so only the important ones show up. +// +// Utility functions are provided to make life easier. Here is some example code to get started: +// +// log := log4go.NewLogger() +// log.AddFilter("stdout", log4go.DEBUG, log4go.NewConsoleLogWriter()) +// log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true)) +// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02")) +// +// The first two lines can be combined with the utility NewDefaultLogger: +// +// log := log4go.NewDefaultLogger(log4go.DEBUG) +// log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true)) +// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02")) +// +// Usage notes: +// - The ConsoleLogWriter does not display the source of the message to standard +// output, but the FileLogWriter does. +// - The utility functions (Info, Debug, Warn, etc) derive their source from the +// calling function, and this incurs extra overhead. +// +// Changes from 2.0: +// - The external interface has remained mostly stable, but a lot of the +// internals have been changed, so if you depended on any of this or created +// your own LogWriter, then you will probably have to update your code. In +// particular, Logger is now a map and ConsoleLogWriter is now a channel +// behind-the-scenes, and the LogWrite method no longer has return values. +// +// Future work: (please let me know if you think I should work on any of these particularly) +// - Log file rotation +// - Logging configuration files ala log4j +// - Have the ability to remove filters? +// - Have GetInfoChannel, GetDebugChannel, etc return a chan string that allows +// for another method of logging +// - Add an XML filter type +package log4go + +import ( + "errors" + "os" + "fmt" + "time" + "strings" + "runtime" +) + +// Version information +const ( + L4G_VERSION = "log4go-v3.0.1" + L4G_MAJOR = 3 + L4G_MINOR = 0 + L4G_BUILD = 1 +) + +/****** Constants ******/ + +// These are the integer logging levels used by the logger +type level int + +const ( + FINEST level = iota + FINE + DEBUG + TRACE + INFO + WARNING + ERROR + CRITICAL +) + +// Logging level strings +var ( + levelStrings = [...]string{"FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"} +) + +func (l level) String() string { + if l < 0 || int(l) > len(levelStrings) { + return "UNKNOWN" + } + return levelStrings[int(l)] +} + +/****** Variables ******/ +var ( + // LogBufferLength specifies how many log messages a particular log4go + // logger can buffer at a time before writing them. + LogBufferLength = 32 +) + +/****** LogRecord ******/ + +// A LogRecord contains all of the pertinent information for each message +type LogRecord struct { + Level level // The log level + Created time.Time // The time at which the log message was created (nanoseconds) + Source string // The message source + Message string // The log message +} + +/****** LogWriter ******/ + +// This is an interface for anything that should be able to write logs +type LogWriter interface { + // This will be called to log a LogRecord message. + LogWrite(rec *LogRecord) + + // This should clean up anything lingering about the LogWriter, as it is called before + // the LogWriter is removed. LogWrite should not be called after Close. + Close() +} + +/****** Logger ******/ + +// A Filter represents the log level below which no log records are written to +// the associated LogWriter. +type Filter struct { + Level level + LogWriter +} + +// A Logger represents a collection of Filters through which log messages are +// written. +type Logger map[string]*Filter + +// Create a new logger. +// +// DEPRECATED: Use make(Logger) instead. +func NewLogger() Logger { + os.Stderr.WriteString("warning: use of deprecated NewLogger\n") + return make(Logger) +} + +// Create a new logger with a "stdout" filter configured to send log messages at +// or above lvl to standard output. +// +// DEPRECATED: use NewDefaultLogger instead. +func NewConsoleLogger(lvl level) Logger { + os.Stderr.WriteString("warning: use of deprecated NewConsoleLogger\n") + return Logger{ + "stdout": &Filter{lvl, NewConsoleLogWriter()}, + } +} + +// Create a new logger with a "stdout" filter configured to send log messages at +// or above lvl to standard output. +func NewDefaultLogger(lvl level) Logger { + return Logger{ + "stdout": &Filter{lvl, NewConsoleLogWriter()}, + } +} + +// Closes all log writers in preparation for exiting the program or a +// reconfiguration of logging. Calling this is not really imperative, unless +// you want to guarantee that all log messages are written. Close removes +// all filters (and thus all LogWriters) from the logger. +func (log Logger) Close() { + // Close all open loggers + for name, filt := range log { + filt.Close() + delete(log, name) + } +} + +// Add a new LogWriter to the Logger which will only log messages at lvl or +// higher. This function should not be called from multiple goroutines. +// Returns the logger for chaining. +func (log Logger) AddFilter(name string, lvl level, writer LogWriter) Logger { + log[name] = &Filter{lvl, writer} + return log +} + +/******* Logging *******/ +// Send a formatted log message internally +func (log Logger) intLogf(lvl level, format string, args ...interface{}) { + skip := true + + // Determine if any logging will be done + for _, filt := range log { + if lvl >= filt.Level { + skip = false + break + } + } + if skip { + return + } + + // Determine caller func + pc, _, lineno, ok := runtime.Caller(2) + src := "" + if ok { + src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno) + } + + msg := format + if len(args) > 0 { + msg = fmt.Sprintf(format, args...) + } + + // Make the log record + rec := &LogRecord{ + Level: lvl, + Created: time.Now(), + Source: src, + Message: msg, + } + + // Dispatch the logs + for _, filt := range log { + if lvl < filt.Level { + continue + } + filt.LogWrite(rec) + } +} + +// Send a closure log message internally +func (log Logger) intLogc(lvl level, closure func() string) { + skip := true + + // Determine if any logging will be done + for _, filt := range log { + if lvl >= filt.Level { + skip = false + break + } + } + if skip { + return + } + + // Determine caller func + pc, _, lineno, ok := runtime.Caller(2) + src := "" + if ok { + src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno) + } + + // Make the log record + rec := &LogRecord{ + Level: lvl, + Created: time.Now(), + Source: src, + Message: closure(), + } + + // Dispatch the logs + for _, filt := range log { + if lvl < filt.Level { + continue + } + filt.LogWrite(rec) + } +} + +// Send a log message with manual level, source, and message. +func (log Logger) Log(lvl level, source, message string) { + skip := true + + // Determine if any logging will be done + for _, filt := range log { + if lvl >= filt.Level { + skip = false + break + } + } + if skip { + return + } + + // Make the log record + rec := &LogRecord{ + Level: lvl, + Created: time.Now(), + Source: source, + Message: message, + } + + // Dispatch the logs + for _, filt := range log { + if lvl < filt.Level { + continue + } + filt.LogWrite(rec) + } +} + +// Logf logs a formatted log message at the given log level, using the caller as +// its source. +func (log Logger) Logf(lvl level, format string, args ...interface{}) { + log.intLogf(lvl, format, args...) +} + +// Logc logs a string returned by the closure at the given log level, using the caller as +// its source. If no log message would be written, the closure is never called. +func (log Logger) Logc(lvl level, closure func() string) { + log.intLogc(lvl, closure) +} + +// Finest logs a message at the finest log level. +// See Debug for an explanation of the arguments. +func (log Logger) Finest(arg0 interface{}, args ...interface{}) { + const ( + lvl = FINEST + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + log.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + log.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Fine logs a message at the fine log level. +// See Debug for an explanation of the arguments. +func (log Logger) Fine(arg0 interface{}, args ...interface{}) { + const ( + lvl = FINE + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + log.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + log.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Debug is a utility method for debug log messages. +// The behavior of Debug depends on the first argument: +// - arg0 is a string +// When given a string as the first argument, this behaves like Logf but with +// the DEBUG log level: the first argument is interpreted as a format for the +// latter arguments. +// - arg0 is a func()string +// When given a closure of type func()string, this logs the string returned by +// the closure iff it will be logged. The closure runs at most one time. +// - arg0 is interface{} +// When given anything else, the log message will be each of the arguments +// formatted with %v and separated by spaces (ala Sprint). +func (log Logger) Debug(arg0 interface{}, args ...interface{}) { + const ( + lvl = DEBUG + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + log.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + log.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Trace logs a message at the trace log level. +// See Debug for an explanation of the arguments. +func (log Logger) Trace(arg0 interface{}, args ...interface{}) { + const ( + lvl = TRACE + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + log.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + log.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Info logs a message at the info log level. +// See Debug for an explanation of the arguments. +func (log Logger) Info(arg0 interface{}, args ...interface{}) { + const ( + lvl = INFO + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + log.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + log.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Warn logs a message at the warning log level and returns the formatted error. +// At the warning level and higher, there is no performance benefit if the +// message is not actually logged, because all formats are processed and all +// closures are executed to format the error message. +// See Debug for further explanation of the arguments. +func (log Logger) Warn(arg0 interface{}, args ...interface{}) error { + const ( + lvl = WARNING + ) + var msg string + switch first := arg0.(type) { + case string: + // Use the string as a format string + msg = fmt.Sprintf(first, args...) + case func() string: + // Log the closure (no other arguments used) + msg = first() + default: + // Build a format string so that it will be similar to Sprint + msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) + } + log.intLogf(lvl, msg) + return errors.New(msg) +} + +// Error logs a message at the error log level and returns the formatted error, +// See Warn for an explanation of the performance and Debug for an explanation +// of the parameters. +func (log Logger) Error(arg0 interface{}, args ...interface{}) error { + const ( + lvl = ERROR + ) + var msg string + switch first := arg0.(type) { + case string: + // Use the string as a format string + msg = fmt.Sprintf(first, args...) + case func() string: + // Log the closure (no other arguments used) + msg = first() + default: + // Build a format string so that it will be similar to Sprint + msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) + } + log.intLogf(lvl, msg) + return errors.New(msg) +} + +// Critical logs a message at the critical log level and returns the formatted error, +// See Warn for an explanation of the performance and Debug for an explanation +// of the parameters. +func (log Logger) Critical(arg0 interface{}, args ...interface{}) error { + const ( + lvl = CRITICAL + ) + var msg string + switch first := arg0.(type) { + case string: + // Use the string as a format string + msg = fmt.Sprintf(first, args...) + case func() string: + // Log the closure (no other arguments used) + msg = first() + default: + // Build a format string so that it will be similar to Sprint + msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) + } + log.intLogf(lvl, msg) + return errors.New(msg) +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/log4go_test.go b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/log4go_test.go new file mode 100644 index 00000000..6f550649 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/log4go_test.go @@ -0,0 +1,537 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +package log4go + +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "io" + "io/ioutil" + "os" + "runtime" + "testing" + "time" +) + +const testLogFile = "_logtest.log" + +var now time.Time = time.Unix(0, 1234567890123456789).In(time.UTC) + +func newLogRecord(lvl level, src string, msg string) *LogRecord { + return &LogRecord{ + Level: lvl, + Source: src, + Created: now, + Message: msg, + } +} + +func TestELog(t *testing.T) { + fmt.Printf("Testing %s\n", L4G_VERSION) + lr := newLogRecord(CRITICAL, "source", "message") + if lr.Level != CRITICAL { + t.Errorf("Incorrect level: %d should be %d", lr.Level, CRITICAL) + } + if lr.Source != "source" { + t.Errorf("Incorrect source: %s should be %s", lr.Source, "source") + } + if lr.Message != "message" { + t.Errorf("Incorrect message: %s should be %s", lr.Source, "message") + } +} + +var formatTests = []struct { + Test string + Record *LogRecord + Formats map[string]string +}{ + { + Test: "Standard formats", + Record: &LogRecord{ + Level: ERROR, + Source: "source", + Message: "message", + Created: now, + }, + Formats: map[string]string{ + // TODO(kevlar): How can I do this so it'll work outside of PST? + FORMAT_DEFAULT: "[2009/02/13 23:31:30 UTC] [EROR] (source) message\n", + FORMAT_SHORT: "[23:31 02/13/09] [EROR] message\n", + FORMAT_ABBREV: "[EROR] message\n", + }, + }, +} + +func TestFormatLogRecord(t *testing.T) { + for _, test := range formatTests { + name := test.Test + for fmt, want := range test.Formats { + if got := FormatLogRecord(fmt, test.Record); got != want { + t.Errorf("%s - %s:", name, fmt) + t.Errorf(" got %q", got) + t.Errorf(" want %q", want) + } + } + } +} + +var logRecordWriteTests = []struct { + Test string + Record *LogRecord + Console string +}{ + { + Test: "Normal message", + Record: &LogRecord{ + Level: CRITICAL, + Source: "source", + Message: "message", + Created: now, + }, + Console: "[Feb 13 23:31:30.123456] [CRIT] message\n", + }, +} + +func TestConsoleLogWriter(t *testing.T) { + r, w := io.Pipe() + stdout = w + console := NewConsoleLogWriter() + + // Write + go func() { + for _, test := range logRecordWriteTests { + console.LogWrite(test.Record) + } + }() + // Read and verify + buf := make([]byte, 1024) + for _, test := range logRecordWriteTests { + name := test.Test + n, _ := r.Read(buf) + if got, want := string(buf[:n]), test.Console; got != want { + t.Errorf("%s: got %q", name, got) + t.Errorf("%s: want %q", name, want) + } + } + + stdout = os.Stdout +} + +func TestFileLogWriter(t *testing.T) { + defer func(buflen int) { + LogBufferLength = buflen + }(LogBufferLength) + LogBufferLength = 0 + + w := NewFileLogWriter(testLogFile, false) + if w == nil { + t.Fatalf("Invalid return: w should not be nil") + } + defer os.Remove(testLogFile) + + w.LogWrite(newLogRecord(CRITICAL, "source", "message")) + w.Close() + runtime.Gosched() + + if contents, err := ioutil.ReadFile(testLogFile); err != nil { + t.Errorf("read(%q): %s", testLogFile, err) + } else if len(contents) != 50 { + t.Errorf("malformed filelog: %q (%d bytes)", string(contents), len(contents)) + } +} + +func TestXMLLogWriter(t *testing.T) { + defer func(buflen int) { + LogBufferLength = buflen + }(LogBufferLength) + LogBufferLength = 0 + + w := NewXMLLogWriter(testLogFile, false) + if w == nil { + t.Fatalf("Invalid return: w should not be nil") + } + defer os.Remove(testLogFile) + + w.LogWrite(newLogRecord(CRITICAL, "source", "message")) + w.Close() + runtime.Gosched() + + if contents, err := ioutil.ReadFile(testLogFile); err != nil { + t.Errorf("read(%q): %s", testLogFile, err) + } else if len(contents) != 185 { + t.Errorf("malformed xmllog: %q (%d bytes)", string(contents), len(contents)) + } +} + +func TestLogger(t *testing.T) { + sl := NewDefaultLogger(WARNING) + if sl == nil { + t.Fatalf("NewDefaultLogger should never return nil") + } + if lw, exist := sl["stdout"]; lw == nil || exist != true { + t.Fatalf("NewDefaultLogger produced invalid logger (DNE or nil)") + } + if sl["stdout"].Level != WARNING { + t.Fatalf("NewDefaultLogger produced invalid logger (incorrect level)") + } + if len(sl) != 1 { + t.Fatalf("NewDefaultLogger produced invalid logger (incorrect map count)") + } + + //func (l *Logger) AddFilter(name string, level int, writer LogWriter) {} + l := make(Logger) + l.AddFilter("stdout", DEBUG, NewConsoleLogWriter()) + if lw, exist := l["stdout"]; lw == nil || exist != true { + t.Fatalf("AddFilter produced invalid logger (DNE or nil)") + } + if l["stdout"].Level != DEBUG { + t.Fatalf("AddFilter produced invalid logger (incorrect level)") + } + if len(l) != 1 { + t.Fatalf("AddFilter produced invalid logger (incorrect map count)") + } + + //func (l *Logger) Warn(format string, args ...interface{}) error {} + if err := l.Warn("%s %d %#v", "Warning:", 1, []int{}); err.Error() != "Warning: 1 []int{}" { + t.Errorf("Warn returned invalid error: %s", err) + } + + //func (l *Logger) Error(format string, args ...interface{}) error {} + if err := l.Error("%s %d %#v", "Error:", 10, []string{}); err.Error() != "Error: 10 []string{}" { + t.Errorf("Error returned invalid error: %s", err) + } + + //func (l *Logger) Critical(format string, args ...interface{}) error {} + if err := l.Critical("%s %d %#v", "Critical:", 100, []int64{}); err.Error() != "Critical: 100 []int64{}" { + t.Errorf("Critical returned invalid error: %s", err) + } + + // Already tested or basically untestable + //func (l *Logger) Log(level int, source, message string) {} + //func (l *Logger) Logf(level int, format string, args ...interface{}) {} + //func (l *Logger) intLogf(level int, format string, args ...interface{}) string {} + //func (l *Logger) Finest(format string, args ...interface{}) {} + //func (l *Logger) Fine(format string, args ...interface{}) {} + //func (l *Logger) Debug(format string, args ...interface{}) {} + //func (l *Logger) Trace(format string, args ...interface{}) {} + //func (l *Logger) Info(format string, args ...interface{}) {} +} + +func TestLogOutput(t *testing.T) { + const ( + expected = "fdf3e51e444da56b4cb400f30bc47424" + ) + + // Unbuffered output + defer func(buflen int) { + LogBufferLength = buflen + }(LogBufferLength) + LogBufferLength = 0 + + l := make(Logger) + + // Delete and open the output log without a timestamp (for a constant md5sum) + l.AddFilter("file", FINEST, NewFileLogWriter(testLogFile, false).SetFormat("[%L] %M")) + defer os.Remove(testLogFile) + + // Send some log messages + l.Log(CRITICAL, "testsrc1", fmt.Sprintf("This message is level %d", int(CRITICAL))) + l.Logf(ERROR, "This message is level %v", ERROR) + l.Logf(WARNING, "This message is level %s", WARNING) + l.Logc(INFO, func() string { return "This message is level INFO" }) + l.Trace("This message is level %d", int(TRACE)) + l.Debug("This message is level %s", DEBUG) + l.Fine(func() string { return fmt.Sprintf("This message is level %v", FINE) }) + l.Finest("This message is level %v", FINEST) + l.Finest(FINEST, "is also this message's level") + + l.Close() + + contents, err := ioutil.ReadFile(testLogFile) + if err != nil { + t.Fatalf("Could not read output log: %s", err) + } + + sum := md5.New() + sum.Write(contents) + if sumstr := hex.EncodeToString(sum.Sum(nil)); sumstr != expected { + t.Errorf("--- Log Contents:\n%s---", string(contents)) + t.Fatalf("Checksum does not match: %s (expecting %s)", sumstr, expected) + } +} + +func TestCountMallocs(t *testing.T) { + const N = 1 + var m runtime.MemStats + getMallocs := func() uint64 { + runtime.ReadMemStats(&m) + return m.Mallocs + } + + // Console logger + sl := NewDefaultLogger(INFO) + mallocs := 0 - getMallocs() + for i := 0; i < N; i++ { + sl.Log(WARNING, "here", "This is a WARNING message") + } + mallocs += getMallocs() + fmt.Printf("mallocs per sl.Log((WARNING, \"here\", \"This is a log message\"): %d\n", mallocs/N) + + // Console logger formatted + mallocs = 0 - getMallocs() + for i := 0; i < N; i++ { + sl.Logf(WARNING, "%s is a log message with level %d", "This", WARNING) + } + mallocs += getMallocs() + fmt.Printf("mallocs per sl.Logf(WARNING, \"%%s is a log message with level %%d\", \"This\", WARNING): %d\n", mallocs/N) + + // Console logger (not logged) + sl = NewDefaultLogger(INFO) + mallocs = 0 - getMallocs() + for i := 0; i < N; i++ { + sl.Log(DEBUG, "here", "This is a DEBUG log message") + } + mallocs += getMallocs() + fmt.Printf("mallocs per unlogged sl.Log((WARNING, \"here\", \"This is a log message\"): %d\n", mallocs/N) + + // Console logger formatted (not logged) + mallocs = 0 - getMallocs() + for i := 0; i < N; i++ { + sl.Logf(DEBUG, "%s is a log message with level %d", "This", DEBUG) + } + mallocs += getMallocs() + fmt.Printf("mallocs per unlogged sl.Logf(WARNING, \"%%s is a log message with level %%d\", \"This\", WARNING): %d\n", mallocs/N) +} + +func TestXMLConfig(t *testing.T) { + const ( + configfile = "example.xml" + ) + + fd, err := os.Create(configfile) + if err != nil { + t.Fatalf("Could not open %s for writing: %s", configfile, err) + } + + fmt.Fprintln(fd, "") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " stdout") + fmt.Fprintln(fd, " console") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " DEBUG") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " file") + fmt.Fprintln(fd, " file") + fmt.Fprintln(fd, " FINEST") + fmt.Fprintln(fd, " test.log") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " [%D %T] [%L] (%S) %M") + fmt.Fprintln(fd, " false ") + fmt.Fprintln(fd, " 0M ") + fmt.Fprintln(fd, " 0K ") + fmt.Fprintln(fd, " true ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " xmllog") + fmt.Fprintln(fd, " xml") + fmt.Fprintln(fd, " TRACE") + fmt.Fprintln(fd, " trace.xml") + fmt.Fprintln(fd, " true ") + fmt.Fprintln(fd, " 100M ") + fmt.Fprintln(fd, " 6K ") + fmt.Fprintln(fd, " false ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " donotopen") + fmt.Fprintln(fd, " socket") + fmt.Fprintln(fd, " FINEST") + fmt.Fprintln(fd, " 192.168.1.255:12124 ") + fmt.Fprintln(fd, " udp ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, "") + fd.Close() + + log := make(Logger) + log.LoadConfiguration(configfile) + defer os.Remove("trace.xml") + defer os.Remove("test.log") + defer log.Close() + + // Make sure we got all loggers + if len(log) != 3 { + t.Fatalf("XMLConfig: Expected 3 filters, found %d", len(log)) + } + + // Make sure they're the right keys + if _, ok := log["stdout"]; !ok { + t.Errorf("XMLConfig: Expected stdout logger") + } + if _, ok := log["file"]; !ok { + t.Fatalf("XMLConfig: Expected file logger") + } + if _, ok := log["xmllog"]; !ok { + t.Fatalf("XMLConfig: Expected xmllog logger") + } + + // Make sure they're the right type + if _, ok := log["stdout"].LogWriter.(ConsoleLogWriter); !ok { + t.Fatalf("XMLConfig: Expected stdout to be ConsoleLogWriter, found %T", log["stdout"].LogWriter) + } + if _, ok := log["file"].LogWriter.(*FileLogWriter); !ok { + t.Fatalf("XMLConfig: Expected file to be *FileLogWriter, found %T", log["file"].LogWriter) + } + if _, ok := log["xmllog"].LogWriter.(*FileLogWriter); !ok { + t.Fatalf("XMLConfig: Expected xmllog to be *FileLogWriter, found %T", log["xmllog"].LogWriter) + } + + // Make sure levels are set + if lvl := log["stdout"].Level; lvl != DEBUG { + t.Errorf("XMLConfig: Expected stdout to be set to level %d, found %d", DEBUG, lvl) + } + if lvl := log["file"].Level; lvl != FINEST { + t.Errorf("XMLConfig: Expected file to be set to level %d, found %d", FINEST, lvl) + } + if lvl := log["xmllog"].Level; lvl != TRACE { + t.Errorf("XMLConfig: Expected xmllog to be set to level %d, found %d", TRACE, lvl) + } + + // Make sure the w is open and points to the right file + if fname := log["file"].LogWriter.(*FileLogWriter).file.Name(); fname != "test.log" { + t.Errorf("XMLConfig: Expected file to have opened %s, found %s", "test.log", fname) + } + + // Make sure the XLW is open and points to the right file + if fname := log["xmllog"].LogWriter.(*FileLogWriter).file.Name(); fname != "trace.xml" { + t.Errorf("XMLConfig: Expected xmllog to have opened %s, found %s", "trace.xml", fname) + } + + // Move XML log file + os.Rename(configfile, "examples/"+configfile) // Keep this so that an example with the documentation is available +} + +func BenchmarkFormatLogRecord(b *testing.B) { + const updateEvery = 1 + rec := &LogRecord{ + Level: CRITICAL, + Created: now, + Source: "source", + Message: "message", + } + for i := 0; i < b.N; i++ { + rec.Created = rec.Created.Add(1 * time.Second / updateEvery) + if i%2 == 0 { + FormatLogRecord(FORMAT_DEFAULT, rec) + } else { + FormatLogRecord(FORMAT_SHORT, rec) + } + } +} + +func BenchmarkConsoleLog(b *testing.B) { + /* This doesn't seem to work on OS X + sink, err := os.Open(os.DevNull) + if err != nil { + panic(err) + } + if err := syscall.Dup2(int(sink.Fd()), syscall.Stdout); err != nil { + panic(err) + } + */ + + stdout = ioutil.Discard + sl := NewDefaultLogger(INFO) + for i := 0; i < b.N; i++ { + sl.Log(WARNING, "here", "This is a log message") + } +} + +func BenchmarkConsoleNotLogged(b *testing.B) { + sl := NewDefaultLogger(INFO) + for i := 0; i < b.N; i++ { + sl.Log(DEBUG, "here", "This is a log message") + } +} + +func BenchmarkConsoleUtilLog(b *testing.B) { + sl := NewDefaultLogger(INFO) + for i := 0; i < b.N; i++ { + sl.Info("%s is a log message", "This") + } +} + +func BenchmarkConsoleUtilNotLog(b *testing.B) { + sl := NewDefaultLogger(INFO) + for i := 0; i < b.N; i++ { + sl.Debug("%s is a log message", "This") + } +} + +func BenchmarkFileLog(b *testing.B) { + sl := make(Logger) + b.StopTimer() + sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false)) + b.StartTimer() + for i := 0; i < b.N; i++ { + sl.Log(WARNING, "here", "This is a log message") + } + b.StopTimer() + os.Remove("benchlog.log") +} + +func BenchmarkFileNotLogged(b *testing.B) { + sl := make(Logger) + b.StopTimer() + sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false)) + b.StartTimer() + for i := 0; i < b.N; i++ { + sl.Log(DEBUG, "here", "This is a log message") + } + b.StopTimer() + os.Remove("benchlog.log") +} + +func BenchmarkFileUtilLog(b *testing.B) { + sl := make(Logger) + b.StopTimer() + sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false)) + b.StartTimer() + for i := 0; i < b.N; i++ { + sl.Info("%s is a log message", "This") + } + b.StopTimer() + os.Remove("benchlog.log") +} + +func BenchmarkFileUtilNotLog(b *testing.B) { + sl := make(Logger) + b.StopTimer() + sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false)) + b.StartTimer() + for i := 0; i < b.N; i++ { + sl.Debug("%s is a log message", "This") + } + b.StopTimer() + os.Remove("benchlog.log") +} + +// Benchmark results (darwin amd64 6g) +//elog.BenchmarkConsoleLog 100000 22819 ns/op +//elog.BenchmarkConsoleNotLogged 2000000 879 ns/op +//elog.BenchmarkConsoleUtilLog 50000 34380 ns/op +//elog.BenchmarkConsoleUtilNotLog 1000000 1339 ns/op +//elog.BenchmarkFileLog 100000 26497 ns/op +//elog.BenchmarkFileNotLogged 2000000 821 ns/op +//elog.BenchmarkFileUtilLog 50000 33945 ns/op +//elog.BenchmarkFileUtilNotLog 1000000 1258 ns/op diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/pattlog.go b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/pattlog.go new file mode 100644 index 00000000..c655f93b --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/pattlog.go @@ -0,0 +1,131 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +package log4go + +import ( + "bytes" + "fmt" + "io" + "os" + "strconv" + "sync/atomic" + "unsafe" +) + +const ( + FORMAT_DEFAULT = "[%D %T] [%L] (%S) %M" + FORMAT_SHORT = "[%t %d] [%L] %M" + FORMAT_ABBREV = "[%L] %M" +) + +type formatCacheType struct { + LastUpdateSeconds int64 + shortTime, shortDate string + longTime, longDate string +} + +var formatCache = unsafe.Pointer(&formatCacheType{}) +var pid = os.Getpid() + +// Known format codes: +// %T - Time (15:04:05 MST) +// %t - Time (15:04) +// %R - Real time (2006-01-02 15:04:05.999999) +// %D - Date (2006/01/02) +// %d - Date (01/02/06) +// %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT) +// %S - Source +// %M - Message +// Ignores unknown formats +// Recommended: "[%D %T] [%L] (%S) %M" +func FormatLogRecord(format string, rec *LogRecord) string { + if rec == nil { + return "" + } + if len(format) == 0 { + return "" + } + + out := bytes.NewBuffer(make([]byte, 0, 64)) + secs := rec.Created.UnixNano() / 1e9 + + cache := *(*formatCacheType)(atomic.LoadPointer(&formatCache)) + if cache.LastUpdateSeconds != secs { + month, day, year := rec.Created.Month(), rec.Created.Day(), rec.Created.Year() + hour, minute, second := rec.Created.Hour(), rec.Created.Minute(), rec.Created.Second() + zone, _ := rec.Created.Zone() + cache = formatCacheType{ + LastUpdateSeconds: secs, + shortTime: fmt.Sprintf("%02d:%02d", hour, minute), + shortDate: fmt.Sprintf("%02d/%02d/%02d", month, day, year%100), + longTime: fmt.Sprintf("%02d:%02d:%02d %s", hour, minute, second, zone), + longDate: fmt.Sprintf("%04d/%02d/%02d", year, month, day), + } + atomic.StorePointer(&formatCache, unsafe.Pointer(&cache)) + } + + // Split the string into pieces by % signs + pieces := bytes.Split([]byte(format), []byte{'%'}) + + // Iterate over the pieces, replacing known formats + for i, piece := range pieces { + if i > 0 && len(piece) > 0 { + switch piece[0] { + case 'P': + out.WriteString(strconv.Itoa(pid)) + case 'T': + out.WriteString(cache.longTime) + case 't': + out.WriteString(cache.shortTime) + case 'D': + out.WriteString(cache.longDate) + case 'd': + out.WriteString(cache.shortDate) + case 'L': + out.WriteString(levelStrings[rec.Level]) + case 'S': + out.WriteString(rec.Source) + case 'M': + out.WriteString(rec.Message) + case 'R': + out.WriteString(rec.Created.Format("2006-01-02 15:04:05.999999")) + } + if len(piece) > 1 { + out.Write(piece[1:]) + } + } else if len(piece) > 0 { + out.Write(piece) + } + } + out.WriteByte('\n') + + return out.String() +} + +// This is the standard writer that prints to standard output. +type FormatLogWriter chan *LogRecord + +// This creates a new FormatLogWriter +func NewFormatLogWriter(out io.Writer, format string) FormatLogWriter { + records := make(FormatLogWriter, LogBufferLength) + go records.run(out, format) + return records +} + +func (w FormatLogWriter) run(out io.Writer, format string) { + for rec := range w { + fmt.Fprint(out, FormatLogRecord(format, rec)) + } +} + +// This is the FormatLogWriter's output method. This will block if the output +// buffer is full. +func (w FormatLogWriter) LogWrite(rec *LogRecord) { + w <- rec +} + +// Close stops the logger from sending messages to standard output. Attempts to +// send log messages to this logger after a Close have undefined behavior. +func (w FormatLogWriter) Close() { + close(w) +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/socklog.go b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/socklog.go new file mode 100644 index 00000000..1d224a99 --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/socklog.go @@ -0,0 +1,57 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +package log4go + +import ( + "encoding/json" + "fmt" + "net" + "os" +) + +// This log writer sends output to a socket +type SocketLogWriter chan *LogRecord + +// This is the SocketLogWriter's output method +func (w SocketLogWriter) LogWrite(rec *LogRecord) { + w <- rec +} + +func (w SocketLogWriter) Close() { + close(w) +} + +func NewSocketLogWriter(proto, hostport string) SocketLogWriter { + sock, err := net.Dial(proto, hostport) + if err != nil { + fmt.Fprintf(os.Stderr, "NewSocketLogWriter(%q): %s\n", hostport, err) + return nil + } + + w := SocketLogWriter(make(chan *LogRecord, LogBufferLength)) + + go func() { + defer func() { + if sock != nil && proto == "tcp" { + sock.Close() + } + }() + + for rec := range w { + // Marshall into JSON + js, err := json.Marshal(rec) + if err != nil { + fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err) + return + } + + _, err = sock.Write(js) + if err != nil { + fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err) + return + } + } + }() + + return w +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/termlog.go b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/termlog.go new file mode 100644 index 00000000..39f60c0e --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/termlog.go @@ -0,0 +1,31 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +package log4go + +import ( + "fmt" + "io" + "os" + "time" +) + +var stdout io.Writer = os.Stdout + +// This is the standard writer that prints to standard output. +type ConsoleLogWriter struct{} + +// This creates a new ConsoleLogWriter +func NewConsoleLogWriter() ConsoleLogWriter { + return ConsoleLogWriter{} +} + +// This is the ConsoleLogWriter's output method. +func (w ConsoleLogWriter) LogWrite(rec *LogRecord) { + timestr := rec.Created.Format(time.StampMicro) + fmt.Fprint(stdout, "[", timestr, "] [", levelStrings[rec.Level], "] ", rec.Message, "\n") +} + +// Close flushes the log. Probably don't need this any more. +func (w ConsoleLogWriter) Close() { + os.Stdout.Sync() +} diff --git a/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/wrapper.go b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/wrapper.go new file mode 100644 index 00000000..10ecd88e --- /dev/null +++ b/vendor/_nuts/github.com/ThomsonReutersEikon/log4go/wrapper.go @@ -0,0 +1,278 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +package log4go + +import ( + "errors" + "os" + "fmt" + "strings" +) + +var ( + Global Logger +) + +func init() { + Global = NewDefaultLogger(DEBUG) +} + +// Wrapper for (*Logger).LoadConfiguration +func LoadConfiguration(filename string) { + Global.LoadConfiguration(filename) +} + +// Wrapper for (*Logger).AddFilter +func AddFilter(name string, lvl level, writer LogWriter) { + Global.AddFilter(name, lvl, writer) +} + +// Wrapper for (*Logger).Close (closes and removes all logwriters) +func Close() { + Global.Close() +} + +func Crash(args ...interface{}) { + if len(args) > 0 { + Global.intLogf(CRITICAL, strings.Repeat(" %v", len(args))[1:], args...) + } + panic(args) +} + +// Logs the given message and crashes the program +func Crashf(format string, args ...interface{}) { + Global.intLogf(CRITICAL, format, args...) + Global.Close() // so that hopefully the messages get logged + panic(fmt.Sprintf(format, args...)) +} + +// Compatibility with `log` +func Exit(args ...interface{}) { + if len(args) > 0 { + Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...) + } + Global.Close() // so that hopefully the messages get logged + os.Exit(0) +} + +// Compatibility with `log` +func Exitf(format string, args ...interface{}) { + Global.intLogf(ERROR, format, args...) + Global.Close() // so that hopefully the messages get logged + os.Exit(0) +} + +// Compatibility with `log` +func Stderr(args ...interface{}) { + if len(args) > 0 { + Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...) + } +} + +// Compatibility with `log` +func Stderrf(format string, args ...interface{}) { + Global.intLogf(ERROR, format, args...) +} + +// Compatibility with `log` +func Stdout(args ...interface{}) { + if len(args) > 0 { + Global.intLogf(INFO, strings.Repeat(" %v", len(args))[1:], args...) + } +} + +// Compatibility with `log` +func Stdoutf(format string, args ...interface{}) { + Global.intLogf(INFO, format, args...) +} + +// Send a log message manually +// Wrapper for (*Logger).Log +func Log(lvl level, source, message string) { + Global.Log(lvl, source, message) +} + +// Send a formatted log message easily +// Wrapper for (*Logger).Logf +func Logf(lvl level, format string, args ...interface{}) { + Global.intLogf(lvl, format, args...) +} + +// Send a closure log message +// Wrapper for (*Logger).Logc +func Logc(lvl level, closure func() string) { + Global.intLogc(lvl, closure) +} + +// Utility for finest log messages (see Debug() for parameter explanation) +// Wrapper for (*Logger).Finest +func Finest(arg0 interface{}, args ...interface{}) { + const ( + lvl = FINEST + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + Global.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + Global.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Utility for fine log messages (see Debug() for parameter explanation) +// Wrapper for (*Logger).Fine +func Fine(arg0 interface{}, args ...interface{}) { + const ( + lvl = FINE + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + Global.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + Global.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Utility for debug log messages +// When given a string as the first argument, this behaves like Logf but with the DEBUG log level (e.g. the first argument is interpreted as a format for the latter arguments) +// When given a closure of type func()string, this logs the string returned by the closure iff it will be logged. The closure runs at most one time. +// When given anything else, the log message will be each of the arguments formatted with %v and separated by spaces (ala Sprint). +// Wrapper for (*Logger).Debug +func Debug(arg0 interface{}, args ...interface{}) { + const ( + lvl = DEBUG + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + Global.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + Global.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Utility for trace log messages (see Debug() for parameter explanation) +// Wrapper for (*Logger).Trace +func Trace(arg0 interface{}, args ...interface{}) { + const ( + lvl = TRACE + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + Global.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + Global.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Utility for info log messages (see Debug() for parameter explanation) +// Wrapper for (*Logger).Info +func Info(arg0 interface{}, args ...interface{}) { + const ( + lvl = INFO + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + Global.intLogf(lvl, first, args...) + case func() string: + // Log the closure (no other arguments used) + Global.intLogc(lvl, first) + default: + // Build a format string so that it will be similar to Sprint + Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) + } +} + +// Utility for warn log messages (returns an error for easy function returns) (see Debug() for parameter explanation) +// These functions will execute a closure exactly once, to build the error message for the return +// Wrapper for (*Logger).Warn +func Warn(arg0 interface{}, args ...interface{}) error { + const ( + lvl = WARNING + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + Global.intLogf(lvl, first, args...) + return errors.New(fmt.Sprintf(first, args...)) + case func() string: + // Log the closure (no other arguments used) + str := first() + Global.intLogf(lvl, "%s", str) + return errors.New(str) + default: + // Build a format string so that it will be similar to Sprint + Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) + return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...)) + } + return nil +} + +// Utility for error log messages (returns an error for easy function returns) (see Debug() for parameter explanation) +// These functions will execute a closure exactly once, to build the error message for the return +// Wrapper for (*Logger).Error +func Error(arg0 interface{}, args ...interface{}) error { + const ( + lvl = ERROR + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + Global.intLogf(lvl, first, args...) + return errors.New(fmt.Sprintf(first, args...)) + case func() string: + // Log the closure (no other arguments used) + str := first() + Global.intLogf(lvl, "%s", str) + return errors.New(str) + default: + // Build a format string so that it will be similar to Sprint + Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) + return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...)) + } + return nil +} + +// Utility for critical log messages (returns an error for easy function returns) (see Debug() for parameter explanation) +// These functions will execute a closure exactly once, to build the error message for the return +// Wrapper for (*Logger).Critical +func Critical(arg0 interface{}, args ...interface{}) error { + const ( + lvl = CRITICAL + ) + switch first := arg0.(type) { + case string: + // Use the string as a format string + Global.intLogf(lvl, first, args...) + return errors.New(fmt.Sprintf(first, args...)) + case func() string: + // Log the closure (no other arguments used) + str := first() + Global.intLogf(lvl, "%s", str) + return errors.New(str) + default: + // Build a format string so that it will be similar to Sprint + Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) + return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...)) + } + return nil +}