diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 0000000..65247a2 --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,7 @@ +memo = "6d565002a9a2af555327a35b973166b0b4d3cc082fa045bd3917e34205a70a05" + +[[projects]] + branch = "master" + name = "github.com/dutchcoders/go-clamd" + packages = ["."] + revision = "b970184f4d9e88ff402581ec3dd3b9d074cfb90c" diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 0000000..9e2692e --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,56 @@ + +## Gopkg.toml example (these lines may be deleted) + +## "required" lists a set of packages (not projects) that must be included in +## Gopkg.lock. This list is merged with the set of packages imported by the current +## project. Use it when your project needs a package it doesn't explicitly import - +## including "main" packages. +# required = ["github.com/user/thing/cmd/thing"] + +## "ignored" lists a set of packages (not projects) that are ignored when +## dep statically analyzes source code. Ignored packages can be in this project, +## or in a dependency. +# ignored = ["github.com/user/project/badpkg"] + +## Dependencies define constraints on dependent projects. They are respected by +## dep whether coming from the Gopkg.toml of the current project or a dependency. +# [[dependencies]] +## Required: the root import path of the project being constrained. +# name = "github.com/user/project" +# +## Recommended: the version constraint to enforce for the project. +## Only one of "branch", "version" or "revision" can be specified. +# version = "1.0.0" +# branch = "master" +# revision = "abc123" +# +## Optional: an alternate location (URL or import path) for the project's source. +# source = "https://github.com/myfork/package.git" + +## Overrides have the same structure as [[dependencies]], but supercede all +## [[dependencies]] declarations from all projects. Only the current project's +## [[overrides]] are applied. +## +## Overrides are a sledgehammer. Use them only as a last resort. +# [[overrides]] +## Required: the root import path of the project being constrained. +# name = "github.com/user/project" +# +## Optional: specifying a version constraint override will cause all other +## constraints on this project to be ignored; only the overriden constraint +## need be satisfied. +## Again, only one of "branch", "version" or "revision" can be specified. +# version = "1.0.0" +# branch = "master" +# revision = "abc123" +# +## Optional: specifying an alternate source location as an override will +## enforce that the alternate location is used for that project, regardless of +## what source location any dependent projects specify. +# source = "https://github.com/myfork/package.git" + + + +[[dependencies]] + branch = "master" + name = "github.com/dutchcoders/go-clamd" diff --git a/vendor/github.com/dutchcoders/go-clamd/.gitignore b/vendor/github.com/dutchcoders/go-clamd/.gitignore new file mode 100644 index 0000000..daf913b --- /dev/null +++ b/vendor/github.com/dutchcoders/go-clamd/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/dutchcoders/go-clamd/.travis.yml b/vendor/github.com/dutchcoders/go-clamd/.travis.yml new file mode 100644 index 0000000..212857d --- /dev/null +++ b/vendor/github.com/dutchcoders/go-clamd/.travis.yml @@ -0,0 +1,10 @@ +language: go +go: + - 1.1 + - 1.2 + - 1.3 + - release + - tip + +script: + - go test -v ./... diff --git a/vendor/github.com/dutchcoders/go-clamd/LICENSE b/vendor/github.com/dutchcoders/go-clamd/LICENSE new file mode 100644 index 0000000..e85f3f1 --- /dev/null +++ b/vendor/github.com/dutchcoders/go-clamd/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 dutchcoders + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/dutchcoders/go-clamd/README.md b/vendor/github.com/dutchcoders/go-clamd/README.md new file mode 100644 index 0000000..f043bbf --- /dev/null +++ b/vendor/github.com/dutchcoders/go-clamd/README.md @@ -0,0 +1,35 @@ +go-clamd +======== + +Interface to clamd (clamav daemon). You can use go-clamd to implement virus detection capabilities to your application. + +[![GoDoc](https://godoc.org/github.com/dutchcoders/go-clamd?status.svg)](https://godoc.org/github.com/dutchcoders/go-clamd) +[![Build Status](https://travis-ci.org/dutchcoders/go-clamd.svg?branch=master)](https://travis-ci.org/dutchcoders/go-clamd) + +## Examples + +``` +c := clamd.NewClamd("/tmp/clamd.socket") + +reader := bytes.NewReader(clamd.EICAR) +response, err := c.ScanStream(reader, make(chan bool)) + +for s := range response { + fmt.Printf("%v %v\n", s, err) +} +``` + +## Contributions + +Contributions are welcome. + +## Creators + +**Remco Verhoef** +- + +- + +## Copyright and license + +Code and documentation copyright 2011-2014 Remco Verhoef. Code released under [the MIT license](LICENSE). diff --git a/vendor/github.com/dutchcoders/go-clamd/clamd.go b/vendor/github.com/dutchcoders/go-clamd/clamd.go new file mode 100644 index 0000000..5199f63 --- /dev/null +++ b/vendor/github.com/dutchcoders/go-clamd/clamd.go @@ -0,0 +1,311 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 DutchCoders + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package clamd + +import ( + "errors" + "fmt" + "io" + "net/url" + "strings" +) + +const ( + RES_OK = "OK" + RES_FOUND = "FOUND" + RES_ERROR = "ERROR" + RES_PARSE_ERROR = "PARSE ERROR" +) + +type Clamd struct { + address string +} + +type Stats struct { + Pools string + State string + Threads string + Memstats string + Queue string +} + +type ScanResult struct { + Raw string + Description string + Path string + Hash string + Size int + Status string +} + +var EICAR = []byte(`X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*`) + +func (c *Clamd) newConnection() (conn *CLAMDConn, err error) { + + var u *url.URL + + if u, err = url.Parse(c.address); err != nil { + return + } + + switch u.Scheme { + case "tcp": + conn, err = newCLAMDTcpConn(u.Host) + case "unix": + conn, err = newCLAMDUnixConn(u.Path) + default: + conn, err = newCLAMDUnixConn(c.address) + } + + return +} + +func (c *Clamd) simpleCommand(command string) (chan *ScanResult, error) { + conn, err := c.newConnection() + if err != nil { + return nil, err + } + + err = conn.sendCommand(command) + if err != nil { + return nil, err + } + + ch, wg, err := conn.readResponse() + + go func() { + wg.Wait() + conn.Close() + }() + + return ch, err +} + +/* +Check the daemon's state (should reply with PONG). +*/ +func (c *Clamd) Ping() error { + ch, err := c.simpleCommand("PING") + if err != nil { + return err + } + + select { + case s := (<-ch): + switch s.Raw { + case "PONG": + return nil + default: + return errors.New(fmt.Sprintf("Invalid response, got %s.", s)) + } + } + + return nil +} + +/* +Print program and database versions. +*/ +func (c *Clamd) Version() (chan *ScanResult, error) { + dataArrays, err := c.simpleCommand("VERSION") + return dataArrays, err +} + +/* +On this command clamd provides statistics about the scan queue, contents of scan +queue, and memory usage. The exact reply format is subject to changes in future +releases. +*/ +func (c *Clamd) Stats() (*Stats, error) { + ch, err := c.simpleCommand("STATS") + if err != nil { + return nil, err + } + + stats := &Stats{} + + for s := range ch { + if strings.HasPrefix(s.Raw, "POOLS") { + stats.Pools = strings.Trim(s.Raw[6:], " ") + } else if strings.HasPrefix(s.Raw, "STATE") { + stats.State = s.Raw + } else if strings.HasPrefix(s.Raw, "THREADS") { + stats.Threads = s.Raw + } else if strings.HasPrefix(s.Raw, "QUEUE") { + stats.Queue = s.Raw + } else if strings.HasPrefix(s.Raw, "MEMSTATS") { + stats.Memstats = s.Raw + } else if strings.HasPrefix(s.Raw, "END") { + } else { + // return nil, errors.New(fmt.Sprintf("Unknown response, got %s.", s)) + } + } + + return stats, nil +} + +/* +Reload the databases. +*/ +func (c *Clamd) Reload() error { + ch, err := c.simpleCommand("RELOAD") + if err != nil { + return err + } + + select { + case s := (<-ch): + switch s.Raw { + case "RELOADING": + return nil + default: + return errors.New(fmt.Sprintf("Invalid response, got %s.", s)) + } + } + + return nil +} + +func (c *Clamd) Shutdown() error { + _, err := c.simpleCommand("SHUTDOWN") + if err != nil { + return err + } + + return err +} + +/* +Scan file or directory (recursively) with archive support enabled (a full path is +required). +*/ +func (c *Clamd) ScanFile(path string) (chan *ScanResult, error) { + command := fmt.Sprintf("SCAN %s", path) + ch, err := c.simpleCommand(command) + return ch, err +} + +/* +Scan file or directory (recursively) with archive and special file support disabled +(a full path is required). +*/ +func (c *Clamd) RawScanFile(path string) (chan *ScanResult, error) { + command := fmt.Sprintf("RAWSCAN %s", path) + ch, err := c.simpleCommand(command) + return ch, err +} + +/* +Scan file in a standard way or scan directory (recursively) using multiple threads +(to make the scanning faster on SMP machines). +*/ +func (c *Clamd) MultiScanFile(path string) (chan *ScanResult, error) { + command := fmt.Sprintf("MULTISCAN %s", path) + ch, err := c.simpleCommand(command) + return ch, err +} + +/* +Scan file or directory (recursively) with archive support enabled and don’t stop +the scanning when a virus is found. +*/ +func (c *Clamd) ContScanFile(path string) (chan *ScanResult, error) { + command := fmt.Sprintf("CONTSCAN %s", path) + ch, err := c.simpleCommand(command) + return ch, err +} + +/* +Scan file or directory (recursively) with archive support enabled and don’t stop +the scanning when a virus is found. +*/ +func (c *Clamd) AllMatchScanFile(path string) (chan *ScanResult, error) { + command := fmt.Sprintf("ALLMATCHSCAN %s", path) + ch, err := c.simpleCommand(command) + return ch, err +} + +/* +Scan a stream of data. The stream is sent to clamd in chunks, after INSTREAM, +on the same socket on which the command was sent. This avoids the overhead +of establishing new TCP connections and problems with NAT. The format of the +chunk is: where is the size of the following data in +bytes expressed as a 4 byte unsigned integer in network byte order and is +the actual chunk. Streaming is terminated by sending a zero-length chunk. Note: +do not exceed StreamMaxLength as defined in clamd.conf, otherwise clamd will +reply with INSTREAM size limit exceeded and close the connection +*/ +func (c *Clamd) ScanStream(r io.Reader, abort chan bool) (chan *ScanResult, error) { + conn, err := c.newConnection() + if err != nil { + return nil, err + } + + go func() { + for { + _, allowRunning := <-abort + if !allowRunning { + break + } + } + conn.Close() + }() + + conn.sendCommand("INSTREAM") + + for { + buf := make([]byte, CHUNK_SIZE) + + nr, err := r.Read(buf) + if nr > 0 { + conn.sendChunk(buf[0:nr]) + } + + if err != nil { + break + } + + } + + err = conn.sendEOF() + if err != nil { + return nil, err + } + + ch, wg, err := conn.readResponse() + + go func() { + wg.Wait() + conn.Close() + }() + + return ch, nil +} + +func NewClamd(address string) *Clamd { + clamd := &Clamd{address: address} + return clamd +} diff --git a/vendor/github.com/dutchcoders/go-clamd/conn.go b/vendor/github.com/dutchcoders/go-clamd/conn.go new file mode 100644 index 0000000..5c9f7f9 --- /dev/null +++ b/vendor/github.com/dutchcoders/go-clamd/conn.go @@ -0,0 +1,178 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 DutchCoders + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package clamd + +import ( + "bufio" + "fmt" + "io" + "net" + "regexp" + "strconv" + "strings" + "sync" + "time" +) + +const CHUNK_SIZE = 1024 +const TCP_TIMEOUT = time.Second * 2 + +var resultRegex = regexp.MustCompile( + `^(?P[^:]+): ((?P[^:]+)(\((?P([^:]+)):(?P\d+)\))? )?(?PFOUND|ERROR|OK)$`, +) + +type CLAMDConn struct { + net.Conn +} + +func (conn *CLAMDConn) sendCommand(command string) error { + commandBytes := []byte(fmt.Sprintf("n%s\n", command)) + + _, err := conn.Write(commandBytes) + return err +} + +func (conn *CLAMDConn) sendEOF() error { + _, err := conn.Write([]byte{0, 0, 0, 0}) + return err +} + +func (conn *CLAMDConn) sendChunk(data []byte) error { + var buf [4]byte + lenData := len(data) + buf[0] = byte(lenData >> 24) + buf[1] = byte(lenData >> 16) + buf[2] = byte(lenData >> 8) + buf[3] = byte(lenData >> 0) + + a := buf + + b := make([]byte, len(a)) + for i := range a { + b[i] = a[i] + } + + conn.Write(b) + + _, err := conn.Write(data) + return err +} + +func (c *CLAMDConn) readResponse() (chan *ScanResult, *sync.WaitGroup, error) { + var wg sync.WaitGroup + + wg.Add(1) + reader := bufio.NewReader(c) + ch := make(chan *ScanResult) + + go func() { + defer func() { + close(ch) + wg.Done() + }() + + for { + line, err := reader.ReadString('\n') + if err == io.EOF { + return + } + + if err != nil { + return + } + + line = strings.TrimRight(line, " \t\r\n") + ch <- parseResult(line) + } + }() + + return ch, &wg, nil +} + +func parseResult(line string) *ScanResult { + res := &ScanResult{} + res.Raw = line + + matches := resultRegex.FindStringSubmatch(line) + if len(matches) == 0 { + res.Description = "Regex had no matches" + res.Status = RES_PARSE_ERROR + return res + } + + for i, name := range resultRegex.SubexpNames() { + switch name { + case "path": + res.Path = matches[i] + case "desc": + res.Description = matches[i] + case "virhash": + res.Hash = matches[i] + case "virsize": + i, err := strconv.Atoi(matches[i]) + if err == nil { + res.Size = i + } + case "status": + switch matches[i] { + case RES_OK: + case RES_FOUND: + case RES_ERROR: + break + default: + res.Description = "Invalid status field: " + matches[i] + res.Status = RES_PARSE_ERROR + return res + } + res.Status = matches[i] + } + } + + return res +} + +func newCLAMDTcpConn(address string) (*CLAMDConn, error) { + conn, err := net.DialTimeout("tcp", address, TCP_TIMEOUT) + + if err != nil { + if nerr, isOk := err.(net.Error); isOk && nerr.Timeout() { + return nil, nerr + } + + return nil, err + } + + return &CLAMDConn{Conn: conn}, err +} + +func newCLAMDUnixConn(address string) (*CLAMDConn, error) { + conn, err := net.Dial("unix", address) + if err != nil { + return nil, err + } + + return &CLAMDConn{Conn: conn}, err +} diff --git a/vendor/github.com/dutchcoders/go-clamd/examples/main.go b/vendor/github.com/dutchcoders/go-clamd/examples/main.go new file mode 100644 index 0000000..1b4ccf2 --- /dev/null +++ b/vendor/github.com/dutchcoders/go-clamd/examples/main.go @@ -0,0 +1,72 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 DutchCoders + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package main + +import ( + _ "bytes" + "fmt" + "github.com/dutchcoders/go-clamd" +) + +func main() { + fmt.Println("Made with <3 DutchCoders") + + c := clamd.NewClamd("/tmp/clamd.socket") + _ = c + + /* + reader := bytes.NewReader(clamd.EICAR) + response, err := c.ScanStream(reader) + + for s := range response { + fmt.Printf("%v %v\n", s, err) + } + + response, err = c.ScanFile(".") + + for s := range response { + fmt.Printf("%v %v\n", s, err) + } + + response, err = c.Version() + + for s := range response { + fmt.Printf("%v %v\n", s, err) + } + */ + + err := c.Ping() + fmt.Printf("Ping: %v\n", err) + + stats, err := c.Stats() + fmt.Printf("%v %v\n", stats, err) + + err = c.Reload() + fmt.Printf("Reload: %v\n", err) + + // response, err = c.Shutdown() + // fmt.Println(response) +}