99 "encoding/binary"
1010 "errors"
1111 "fmt"
12+ "hash"
1213 "io"
1314 "sync"
1415 "time"
@@ -712,14 +713,14 @@ func convertHeadPrm(signer ecdsa.PrivateKey, req *protoobject.HeadRequest, resp
712713 var hdr * object.Object
713714 return hdr , c .ForEachGRPCConn (ctx , func (ctx context.Context , conn * grpc.ClientConn ) error {
714715 var err error
715- hdr , err = getHeaderFromRemoteNode (ctx , conn , req )
716+ hdr , err = getHeaderFromRemoteNode (ctx , conn , req , addr . Object () )
716717 return err // TODO: log error
717718 })
718719 })
719720 return p , nil
720721}
721722
722- func getHeaderFromRemoteNode (ctx context.Context , conn * grpc.ClientConn , req * protoobject.HeadRequest ) (* object.Object , error ) {
723+ func getHeaderFromRemoteNode (ctx context.Context , conn * grpc.ClientConn , req * protoobject.HeadRequest , reqOID oid. ID ) (* object.Object , error ) {
723724 resp , err := protoobject .NewObjectServiceClient (conn ).Head (ctx , req )
724725 if err != nil {
725726 return nil , fmt .Errorf ("sending the request failed: %w" , err )
@@ -770,6 +771,10 @@ func getHeaderFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *pr
770771 return nil , errors .New ("missing signature" )
771772 }
772773
774+ if err := checkHeaderAgainstID (v .Header .Header , reqOID ); err != nil {
775+ return nil , err
776+ }
777+
773778 hdr = v .Header .Header
774779 idSig = v .Header .Signature
775780 case * protoobject.HeadResponse_Body_SplitInfo :
@@ -1101,12 +1106,17 @@ func convertGetPrm(signer ecdsa.PrivateKey, req *protoobject.GetRequest, stream
11011106 }
11021107
11031108 var onceResign sync.Once
1104- var onceHdr sync.Once
1105- var respondedPayload int
11061109 meta := req .GetMetaHeader ()
11071110 if meta == nil {
11081111 return getsvc.Prm {}, errors .New ("missing meta header" )
11091112 }
1113+
1114+ proxyCtx := getProxyContext {
1115+ req : req ,
1116+ reqOID : addr .Object (),
1117+ respStream : stream ,
1118+ }
1119+
11101120 p .SetRequestForwarder (func (ctx context.Context , node client.NodeInfo , c client.MultiAddressClient ) (* object.Object , error ) {
11111121 var err error
11121122 onceResign .Do (func () {
@@ -1122,7 +1132,7 @@ func convertGetPrm(signer ecdsa.PrivateKey, req *protoobject.GetRequest, stream
11221132 }
11231133
11241134 return nil , c .ForEachGRPCConn (ctx , func (ctx context.Context , conn * grpc.ClientConn ) error {
1125- err := continueGetFromRemoteNode (ctx , conn , req , stream , & onceHdr , & respondedPayload )
1135+ err := proxyCtx . continueWithConn (ctx , conn )
11261136 if errors .Is (err , io .EOF ) {
11271137 return nil
11281138 }
@@ -1132,8 +1142,19 @@ func convertGetPrm(signer ecdsa.PrivateKey, req *protoobject.GetRequest, stream
11321142 return p , nil
11331143}
11341144
1135- func continueGetFromRemoteNode (ctx context.Context , conn * grpc.ClientConn , req * protoobject.GetRequest , stream * getStream , onceHdr * sync.Once , respondedPayload * int ) error {
1136- getStream , err := protoobject .NewObjectServiceClient (conn ).Get (ctx , req )
1145+ type getProxyContext struct {
1146+ req * protoobject.GetRequest
1147+ reqOID oid.ID
1148+ respStream * getStream
1149+
1150+ onceHdr sync.Once
1151+ respondedPayload int
1152+ payloadHashCheck []byte
1153+ payloadHashGot hash.Hash
1154+ }
1155+
1156+ func (x * getProxyContext ) continueWithConn (ctx context.Context , conn * grpc.ClientConn ) error {
1157+ getStream , err := protoobject .NewObjectServiceClient (conn ).Get (ctx , x .req )
11371158 if err != nil {
11381159 return fmt .Errorf ("stream opening failed: %w" , err )
11391160 }
@@ -1147,6 +1168,9 @@ func continueGetFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *
11471168 if ! headWas {
11481169 return io .ErrUnexpectedEOF
11491170 }
1171+ if ! bytes .Equal (x .payloadHashGot .Sum (nil ), x .payloadHashCheck ) {
1172+ return errors .New ("received payload mismatches checksum from header" )
1173+ }
11501174 return io .EOF
11511175 }
11521176 return fmt .Errorf ("reading the response failed: %w" , err )
@@ -1167,6 +1191,17 @@ func continueGetFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *
11671191 if v == nil || v .Init == nil {
11681192 return errors .New ("nil header oneof field" )
11691193 }
1194+
1195+ if v .Init .Header == nil {
1196+ return errors .New ("invalid response: missing header" )
1197+ }
1198+ if v .Init .Header .PayloadHash == nil {
1199+ return errors .New ("invalid response: invalid header: missing payload hash" )
1200+ }
1201+ if err := checkHeaderAgainstID (v .Init .Header , x .reqOID ); err != nil {
1202+ return err
1203+ }
1204+
11701205 mo := & protoobject.Object {
11711206 ObjectId : v .Init .ObjectId ,
11721207 Signature : v .Init .Signature ,
@@ -1177,27 +1212,31 @@ func continueGetFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *
11771212 if err != nil {
11781213 return err
11791214 }
1180- onceHdr .Do (func () {
1181- err = stream .WriteHeader (obj )
1215+ x . onceHdr .Do (func () {
1216+ err = x . respStream .WriteHeader (obj )
11821217 })
11831218 if err != nil {
11841219 return fmt .Errorf ("could not write object header in Get forwarder: %w" , err )
11851220 }
1221+
1222+ x .payloadHashCheck = v .Init .Header .PayloadHash .Sum
1223+ x .payloadHashGot = sha256 .New ()
11861224 case * protoobject.GetResponse_Body_Chunk :
11871225 if ! headWas {
11881226 return errors .New ("incorrect message sequence" )
11891227 }
11901228 fullChunk := v .Chunk
1191- respChunk := chunkToSend (* respondedPayload , readPayload , fullChunk )
1229+ respChunk := chunkToSend (x . respondedPayload , readPayload , fullChunk )
11921230 if len (respChunk ) == 0 {
11931231 readPayload += len (fullChunk )
11941232 continue
11951233 }
1196- if err := stream .WriteChunk (respChunk ); err != nil {
1234+ if err := x . respStream .WriteChunk (respChunk ); err != nil {
11971235 return fmt .Errorf ("could not write object chunk in Get forwarder: %w" , err )
11981236 }
11991237 readPayload += len (fullChunk )
1200- * respondedPayload += len (respChunk )
1238+ x .respondedPayload += len (respChunk )
1239+ x .payloadHashGot .Write (respChunk ) // never returns an error according to docs
12011240 case * protoobject.GetResponse_Body_SplitInfo :
12021241 if v == nil || v .SplitInfo == nil {
12031242 return errors .New ("nil split info oneof field" )
@@ -2261,3 +2300,14 @@ func needSignGetResponse(req interface {
22612300 mjr := ver .GetMajor () // NPE-safe
22622301 return mjr < 2 || mjr == 2 && ver .GetMinor () <= 17
22632302}
2303+
2304+ func checkHeaderAgainstID (hdr * protoobject.Header , id oid.ID ) error {
2305+ b := make ([]byte , hdr .MarshaledSize ())
2306+ hdr .MarshalStable (b )
2307+
2308+ if oid .NewFromObjectHeaderBinary (b ) != id {
2309+ return errors .New ("received header mismatches ID" )
2310+ }
2311+
2312+ return nil
2313+ }
0 commit comments