master
go 691 lines 17.8 KB
Raw
1 package protocol
2
3 const (
4 NipcUIDUnset uint32 = ^uint32(0)
5
6 PidLookupKnown uint16 = 0
7 PidLookupUnknown uint16 = 1
8
9 AppsCgroupKnown uint16 = 0
10 AppsCgroupUnknownRetryLater uint16 = 1
11 AppsCgroupUnknownPermanent uint16 = 2
12 AppsCgroupHostRoot uint16 = 3
13
14 AppsLookupReqHdr = 16
15 AppsLookupRespHdr = 16
16 AppsLookupItemHdr = 60
17 AppsLookupKeySize = 8
18
19 appsLookupUnknownItemSize = AppsLookupItemHdr + 3
20 )
21
22 type AppsLookupRequestView struct {
23 ItemCount uint32
24 payload []byte
25 }
26
27 type AppsLookupResponseView struct {
28 LayoutVersion uint16
29 Flags uint16
30 ItemCount uint32
31 Generation uint64
32 payload []byte
33 }
34
35 type AppsLookupItemView struct {
36 Status uint16
37 Orchestrator uint16
38 CgroupStatus uint16
39 Pid uint32
40 Ppid uint32
41 Uid uint32
42 Starttime uint64
43 Comm CStringView
44 CgroupPath CStringView
45 CgroupName CStringView
46 LabelCount uint16
47 item []byte
48 labelTableOffset int
49 }
50
51 type appsLookupSemantics struct {
52 status uint16
53 cgroupStatus uint16
54 orchestrator uint16
55 ppid uint32
56 uid uint32
57 starttime uint64
58 commLen int
59 pathLen int
60 nameLen int
61 labelCount int
62 }
63
64 func validateAppsLookupSemantics(v appsLookupSemantics) error {
65 if err := validateAppsLookupDomains(v.status, v.cgroupStatus, v.commLen); err != nil {
66 return err
67 }
68 if v.status == PidLookupUnknown {
69 return validateAppsLookupUnknown(v)
70 }
71 return validateAppsLookupKnown(v)
72 }
73
74 func validateAppsLookupDomains(status, cgroupStatus uint16, commLen int) error {
75 if status != PidLookupKnown && status != PidLookupUnknown {
76 return ErrBadLayout
77 }
78 if cgroupStatus != AppsCgroupKnown && cgroupStatus != AppsCgroupUnknownRetryLater &&
79 cgroupStatus != AppsCgroupUnknownPermanent && cgroupStatus != AppsCgroupHostRoot {
80 return ErrBadLayout
81 }
82 if commLen > 15 {
83 return ErrBadLayout
84 }
85 return nil
86 }
87
88 func validateAppsLookupUnknown(v appsLookupSemantics) error {
89 if v.orchestrator != 0 || v.cgroupStatus != 0 || v.ppid != 0 || v.uid != NipcUIDUnset ||
90 v.starttime != 0 || v.commLen != 0 || v.pathLen != 0 || v.nameLen != 0 || v.labelCount != 0 {
91 return ErrBadLayout
92 }
93 return nil
94 }
95
96 func validateAppsLookupKnown(v appsLookupSemantics) error {
97 if v.commLen == 0 {
98 return ErrBadLayout
99 }
100 switch v.cgroupStatus {
101 case AppsCgroupKnown:
102 if v.pathLen == 0 {
103 return ErrBadLayout
104 }
105 case AppsCgroupUnknownRetryLater:
106 if v.orchestrator != 0 || v.nameLen != 0 || v.labelCount != 0 {
107 return ErrBadLayout
108 }
109 case AppsCgroupUnknownPermanent:
110 if v.pathLen == 0 || v.orchestrator != 0 || v.nameLen != 0 || v.labelCount != 0 {
111 return ErrBadLayout
112 }
113 case AppsCgroupHostRoot:
114 if v.orchestrator != 0 || v.pathLen != 0 || v.nameLen != 0 || v.labelCount != 0 {
115 return ErrBadLayout
116 }
117 }
118 return nil
119 }
120
121 func EncodeAppsLookupRequest(pids []uint32, buf []byte) (int, error) {
122 count := len(pids)
123 if uint64(count) > uint64(^uint32(0)) {
124 return 0, ErrOverflow
125 }
126 dirSize, ok := checkedMulInt(count, LookupDirEntrySize)
127 if !ok {
128 return 0, ErrOverflow
129 }
130 keySize, ok := checkedMulInt(count, AppsLookupKeySize)
131 if !ok {
132 return 0, ErrOverflow
133 }
134 packedStart, ok := checkedAddInt(AppsLookupReqHdr, dirSize)
135 if !ok {
136 return 0, ErrOverflow
137 }
138 total, ok := checkedAddInt(packedStart, keySize)
139 if !ok {
140 return 0, ErrOverflow
141 }
142 if total > len(buf) {
143 return 0, ErrOverflow
144 }
145 for i, pid := range pids {
146 offset32, ok := checkedU32Int(i * AppsLookupKeySize)
147 if !ok {
148 return 0, ErrOverflow
149 }
150 base := AppsLookupReqHdr + i*LookupDirEntrySize
151 ne.PutUint32(buf[base:base+4], offset32)
152 ne.PutUint32(buf[base+4:base+8], AppsLookupKeySize)
153 key := packedStart + i*AppsLookupKeySize
154 ne.PutUint32(buf[key:key+4], pid)
155 ne.PutUint32(buf[key+4:key+8], 0)
156 }
157 ne.PutUint16(buf[0:2], 1)
158 ne.PutUint16(buf[2:4], 0)
159 ne.PutUint32(buf[4:8], uint32(count))
160 ne.PutUint32(buf[8:12], 0)
161 ne.PutUint32(buf[12:16], 0)
162 return total, nil
163 }
164
165 func DecodeAppsLookupRequest(buf []byte) (*AppsLookupRequestView, error) {
166 if len(buf) < AppsLookupReqHdr {
167 return nil, ErrTruncated
168 }
169 if ne.Uint16(buf[0:2]) != 1 || ne.Uint16(buf[2:4]) != 0 ||
170 ne.Uint32(buf[8:12]) != 0 || ne.Uint32(buf[12:16]) != 0 {
171 return nil, ErrBadLayout
172 }
173 itemCount := ne.Uint32(buf[4:8])
174 dirSize64 := uint64(itemCount) * uint64(LookupDirEntrySize)
175 dirEnd, ok := checkedInt(uint64(AppsLookupReqHdr) + dirSize64)
176 if !ok {
177 return nil, ErrBadItemCount
178 }
179 if dirEnd > len(buf) {
180 return nil, ErrTruncated
181 }
182 if err := validateLookupDir(buf, AppsLookupReqHdr, itemCount, len(buf)-dirEnd, 0, AppsLookupKeySize); err != nil {
183 return nil, err
184 }
185 for i := range itemCount {
186 base := AppsLookupReqHdr + int(i)*LookupDirEntrySize
187 off, _, err := lookupDirEntry(buf, base)
188 if err != nil {
189 return nil, err
190 }
191 key, err := lookupPayloadSlice(buf, dirEnd, off, AppsLookupKeySize)
192 if err != nil {
193 return nil, err
194 }
195 if ne.Uint32(key[4:8]) != 0 {
196 return nil, ErrBadLayout
197 }
198 }
199 return &AppsLookupRequestView{ItemCount: itemCount, payload: buf}, nil
200 }
201
202 func (v *AppsLookupRequestView) Item(index uint32) (uint32, error) {
203 if index >= v.ItemCount {
204 return 0, ErrOutOfBounds
205 }
206 dirEnd, ok := lookupBuilderDataOffset(AppsLookupReqHdr, v.ItemCount)
207 if !ok {
208 return 0, ErrOverflow
209 }
210 base, ok := lookupDirOffset(AppsLookupReqHdr, index)
211 if !ok {
212 return 0, ErrOverflow
213 }
214 off, _, err := lookupDirEntry(v.payload, base)
215 if err != nil {
216 return 0, err
217 }
218 key, err := lookupPayloadSlice(v.payload, dirEnd, off, AppsLookupKeySize)
219 if err != nil {
220 return 0, err
221 }
222 return ne.Uint32(key[0:4]), nil
223 }
224
225 func DecodeAppsLookupResponse(buf []byte) (*AppsLookupResponseView, error) {
226 if len(buf) < AppsLookupRespHdr {
227 return nil, ErrTruncated
228 }
229 if ne.Uint16(buf[0:2]) != 1 || ne.Uint16(buf[2:4]) != 0 {
230 return nil, ErrBadLayout
231 }
232 itemCount := ne.Uint32(buf[4:8])
233 dirEnd, ok := checkedInt(uint64(AppsLookupRespHdr) + uint64(itemCount)*uint64(LookupDirEntrySize))
234 if !ok {
235 return nil, ErrBadItemCount
236 }
237 if dirEnd > len(buf) {
238 return nil, ErrTruncated
239 }
240 if err := validateLookupDir(buf, AppsLookupRespHdr, itemCount, len(buf)-dirEnd, AppsLookupItemHdr, -1); err != nil {
241 return nil, err
242 }
243 for i := range itemCount {
244 base := AppsLookupRespHdr + int(i)*LookupDirEntrySize
245 off, length, err := lookupDirEntry(buf, base)
246 if err != nil {
247 return nil, err
248 }
249 item, err := lookupPayloadSlice(buf, dirEnd, off, length)
250 if err != nil {
251 return nil, err
252 }
253 if _, err := decodeAppsLookupItem(item); err != nil {
254 return nil, err
255 }
256 }
257 return &AppsLookupResponseView{
258 LayoutVersion: 1,
259 Flags: 0,
260 ItemCount: itemCount,
261 Generation: ne.Uint64(buf[8:16]),
262 payload: buf,
263 }, nil
264 }
265
266 func (v *AppsLookupResponseView) Item(index uint32) (*AppsLookupItemView, error) {
267 if index >= v.ItemCount {
268 return nil, ErrOutOfBounds
269 }
270 dirEnd, ok := lookupBuilderDataOffset(AppsLookupRespHdr, v.ItemCount)
271 if !ok {
272 return nil, ErrOverflow
273 }
274 base, ok := lookupDirOffset(AppsLookupRespHdr, index)
275 if !ok {
276 return nil, ErrOverflow
277 }
278 off, length, err := lookupDirEntry(v.payload, base)
279 if err != nil {
280 return nil, err
281 }
282 item, err := lookupPayloadSlice(v.payload, dirEnd, off, length)
283 if err != nil {
284 return nil, err
285 }
286 return decodeAppsLookupItem(item)
287 }
288
289 func decodeAppsLookupItem(item []byte) (*AppsLookupItemView, error) {
290 if len(item) < AppsLookupItemHdr {
291 return nil, ErrTruncated
292 }
293 status := ne.Uint16(item[2:4])
294 orchestrator := ne.Uint16(item[4:6])
295 cgroupStatus := ne.Uint16(item[6:8])
296 pid := ne.Uint32(item[8:12])
297 ppid := ne.Uint32(item[12:16])
298 uid := ne.Uint32(item[16:20])
299 starttime := ne.Uint64(item[24:32])
300 commOff, err := checkedWireU32Int(item, 32)
301 if err != nil {
302 return nil, err
303 }
304 commLen, err := checkedWireU32Int(item, 36)
305 if err != nil {
306 return nil, err
307 }
308 pathOff, err := checkedWireU32Int(item, 40)
309 if err != nil {
310 return nil, err
311 }
312 pathLen, err := checkedWireU32Int(item, 44)
313 if err != nil {
314 return nil, err
315 }
316 nameOff, err := checkedWireU32Int(item, 48)
317 if err != nil {
318 return nil, err
319 }
320 nameLen, err := checkedWireU32Int(item, 52)
321 if err != nil {
322 return nil, err
323 }
324 labelCount := ne.Uint16(item[56:58])
325 if ne.Uint16(item[0:2]) != 1 || ne.Uint32(item[20:24]) != 0 || ne.Uint16(item[58:60]) != 0 {
326 return nil, ErrBadLayout
327 }
328 if err := validateAppsLookupSemantics(appsLookupSemantics{
329 status: status,
330 cgroupStatus: cgroupStatus,
331 orchestrator: orchestrator,
332 ppid: ppid,
333 uid: uid,
334 starttime: starttime,
335 commLen: commLen,
336 pathLen: pathLen,
337 nameLen: nameLen,
338 labelCount: int(labelCount),
339 }); err != nil {
340 return nil, err
341 }
342 if status == PidLookupUnknown {
343 return decodeAppsLookupUnknownItem(item, pid, commOff, pathOff, nameOff)
344 }
345 comm, commEnd, err := lookupString(item, AppsLookupItemHdr, commOff, commLen)
346 if err != nil {
347 return nil, err
348 }
349 path, pathEnd, err := lookupString(item, AppsLookupItemHdr, pathOff, pathLen)
350 if err != nil {
351 return nil, err
352 }
353 name, nameEnd, err := lookupString(item, AppsLookupItemHdr, nameOff, nameLen)
354 if err != nil {
355 return nil, err
356 }
357 if overlap(commOff, commEnd, pathOff, pathEnd) || overlap(commOff, commEnd, nameOff, nameEnd) ||
358 overlap(pathOff, pathEnd, nameOff, nameEnd) {
359 return nil, ErrBadLayout
360 }
361 table, err := validateLabels(item, AppsLookupItemHdr, labelCount, max(commEnd, max(pathEnd, nameEnd)))
362 if err != nil {
363 return nil, err
364 }
365 return &AppsLookupItemView{
366 Status: status,
367 Orchestrator: orchestrator,
368 CgroupStatus: cgroupStatus,
369 Pid: pid,
370 Ppid: ppid,
371 Uid: uid,
372 Starttime: starttime,
373 Comm: comm,
374 CgroupPath: path,
375 CgroupName: name,
376 LabelCount: labelCount,
377 item: item,
378 labelTableOffset: table,
379 }, nil
380 }
381
382 func decodeAppsLookupUnknownItem(item []byte, pid uint32, commOff, pathOff, nameOff int) (*AppsLookupItemView, error) {
383 comm, commEnd, err := lookupEmptyString(item, AppsLookupItemHdr, commOff)
384 if err != nil {
385 return nil, err
386 }
387 path, pathEnd, err := lookupEmptyString(item, AppsLookupItemHdr, pathOff)
388 if err != nil {
389 return nil, err
390 }
391 name, nameEnd, err := lookupEmptyString(item, AppsLookupItemHdr, nameOff)
392 if err != nil {
393 return nil, err
394 }
395 if overlap(commOff, commEnd, pathOff, pathEnd) || overlap(commOff, commEnd, nameOff, nameEnd) ||
396 overlap(pathOff, pathEnd, nameOff, nameEnd) {
397 return nil, ErrBadLayout
398 }
399 labelTableOffset := max(commEnd, max(pathEnd, nameEnd))
400 if labelTableOffset != len(item) {
401 return nil, ErrBadLayout
402 }
403 return &AppsLookupItemView{
404 Status: PidLookupUnknown,
405 CgroupStatus: AppsCgroupKnown,
406 Pid: pid,
407 Uid: NipcUIDUnset,
408 Comm: comm,
409 CgroupPath: path,
410 CgroupName: name,
411 item: item,
412 labelTableOffset: labelTableOffset,
413 }, nil
414 }
415
416 func (v *AppsLookupItemView) Label(index uint32) (LookupLabelView, error) {
417 return lookupLabelAt(v.item, AppsLookupItemHdr, v.LabelCount, v.labelTableOffset, index)
418 }
419
420 type AppsLookupBuilder struct {
421 buf []byte
422 generation uint64
423 itemCount uint32
424 maxItems uint32
425 dataOffset int
426 err error
427 }
428
429 func NewAppsLookupBuilder(buf []byte, maxItems uint32, generation uint64) *AppsLookupBuilder {
430 minRequired, ok := lookupBuilderDataOffset(AppsLookupRespHdr, maxItems)
431 if !ok {
432 panic("AppsLookupBuilder buffer too small")
433 }
434 if len(buf) < minRequired {
435 panic("AppsLookupBuilder buffer too small")
436 }
437 return &AppsLookupBuilder{buf: buf, generation: generation, maxItems: maxItems, dataOffset: minRequired}
438 }
439
440 func (b *AppsLookupBuilder) SetGeneration(generation uint64) {
441 b.generation = generation
442 }
443
444 // Add appends one APPS_LOOKUP wire item; parameters mirror the fixed protocol fields.
445 func (b *AppsLookupBuilder) Add(status, cgroupStatus, orchestrator uint16, pid, ppid, uid uint32, starttime uint64, comm, cgroupPath, cgroupName []byte, labels []struct{ Key, Value []byte }) error { //NOSONAR
446 if b.itemCount >= b.maxItems {
447 b.err = ErrOverflow
448 return ErrOverflow
449 }
450 if err := validateAppsLookupSemantics(appsLookupSemantics{
451 status: status,
452 cgroupStatus: cgroupStatus,
453 orchestrator: orchestrator,
454 ppid: ppid,
455 uid: uid,
456 starttime: starttime,
457 commLen: len(comm),
458 pathLen: len(cgroupPath),
459 nameLen: len(cgroupName),
460 labelCount: len(labels),
461 }); err != nil {
462 b.err = err
463 return err
464 }
465 if status == PidLookupUnknown {
466 return b.addUnknown(pid)
467 }
468 if invalidSourceString(comm, status == PidLookupKnown) ||
469 invalidSourceString(cgroupPath, false) || invalidSourceString(cgroupName, false) {
470 b.err = ErrBadLayout
471 return ErrBadLayout
472 }
473 labelCount, ok := checkedU16Int(len(labels))
474 if !ok {
475 b.err = ErrOverflow
476 return ErrOverflow
477 }
478 itemStart, ok := checkedAlign8(b.dataOffset)
479 if !ok {
480 b.err = ErrOverflow
481 return ErrOverflow
482 }
483 commOff := AppsLookupItemHdr
484 pathOff, ok := checkedAddInt(commOff, len(comm))
485 if ok {
486 pathOff, ok = checkedAddInt(pathOff, 1)
487 }
488 if !ok {
489 b.err = ErrOverflow
490 return ErrOverflow
491 }
492 nameOff, ok := checkedAddInt(pathOff, len(cgroupPath))
493 if ok {
494 nameOff, ok = checkedAddInt(nameOff, 1)
495 }
496 if !ok {
497 b.err = ErrOverflow
498 return ErrOverflow
499 }
500 fixedEnd, ok := checkedAddInt(nameOff, len(cgroupName))
501 if ok {
502 fixedEnd, ok = checkedAddInt(fixedEnd, 1)
503 }
504 if !ok {
505 b.err = ErrOverflow
506 return ErrOverflow
507 }
508 tableStart, tableBytes, itemSize, err := labelLayoutGo(fixedEnd, labels)
509 if err != nil {
510 b.err = err
511 return err
512 }
513 itemEnd, ok := checkedAddInt(itemStart, itemSize)
514 if !ok {
515 b.err = ErrOverflow
516 return ErrOverflow
517 }
518 if itemEnd > len(b.buf) {
519 b.err = ErrOverflow
520 return ErrOverflow
521 }
522 commOff32, ok := checkedU32Int(commOff)
523 if !ok {
524 b.err = ErrOverflow
525 return ErrOverflow
526 }
527 commLen32, ok := checkedU32Int(len(comm))
528 if !ok {
529 b.err = ErrOverflow
530 return ErrOverflow
531 }
532 pathOff32, ok := checkedU32Int(pathOff)
533 if !ok {
534 b.err = ErrOverflow
535 return ErrOverflow
536 }
537 pathLen32, ok := checkedU32Int(len(cgroupPath))
538 if !ok {
539 b.err = ErrOverflow
540 return ErrOverflow
541 }
542 nameOff32, ok := checkedU32Int(nameOff)
543 if !ok {
544 b.err = ErrOverflow
545 return ErrOverflow
546 }
547 nameLen32, ok := checkedU32Int(len(cgroupName))
548 if !ok {
549 b.err = ErrOverflow
550 return ErrOverflow
551 }
552 itemStart32, ok := checkedU32Int(itemStart)
553 if !ok {
554 b.err = ErrOverflow
555 return ErrOverflow
556 }
557 itemSize32, ok := checkedU32Int(itemSize)
558 if !ok {
559 b.err = ErrOverflow
560 return ErrOverflow
561 }
562 clear(b.buf[b.dataOffset:itemStart])
563 item := b.buf[itemStart:itemEnd]
564 ne.PutUint16(item[0:2], 1)
565 ne.PutUint16(item[2:4], status)
566 ne.PutUint16(item[4:6], orchestrator)
567 ne.PutUint16(item[6:8], cgroupStatus)
568 ne.PutUint32(item[8:12], pid)
569 ne.PutUint32(item[12:16], ppid)
570 ne.PutUint32(item[16:20], uid)
571 ne.PutUint32(item[20:24], 0)
572 ne.PutUint64(item[24:32], starttime)
573 ne.PutUint32(item[32:36], commOff32)
574 ne.PutUint32(item[36:40], commLen32)
575 ne.PutUint32(item[40:44], pathOff32)
576 ne.PutUint32(item[44:48], pathLen32)
577 ne.PutUint32(item[48:52], nameOff32)
578 ne.PutUint32(item[52:56], nameLen32)
579 ne.PutUint16(item[56:58], labelCount)
580 ne.PutUint16(item[58:60], 0)
581 copy(item[commOff:], comm)
582 item[commOff+len(comm)] = 0
583 copy(item[pathOff:], cgroupPath)
584 item[pathOff+len(cgroupPath)] = 0
585 copy(item[nameOff:], cgroupName)
586 item[nameOff+len(cgroupName)] = 0
587 if len(labels) > 0 {
588 clear(item[fixedEnd:tableStart])
589 next, err := writeLookupLabels(item, tableStart, tableBytes, labels)
590 if err != nil {
591 b.err = err
592 return err
593 }
594 itemSize = next
595 }
596 dir, ok := lookupDirOffset(AppsLookupRespHdr, b.itemCount)
597 if !ok {
598 b.err = ErrOverflow
599 return ErrOverflow
600 }
601 ne.PutUint32(b.buf[dir:dir+4], itemStart32)
602 ne.PutUint32(b.buf[dir+4:dir+8], itemSize32)
603 b.dataOffset = itemStart + itemSize
604 b.itemCount++
605 return nil
606 }
607
608 func (b *AppsLookupBuilder) addUnknown(pid uint32) error {
609 itemStart, ok := checkedAlign8(b.dataOffset)
610 if !ok {
611 b.err = ErrOverflow
612 return ErrOverflow
613 }
614 itemEnd, ok := checkedAddInt(itemStart, appsLookupUnknownItemSize)
615 if !ok || itemEnd > len(b.buf) {
616 b.err = ErrOverflow
617 return ErrOverflow
618 }
619 itemStart32, ok := checkedU32Int(itemStart)
620 if !ok {
621 b.err = ErrOverflow
622 return ErrOverflow
623 }
624 itemSize32, ok := checkedU32Int(appsLookupUnknownItemSize)
625 if !ok {
626 b.err = ErrOverflow
627 return ErrOverflow
628 }
629 clear(b.buf[b.dataOffset:itemStart])
630 item := b.buf[itemStart:itemEnd]
631 clear(item)
632 ne.PutUint16(item[0:2], 1)
633 ne.PutUint16(item[2:4], PidLookupUnknown)
634 ne.PutUint32(item[8:12], pid)
635 ne.PutUint32(item[16:20], NipcUIDUnset)
636 ne.PutUint32(item[32:36], AppsLookupItemHdr)
637 ne.PutUint32(item[40:44], AppsLookupItemHdr+1)
638 ne.PutUint32(item[48:52], AppsLookupItemHdr+2)
639
640 dir, ok := lookupDirOffset(AppsLookupRespHdr, b.itemCount)
641 if !ok {
642 b.err = ErrOverflow
643 return ErrOverflow
644 }
645 ne.PutUint32(b.buf[dir:dir+4], itemStart32)
646 ne.PutUint32(b.buf[dir+4:dir+8], itemSize32)
647 b.dataOffset = itemEnd
648 b.itemCount++
649 return nil
650 }
651
652 func (b *AppsLookupBuilder) Finish() int {
653 return finishLookupResponse(b.buf, AppsLookupRespHdr, b.itemCount, b.dataOffset, b.generation)
654 }
655
656 func (b *AppsLookupBuilder) Error() error {
657 return b.err
658 }
659
660 func (b *AppsLookupBuilder) ItemCount() uint32 {
661 return b.itemCount
662 }
663
664 func DispatchAppsLookup(req []byte, resp []byte, handler func(*AppsLookupRequestView, *AppsLookupBuilder) bool) (int, error) {
665 request, err := DecodeAppsLookupRequest(req)
666 if err != nil {
667 return 0, err
668 }
669 minRequired, ok := lookupBuilderDataOffset(AppsLookupRespHdr, request.ItemCount)
670 if !ok || len(resp) < minRequired {
671 return 0, ErrOverflow
672 }
673 builder := NewAppsLookupBuilder(resp, request.ItemCount, 0)
674 if !handler(request, builder) {
675 if builder.Error() != nil {
676 return 0, builder.Error()
677 }
678 return 0, ErrBadLayout
679 }
680 if builder.Error() != nil {
681 return 0, builder.Error()
682 }
683 if builder.itemCount != request.ItemCount {
684 return 0, ErrBadItemCount
685 }
686 n := builder.Finish()
687 if n == 0 {
688 return 0, ErrOverflow
689 }
690 return n, nil
691 }