コード例 #1
0
ファイル: hooktftp.go プロジェクト: brmzkw/hooktftp
func handleRRQ(res *tftp.RRQresponse) {

	started := time.Now()

	path := res.Request.Path

	logger.Info(fmt.Sprintf(
		"GET %s blocksize %d from %s",
		path,
		res.Request.Blocksize,
		*res.Request.Addr,
	))

	defer res.End()

	var hookResult *hooks.HookResult

	for _, hook := range HOOKS {

		var err error

		hookResult, err = hook(res.Request.Path, *res.Request)
		if err == hooks.NO_MATCH {
			continue
		} else if err != nil {

			if err, ok := err.(*os.PathError); ok {
				res.WriteError(tftp.NOT_FOUND, err.Error())
				return
			}

			logger.Err("Failed to execute hook for '%v' error: %v", res.Request.Path, err)
			res.WriteError(tftp.UNKNOWN_ERROR, "Hook failed: "+err.Error())
			return
		}
		break
	}

	if hookResult == nil {
		res.WriteError(tftp.NOT_FOUND, "No hook matches")
		return
	}

	// Consume stderr in a go routine, and defer the call to hook.Finalize.
	if hookResult.Stderr != nil {

		stderrReady := make(chan bool)

		go func() {
			defer func() {
				if err := hookResult.Stderr.Close(); err != nil {
					logger.Err("Failed to close error reader for %s: %s", res.Request.Path, err)
				}
				stderrReady <- true
			}()

			var bytesRead int
			var err error
			b := make([]byte, 4096)

			for ; err != io.EOF; bytesRead, err = hookResult.Stderr.Read(b) {
				if bytesRead > 0 {
					logger.Warning("Hook error: %s", b[:bytesRead])
				}
				if err != nil {
					logger.Err("Error while reading error reader: %s", err)
					return
				}
			}
		}()

		if hookResult.Finalize != nil {
			defer func() {
				<-stderrReady
				err := hookResult.Finalize()
				if err != nil {
					logger.Err("Hook for %v failed to finalize: %v", res.Request.Path, err)
				}
			}()
		}

	}

	// Close stdout before calling Finalize.
	defer func() {
		err := hookResult.Stdout.Close()
		if err != nil {
			logger.Err("Failed to close reader for %s: %s", res.Request.Path, err)
		}
	}()

	if res.Request.TransferSize != -1 {
		res.TransferSize = hookResult.Length
	}

	if err := res.WriteOACK(); err != nil {
		logger.Err("Failed to write OACK", err)
		return
	}

	b := make([]byte, res.Request.Blocksize)

	totalBytes := 0

	for {
		bytesRead, err := hookResult.Stdout.Read(b)
		totalBytes += bytesRead

		if err == io.EOF {
			if _, err := res.Write(b[:bytesRead]); err != nil {
				logger.Err("Failed to write last bytes of the reader: %s", err)
				return
			}
			break
		} else if err != nil {
			logger.Err("Error while reading %s: %s", hookResult.Stdout, err)
			res.WriteError(tftp.UNKNOWN_ERROR, err.Error())
			return
		}

		if _, err := res.Write(b[:bytesRead]); err != nil {
			logger.Err("Failed to write bytes for %s: %s", path, err)
			return
		}
	}

	took := time.Since(started)

	speed := float64(totalBytes) / took.Seconds() / 1024 / 1024

	logger.Info("Sent %v bytes in %v %f MB/s\n", totalBytes, took, speed)
}
コード例 #2
0
ファイル: shell_hook.go プロジェクト: moul/hooktftp
		cmd := exec.Command("sh", "-c", command)
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			return nil, -1, err
		}
		err = cmd.Start()

		// Buffering content to avoid Reader closing after cmd.Wait()
		// For more informations please see:
		//    http://stackoverflow.com/questions/20134095/why-do-i-get-bad-file-descriptor-in-this-go-program-using-stderr-and-ioutil-re
		// Note:
		//    This is not a perfect solution because of buffering. (Memory usage...)
		//    If you have better solution ...
		out, err := ioutil.ReadAll(stdout)
		if err != nil {
			logger.Err("Shell output buffering failed: %v", err)
		}

		// Use goroutine to log the exit status for debugging purposes.
		// XXX: It probably is bad practice to access variables from multiple
		// goroutines, but I hope it is ok in this case since the purpose is
		// only to read and log the status. If not we must remove this bit.
		// Please let me know if you know better.
		go func() {
			err := cmd.Wait()
			if err != nil {
				logger.Err("Command '%v' failed to execute: '%v'", command, err)
			}
		}()

		return ioutil.NopCloser(bytes.NewReader(out)), -1, err
コード例 #3
0
ファイル: hooktftp.go プロジェクト: brmzkw/hooktftp
func main() {

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "\nUsage: %s [-v] [config]\n", os.Args[0])
	}
	verbose := flag.Bool("v", false, "a bool")
	flag.Parse()

	if !*verbose {
		e := logger.Initialize("hooktftp")
		if e != nil {
			log.Fatal("Failed to initialize logger")
		}
	}

	if len(flag.Args()) > 0 {
		CONFIG_PATH = flag.Args()[0]
	}

	logger.Info("Reading hooks from %s", CONFIG_PATH)

	configData, err := ioutil.ReadFile(CONFIG_PATH)

	if err != nil {
		logger.Crit("Failed to read config: %s", err)
		return
	}

	conf, err := config.ParseYaml(configData)
	if err != nil {
		logger.Crit("Failed to parse config: %s", err)
		return
	}

	for _, hookDef := range conf.HookDefs {
		logger.Notice("Compiling hook %s", hookDef)

		// Create new hookDef variable for the hookDef pointer for each loop
		// iteration. Go reuses the hookDef variable and if we pass pointer to
		// that terrible things happen.
		newPointer := hookDef
		hook, err := hooks.CompileHook(&newPointer)
		if err != nil {
			logger.Crit("Failed to compile hook %s: %s", hookDef, err)
			return
		}
		HOOKS = append(HOOKS, hook)
	}

	if conf.Port == "" {
		conf.Port = "69"
	}

	addr, err := net.ResolveUDPAddr("udp", conf.Host+":"+conf.Port)
	if err != nil {
		logger.Crit("Failed to resolve address: %s", err)
		return
	}

	server, err := tftp.NewTFTPServer(addr)
	if err != nil {
		logger.Crit("Failed to listen: %s", err)
		return
	}

	logger.Notice("Listening on %s:%d", conf.Host, conf.Port)

	if conf.User != "" {
		err := DropPrivileges(conf.User)
		if err != nil {
			logger.Crit("Failed to drop privileges to '%s' error: %v", conf.User, err)
			return
		}
		currentUser, _ := user.Current()
		logger.Notice("Dropped privileges to %s", currentUser)
	}

	if conf.User == "" && syscall.Getuid() == 0 {
		logger.Warning("Running as root and 'user' is not set in %s", CONFIG_PATH)
	}

	for {
		res, err := server.Accept()
		if err != nil {
			logger.Err("Bad tftp request: %s", err)
			continue
		}

		go handleRRQ(res)
	}

	logger.Close()

}
コード例 #4
0
ファイル: hooktftp.go プロジェクト: moul/hooktftp
func handleRRQ(res *tftp.RRQresponse) {

	started := time.Now()

	path := res.Request.Path

	logger.Info(fmt.Sprintf(
		"GET %s blocksize %d from %s",
		path,
		res.Request.Blocksize,
		*res.Request.Addr,
	))

	var reader io.ReadCloser
	var len int
	for _, hook := range HOOKS {
		var err error
		reader, len, err = hook(res.Request.Path)
		if err == hooks.NO_MATCH {
			continue
		} else if err != nil {

			if err, ok := err.(*os.PathError); ok {
				res.WriteError(tftp.NOT_FOUND, err.Error())
				return
			}

			logger.Err("Failed to execute hook for '%v' error: %v", res.Request.Path, err)
			res.WriteError(tftp.UNKNOWN_ERROR, "Hook failed: "+err.Error())
			return
		}
		defer func() {
			err := reader.Close()
			if err != nil {
				logger.Err("Failed to close reader for %s: %s", res.Request.Path, err)
			}
		}()
		break
	}

	if reader == nil {
		res.WriteError(tftp.NOT_FOUND, "No hook matches")
		return
	}

	if res.Request.TransferSize != -1 {
		res.TransferSize = len
	}

	if err := res.WriteOACK(); err != nil {
		logger.Err("Failed to write OACK", err)
		return
	}

	b := make([]byte, res.Request.Blocksize)

	totalBytes := 0

	for {
		bytesRead, err := reader.Read(b)
		totalBytes += bytesRead

		if err == io.EOF {
			if _, err := res.Write(b[:bytesRead]); err != nil {
				logger.Err("Failed to write last bytes of the reader: %s", err)
				return
			}
			res.End()
			break
		} else if err != nil {
			logger.Err("Error while reading %s: %s", reader, err)
			res.WriteError(tftp.UNKNOWN_ERROR, err.Error())
			return
		}

		if _, err := res.Write(b[:bytesRead]); err != nil {
			logger.Err("Failed to write bytes for %s: %s", path, err)
			return
		}
	}

	took := time.Since(started)

	speed := float64(totalBytes) / took.Seconds() / 1024 / 1024

	logger.Info("Sent %v bytes in %v %f MB/s\n", totalBytes, took, speed)
}