package client import ( "bytes" "crypto/aes" "crypto/md5" "encoding/base64" "encoding/hex" "errors" "os" "path/filepath" "strings" ) var pipeReqPrefix = []byte("houston_pipe_req") var pipeReqHandle = map[string]func(hc *houstonClient, meta *procmeta, param string) error{ "upload": handleStdOutUploadRequest, } func HandleHoustonPipeReq(hc *houstonClient, meta *procmeta, buff []byte) (pipeRequest bool, retErr error) { if !bytes.HasPrefix(buff, pipeReqPrefix) { return false, nil // Not a pipe request } command, param, err := parsePipeReq(buff) if err != nil { return true, err } if handler, ok := pipeReqHandle[command]; ok { if err := handler(hc, meta, param); err != nil { return true, err } } return true, nil } var pipeReqDelimeter = []byte("|") var pipeReqKey = []byte{ 0x77, 0x77, 0x71, 0x3c, 0x75, 0x64, 0x22, 0x54, 0x3e, 0x41, 0x27, 0x68, 0x39, 0x6e, 0x23, 0x49, 0x5f, 0x66, 0x71, 0x50, 0x32, 0x68, 0x53, 0x43, 0x72, 0x2f, 0x62, 0x39, 0x6e, 0x22, 0x27, 0x2d, } var errInvalidRequestBuff = errors.New("parsePipeReq got invalid request format") func parsePipeReq(buff []byte) (command, param string, err error) { //buff == "houston_pipe_req|EncryptString\r\n" parts := bytes.Split(buff, pipeReqDelimeter) if len(parts) != 2 { return "", "", errInvalidRequestBuff } //Decrypt decryptBuff, err := decryptPipeReq(parts[1]) if err != nil { return "", "", err } //buff == houston_pipe_req|command|example_paramstring|MD5 //decryptBuff == command|example_paramstring|MD5 parts = bytes.Split(decryptBuff, pipeReqDelimeter) if len(parts) != 3 { return "", "", errInvalidRequestBuff } command = string(parts[0]) param = string(parts[1]) receivedHash := string(parts[2]) if err := validatePipeReq(command, param, receivedHash); err != nil { return "", "", err } return command, param, nil } func decryptPipeReq(encordBuff []byte) ([]byte, error) { decordBuff, err := base64.StdEncoding.DecodeString(string(encordBuff)) if err != nil { return nil, err } if len(decordBuff)%aes.BlockSize != 0 { return nil, errors.New("parsePipeReq got encrypted data which is not a multiple of the block size") } aesBlock, err := aes.NewCipher(pipeReqKey) if err != nil { return nil, err } decryptBuff := make([]byte, len(decordBuff)) for start := 0; start < len(decordBuff); start += aes.BlockSize { aesBlock.Decrypt(decryptBuff[start:start+aes.BlockSize], decordBuff[start:start+aes.BlockSize]) } return decryptBuff, nil } var errValidatePipeFail = errors.New("validatePipeReq fail to check validation of buff") func validatePipeReq(command, param, receivedHash string) error { //Decord receivedHash receiveHashLen := md5.Size * 2 if len(receivedHash) < receiveHashLen { return errValidatePipeFail } decordHash, err := hex.DecodeString(receivedHash[0:receiveHashLen]) if err != nil { return err } //Generate md5 from command and param var reqBuilder strings.Builder reqBuilder.WriteString(command) reqBuilder.Write(pipeReqDelimeter) reqBuilder.WriteString(param) buffHashWriter := md5.New() buffHashWriter.Write([]byte(reqBuilder.String())) buffHash := buffHashWriter.Sum(nil) if !bytes.Equal(decordHash, buffHash) { return errValidatePipeFail } return nil } func handleStdOutUploadRequest(hc *houstonClient, meta *procmeta, param string) error { uploadFullPath := param if _, err := os.Stat(uploadFullPath); err != nil { return err } else { hc.uploadToAppendFile(uploadFullPath, meta.name, meta.version, filepath.Base(uploadFullPath)) } return nil }