github.com/LanceLRQ/deer-common@v0.0.9-0.20210319081233-e8222ac018a8/persistence/judge_result/reader.go (about) 1 package judge_result 2 3 import ( 4 "bufio" 5 "bytes" 6 "compress/gzip" 7 "encoding/binary" 8 "github.com/LanceLRQ/deer-common/constants" 9 "github.com/LanceLRQ/deer-common/persistence" 10 commonStructs "github.com/LanceLRQ/deer-common/structs" 11 "github.com/LanceLRQ/deer-common/utils" 12 "github.com/pkg/errors" 13 uuid "github.com/satori/go.uuid" 14 "io" 15 "os" 16 "path" 17 "reflect" 18 "strings" 19 ) 20 21 // 解析判题结果 22 func parseJudgeResultBinary(reader io.Reader) (*JudgeResultPackage, error) { 23 // 校验魔数 24 magic := uint16(0) 25 if err := binary.Read(reader, binary.BigEndian, &magic); err != nil { 26 return nil, errors.Errorf("read file error: %s", err.Error()) 27 } 28 if magic != constants.JudgeResultMagicCode { 29 return nil, errors.Errorf("not deer-executor judge result file") 30 } 31 // 开始解析package 32 pack := JudgeResultPackage{} 33 if err := binary.Read(reader, binary.BigEndian, &pack.Version); err != nil { 34 return nil, errors.Errorf("read [version] error: %s", err.Error()) 35 } 36 if err := binary.Read(reader, binary.BigEndian, &pack.CompressorType); err != nil { 37 return nil, errors.Errorf("read [compressor] type error: %s", err.Error()) 38 } 39 if err := binary.Read(reader, binary.BigEndian, &pack.ResultSize); err != nil { 40 return nil, errors.Errorf("read [result size] error: %s", err.Error()) 41 } 42 if err := binary.Read(reader, binary.BigEndian, &pack.BodySize); err != nil { 43 return nil, errors.Errorf("read [body size] error: %s", err.Error()) 44 } 45 if err := binary.Read(reader, binary.BigEndian, &pack.CertSize); err != nil { 46 return nil, errors.Errorf("read [cert size] error: %s", err.Error()) 47 } 48 // 如果有证书 49 if pack.CertSize > 0 { 50 pack.Certificate = make([]byte, pack.CertSize) 51 if err := binary.Read(reader, binary.BigEndian, &pack.Certificate); err != nil { 52 return nil, errors.Errorf("read [cert public key] error: %s", err.Error()) 53 } 54 } 55 // 读取签名 56 if err := binary.Read(reader, binary.BigEndian, &pack.SignSize); err != nil { 57 return nil, errors.Errorf("read [sign size] error: %s", err.Error()) 58 } 59 pack.Signature = make([]byte, pack.SignSize) 60 if err := binary.Read(reader, binary.BigEndian, &pack.Signature); err != nil { 61 return nil, errors.Errorf("read [signature] error: %s", err.Error()) 62 } 63 // 读取Result 64 pack.Result = make([]byte, pack.ResultSize) 65 if err := binary.Read(reader, binary.BigEndian, &pack.Result); err != nil { 66 return nil, errors.Errorf("read [result] error: %s", err.Error()) 67 } 68 // 理论上BodySize是多余的,剩下的都是body,这里就作为校验吧! 69 tmpBodyFileName := uuid.NewV1().String() + ".tmp.gz" 70 tmpBodyFilePath := path.Join("/tmp/", tmpBodyFileName) 71 pack.BodyPackageFile = tmpBodyFilePath 72 tmpBodyFile, err := os.Create(pack.BodyPackageFile) 73 if err != nil { 74 return nil, errors.Errorf("create body package temp file error: %s", err.Error()) 75 } 76 defer tmpBodyFile.Close() 77 if _, err := io.Copy(tmpBodyFile, reader); err != nil { 78 return nil, errors.Errorf("write body package temp file error: %s", err.Error()) 79 } 80 81 return &pack, nil 82 } 83 84 // 校验判题结果数据包 85 func validateJudgeResultPackage(pack *JudgeResultPackage) (bool, error) { 86 // 打开临时文件 87 tmpBodyFile, err := os.Open(pack.BodyPackageFile) 88 if err != nil { 89 return false, errors.Errorf("open body package temp file error: %s", err.Error()) 90 } 91 defer tmpBodyFile.Close() 92 93 hash, err := persistence.SHA256Streams([]io.Reader{ 94 bytes.NewReader(pack.Result), 95 tmpBodyFile, 96 }) 97 if err != nil { 98 return false, err 99 } 100 101 // 进行签名校验 102 if pack.CertSize > 0 { 103 publicKey, err := persistence.ReadAndParsePublicKey(pack.Certificate) 104 if err != nil { 105 return false, err 106 } 107 err = persistence.RSA2048Verify(hash, pack.Signature, publicKey) 108 if err != nil { 109 return false, err 110 } 111 } else { 112 return reflect.DeepEqual(hash, pack.Signature), nil 113 } 114 return true, nil 115 } 116 117 // 读取判题结果 118 func ReadJudgeResult(resultFile string) (*commonStructs.JudgeResult, error) { 119 rf, err := os.Open(resultFile) 120 if err != nil { 121 return nil, errors.Errorf("open file (%s) error: %s", resultFile, err.Error()) 122 } 123 reader := bufio.NewReader(rf) 124 125 pack, err := parseJudgeResultBinary(reader) 126 if err != nil { 127 return nil, err 128 } 129 130 ok, err := validateJudgeResultPackage(pack) 131 if !ok || err != nil { 132 if err != nil { 133 return nil, errors.Errorf("validate package hash error: %s", err.Error()) 134 } 135 return nil, errors.Errorf("validate package hash error") 136 } 137 138 judgeResult := commonStructs.JudgeResult{} 139 utils.JSONBytesObject(pack.Result, &judgeResult) 140 141 // 如果使用了Gz 142 if pack.CompressorType == 1 { 143 fp, err := os.Open(pack.BodyPackageFile) 144 if err != nil { 145 return nil, err 146 } 147 zipReader, err := gzip.NewReader(fp) 148 if err != nil { 149 return nil, err 150 } 151 fn := strings.Replace(pack.BodyPackageFile, ".tmp.gz", ".tmp", -1) 152 fout, err := os.Create(fn) 153 if err != nil { 154 return nil, err 155 } 156 defer fout.Close() 157 if _, err = io.Copy(fout, zipReader); err != nil { 158 return nil, err 159 } 160 pack.BodyPackageFile = fn 161 } else { 162 newPath := strings.Replace(pack.BodyPackageFile, ".tmp.gz", ".tmp", -1) 163 err := os.Rename(pack.BodyPackageFile, newPath) 164 if err != nil { 165 return nil, err 166 } 167 pack.BodyPackageFile = newPath 168 } 169 170 return &judgeResult, nil 171 }