1 /*
2  **      $Id: api.c 1038 2009-01-07 17:33:42Z aaron $
3  */
4 /************************************************************************
5  *                                                                       *
6  *                             Copyright (C)  2002                       *
7  *                                Internet2                              *
8  *                             All Rights Reserved                       *
9  *                                                                       *
10  ************************************************************************/
11 /*
12  **        File:         api.c
13  **
14  **        Author:       Jeff W. Boote
15  **                      Anatoly Karp
16  **
17  **        Date:         Fri Mar 29 15:36:44  2002
18  **
19  **        Description:
20  */
21 #include "./owampP.h"
22 
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <signal.h>
29 #include <netinet/in.h>
30 #include <string.h>
31 #include <assert.h>
32 #include <libgen.h>
33 
34 
35 /*
36  * Function:        OWPGetContext
37  *
38  * Description:
39  *         Returns the context pointer that was referenced when the
40  *         given control connection was created.
41  *
42  * In Args:
43  *
44  * Out Args:
45  *
46  * Scope:
47  * Returns:
48  * Side Effect:
49  */
50 OWPContext
OWPGetContext(OWPControl cntrl)51 OWPGetContext(
52         OWPControl  cntrl
53         )
54 {
55     return cntrl->ctx;
56 }
57 
58 /*
59  * Function:        OWPGetMode
60  *
61  * Description:
62  *         Returns the "mode" of the control connection.
63  *
64  * In Args:
65  *
66  * Out Args:
67  *
68  * Scope:
69  * Returns:
70  * Side Effect:
71  */
72 OWPSessionMode
OWPGetMode(OWPControl cntrl)73 OWPGetMode(
74         OWPControl  cntrl
75         )
76 {
77     return cntrl->mode;
78 }
79 
80 /*
81  * Function:        OWPControlFD
82  *
83  * Description:
84  *
85  * In Args:
86  *
87  * Out Args:
88  *
89  * Scope:
90  * Returns:
91  * Side Effect:
92  */
93 int
OWPControlFD(OWPControl cntrl)94 OWPControlFD(
95         OWPControl  cntrl
96         )
97 {
98     return cntrl->sockfd;
99 }
100 
101 /*
102  * Function:        OWPGetRTTBound
103  *
104  * Description:        Returns a very rough estimate of the upper-bound rtt to
105  *                 the server.
106  *
107  * In Args:
108  *
109  * Out Args:
110  *
111  * Scope:
112  * Returns:
113  *                 bound or 0 if unavailable
114  * Side Effect:
115  */
116 OWPNum64
OWPGetRTTBound(OWPControl cntrl)117 OWPGetRTTBound(
118         OWPControl  cntrl
119         )
120 {
121     return cntrl->rtt_bound;
122 }
123 
124 /*
125  * Function:        _OWPFailControlSession
126  *
127  * Description:
128  *         Simple convienience to set the state and return the failure at
129  *         the same time.
130  *
131  * In Args:
132  *
133  * Out Args:
134  *
135  * Scope:
136  * Returns:
137  * Side Effect:
138  */
139 OWPErrSeverity
_OWPFailControlSession(OWPControl cntrl,int level)140 _OWPFailControlSession(
141         OWPControl  cntrl,
142         int         level
143         )
144 {
145     cntrl->state = _OWPStateInvalid;
146     return (OWPErrSeverity)level;
147 }
148 
149 /*
150  * Function:        _OWPTestSessionAlloc
151  *
152  * Description:
153  *         This function is used to allocate/initialize the memory record used
154  *         to maintain state information about a "configured" test.
155  *
156  * In Args:
157  *
158  * Out Args:
159  *
160  * Scope:
161  * Returns:
162  * Side Effect:
163  */
164 OWPTestSession
_OWPTestSessionAlloc(OWPControl cntrl,I2Addr sender,OWPBoolean conf_sender,I2Addr receiver,OWPBoolean conf_receiver,OWPTestSpec * test_spec)165 _OWPTestSessionAlloc(
166         OWPControl  cntrl,
167         I2Addr      sender,
168         OWPBoolean  conf_sender,
169         I2Addr      receiver,
170         OWPBoolean  conf_receiver,
171         OWPTestSpec *test_spec
172         )
173 {
174     OWPTestSession  test;
175 
176     /*
177      * Address records must exist.
178      */
179     if(!sender || ! receiver){
180         OWPError(cntrl->ctx,OWPErrFATAL,OWPErrINVALID,
181                 "_OWPTestSessionAlloc:Invalid Addr arg");
182         return NULL;
183     }
184 
185     if(!(test = calloc(1,sizeof(OWPTestSessionRec)))){
186         OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
187                 "calloc(1,OWPTestSessionRec): %M");
188         return NULL;
189     }
190 
191     /*
192      * Initialize address records and test description record fields.
193      */
194     test->cntrl = cntrl;
195     test->sender = sender;
196     test->conf_sender = conf_sender;
197     test->receiver = receiver;
198     test->conf_receiver = conf_receiver;
199     memcpy(&test->test_spec,test_spec,sizeof(OWPTestSpec));
200 
201     /*
202      * Allocate memory for slot records if they won't fit in the
203      * pre-allocated "buffer" already associated with the TestSession
204      * record. Then copy the slot records.
205      * (From the server side, slots will be 0 at this point - the
206      * SessionRecord is allocated before reading the slots off the
207      * socket so the SessionRecord slot "buffer" can potentially be used.)
208      */
209     if(test->test_spec.slots){
210         if(test->test_spec.nslots > _OWPSLOT_BUFSIZE){
211             if(!(test->test_spec.slots =
212                         calloc(test->test_spec.nslots,
213                             sizeof(OWPSlot)))){
214                 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
215                         "calloc(%d,OWPSlot): %M",
216                         test->test_spec.nslots);
217                 free(test);
218                 return NULL;
219             }
220         }else{
221             test->test_spec.slots = test->slot_buffer;
222         }
223         memcpy(test->test_spec.slots,test_spec->slots,
224                 test_spec->nslots*sizeof(OWPSlot));
225     }
226 
227     return test;
228 }
229 
230 /*
231  * Function:        _OWPTestSessionFree
232  *
233  * Description:
234  *         This function is used to free the memory associated with a "configured"
235  *         test session.
236  *
237  * In Args:
238  *
239  * Out Args:
240  *
241  * Scope:
242  * Returns:
243  * Side Effect:
244  */
245 OWPErrSeverity
_OWPTestSessionFree(OWPTestSession tsession,OWPAcceptType aval)246 _OWPTestSessionFree(
247         OWPTestSession  tsession,
248         OWPAcceptType   aval
249         )
250 {
251     OWPTestSession  *sptr;
252     OWPAcceptType   alocal = aval;
253     OWPErrSeverity  err=OWPErrOK;
254 
255     if(!tsession){
256         return OWPErrOK;
257     }
258 
259     /*
260      * remove this tsession from the cntrl->tests lists.
261      */
262     for(sptr = &tsession->cntrl->tests;*sptr;sptr = &(*sptr)->next){
263         if(*sptr == tsession){
264             *sptr = tsession->next;
265             break;
266         }
267     }
268 
269     if(tsession->endpoint){
270         _OWPEndpointFree(tsession->endpoint,&alocal,&err);
271     }
272 
273     if(tsession->closure){
274         _OWPCallTestComplete(tsession,aval);
275     }
276 
277     I2AddrFree(tsession->sender);
278     I2AddrFree(tsession->receiver);
279 
280     if(tsession->sctx){
281         OWPScheduleContextFree(tsession->sctx);
282     }
283 
284     if(tsession->test_spec.slots &&
285             (tsession->test_spec.slots != tsession->slot_buffer)){
286         free(tsession->test_spec.slots);
287     }
288 
289     free(tsession);
290 
291     return err;
292 }
293 
294 
295 /*
296  * Function:        _OWPCreateSID
297  *
298  * Description:
299  *         Generate a "unique" SID from addr(4)/time(8)/random(4) values.
300  *
301  * In Args:
302  *
303  * Out Args:
304  *
305  * Scope:
306  * Returns:
307  *         0 on success
308  * Side Effect:
309  */
310 int
_OWPCreateSID(OWPTestSession tsession)311 _OWPCreateSID(
312         OWPTestSession        tsession
313         )
314 {
315     OWPTimeStamp    tstamp;
316     uint8_t        *aptr;
317     struct sockaddr *saddr;
318 
319     if( !(saddr = I2AddrSAddr(tsession->receiver,NULL))){
320             OWPError(tsession->cntrl->ctx,OWPErrFATAL,OWPErrUNSUPPORTED,
321                     "_OWPCreateSID: Invalid socket address");
322         return 1;
323     }
324 
325 #ifdef        AF_INET6
326     if(saddr->sa_family == AF_INET6){
327         struct sockaddr_in6        *s6;
328 
329         s6 = (struct sockaddr_in6*)saddr;
330         /* point at last 4 bytes of addr */
331         aptr = &s6->sin6_addr.s6_addr[12];
332     }else
333 #endif
334         if(saddr->sa_family == AF_INET){
335             struct sockaddr_in        *s4;
336 
337             s4 = (struct sockaddr_in*)saddr;
338             aptr = (uint8_t*)&s4->sin_addr;
339         }
340         else{
341             OWPError(tsession->cntrl->ctx,OWPErrFATAL,OWPErrUNSUPPORTED,
342                     "_OWPCreateSID:Unknown address family");
343             return 1;
344         }
345 
346     memcpy(&tsession->sid[0],aptr,4);
347 
348     (void)OWPGetTimeOfDay(tsession->cntrl->ctx,&tstamp);
349     _OWPEncodeTimeStamp(&tsession->sid[4],&tstamp);
350 
351     if(I2RandomBytes(tsession->cntrl->ctx->rand_src,
352                 (uint8_t *)&tsession->sid[12],4)
353             != 0){
354         return 1;
355     }
356 
357     return 0;
358 }
359 
360 OWPPacketSizeT
OWPTestPayloadSize(OWPSessionMode mode,uint32_t padding)361 OWPTestPayloadSize(
362         OWPSessionMode  mode,
363         uint32_t       padding
364         )
365 {
366     OWPPacketSizeT msg_size;
367 
368     switch (mode) {
369         case OWP_MODE_OPEN:
370             msg_size = 14;
371             break;
372         case OWP_MODE_AUTHENTICATED:
373         case OWP_MODE_ENCRYPTED:
374             msg_size = 48;
375             break;
376         default:
377             return 0;
378             /* UNREACHED */
379     }
380 
381     return msg_size + padding;
382 }
383 
384 /*
385  * Function:        OWPTestPacketRate
386  *
387  * Description:
388  *         This function returns the # packets/ second as a double.
389  *
390  * In Args:
391  *
392  * Out Args:
393  *
394  * Scope:
395  * Returns:
396  * Side Effect:
397  */
398 double
OWPTestPacketRate(OWPContext ctx,OWPTestSpec * tspec)399 OWPTestPacketRate(
400         OWPContext  ctx,
401         OWPTestSpec *tspec
402         )
403 {
404     OWPNum64    duration = OWPULongToNum64(0);
405     uint32_t   i;
406 
407     if(!tspec){
408         OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
409                 "OWPTestPacketRate: Invalid tspec arg");
410         return 0;
411     }
412 
413     if(!tspec->nslots || !tspec->slots){
414         OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
415                 "OWPTestPacketRate: Invalid empty test specification");
416         return 0;
417     }
418 
419     for(i=0;i<tspec->nslots;i++){
420         duration = OWPNum64Add(duration,tspec->slots[i].any.mean_delay);
421     }
422 
423     if(duration <= 0){
424         return 0;
425     }
426 
427     return (double)tspec->nslots / OWPNum64ToDouble(duration);
428 }
429 
430 /* These lengths assume no IP options. */
431 #define OWP_IP4_HDR_SIZE        20        /* rfc 791 */
432 #define OWP_IP6_HDR_SIZE        40        /* rfc 2460 */
433 #define OWP_UDP_HDR_SIZE        8        /* rfc 768 */
434 
435 /*
436  ** Given the protocol family, OWAMP mode and packet padding,
437  ** compute the size of resulting full IP packet.
438  */
439 OWPPacketSizeT
OWPTestPacketSize(int af,OWPSessionMode mode,uint32_t padding)440 OWPTestPacketSize(
441         int             af,    /* AF_INET, AF_INET6 */
442         OWPSessionMode  mode,
443         uint32_t       padding
444         )
445 {
446     OWPPacketSizeT payload_size, header_size;
447 
448     switch (af) {
449         case AF_INET:
450             header_size = OWP_IP4_HDR_SIZE + OWP_UDP_HDR_SIZE;
451             break;
452         case AF_INET6:
453             header_size = OWP_IP6_HDR_SIZE + OWP_UDP_HDR_SIZE;
454             break;
455         default:
456             return 0;
457             /* UNREACHED */
458     }
459 
460     if(!(payload_size = OWPTestPayloadSize(mode,padding)))
461         return 0;
462 
463     return payload_size + header_size;
464 }
465 
466 /*
467  * Function:        OWPTestPacketBandwidth
468  *
469  * Description:
470  *         returns the average bandwidth requirements of the given test using
471  *         the given address family, and authentication mode.
472  *
473  * In Args:
474  *
475  * Out Args:
476  *
477  * Scope:
478  * Returns:
479  * Side Effect:
480  */
481 double
OWPTestPacketBandwidth(OWPContext ctx,int af,OWPSessionMode mode,OWPTestSpec * tspec)482 OWPTestPacketBandwidth(
483         OWPContext      ctx,
484         int             af,
485         OWPSessionMode  mode,
486         OWPTestSpec     *tspec
487         )
488 {
489     if(!tspec){
490         OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
491                 "OWPTestPacketBandwidth: Invalid tspec arg");
492         return 0;
493     }
494 
495     return OWPTestPacketRate(ctx,tspec) *
496         OWPTestPacketSize(af,mode,tspec->packet_size_padding) * 8;
497 }
498 
499 /*
500  * Function:        OWPSessionStatus
501  *
502  * Description:
503  *         This function returns the "status" of the test session identified
504  *         by the sid. "send" indicates which "side" of the test to retrieve
505  *         information about.
506  *
507  * In Args:
508  *
509  * Out Args:
510  *
511  * Scope:
512  * Returns:        True if status was available, False otherwise.
513  *                 aval contains the actual "status":
514  *                         <0        Test is not yet complete
515  *                         >=0        Valid OWPAcceptType - see enum for meaning.
516  * Side Effect:
517  */
518 OWPBoolean
OWPSessionStatus(OWPControl cntrl,OWPSID sid,OWPAcceptType * aval)519 OWPSessionStatus(
520         OWPControl      cntrl,
521         OWPSID          sid,
522         OWPAcceptType   *aval
523         )
524 {
525     OWPTestSession  tsession;
526     OWPErrSeverity  err;
527 
528     /*
529      * First find the tsession record for this test.
530      */
531     for(tsession=cntrl->tests;tsession;tsession=tsession->next)
532         if(memcmp(sid,tsession->sid,sizeof(OWPSID)) == 0)
533             goto found;
534 
535     return False;
536 
537 found:
538     if(tsession->endpoint){
539         _OWPEndpointStatus(tsession->endpoint,aval,&err);
540         return True;
541     }
542 
543     return False;
544 }
545 
546 int
OWPSessionsActive(OWPControl cntrl,OWPAcceptType * aval)547 OWPSessionsActive(
548         OWPControl      cntrl,
549         OWPAcceptType   *aval
550         )
551 {
552     OWPTestSession  tsession;
553     OWPAcceptType   laval;
554     OWPAcceptType   raval = 0;
555     int             n=0;
556     OWPErrSeverity  err;
557 
558     for(tsession = cntrl->tests;tsession;tsession = tsession->next){
559         if(tsession->endpoint){
560             /* initialize laval before querying status */
561             laval = OWP_CNTRL_ACCEPT;
562             _OWPEndpointStatus(tsession->endpoint,&laval,&err);
563             if(laval < 0){
564                 n++;
565             } else{
566                 raval = MAX(laval,raval);
567             }
568         }
569     }
570 
571     if(aval)
572         *aval = raval;
573 
574     return n;
575 }
576 
577 /*
578  * Function:    _OWPStopSendSessions
579  *
580  * Description:
581  *              This function is used to stop send sessions. Skip
582  *              records are somewhat validated for use in the
583  *              _OWPWriteStopSessions function.
584  *
585  * In Args:
586  *
587  * Out Args:
588  *
589  * Scope:
590  * Returns:
591  * Side Effect:
592  */
593 static OWPErrSeverity
_OWPStopSendSessions(OWPControl cntrl,OWPAcceptType * acceptval_ret,uint32_t * num_sessions)594 _OWPStopSendSessions(
595         OWPControl      cntrl,
596         OWPAcceptType   *acceptval_ret,  /* in/out */
597         uint32_t        *num_sessions
598         )
599 {
600     OWPErrSeverity  err,err2=OWPErrOK;
601     OWPAcceptType   aval=OWP_CNTRL_ACCEPT;
602     OWPAcceptType   *acceptval = &aval;
603     OWPTestSession  sptr;
604     uint32_t       num_senders=0;
605 
606     if(acceptval_ret){
607         acceptval = acceptval_ret;
608     }
609     *num_sessions = 0;
610 
611     /*
612      * Stop each session - count the "send" sessions and verify that
613      * the "skip records" saved at each fd are at least a consistent size.
614      */
615     for(sptr=cntrl->tests; sptr; sptr = sptr->next){
616         uint32_t   sdr[2];
617         uint32_t   nskip;
618         struct stat sbuf;
619 
620         /*
621          * Validity check.
622          */
623         if(!sptr->endpoint){
624             OWPError(cntrl->ctx,OWPErrWARNING,EINVAL,
625                     "_OWPStopSendSessions: no endpoint state!");
626             *acceptval = OWP_CNTRL_FAILURE;
627             break;
628         }
629 
630         /*
631          * Receive sessions are not done here.
632          */
633         if(!sptr->endpoint->send) continue;
634 
635         /*
636          * Stop local sessions
637          */
638         _OWPEndpointStop(sptr->endpoint,acceptval,&err);
639         err2 = MIN(err,err2);
640 
641         /* count senders for inclusion in StopSessions message */
642         num_senders++;
643 
644         /*
645          * simple check to validate skip records:
646          * Just verify size of file matches reported number
647          * of skip records.
648          */
649         if(fstat(sptr->endpoint->skiprecfd,&sbuf) != 0){
650             OWPError(cntrl->ctx,OWPErrWARNING,errno,"fstat(skiprecfd): %M");
651             *acceptval = OWP_CNTRL_FAILURE;
652             err2 = MIN(OWPErrWARNING,err2);
653             continue;
654         }
655 
656         /*
657          * Seek to beginning of file for reading.
658          */
659         if(lseek(sptr->endpoint->skiprecfd,0,SEEK_SET) == -1){
660             OWPError(cntrl->ctx,OWPErrWARNING,errno,"lseek(skiprecfd,0): %M");
661             *acceptval = OWP_CNTRL_FAILURE;
662             err2 = MIN(OWPErrWARNING,err2);
663             continue;
664         }
665 
666         /*
667          * Read next_seqno and num_skips for verification purposes.
668          * (IGNORE intr for this local file i/o)
669          */
670         if(I2Readn(sptr->endpoint->skiprecfd,sdr,8) != 8){
671             OWPError(cntrl->ctx,OWPErrWARNING,errno,"I2Readn(skiprecfd): %M");
672             *acceptval = OWP_CNTRL_FAILURE;
673             err2 = MIN(OWPErrWARNING,err2);
674             continue;
675         }
676 
677         /*
678          * Reset fd to beginning of file for reading.
679          */
680         if(lseek(sptr->endpoint->skiprecfd,0,SEEK_SET) == -1){
681             OWPError(cntrl->ctx,OWPErrWARNING,errno,"lseek(skiprecfd,0): %M");
682             *acceptval = OWP_CNTRL_FAILURE;
683             err2 = MIN(OWPErrWARNING,err2);
684             continue;
685         }
686 
687         nskip = ntohl(sdr[1]);
688 
689         /*
690          * Each skip record is 8 bytes, plus 8 bytes for next_seqno and
691          * num_skip_records means: filesize == ((nskip+1)*8)
692          */
693         if((off_t)((nskip+1)*8) != sbuf.st_size){
694             OWPError(cntrl->ctx,OWPErrWARNING,EINVAL,
695                     "_OWPStopSendSessions: Invalid skiprecfd data");
696             *acceptval = OWP_CNTRL_FAILURE;
697             err2 = MIN(OWPErrWARNING,err2);
698             continue;
699         }
700 
701         sptr->endpoint->skiprecsize = sbuf.st_size;
702     }
703 
704     *num_sessions = num_senders;
705     return err2;
706 }
707 
708 /*
709  * Function:    _OWPStopRecvSessions
710  *
711  * Description:
712  *              This function is used to stop recv sessions.
713  *              Skip records and next_seqno reported from the send side
714  *              of the test will be used to finish writing the datafile.
715  *
716  * In Args:
717  *
718  * Out Args:
719  *
720  * Scope:
721  * Returns:
722  * Side Effect:
723  */
724 static OWPErrSeverity
_OWPStopRecvSessions(OWPControl cntrl,OWPAcceptType * acceptval_ret)725 _OWPStopRecvSessions(
726         OWPControl      cntrl,
727         OWPAcceptType   *acceptval_ret
728         )
729 {
730     OWPErrSeverity  err,err2=OWPErrOK;
731     OWPAcceptType   aval=OWP_CNTRL_ACCEPT;
732     OWPAcceptType   *acceptval = &aval;
733     OWPTestSession  sptr;
734 
735     if(acceptval_ret){
736         acceptval = acceptval_ret;
737     }
738 
739     /*
740      * Stop each "recv" session
741      */
742     for(sptr=cntrl->tests; sptr; sptr = sptr->next){
743         /*
744          * Validity check.
745          */
746         if(!sptr->endpoint){
747             OWPError(cntrl->ctx,OWPErrWARNING,EINVAL,
748                     "_OWPStopRecvSessions: no endpoint state!");
749             *acceptval = OWP_CNTRL_FAILURE;
750             break;
751         }
752 
753         /*
754          * Send sessions not done here.
755          */
756         if(sptr->endpoint->send) continue;
757 
758         /*
759          * Stop local sessions
760          */
761         _OWPEndpointStop(sptr->endpoint,acceptval,&err);
762         err2 = MIN(err,err2);
763     }
764 
765     return err2;
766 }
767 
768 /*
769  * Function:    _OWPCleanDataRecs
770  *
771  * Description:
772  *          Function is used to remove data records from the recieve
773  *          side test based on the information from the StopSession
774  *          message. (stoptime, next_seqno)
775  *
776  *          If StopSession message is not exchanged, then next_seqno
777  *          MUST be set to 0xFFFFFFFF.
778  *
779  *          The caller should have locked the file before calling this.
780  * In Args:
781  *
782  * Out Args:
783  *
784  * Scope:
785  * Returns:
786  * Side Effect:
787  */
788 OWPBoolean
_OWPCleanDataRecs(OWPContext ctx,OWPTestSession tptr,uint32_t next_seqno,OWPTimeStamp stoptime,uint32_t * max_recv_rtn,off_t * off_start_rtn)789 _OWPCleanDataRecs(
790         OWPContext      ctx,
791         OWPTestSession  tptr,
792         uint32_t        next_seqno,
793         OWPTimeStamp    stoptime,
794         uint32_t        *max_recv_rtn,
795         off_t           *off_start_rtn
796         )
797 {
798     FILE        *rfp, *wfp;
799     char        sid_name[sizeof(OWPSID)*2+1];
800     off_t       toff;
801     OWPDataRec  rec;
802     _OWPSessionHeaderInitialRec fhdr;
803     char        rbuf[_OWP_MAXDATAREC_SIZE];
804     uint32_t    j;
805     uint32_t    lowI,midI,highI,num_recs;
806     OWPNum64    lowR,midR,highR,threshR;
807     uint32_t    max_recv_data;
808     uint32_t    *max_recv = &max_recv_data;
809     off_t       off_start_data;
810     off_t       *off_start = &off_start_data;
811 
812     if(max_recv_rtn)
813         max_recv = max_recv_rtn;
814     *max_recv = 0;
815     if(off_start_rtn)
816         off_start = off_start_rtn;
817     *off_start = 0;
818 
819 
820     I2HexEncode(sid_name,tptr->sid,sizeof(OWPSID));
821 
822     rfp = tptr->endpoint->datafile;
823     wfp = rfp;
824 
825     /*
826      * Read needed file header info.
827      */
828     if(!_OWPReadDataHeaderInitial(ctx,rfp,&fhdr)){
829         goto err;
830     }
831 
832     /*
833      * Compute number of data records currently in file using filesizes.
834      * (Verify that disk space is a multiple of datarec size too...)
835      */
836     toff = fhdr.sbuf.st_size - fhdr.oset_datarecs;
837     if(toff % fhdr.rec_size){
838         OWPError(ctx,OWPErrFATAL,EFTYPE,
839                 "_OWPCleanDataRecs: Invalid records for sid(%s)",
840                 sid_name);
841         goto err;
842     }
843     fhdr.num_datarecs = num_recs = toff / fhdr.rec_size;
844 
845     /*
846      * If there is no data, this is a very simple file...
847      */
848     if(!fhdr.num_datarecs) goto clean_data;
849 
850     /*
851      * Seek to beginning of data records.
852      */
853     if(fseeko(rfp,fhdr.oset_datarecs,SEEK_SET) != 0){
854         OWPError(ctx,OWPErrFATAL,errno,"fseeko(): %M");
855         goto err;
856     }
857 
858     /*
859      * Delete data records with a computed send time later than
860      * currenttime (ie. stoptime) - timeout as per section 3.8 RFC 4656.
861      *
862      * To do this is somewhat non-trivial. The records in the file
863      * are sorted by recv time. There is a relationship between
864      * recv time and send time because of the "timeout" parameter
865      * of a test. A packet is only accepted and saved if the recv
866      * time is within "timeout" of the recv time. Therefore, this
867      * algorithm starts by finding the first packet record in the
868      * file with a recv time greater than (stoptime - (2 * timeout)).
869      * (recv time can be timeout less than send time if clocks are
870      * offset).
871      *
872      * Additionally, the next_seqno from the StopSessions message
873      * comes into play. Let the presumed sendtime of next_seqno be
874      * next_seqno_time. Then, the parsing may need to start
875      * as early as (next_seqno_time - (2 * timeout)).
876      *
877      * Therefore, the search algorithm actually looks for the minimum
878      * of those two values.
879      *
880      * lowI will be the index to the packet record with the
881      * largest recv time less than the threshold (stoptime - (2 * timeout))
882      * upon completion of the binary search. (If one exists.)
883      *
884      * After the binary search, the algorithm sequentially goes
885      * forward through the file deleting all packet records with
886      * (sendtime > (stoptime - timeout)).  During this pass, if any
887      * index is >= next_seqno, the entire session will be declared
888      * invalid. Additionally, any LostPacket records with
889      * index >= next_seqno will be removed.
890      *
891      */
892     if(next_seqno == 0xFFFFFFFF){
893         next_seqno = tptr->test_spec.npackets;
894     }
895 
896     if(next_seqno > tptr->test_spec.npackets){
897         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
898                 "_OWPCleanDataRecs: Invalid \'next_seqno\': %lu",next_seqno);
899         goto err;
900     }
901 
902     /*
903      * First use an interpolated binary search to find the "threshold"
904      * point in the file.
905      */
906 
907     /* Initializing variables for search. */
908 
909     /* find threshold time. MIN(stoptime,next_seqno_time) - (2 * timeout) */
910     if(OWPScheduleContextReset(tptr->sctx,NULL,NULL) != OWPErrOK){
911         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
912                 "_OWPCleanDataRecs: SchedulecontextReset(): FAILED");
913         goto err;
914     }
915 
916     /* find next_seqno_time */
917     lowI = 0;
918     threshR = tptr->test_spec.start_time;
919     while(lowI < next_seqno){
920         threshR = OWPNum64Add(threshR,
921                 OWPScheduleContextGenerateNextDelta(tptr->sctx));
922         lowI++;
923     }
924 
925     /* find MIN(next_seqno_time,stoptime) */
926     threshR = OWPNum64Min(threshR,stoptime.owptime);
927 
928     /*
929      * Set actual threshold to 2*timeout less than that to deal with
930      * offset clocks.
931      */
932     threshR = OWPNum64Sub(threshR,
933             OWPNum64Mult(tptr->test_spec.loss_timeout,OWPULongToNum64(2)));
934     highI = fhdr.num_datarecs;
935     highR = stoptime.owptime;
936     lowI = 0;
937 
938     /*
939      * Read the first packet record to get the recv(0) for lowR.
940      */
941     if(fread(rbuf,fhdr.rec_size,1,rfp) != 1){
942         OWPError(ctx,OWPErrFATAL,errno,
943                 "fread(): Reading session file for sid(%s): %M",sid_name);
944         goto err;
945     }
946     if(!_OWPDecodeDataRecord(fhdr.version,&rec,rbuf)){
947         errno = EFTYPE;
948         OWPError(ctx,OWPErrFATAL,errno,
949                 "_OWPCleanDataRecs: Invalid data record for sid(%s)",
950                 sid_name);
951         goto err;
952     }
953 
954     if(OWPIsLostRecord(&rec)){
955         lowR = OWPNum64Add(rec.send.owptime,tptr->test_spec.loss_timeout);
956     }
957     else{
958         lowR = rec.recv.owptime;
959     }
960 
961     /*
962      * If lowR is not less than threshR than we are done.
963      */
964     if(!(OWPNum64Cmp(lowR,threshR) < 0)){
965         goto thresh_pos;
966     }
967 
968     /*
969      * This loop is the meat of the interpolated binary search
970      */
971     while((highI - lowI) > 1){
972         OWPNum64    portion;
973         OWPNum64    range;
974 
975         range = OWPNum64Sub(highR,lowR);
976 
977         /*
978          * If there are multiple records with the same recv time,
979          * interpolation will fail - in this case fall back to strict
980          * binary.
981          */
982         if(!range){
983             midI = (highI - lowI) / 2;
984         }
985         else{
986             /*
987              * Interpolate
988              */
989             portion = OWPNum64Sub(threshR,lowR);
990             midI = lowI + ((OWPNum64ToDouble(portion) * (highI - lowI)) /
991                     OWPNum64ToDouble(range));
992             if(midI == lowI) midI++;
993         }
994 
995         /*
996          * determine offset from midI
997          */
998         toff = fhdr.oset_datarecs + midI * fhdr.rec_size;
999 
1000         /*
1001          * Seek to midI data record.
1002          */
1003         if(fseeko(rfp,toff,SEEK_SET) != 0){
1004             OWPError(ctx,OWPErrFATAL,errno,"fseeko(): %M");
1005             goto err;
1006         }
1007 
1008         /*
1009          * Read the packet record from midI.
1010          */
1011         if(fread(rbuf,fhdr.rec_size,1,rfp) != 1){
1012             OWPError(ctx,OWPErrFATAL,errno,
1013                     "fread(): Reading session file for sid(%s): %M",
1014                     sid_name);
1015             goto err;
1016         }
1017         if(!_OWPDecodeDataRecord(fhdr.version,&rec,rbuf)){
1018             errno = EFTYPE;
1019             OWPError(ctx,OWPErrFATAL,errno,
1020                     "_OWPCleanDataRecs: Invalid data record for sid(%s)",
1021                     sid_name);
1022             goto err;
1023         }
1024 
1025         /*
1026          * If midR is less than thresh, update lowI. Otherwise,
1027          * update highI.
1028          */
1029         if(OWPIsLostRecord(&rec)){
1030             midR = OWPNum64Add(rec.send.owptime,
1031                     tptr->test_spec.loss_timeout);
1032         }
1033         else{
1034             midR = rec.recv.owptime;
1035         }
1036         if(OWPNum64Cmp(midR,threshR) < 0){
1037             lowI = midI;
1038             lowR = midR;
1039         }
1040         else{
1041             highI = midI;
1042             highR = midR;
1043         }
1044     }
1045 thresh_pos:
1046 
1047     /*
1048      * Now, step through all records lowI and after to examine the
1049      * sent time. The sent time must be less than (stop - timeout)
1050      * and the index must be less than next_seqno for the record
1051      * to be kept. (If index is greater than or equal to next_seqno,
1052      * and it is not a lost packet record, the entire session
1053      * MUST be deleted as per the spec.)
1054      *
1055      */
1056     *off_start = toff = fhdr.oset_datarecs + (lowI * fhdr.rec_size);
1057     threshR = OWPNum64Sub(stoptime.owptime,tptr->test_spec.loss_timeout);
1058 
1059     /*
1060      * Seek to lowI data record to start parsing.
1061      */
1062     if(fseeko(rfp,toff,SEEK_SET) != 0){
1063         OWPError(ctx,OWPErrFATAL,errno,"fseeko(): %M");
1064         goto err;
1065     }
1066 
1067     for(j=lowI;j<fhdr.num_datarecs;j++){
1068 
1069         /*
1070          * Read the packet record from midI.
1071          */
1072         if(fread(rbuf,fhdr.rec_size,1,rfp) != 1){
1073             OWPError(ctx,OWPErrFATAL,errno,
1074                     "fread(): Reading session file sid(%s): %M",
1075                     sid_name);
1076             goto loop_err;
1077         }
1078         if(!_OWPDecodeDataRecord(fhdr.version,&rec,rbuf)){
1079             errno = EFTYPE;
1080             OWPError(ctx,OWPErrFATAL,errno,
1081                     "_OWPCleanDataRecs: Invalid data record sid(%s)",
1082                     sid_name);
1083             goto loop_err;
1084         }
1085 
1086         /*
1087          * If the seq_no is >= next_seqno, and it is not a lost
1088          * packet record, then this session MUST be thrown out.
1089          * Otherwise, if the packet was not sent after threshR, then keep it
1090          * by writing it back into the file if necessary.
1091          * Finally, drop the packet.
1092          */
1093 
1094         /* Invalid session */
1095         if((rec.seq_no >= next_seqno) && !OWPIsLostRecord(&rec)){
1096             errno = EFTYPE;
1097             OWPError(ctx,OWPErrFATAL,errno,
1098                     "_OWPCleanDataRecs: Invalid data record (seq_no too large) sid(%s)",
1099                     sid_name);
1100             goto loop_err;
1101         }
1102         /* Good record */
1103         else if((rec.seq_no < next_seqno) &&
1104                 (OWPNum64Cmp(rec.send.owptime,threshR) <= 0)){
1105             *max_recv = MAX(rec.seq_no,*max_recv);
1106             if(wfp != rfp){
1107                 if(fwrite(rbuf,fhdr.rec_size,1,wfp) != 1){
1108                     OWPError(ctx,OWPErrFATAL,errno,
1109                             "fwrite(): Writing session file sid(%s): %M",
1110                             sid_name);
1111                     goto loop_err;
1112                 }
1113             }
1114         }
1115         /*
1116          * The packet record should not be kept.
1117          */
1118         else{
1119             num_recs--;
1120             /*
1121              * If wfp==rfp, then create another fp for wfp and point
1122              * it at the current record so it will be written over.
1123              */
1124             if(wfp == rfp){
1125                 int     newfd;
1126                 char    tmpfname[PATH_MAX];
1127                 char    *dname;
1128                 char    *tmpl = "owamp.XXXXXXXX";
1129 
1130                 /*
1131                  * Need another file for bookkeeping... First determine
1132                  * what dir to put it in.
1133                  */
1134                 dname = NULL;
1135                 memset(tmpfname,'\0',sizeof(tmpfname));
1136 
1137                 /* put it in the same dir as session data if possible */
1138                 if( strlen(tptr->endpoint->fname) > 0){
1139                     strncpy(tmpfname,tptr->endpoint->fname,PATH_MAX);
1140                     dname = dirname(tmpfname);
1141                 }
1142 
1143                 /* otherwise use tmpdir */
1144                 if( !dname){
1145                     dname = getenv("TMPDIR");
1146                 }
1147                 if( !dname){
1148                     dname = "/tmp";
1149                 }
1150 
1151                 /* Make sure pathname will not over-run memory. */
1152                 if(strlen(tmpl) + OWP_PATH_SEPARATOR_LEN + strlen(dname) >
1153                         PATH_MAX){
1154                     OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
1155                             "_OWPCleanDataRecs: Unable to create temp file: Path Too Long");
1156                     goto err;
1157                 }
1158 
1159                 /* create template (fname) string for mkstemp */
1160                 strcpy(tmpfname,dname);
1161                 strcat(tmpfname,OWP_PATH_SEPARATOR);
1162                 strcat(tmpfname,tmpl);
1163                 if( (newfd = mkstemp(tmpfname)) < 0){
1164                     OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
1165                             "_OWPCleanDataRecs: mkstemp(%s): %M",
1166                             tmpfname);
1167                     goto err;
1168                 }
1169 
1170                 /* immediately unlink - no need for a directory entry */
1171                 if(unlink(tmpfname) != 0){
1172                     OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
1173                             "unlink(%s): %M",tmpfname);
1174                     close(newfd);
1175                     goto err;
1176                 }
1177 
1178                 /*
1179                  * Copy original file into tmpfile from the beginning
1180                  * until just before the current record.
1181                  */
1182                 toff = fhdr.oset_datarecs + (j * fhdr.rec_size);
1183                 if(I2CopyFile(OWPContextErrHandle(ctx),
1184                             newfd,fileno(rfp),toff) != 0){
1185                     OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
1186                             "_OWPCleanDataRecs: Unable to copy session data: I2CopyFile(): %M");
1187                     close(newfd);
1188                     goto err;
1189                 }
1190 
1191                 if( !(wfp = fdopen(newfd,"r+b"))){
1192                     OWPError(ctx,OWPErrFATAL,errno,"fdopen(%d): %M",
1193                             newfd);
1194                     close(newfd);
1195                     goto err;
1196                 }
1197 
1198                 /*
1199                  * Seek new wfp to end of tmpfile.
1200                  */
1201                 if(fseeko(wfp,0,SEEK_END) != 0){
1202                     OWPError(ctx,OWPErrFATAL,errno,"fseeko(): %M");
1203                     goto loop_err;
1204                 }
1205             }
1206         }
1207         continue;
1208 loop_err:
1209         if(wfp != rfp){
1210             fclose(wfp);
1211         }
1212         goto err;
1213     }
1214 
1215 clean_data:
1216 
1217     /*
1218      * If two fp's were used, then the tmpfile needs to be copied
1219      * back to the original file.
1220      */
1221     if(wfp != rfp){
1222         if(I2CopyFile(OWPContextErrHandle(ctx),
1223                     fileno(rfp),fileno(wfp),0) != 0){
1224             OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
1225                     "_OWPCleanDataRecs: Unable to copy session data: I2CopyFile(): %M");
1226             fclose(wfp);
1227             goto err;
1228         }
1229         fclose(wfp);
1230     }
1231 
1232     /*
1233      * Write NumDataRecords into file.
1234      * (This MUST be done before adding any skip records to the file
1235      * or there is a race condition where partial session results could
1236      * interpret skip records as data records!)
1237      */
1238     if( !OWPWriteDataHeaderNumDataRecs(ctx,rfp,num_recs)){
1239         goto err;
1240     }
1241 
1242     return True;
1243 
1244 err:
1245     OWPError(ctx, OWPErrFATAL,OWPErrUNKNOWN,
1246             "_OWPCleanDataRecs: Failed");
1247     return False;
1248 }
1249 
1250 typedef struct _maxrecv_rec{
1251     OWPBoolean  found;
1252     uint32_t    index;          /* count records (index in file) */
1253     uint32_t    maxrecv;        /* max seqno for received records */
1254     uint32_t    badlost;        /* seqno for *first* throw-away lost rec */
1255     uint32_t    index_badlost;  /* record index in file for badlost */
1256 } _maxrecv_rec;
1257 
1258 static int
GetMaxRecv(OWPDataRec * rec,void * data)1259 GetMaxRecv(
1260         OWPDataRec  *rec,
1261         void        *data
1262         )
1263 {
1264     _maxrecv_rec    *mrec = (_maxrecv_rec *)data;
1265 
1266 
1267     /*
1268      * If this record is not lost, update maxrecv
1269      */
1270     if(!OWPIsLostRecord(rec)){
1271         mrec->found = True;
1272         mrec->maxrecv = MAX(mrec->maxrecv,rec->seq_no);
1273     }
1274     /*
1275      * lost - if this is the first one greater than maxrecv, then
1276      * record the badlost.
1277      */
1278     else if((rec->seq_no > mrec->maxrecv) &&
1279             (mrec->badlost < mrec->maxrecv)){
1280         mrec->badlost = rec->seq_no;
1281         mrec->index_badlost = mrec->index;
1282     }
1283 
1284     mrec->index++;
1285 
1286     return 0;
1287 }
1288 
1289 /*
1290  * Function:    _OWPCleanUpSessions
1291  *
1292  * Description:
1293  *              This function updates the "recv" side sessions.
1294  *              The StopSessions message was not read in this case,
1295  *              so it relies on the stoptime of the session to
1296  *              clean things up. Also, it deletes all trailing
1297  *              Missing packet records in the file.
1298  *
1299  * In Args:
1300  *
1301  * Out Args:
1302  *
1303  * Scope:
1304  * Returns:
1305  * Side Effect:
1306  */
1307 static OWPBoolean
_OWPCleanUpSessions(OWPControl cntrl,int * intr,OWPAcceptType * acceptval_ret,OWPTimeStamp stoptime)1308 _OWPCleanUpSessions(
1309         OWPControl      cntrl,
1310         int             *intr __attribute__((unused)),
1311         OWPAcceptType   *acceptval_ret,
1312         OWPTimeStamp    stoptime
1313         )
1314 {
1315     OWPAcceptType   aval;
1316     OWPAcceptType   *acceptval = &aval;
1317     OWPTestSession  tptr;
1318 
1319     if(acceptval_ret)
1320         acceptval = acceptval_ret;
1321     *acceptval = OWP_CNTRL_ACCEPT;
1322 
1323     /*
1324      * Parse test session list and pull recv sessions into the receivers
1325      * list.
1326      */
1327     for(tptr = cntrl->tests;tptr;tptr = tptr->next){
1328         char                        sid_name[sizeof(OWPSID)*2+1];
1329         off_t                       toff;
1330         FILE                        *rfp,*wfp;
1331         _OWPSessionHeaderInitialRec fhdr;
1332         struct flock                flk;
1333         uint32_t                    j;
1334         OWPDataRec                  rec;
1335         char                        rbuf[_OWP_MAXDATAREC_SIZE];
1336         uint32_t                    max_recv,num_recs;
1337 
1338         if(!tptr->endpoint){
1339             OWPError(cntrl->ctx,OWPErrFATAL,EINVAL,
1340                     "_OWPCleanUpSessions: no endpoint state!");
1341             goto err;
1342         }
1343 
1344         /*
1345          * Only need to clean recv sessions.
1346          */
1347         if(tptr->endpoint->send){
1348             continue;
1349         }
1350 
1351 
1352         I2HexEncode(sid_name,tptr->sid,sizeof(OWPSID));
1353 
1354         rfp = wfp = tptr->endpoint->datafile;
1355 
1356         /*
1357          * Lock the data file for writing. This is needed so FetchSessions
1358          * coming in from other control connections will get consistent
1359          * information.
1360          */
1361         memset(&flk,0,sizeof(flk));
1362         flk.l_start = 0;
1363         flk.l_len = 0;
1364         flk.l_whence = SEEK_SET;
1365         flk.l_type = F_WRLCK;
1366 
1367         if( fcntl(fileno(rfp), F_SETLKW, &flk) < 0){
1368             OWPError(cntrl->ctx,OWPErrFATAL,errno,
1369                     "_OWPCleanUpSessions: Unable to lock file sid(%s): %M",
1370                     sid_name);
1371             goto err;
1372         }
1373 
1374         max_recv = 0;
1375         if( !_OWPCleanDataRecs(cntrl->ctx,tptr,0xFFFFFFFF,stoptime,&max_recv,
1376                     &toff)){
1377             OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1378                     "_OWPCleanUpSessions: Unable to clean data sid(%s): %M",
1379                     sid_name);
1380             goto err;
1381         }
1382 
1383         /*
1384          * Read needed file header info.
1385          */
1386         if(!_OWPReadDataHeaderInitial(cntrl->ctx,rfp,&fhdr)){
1387             goto err;
1388         }
1389 
1390         /*
1391          * Because this is from a broken control connection, I
1392          * now need to clean out any records with an index greater than
1393          * the last received packet.
1394          *
1395          * If CleanDataRecs was unable to get a max index, then
1396          * this degrades to a full parse of the dataset for the max
1397          * 'found' index.
1398          */
1399         if(!max_recv){
1400             _maxrecv_rec    maxrec;
1401 
1402             memset(&maxrec,0,sizeof(maxrec));
1403             if(fseeko(rfp,fhdr.oset_datarecs,SEEK_SET) != 0){
1404                 OWPError(cntrl->ctx,OWPErrFATAL,errno,"fseeko(): %M");
1405                 goto err;
1406             }
1407             if(OWPParseRecords(cntrl->ctx,rfp,fhdr.num_datarecs,fhdr.version,
1408                         GetMaxRecv,(void*)&maxrec) != OWPErrOK){
1409                 OWPError(cntrl->ctx,OWPErrFATAL,errno,
1410                         "_OWPCleanUpSessions: GetMaxRecv failed");
1411                 goto err;
1412             }
1413 
1414             /*
1415              * If no records were actually received - truncate the
1416              * file to oset_datarecs and set num_datarecs to 0 and
1417              * be done.
1418              */
1419             if(!maxrec.found){
1420                 if(ftruncate(fileno(rfp),fhdr.oset_datarecs) != 0){
1421                     OWPError(cntrl->ctx,OWPErrFATAL,errno,"ftruncate(): %M");
1422                     goto err;
1423                 }
1424                 if( !OWPWriteDataHeaderNumDataRecs(cntrl->ctx,rfp,0)){
1425                     goto err;
1426                 }
1427 
1428                 /* goto next session */
1429                 continue;
1430             }
1431             max_recv = maxrec.maxrecv;
1432             toff = fhdr.oset_datarecs + (maxrec.index_badlost * fhdr.rec_size);
1433         }
1434 
1435         /*
1436          * Advance fp to toff and remove all lost records with
1437          * index greater than max_recv from there to the end.
1438          */
1439         if(fseeko(rfp,toff,SEEK_SET) != 0){
1440             OWPError(cntrl->ctx,OWPErrFATAL,errno,"fseeko(): %M");
1441             goto err;
1442         }
1443 
1444         num_recs = fhdr.num_datarecs;
1445         for(j=(toff - fhdr.oset_datarecs)/fhdr.rec_size;
1446                 j<fhdr.num_datarecs;
1447                 j++){
1448 
1449             /*
1450              * Read the packet record from midI.
1451              */
1452             if(fread(rbuf,fhdr.rec_size,1,rfp) != 1){
1453                 OWPError(cntrl->ctx,OWPErrFATAL,errno,
1454                         "fread(): Reading session file sid(%s): %M",
1455                         sid_name);
1456                 goto loop_err;
1457             }
1458             if(!_OWPDecodeDataRecord(fhdr.version,&rec,rbuf)){
1459                 errno = EFTYPE;
1460                 OWPError(cntrl->ctx,OWPErrFATAL,errno,
1461                         "_OWPCleanDataRecs: Invalid data record sid(%s)",
1462                         sid_name);
1463                 goto loop_err;
1464             }
1465 
1466             /*
1467              * If seq_no is > max_recv then delete the packet
1468              */
1469             if(rec.seq_no > max_recv){
1470                 num_recs--;
1471                 /*
1472                  * If wfp==rfp, then create another fp for wfp and point
1473                  * it at the current record so it will be written over.
1474                  */
1475                 if(wfp == rfp){
1476                     int     newfd;
1477                     char    tmpfname[PATH_MAX];
1478                     char    *dname;
1479                     char    *tmpl = "owamp.XXXXXXXX";
1480 
1481                     /*
1482                      * Need another file for bookkeeping... First determine
1483                      * what dir to put it in.
1484                      */
1485                     dname = NULL;
1486                     memset(tmpfname,'\0',sizeof(tmpfname));
1487 
1488                     /* put it in the same dir as session data if possible */
1489                     if( strlen(tptr->endpoint->fname) > 0){
1490                         strncpy(tmpfname,tptr->endpoint->fname,PATH_MAX);
1491                         dname = dirname(tmpfname);
1492                     }
1493 
1494                     /* otherwise use tmpdir */
1495                     if( !dname){
1496                         dname = getenv("TMPDIR");
1497                     }
1498                     if( !dname){
1499                         dname = "/tmp";
1500                     }
1501 
1502                     /* Make sure pathname will not over-run memory. */
1503                     if(strlen(tmpl) + OWP_PATH_SEPARATOR_LEN + strlen(dname) >
1504                             PATH_MAX){
1505                         OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1506                                 "_OWPCleanUpSessions: Unable to create temp file: Path Too Long");
1507                         goto err;
1508                     }
1509 
1510                     /* create template (fname) string for mkstemp */
1511                     strcpy(tmpfname,dname);
1512                     strcat(tmpfname,OWP_PATH_SEPARATOR);
1513                     strcat(tmpfname,tmpl);
1514                     if( (newfd = mkstemp(tmpfname)) < 0){
1515                         OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1516                                 "_OWPCleanUpSessions: mkstemp(%s): %M",
1517                                 tmpfname);
1518                         goto err;
1519                     }
1520 
1521                     /* immediately unlink - no need for a directory entry */
1522                     if(unlink(tmpfname) != 0){
1523                         OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1524                                 "unlink(%s): %M",tmpfname);
1525                         close(newfd);
1526                         goto err;
1527                     }
1528 
1529                     /*
1530                      * Copy original file into tmpfile from the beginning
1531                      * until just before the current record.
1532                      */
1533                     toff = fhdr.oset_datarecs + (j * fhdr.rec_size);
1534                     if(I2CopyFile(OWPContextErrHandle(cntrl->ctx),
1535                                 newfd,fileno(rfp),toff) != 0){
1536                         OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1537                                 "_OWPCleanUpSessions: Unable to copy session data: I2CopyFile(): %M");
1538                         close(newfd);
1539                         goto err;
1540                     }
1541 
1542                     if( !(wfp = fdopen(newfd,"r+b"))){
1543                         OWPError(cntrl->ctx,OWPErrFATAL,errno,"fdopen(%d): %M",
1544                                 newfd);
1545                         close(newfd);
1546                         goto err;
1547                     }
1548 
1549                     /*
1550                      * Seek new wfp to end of tmpfile.
1551                      */
1552                     if(fseeko(wfp,0,SEEK_END) != 0){
1553                         OWPError(cntrl->ctx,OWPErrFATAL,errno,"fseeko(): %M");
1554                         goto loop_err;
1555                     }
1556                 }
1557             }
1558             /*
1559              * Otherwise, this is a good packet and it should be kept.
1560              */
1561             else{
1562                 if(wfp != rfp){
1563                     if(fwrite(rbuf,fhdr.rec_size,1,wfp) != 1){
1564                         OWPError(cntrl->ctx,OWPErrFATAL,errno,
1565                                 "fwrite(): Writing session file sid(%s): %M",
1566                                 sid_name);
1567                         goto loop_err;
1568                     }
1569                 }
1570             }
1571             continue;
1572 loop_err:
1573             if(wfp != rfp){
1574                 fclose(wfp);
1575             }
1576             goto err;
1577         }
1578 
1579         /*
1580          * If two fp's were used, then the tmpfile needs to be copied
1581          * back to the original file.
1582          */
1583         if(wfp != rfp){
1584             if(I2CopyFile(OWPContextErrHandle(cntrl->ctx),
1585                         fileno(rfp),fileno(wfp),0) != 0){
1586                 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1587                         "_OWPCleanUpSessions: Unable to copy session data: I2CopyFile(): %M");
1588                 fclose(wfp);
1589                 goto err;
1590             }
1591             fclose(wfp);
1592         }
1593 
1594         /*
1595          * Write num_recs and "finished" into file.
1596          */
1597         if( !OWPWriteDataHeaderNumDataRecs(cntrl->ctx,rfp,num_recs)){
1598             goto err;
1599         }
1600 
1601         /*
1602          * Session should still be marked as incomplete because
1603          * the StopSessions message information was not available.
1604          */
1605         if( !_OWPWriteDataHeaderFinished(cntrl->ctx,rfp,
1606                     OWP_SESSION_FINISHED_INCOMPLETE,0)){
1607             goto err;
1608         }
1609 
1610         flk.l_type = F_UNLCK;
1611         if( fcntl(fileno(rfp), F_SETLKW, &flk) < 0){
1612             OWPError(cntrl->ctx,OWPErrFATAL,errno,
1613                     "_OWPCleanUpSessions: Unable to unlock file sid(%s): %M",
1614                     sid_name);
1615             goto err;
1616         }
1617     }
1618 
1619     return True;
1620 
1621 err:
1622 
1623     *acceptval = OWP_CNTRL_FAILURE;
1624     OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1625                                             "_OWPCleanUpSessions: Failed");
1626     return False;
1627 }
1628 
1629 /*
1630  * Function:
1631  *
1632  * Description:
1633  *
1634  * In Args:
1635  *
1636  * Out Args:
1637  *
1638  * Scope:
1639  * Returns:
1640  * Side Effect:
1641  */
1642 OWPErrSeverity
OWPStopSessions(OWPControl cntrl,int * retn_on_intr,OWPAcceptType * acceptval_ret)1643 OWPStopSessions(
1644         OWPControl      cntrl,
1645         int             *retn_on_intr,
1646         OWPAcceptType   *acceptval_ret        /* in/out        */
1647         )
1648 {
1649     OWPErrSeverity  err,err2=OWPErrOK;
1650     OWPRequestType  msgtype;
1651     OWPAcceptType   aval=OWP_CNTRL_ACCEPT;
1652     OWPAcceptType   *acceptval=&aval;
1653     int             ival=0;
1654     int             *intr=&ival;
1655     uint32_t        num_sessions=0;
1656     OWPTimeStamp    stoptime;
1657     OWPBoolean      readstop = True;
1658 
1659     if(acceptval_ret){
1660         acceptval = acceptval_ret;
1661     }
1662 
1663     if(retn_on_intr){
1664         intr = retn_on_intr;
1665     }
1666 
1667     err = _OWPStopSendSessions(cntrl,acceptval,&num_sessions);
1668     err2 = MIN(err,err2);
1669     if(err2 < OWPErrWARNING){
1670         goto done;
1671     }
1672     err = _OWPWriteStopSessions(cntrl,intr,*acceptval,num_sessions);
1673     err2 = MIN(err,err2);
1674     if(err2 < OWPErrWARNING){
1675         readstop = False;
1676         goto clean_sessions;
1677     }
1678 
1679     msgtype = OWPReadRequestType(cntrl,intr);
1680     switch(msgtype){
1681         case OWPReqStopSessions:
1682             break;
1683 
1684         case OWPReqSockClose:
1685         case OWPReqSockIntr:
1686             readstop = False;
1687             break;
1688 
1689         default:
1690             OWPError(cntrl->ctx,OWPErrFATAL,OWPErrINVALID,
1691                     "OWPStopSessions: Invalid protocol message received.");
1692             err2 = OWPErrFATAL;
1693             goto done;
1694     }
1695 
1696 clean_sessions:
1697 
1698     /*
1699      * Get time StopSessions was recieved and use that to
1700      * delete all packets with a presumed send time later
1701      * than stoptime-timeout. (As per section 3.8 owamp draft 14)
1702      */
1703     (void)OWPGetTimeOfDay(cntrl->ctx,&stoptime);
1704 
1705     /*
1706      * Stop Recv side sessions now that we have received the
1707      * StopSessions message (even though it has not been completely
1708      * read yet).
1709      */
1710     err = _OWPStopRecvSessions(cntrl,acceptval);
1711     err2 = MIN(err,err2);
1712     if(err2 < OWPErrWARNING){
1713         goto done;
1714     }
1715 
1716     /*
1717      * Now read the full StopSessions message.
1718      * This will take the NextSeq and SkipRecords and
1719      * put them in the recv session files as well.
1720      */
1721     if(readstop){
1722         err = _OWPReadStopSessions(cntrl,intr,acceptval,stoptime);
1723         err2 = MIN(err,err2);
1724     }
1725     else if(!_OWPCleanUpSessions(cntrl,intr,acceptval,stoptime)){
1726         err2 = OWPErrFATAL;
1727     }
1728 
1729 done:
1730     /*
1731      * Free memory from sessions
1732      */
1733     while(cntrl->tests){
1734         err = _OWPTestSessionFree(cntrl->tests,*acceptval);
1735         err2 = MIN(err,err2);
1736     }
1737 
1738     if(err2 < OWPErrWARNING){
1739         if(*acceptval == OWP_CNTRL_ACCEPT){
1740             *acceptval = OWP_CNTRL_FAILURE;
1741         }
1742         return _OWPFailControlSession(cntrl,err2);
1743     }
1744     else if(!readstop){
1745         return _OWPFailControlSession(cntrl,OWPErrFATAL);
1746     }
1747 
1748     cntrl->state &= ~_OWPStateTest;
1749 
1750     return err2;
1751 }
1752 
1753 int
OWPStopSessionsWait(OWPControl cntrl,OWPNum64 * wake,int * retn_on_intr,OWPAcceptType * acceptval_ret,OWPErrSeverity * err_ret)1754 OWPStopSessionsWait(
1755         OWPControl      cntrl,
1756         OWPNum64        *wake,
1757         int             *retn_on_intr,
1758         OWPAcceptType   *acceptval_ret,
1759         OWPErrSeverity  *err_ret
1760         )
1761 {
1762     struct timeval  reltime;
1763     struct timeval  *waittime = NULL;
1764     fd_set          readfds;
1765     fd_set          exceptfds;
1766     int             rc;
1767     int             msgtype;
1768     OWPErrSeverity  err2=OWPErrOK;
1769     OWPAcceptType   aval;
1770     OWPAcceptType   *acceptval=&aval;
1771     int             ival=0;
1772     int             *intr=&ival;
1773     uint32_t        num_sessions=0;
1774     OWPTimeStamp    stoptime;
1775     OWPBoolean      readstop=True;
1776 
1777     *err_ret = OWPErrOK;
1778     if(acceptval_ret){
1779         acceptval = acceptval_ret;
1780     }
1781     *acceptval = OWP_CNTRL_ACCEPT;
1782 
1783     if(retn_on_intr){
1784         intr = retn_on_intr;
1785     }
1786 
1787     if(!cntrl || cntrl->sockfd < 0){
1788         *err_ret = OWPErrFATAL;
1789         return -1;
1790     }
1791 
1792     /*
1793      * If there are no active sessions, get the status and return.
1794      */
1795     if(!OWPSessionsActive(cntrl,acceptval) || (*acceptval)){
1796         /*
1797          * Sessions are complete - send StopSessions message.
1798          */
1799         *err_ret = OWPStopSessions(cntrl,intr,acceptval);
1800         return 0;
1801     }
1802 
1803     if(wake){
1804         OWPTimeStamp    currstamp;
1805         OWPNum64        wakenum;
1806 
1807         if(!OWPGetTimeOfDay(cntrl->ctx,&currstamp)){
1808             OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1809                     "OWPGetTimeOfDay(): %M");
1810             return -1;
1811         }
1812 
1813         if(OWPNum64Cmp(currstamp.owptime,*wake) < 0){
1814             wakenum = OWPNum64Sub(*wake,currstamp.owptime);
1815             OWPNum64ToTimeval(&reltime,wakenum);
1816         }
1817         else{
1818             tvalclear(&reltime);
1819         }
1820 
1821         waittime = &reltime;
1822     }
1823 
1824 
1825     FD_ZERO(&readfds);
1826     FD_SET(cntrl->sockfd,&readfds);
1827     FD_ZERO(&exceptfds);
1828     FD_SET(cntrl->sockfd,&exceptfds);
1829 AGAIN:
1830     rc = select(cntrl->sockfd+1,&readfds,NULL,&exceptfds,waittime);
1831 
1832     if(rc < 0){
1833         if(errno != EINTR){
1834             OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1835                     "select():%M");
1836             *err_ret = OWPErrFATAL;
1837             return -1;
1838         }
1839         if(waittime || *intr){
1840             return 2;
1841         }
1842 
1843         /*
1844          * If there are tests still happening, and no tests have
1845          * ended in error - go back to select and wait for the
1846          * rest of the tests to complete.
1847          */
1848         if(OWPSessionsActive(cntrl,acceptval) && !*acceptval){
1849             goto AGAIN;
1850         }
1851 
1852         /*
1853          * Sessions are complete - send StopSessions message.
1854          */
1855         *err_ret = OWPStopSessions(cntrl,intr,acceptval);
1856 
1857         return 0;
1858     }
1859     if(rc == 0)
1860         return 1;
1861 
1862     if(!FD_ISSET(cntrl->sockfd,&readfds) &&
1863             !FD_ISSET(cntrl->sockfd,&exceptfds)){
1864         OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1865                 "select():cntrl fd not ready?:%M");
1866         *err_ret = _OWPFailControlSession(cntrl,OWPErrFATAL);
1867         goto done;
1868     }
1869 
1870     msgtype = OWPReadRequestType(cntrl,intr);
1871     switch(msgtype){
1872         case OWPReqStopSessions:
1873             break;
1874 
1875         case OWPReqSockClose:
1876         case OWPReqSockIntr:
1877             /*
1878              * Go through all recv sessions and delete
1879              * all missing packet records *after* the last
1880              * good one. (section 3.8 of draft 14)
1881              * (readstop indicates the call of _OWPCleanUpSessions()
1882              * and that does the section 3.8 cleanup.)
1883              */
1884             readstop = False;
1885             break;
1886 
1887         default:
1888             OWPError(cntrl->ctx,OWPErrFATAL,OWPErrINVALID,
1889                     "OWPStopSessionsWait: Invalid protocol message received.");
1890             *err_ret = OWPErrFATAL;
1891             goto done;
1892     }
1893 
1894     /*
1895      * Get time StopSessions was recieved and use that to
1896      * delete all packets with a presumed send time later
1897      * than stoptime-timeout. (As per section 3.8 owamp draft 14)
1898      */
1899     (void)OWPGetTimeOfDay(cntrl->ctx,&stoptime);
1900 
1901     /*
1902      * Stop Send sessions - the other side does not want more packets.
1903      * Do this first to be a "good" citizen.
1904      */
1905     err2 = _OWPStopSendSessions(cntrl,acceptval,&num_sessions);
1906     *err_ret = MIN(*err_ret,err2);
1907     if(*err_ret < OWPErrWARNING){
1908         goto done;
1909     }
1910 
1911     /*
1912      * Stop Recv side sessions now that we have received the
1913      * StopSessions message (even though it has not been completely
1914      * read yet).
1915      */
1916     err2 = _OWPStopRecvSessions(cntrl,acceptval);
1917     *err_ret = MIN(*err_ret,err2);
1918     if(*err_ret < OWPErrWARNING){
1919         goto done;
1920     }
1921 
1922     /*
1923      * Read the rest of the stop sessions message and complete
1924      * the recv side session files.
1925      */
1926     if(readstop){
1927         err2 = _OWPReadStopSessions(cntrl,intr,acceptval,stoptime);
1928         *err_ret = MIN(*err_ret,err2);
1929     }
1930     else if(!_OWPCleanUpSessions(cntrl,intr,acceptval,stoptime)){
1931         *err_ret = OWPErrFATAL;
1932     }
1933 
1934 done:
1935 
1936     if(*err_ret < OWPErrWARNING){
1937         *acceptval = OWP_CNTRL_FAILURE;
1938     }
1939 
1940     /*
1941      * If errors are non-fatal (warning or better) then send the
1942      * stop sessions message.
1943      */
1944     if(readstop && (*err_ret >= OWPErrWARNING)){
1945         err2 = _OWPWriteStopSessions(cntrl,intr,*acceptval,num_sessions);
1946         *err_ret = MIN(*err_ret,err2);
1947     }
1948 
1949     /*
1950      * Clean up memory.
1951      */
1952     while(cntrl->tests){
1953         err2 = _OWPTestSessionFree(cntrl->tests,*acceptval);
1954         *err_ret = MIN(*err_ret,err2);
1955     }
1956 
1957     /*
1958      * If anything has failed along the way, report failure.
1959      */
1960     if(*err_ret < OWPErrWARNING){
1961         *acceptval = OWP_CNTRL_FAILURE;
1962         _OWPFailControlSession(cntrl,OWPErrFATAL);
1963         return -1;
1964     }
1965     else if(!readstop){
1966         _OWPFailControlSession(cntrl,OWPErrFATAL);
1967         return -1;
1968     }
1969 
1970 
1971     /*
1972      * Otherwise, report success.
1973      */
1974     cntrl->state &= ~_OWPStateTest;
1975 
1976     return 0;
1977 }
1978 
1979 static OWPSessionFinishedType
GetSessionFinishedType(OWPContext ctx,uint32_t val)1980 GetSessionFinishedType(
1981         OWPContext  ctx,
1982         uint32_t   val
1983         )
1984 {
1985     switch(val){
1986         case OWP_SESSION_FINISHED_ERROR:
1987             return OWP_SESSION_FINISHED_ERROR;
1988         case OWP_SESSION_FINISHED_NORMAL:
1989             return OWP_SESSION_FINISHED_NORMAL;
1990         case OWP_SESSION_FINISHED_INCOMPLETE:
1991             return OWP_SESSION_FINISHED_INCOMPLETE;
1992         default:
1993             OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
1994                     "GetSessionFinishedType: Invalid val %u",val);
1995             return OWP_SESSION_FINISHED_ERROR;
1996     }
1997 }
1998 
1999 /*
2000  *  Functions for writing and reading headers. The format varies
2001  *  according to the version. In all cases the files starts
2002  *  with 4 bytes of magic number, 4 bytes of version, and
2003  *  8 bytes of total header length (version and header length
2004  *  fields given in network byte order). The rest depends on
2005  *  the version as follows:
2006  *
2007  *  Version 0: nothing - data records follow "hdr length".
2008  *  Version 2: Session Request as per version 5 of the protocol (use hdr len
2009  *  to skip session request, or read it using the format described
2010  *  below. (All values are in network byte order.)
2011  *
2012  *  0 format is as follows:
2013  *
2014  *       0                   1                   2                   3
2015  *       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
2016  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2017  *    00|       "O"     |       "w"     |       "A"     |       \0      |
2018  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2019  *    04|                        Version                                |
2020  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2021  *    08|                      hdr length (unsigned 64bit)              |
2022  *    12|                                                               |
2023  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2024  *
2025  *  2 format is as follows:
2026  *
2027  *
2028  *       0                   1                   2                   3
2029  *       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
2030  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2031  *    00|       "O"     |       "w"     |       "A"     |       \0      |
2032  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2033  *    04|                        Version                                |
2034  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2035  *    08|                      hdr length (unsigned 64bit)              |
2036  *    12|                                                               |
2037  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2038  *    16|                        Finished                               |
2039  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2040  *    20|                                                               |
2041  *      ...                 TestRequestPreamble (protocol.c)          ...
2042  *   128|                                                               |
2043  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2044  *   132|                                                               |
2045  *   136|                   Slot(1) definitions (16 octets each)        |
2046  *   140|                                                               |
2047  *   144|                                                               |
2048  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2049  *   148:(148+(16*(nslots-1)) (16 octets for each additional slot)
2050  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2051  *      |                                                               |
2052  *      |                   Zero Integrity Padding (16 octets)          |
2053  *      |                                                               |
2054  *      |                                                               |
2055  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2056  *
2057  *  Then individual packet records start. (hdr_len should point to here.)
2058  *
2059  *       0                   1                   2                   3
2060  *       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
2061  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2062  *    00|                   Sequence Number                             |
2063  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2064  *    04|                                                               |
2065  *    08|                   Send Timestamp                              |
2066  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2067  *    12|  Send Error Estimate          |                               |
2068  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
2069  *    16|                   Recv Timestamp                              |
2070  *      +                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2071  *    20|                               |       Recv Error Estimate     |
2072  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2073  *
2074  *  Version 3: Session Request as per version 12 of the protocol (use hdr len
2075  *  to skip the file header which includes file specific fields, the
2076  *  session request and possibly the skip records. (skip records
2077  *  can be between the session request and the packet records or
2078  *  follow everything.
2079  *
2080  *       0                   1                   2                   3
2081  *       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
2082  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2083  *    00|       "O"     |       "w"     |       "A"     |       \0      |
2084  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2085  *    04|                            Version                            |
2086  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2087  *    08|                            Finished                           |
2088  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2089  *    12|                           Next Seqno                          |
2090  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2091  *    16|                     Number of Skip Ranges                     |
2092  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2093  *    20|                       Number of Records                       |
2094  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2095  *    24|                      oset to Skip Ranges                      |
2096  *    28|                                                               |
2097  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2098  *    32|                        oset to Records                        |
2099  *    36|                                                               |
2100  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2101  *    40|                                                               |
2102  *      ...                 TestRequestPreamble (protocol.c)          ...
2103  *   148|                                                               |
2104  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2105  *   152|                                                               |
2106  *   156|                   Slot(1) definitions (16 octets each)        |
2107  *   160|                                                               |
2108  *   164|                                                               |
2109  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2110  *   168:(168+(16*(nslots-1))) (16 octets for each additional slot)
2111  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2112  *      |                                                               |
2113  *      |                   Integrity Zero Padding (16 octets)          |
2114  *      |                                                               |
2115  *      |                                                               |
2116  *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2117  *
2118  * Then individual packet records or skip records start. It does not matter
2119  * which is first. The Num Skip Records and Num Data Records fields are
2120  * used to determine how long these ranges will be.
2121  *
2122  * The format for individual packet records is documented in the
2123  * header for the _OWPDecodeDataRecord function which should be used
2124  * to fetch them.
2125  *
2126  * The format for individual skip records is documented in the
2127  * header for the _OWPDecodeSkipRecord function which should be used
2128  * to fetch them.
2129  *
2130  * If Number of Skip Ranges == 0, then the value of oset to Skip Ranges
2131  * can remain nil.
2132  *
2133  * The API is setup to work from two points of view.
2134  *
2135  * Endpoint Reciever:
2136  *  Packets come in and it will not know about skips until
2137  *  after all data records are done. If WriteDataHeader is called with
2138  *  num_skiprecs == 0, then the file will be setup in this mode. Then
2139  *  the WriteDataHeaderNumDataRecs function will need to be called to
2140  *  set that field in the file data records are complete. When the
2141  *  StopSessions message comes across it will include the skip_recs and
2142  *  the WriteDataHeaaderNumSkipRecs function can be called to add skip
2143  *  records. This function will set the skip_oset if skip_oset is
2144  *  currently nil in addition to the num_skips. Then skip records
2145  *  are written until complete. The num_datarecs field MUST be set
2146  *  before the WriteDataHeaderNumskips can be called.
2147  *
2148  * FetchClient:
2149  *  Entire session is retrieved. Skip records come before data records
2150  *  in a Fetch response so WriteDataHeader is called with num_skiprecs
2151  *  set. (If num_skiprecs is 0, then oset_skips will be initialized
2152  *  to null.) SkipRecords can be written until they are complete. Then
2153  *  datarecords can be written. In this case the number of records will
2154  *  also have already been set with WriteDataHeader.
2155  *  WriteDataHeaderNumSkipRecs is not valid to be called for a file that
2156  *  is initialized this way but WriteDataHeaderNumDataRecs
2157  *
2158  */
2159 
2160  /*
2161   * Function:    _OWPReadDataHeaderInitial
2162   *
2163   * Description:
2164   *      This function reads the initial file fields and also verifies the
2165   *      file is valid.
2166   *
2167   *
2168   * In Args:
2169   *
2170   * Out Args:
2171   *
2172   * Scope:
2173   * Returns:
2174   * Side Effect:
2175   *      fp will be advanced to the start of the TestRequestPreamble
2176   *      for version >= 2 files. For version <=1 files fp will be
2177   *      at the begining of data records.
2178   */
2179  static uint8_t owp_magic[] = _OWP_MAGIC_FILETYPE;
2180  OWPBoolean
_OWPReadDataHeaderInitial(OWPContext ctx,FILE * fp,_OWPSessionHeaderInitial phdr)2181  _OWPReadDataHeaderInitial(
2182          OWPContext                  ctx,
2183          FILE                        *fp,
2184          _OWPSessionHeaderInitial    phdr
2185          )
2186 {
2187     uint8_t    read_magic[sizeof(owp_magic)];
2188     int         err;
2189     uint64_t   oset;
2190     uint32_t   finished=0;
2191 
2192     /*
2193      * Initialize private file header record.
2194      */
2195     memset(phdr,0,sizeof(*phdr));
2196 
2197     /*
2198      * Stat the file to get the size and check that it is really there.
2199      */
2200     if(fstat(fileno(fp),&phdr->sbuf) < 0){
2201         err = errno;
2202         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fstat(): %M");
2203         errno = err;
2204         return False;
2205     }
2206 
2207     /*
2208      * Position fp to beginning of file.
2209      */
2210     if(fseeko(fp,0,SEEK_SET)){
2211         err = errno;
2212         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fseeko(): %M");
2213         errno = err;
2214         return False;
2215     }
2216 
2217     /*
2218      * File must be at least as big as the initial header information.
2219      * 16 bytes is magic+version+hdr_length which is the minimum
2220      * size of any valid owp file. (version 0 files)
2221      */
2222     if(phdr->sbuf.st_size < (off_t)16){
2223         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2224                 "_OWPReadDataHeaderInitial: Invalid owp file");
2225         /*
2226          * TODO: Check validity of this errno... May need to
2227          * use ENOSYS...
2228          */
2229         errno = EFTYPE;
2230         return False;
2231     }
2232 
2233     /*
2234      * Read and check "magic".
2235      * 4 bytes
2236      */
2237     if(fread(&read_magic[0], 1, 4, fp) != 4){
2238         err = errno;
2239         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2240         errno = err;
2241         return False;
2242     }
2243     if(memcmp(read_magic,owp_magic,4) != 0){
2244         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2245                 "_OWPReadDataHeaderInitial: Invalid owp file:wrong magic");
2246         /*
2247          * TODO: Check validity of this errno... May need to
2248          * use ENOSYS...
2249          */
2250         errno = EFTYPE;
2251         return False;
2252     }
2253 
2254     /*
2255      * Get the file "version".
2256      * 4 byte "network long" quantity
2257      */
2258     if(fread(&phdr->version, 1, 4, fp) != 4){
2259         err = errno;
2260         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2261         errno = err;
2262         return False;
2263     }
2264     phdr->version = ntohl(phdr->version);
2265 
2266     /*
2267      * Currently it supports 0 and 2 and 3.
2268      */
2269     phdr->header = True;
2270     switch(phdr->version){
2271         case 0:
2272             phdr->header = False;
2273         case 2:
2274             if(fread(&oset, 1, 8, fp) != 8){
2275                 err = errno;
2276                 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2277                 errno = err;
2278                 return False;
2279             }
2280             oset = ntohll(oset);
2281             phdr->hdr_len = (off_t)oset;
2282             if(oset != (uint64_t)phdr->hdr_len){
2283                 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2284                         "OWPReadDataHeaderInitial: Unable to represent file offset (%ull)",
2285                         oset);
2286                 return False;
2287             }
2288             phdr->rec_size = _OWP_DATARECV2_SIZE;
2289 
2290             break;
2291         case 3:
2292             phdr->rec_size = _OWP_DATARECV3_SIZE;
2293             break;
2294         default:
2295             OWPError(ctx,OWPErrFATAL,EINVAL,
2296                     "_OWPReadDataHeaderInitial: Invalid file version (%ul)",
2297                     phdr->version);
2298             return False;
2299     }
2300 
2301     if(phdr->version == 0)
2302         return True;
2303 
2304     /*
2305      * Finished
2306      */
2307     if(fread(&finished, 1, 4, fp) != 4){
2308         err = errno;
2309         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2310         errno = err;
2311         return False;
2312     }
2313     phdr->finished = GetSessionFinishedType(ctx,ntohl(finished));
2314 
2315     if(phdr->version < 3){
2316         phdr->oset_skiprecs = NULL;
2317         phdr->num_skiprecs = 0;
2318         phdr->oset_datarecs = phdr->hdr_len;
2319         phdr->num_datarecs = (phdr->sbuf.st_size - phdr->hdr_len)/
2320             phdr->rec_size;
2321         phdr->next_seqno = 0;
2322 
2323         return True;
2324     }
2325 
2326     /*
2327      * Next Seqno
2328      */
2329     if(fread(&phdr->next_seqno, 1, 4, fp) != 4){
2330         err = errno;
2331         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2332         errno = err;
2333         return False;
2334     }
2335     phdr->next_seqno = ntohl(phdr->next_seqno);
2336 
2337     /*
2338      * Num Skips
2339      */
2340     if(fread(&phdr->num_skiprecs, 1, 4, fp) != 4){
2341         err = errno;
2342         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2343         errno = err;
2344         return False;
2345     }
2346     phdr->num_skiprecs = ntohl(phdr->num_skiprecs);
2347 
2348     /*
2349      * Num Datarecs
2350      */
2351     if(fread(&phdr->num_datarecs, 1, 4, fp) != 4){
2352         err = errno;
2353         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2354         errno = err;
2355         return False;
2356     }
2357     phdr->num_datarecs = ntohl(phdr->num_datarecs);
2358 
2359     /*
2360      * Skips oset
2361      */
2362     if(fread(&oset, 1, 8, fp) != 8){
2363         err = errno;
2364         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2365         errno = err;
2366         return False;
2367     }
2368     oset = ntohll(oset);
2369     phdr->oset_skiprecs = (off_t)oset;
2370     if(oset != (uint64_t)phdr->oset_skiprecs){
2371         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2372                 "OWPReadDataHeaderInitial: Unable to represent file offset (%ull)",
2373                 oset);
2374         return False;
2375     }
2376 
2377     /*
2378      * Datarecs oset
2379      */
2380     if(fread(&oset, 1, 8, fp) != 8){
2381         err = errno;
2382         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2383         errno = err;
2384         return False;
2385     }
2386     oset = ntohll(oset);
2387     phdr->oset_datarecs = (off_t)oset;
2388     if(oset != (uint64_t)phdr->oset_datarecs){
2389         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2390                 "OWPReadDataHeaderInitial: Unable to represent file offset (%ull)",
2391                 oset);
2392         return False;
2393     }
2394     phdr->hdr_len = phdr->oset_datarecs;
2395 
2396     return True;
2397 }
2398 
2399 /*
2400  * Function:        _OWPWriteDataHeaderFinished
2401  *
2402  * Description:
2403  *        Write a new "finished" word into the file. This function seeks to
2404  *        the correct offset for a version 3 file.
2405  *
2406  * In Args:
2407  *
2408  * Out Args:
2409  *
2410  * Scope:
2411  * Returns:
2412  * Side Effect:
2413  */
2414 OWPBoolean
_OWPWriteDataHeaderFinished(OWPContext ctx,FILE * fp,OWPSessionFinishedType finished,uint32_t next_seqno)2415 _OWPWriteDataHeaderFinished(
2416         OWPContext              ctx,
2417         FILE                    *fp,
2418         OWPSessionFinishedType  finished,
2419         uint32_t               next_seqno
2420         )
2421 {
2422     int err;
2423     uint32_t   finword;
2424 
2425     if(finished > 2){
2426         OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
2427                 "_OWPWriteDataHeaderFinished: Invalid \"finished\"");
2428         return False;
2429     }
2430 
2431     /*
2432      * seek to finished word.
2433      */
2434     if(fseeko(fp,8,SEEK_SET)){
2435         err = errno;
2436         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fseeko(): %M");
2437         errno = err;
2438         return False;
2439     }
2440 
2441     /*
2442      * Write
2443      */
2444     finword = htonl((uint32_t)finished);
2445     if(fwrite(&finword,1,sizeof(finword),fp) != 4){
2446         err = errno;
2447         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fwrite(): %M");
2448         errno = err;
2449         return False;
2450     }
2451 
2452     next_seqno = htonl(next_seqno);
2453     if(fwrite(&next_seqno,1,sizeof(next_seqno),fp) != 4){
2454         err = errno;
2455         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fwrite(): %M");
2456         errno = err;
2457         return False;
2458     }
2459 
2460     if(fflush(fp) != 0){
2461         err = errno;
2462         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fflush(): %M");
2463         errno = err;
2464         return False;
2465     }
2466 
2467     return True;
2468 }
2469 
2470 /*
2471  * Function:    OWPWriteDataHeaderNumSkipRecs
2472  *
2473  * Description:
2474  *              Sets the num_skips field and the oset_skips
2475  *              field. oset_datarecs and num_datarecs
2476  *              MUST be set prior to this call. (Either by calling
2477  *              WriteDataHeader with num_datarecs or by calling
2478  *              WriteDataHeaderRecords.)
2479  *
2480  *              This function should only be called if skip records are
2481  *              being placed after datarecs, and then only after the
2482  *              number of datarecs has been fixed. If skip records are
2483  *              first in the file these fields MUST be initalized with
2484  *              the proper num_skips.
2485  *
2486  * In Args:
2487  *
2488  * Out Args:
2489  *
2490  * Scope:
2491  * Returns:
2492  * Side Effect:
2493  *              fp will be advanced to an undefined offset.
2494  */
2495 OWPBoolean
OWPWriteDataHeaderNumSkipRecs(OWPContext ctx,FILE * fp,uint32_t num_skiprecs)2496 OWPWriteDataHeaderNumSkipRecs(
2497         OWPContext ctx,
2498         FILE       *fp,
2499         uint32_t  num_skiprecs
2500         )
2501 {
2502     _OWPSessionHeaderInitialRec phrec;
2503     uint32_t                   n32;
2504     uint64_t                   n64;
2505     int                         err;
2506 
2507     if(!_OWPReadDataHeaderInitial(ctx,fp,&phrec)){
2508         return False;
2509     }
2510 
2511     /*
2512      * Files before version 3 don't have skips.
2513      */
2514     if(phrec.version < 3){
2515         OWPError(ctx,OWPErrFATAL,EINVAL,
2516                 "_OWPWriteDataHeaderNumSkipRecs: Invalid file version (%ul)",
2517                 phrec.version);
2518         errno = EINVAL;
2519         return False;
2520     }
2521 
2522     /*
2523      * This function should not be called on a file that already has
2524      * initialized num_skiprecs and oset_skiprecs.
2525      */
2526     if(phrec.num_skiprecs || phrec.oset_skiprecs){
2527         OWPError(ctx,OWPErrFATAL,EINVAL,
2528                 "_OWPWriteDataHeaderNumSkipRecs: Number skips already defined");
2529         errno = EINVAL;
2530         return False;
2531     }
2532 
2533     /*
2534      * Position fp to num_skiprecs field.
2535      */
2536     if(fseeko(fp,16,SEEK_SET)){
2537         err = errno;
2538         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fseeko(): %M");
2539         errno = err;
2540         return False;
2541     }
2542 
2543     /*
2544      * write num_skiprecs
2545      */
2546     n32 = htonl(num_skiprecs);
2547     if(fwrite(&n32, 1, sizeof(n32), fp) != sizeof(n32)){
2548         err = errno;
2549         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fwrite(): %M");
2550         errno = err;
2551         return False;
2552     }
2553 
2554     /*
2555      * Position fp to oset_skiprecs field.
2556      */
2557     if(fseeko(fp,24,SEEK_SET)){
2558         err = errno;
2559         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fseeko(): %M");
2560         errno = err;
2561         return False;
2562     }
2563 
2564     /*
2565      * Convert off_t oset_skiprecs to network ordered uint64_t
2566      */
2567     phrec.oset_skiprecs = phrec.oset_datarecs +
2568         (phrec.rec_size * phrec.num_datarecs);
2569     n64 = (uint64_t)phrec.oset_skiprecs;
2570     if(phrec.oset_skiprecs != (off_t)n64){
2571         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2572                 "_OWPWriteDataHeaderNumSkipRecs: Unable to represet file offset (%ull)",
2573                 phrec.oset_skiprecs);
2574         return False;
2575     }
2576     n64 = htonll(n64);
2577 
2578     /*
2579      * write oset_skiprecs
2580      */
2581     if(fwrite(&n64, 1, sizeof(n64), fp) != sizeof(n64)){
2582 
2583         err = errno;
2584         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fwrite(): %M");
2585         errno = err;
2586         return False;
2587     }
2588 
2589     return True;
2590 }
2591 
2592 /*
2593  * Function:    OWPWriteDataHeaderNumDataRecs
2594  *
2595  * Description:
2596  *              Sets the num_datarecs field.
2597  *              If oset_skiprecs is nil, this function sets that to
2598  *              just beyond the data records.
2599  *
2600  * In Args:
2601  *
2602  * Out Args:
2603  *
2604  * Scope:
2605  * Returns:
2606  * Side Effect:
2607  */
2608 OWPBoolean
OWPWriteDataHeaderNumDataRecs(OWPContext ctx,FILE * fp,uint32_t num_datarecs)2609 OWPWriteDataHeaderNumDataRecs(
2610         OWPContext ctx,
2611         FILE       *fp,
2612         uint32_t  num_datarecs
2613         )
2614 {
2615     _OWPSessionHeaderInitialRec phrec;
2616     uint32_t                   n32;
2617     int                         err;
2618 
2619     if(!_OWPReadDataHeaderInitial(ctx,fp,&phrec)){
2620         return False;
2621     }
2622 
2623     /*
2624      * Files before version 3 not supported for writing.
2625      */
2626     if(phrec.version < 3){
2627         OWPError(ctx,OWPErrFATAL,EINVAL,
2628                 "_OWPWriteDataHeaderNumDataRecs: Invalid file version (%ul)",
2629                 phrec.version);
2630         errno = EINVAL;
2631         return False;
2632     }
2633 
2634     /*
2635      * This function should not be called on a file that has
2636      * initialized oset_skiprecs to a greater offset than oset_datarecs.
2637      */
2638     if(phrec.oset_datarecs < phrec.oset_skiprecs){
2639         OWPError(ctx,OWPErrFATAL,EINVAL,
2640                 "_OWPWriteDataHeaderNumDataRecs: Can't change number of datarecs.");
2641         errno = EINVAL;
2642         return False;
2643     }
2644 
2645     /*
2646      * Position fp to num_datarecs field.
2647      */
2648     if(fseeko(fp,20,SEEK_SET)){
2649         err = errno;
2650         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fseeko(): %M");
2651         errno = err;
2652         return False;
2653     }
2654 
2655     /*
2656      * write num_datarecs
2657      */
2658     n32 = htonl(num_datarecs);
2659     if(fwrite(&n32, 1, sizeof(n32), fp) != sizeof(n32)){
2660 
2661         err = errno;
2662         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fwrite(): %M");
2663         errno = err;
2664         return False;
2665     }
2666 
2667 
2668     return True;
2669 }
2670 
2671 /*
2672  * Function:    OWPWriteDataHeader
2673  *
2674  * Description:
2675  *    Write data header to the file.
2676  *
2677  * The fp is left pointing just past the IZP ready for either skip records
2678  * or data records.
2679  *
2680  * If num_skiprecs is non-zero, the file is configured for skip records
2681  * to come before data records and oset_skiprecs and oset_datarecs is
2682  * initialized in the file and returned in hdr.
2683  *
2684  * If num_skiprecs is zero, the file is configured for data records to
2685  * come before skip records. oset_datarecs will be set to just beyond
2686  * the header. If num_datarecs is zero, oset_skiprecs will be nil. If
2687  * num_datarecs is set, oset_skiprecs will be initialized as well.
2688  *
2689  *
2690  * In Args:
2691  *
2692  * Out Args:
2693  *
2694  * Scope:
2695  * Returns:
2696  * Side Effect:
2697  *      This function does not catch signals in the I/O with the
2698  *      file. If there is a signal - the fwrite will fail and this
2699  *      function will fail. The caller is responsible for checking any
2700  *      signal state.
2701  */
2702 OWPBoolean
OWPWriteDataHeader(OWPContext ctx,FILE * fp,OWPSessionHeader hdr)2703 OWPWriteDataHeader(
2704         OWPContext         ctx,
2705         FILE               *fp,
2706         OWPSessionHeader   hdr
2707         )
2708 {
2709     uint32_t   ver;
2710     uint32_t   finished = OWP_SESSION_FINISHED_INCOMPLETE;
2711     uint64_t   oset;
2712     uint64_t   skip_oset = 0;
2713     uint64_t   data_oset;
2714     off_t       oset_off;
2715 
2716     /* use uint32_t for proper alignment */
2717     uint32_t   msg[_OWP_TEST_REQUEST_PREAMBLE_SIZE/sizeof(uint32_t)];
2718     uint32_t   len = sizeof(msg);
2719     uint32_t   i;
2720     uint32_t   net32;
2721     uint64_t   net64;
2722 
2723     if(!hdr){
2724         OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
2725                 "OWPWriteDataHeader: No hdr data specified");
2726         return False;
2727     }
2728 
2729     /*
2730      * encode test_spec early so failure is detected early.
2731      */
2732     if((_OWPEncodeTestRequestPreamble(ctx,msg,&len,
2733                     (struct sockaddr*)&hdr->addr_sender,
2734                     (struct sockaddr*)&hdr->addr_receiver,
2735                     hdr->conf_sender,hdr->conf_receiver,
2736                     hdr->sid,&hdr->test_spec) != 0) || !len){
2737         return False;
2738     }
2739     ver = htonl(3);
2740 
2741     /*
2742      * Compute the offset to the end of the "header" information. Either
2743      * the data records, or the skip records depending upon which comes
2744      * first:
2745      *     MAGIC +
2746      *     Version +
2747      *     Finished +
2748      *     NextSeqno +
2749      *     NumSkips +
2750      *     NumRecs +
2751      *     OsetSkips +
2752      *     OsetRecs +
2753      *     TestRequestPramble +
2754      *     Slots
2755      */
2756     oset = sizeof(owp_magic) +
2757         sizeof(ver) +
2758         sizeof(finished) +
2759         sizeof(hdr->next_seqno) +
2760         sizeof(hdr->num_skiprecs) +
2761         sizeof(hdr->num_datarecs) +
2762         sizeof(oset)+
2763         sizeof(oset)+
2764         len +
2765         16*(hdr->test_spec.nslots+1);
2766 
2767     oset_off = (off_t)oset;
2768     if(oset != (uint64_t)oset_off){
2769         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2770             "OWPWriteDataHeader: Header too large for format representation (%"
2771             PRIu64 ")", oset);
2772         return False;
2773     }
2774 
2775 
2776     /*
2777      * write magic
2778      */
2779     if(fwrite(owp_magic, 1, sizeof(owp_magic), fp) != sizeof(owp_magic)){
2780         return False;
2781     }
2782 
2783     /*
2784      * write version
2785      */
2786     if(fwrite(&ver, 1, sizeof(ver), fp) != sizeof(ver)){
2787         return False;
2788     }
2789 
2790     /*
2791      * write finished
2792      */
2793     if(hdr){
2794         switch(hdr->finished){
2795             case 0:
2796             case 1:
2797                 finished = hdr->finished;
2798                 break;
2799             default:
2800                 break;
2801         }
2802     }
2803     net32 = htonl(finished);
2804     if(fwrite(&net32,1,sizeof(net32),fp) != sizeof(net32)){
2805         return False;
2806     }
2807 
2808     /*
2809      * Rest of "fixed" header.
2810      */
2811     net32 = htonl(hdr->next_seqno);
2812     if(fwrite(&net32,1,sizeof(net32),fp) != sizeof(net32)){
2813         return False;
2814     }
2815     net32 = htonl(hdr->num_skiprecs);
2816     if(fwrite(&net32,1,sizeof(net32),fp) != sizeof(net32)){
2817         return False;
2818     }
2819     net32 = htonl(hdr->num_datarecs);
2820     if(fwrite(&net32,1,sizeof(net32),fp) != sizeof(net32)){
2821         return False;
2822     }
2823 
2824     /*
2825      * write osets
2826      *
2827      * This logic puts the skip records first in the file if num_skiprecs
2828      * is set, and puts datarecords first otherwise. If data records are
2829      * first, the number of datarecs MUST be set in the file before any
2830      * skip records can be written.
2831      *
2832      */
2833     if(hdr->num_skiprecs){
2834         skip_oset = oset;
2835         data_oset = oset + (hdr->num_skiprecs * _OWP_SKIPREC_SIZE);
2836     }
2837     else{
2838         data_oset = oset;
2839 
2840         if(hdr->num_datarecs){
2841             skip_oset = oset + (hdr->num_datarecs * hdr->rec_size);
2842         }
2843     }
2844 
2845     net64 = htonll(skip_oset);
2846     if(fwrite(&net64,1,sizeof(net64),fp)!=sizeof(net64)){
2847         return False;
2848     }
2849 
2850     net64 = htonll(data_oset);
2851     if(fwrite(&net64,1,sizeof(net64),fp)!=sizeof(net64)){
2852         return False;
2853     }
2854 
2855     /*
2856      * write TestRequest preamble
2857      */
2858     if(fwrite(msg,1,len,fp) != len){
2859         return False;
2860     }
2861 
2862     /*
2863      * write slots
2864      */
2865     for(i=0;i<hdr->test_spec.nslots;i++){
2866         /*
2867          * Each slot is one block (16 bytes)
2868          */
2869         if(_OWPEncodeSlot(msg,&hdr->test_spec.slots[i]) !=
2870                 OWPErrOK){
2871             OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
2872                     "OWPWriteDataHeader: Invalid slot record");
2873             return False;
2874         }
2875         if(fwrite(msg,1,16,fp) != 16){
2876             return False;
2877         }
2878 
2879     }
2880 
2881     /*
2882      * write 16 Zero bytes in place of HMAC
2883      */
2884     memset(msg,0,16);
2885     if(fwrite(msg,1,16,fp) != 16){
2886         return False;
2887     }
2888 
2889     fflush(fp);
2890     return True;
2891 }
2892 
2893 /*
2894  * Function:        OWPWriteDataRecord
2895  *
2896  * Description:
2897  *         Write a single data record described by rec to file fp.
2898  *
2899  * In Args:
2900  *
2901  * Out Args:
2902  *
2903  * Scope:
2904  * Returns:
2905  * Side Effect:
2906  */
2907 OWPBoolean
OWPWriteDataRecord(OWPContext ctx,FILE * fp,OWPDataRec * rec)2908 OWPWriteDataRecord(
2909         OWPContext  ctx,
2910         FILE        *fp,
2911         OWPDataRec  *rec
2912         )
2913 {
2914     char    buf[_OWP_DATAREC_SIZE];
2915 
2916     if(!_OWPEncodeDataRecord(buf,rec)){
2917         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2918                 "OWPWriteDataRecord: Unable to encode data record");
2919         return False;
2920     }
2921 
2922     /*
2923      * write data record
2924      */
2925     if(fwrite(buf,1,_OWP_DATAREC_SIZE,fp) != _OWP_DATAREC_SIZE){
2926         OWPError(ctx,OWPErrFATAL,errno,
2927                 "OWPWriteDataRecord: fwrite(): %M");
2928         return False;
2929     }
2930 
2931     return True;
2932 }
2933 
2934 
2935 /*
2936  * Function:        OWPReadDataHeader
2937  *
2938  * Description:
2939  * Version 0:
2940  *      nothing - data records follow.
2941  * Version 2:
2942  *      Session Request as per version 5 of the protocol
2943  *         This function does NOT read the slots into the hdr_ret->test_spec.
2944  *         A separate function OWPReadDataHeaderSlots has been provided to do
2945  *         that. (Memory for the slots must be provided by the caller.)
2946  * Version 3:
2947  *      Same as 2, but api modifed to not return hdr_len as a field.
2948  *      hdr_ret is now REQUIRED to be filled in, and the oset to data
2949  *      and/or skip records can be retrieved from fields in that record.
2950  *
2951  *
2952  * In Args:
2953  *
2954  * Out Args:
2955  *
2956  * Scope:
2957  * Returns:
2958  * Side Effect:
2959  */
2960 uint32_t
OWPReadDataHeader(OWPContext ctx,FILE * fp,OWPSessionHeader hdr_ret)2961 OWPReadDataHeader(
2962         OWPContext          ctx,
2963         FILE                *fp,
2964         OWPSessionHeader    hdr_ret
2965         )
2966 {
2967     _OWPSessionHeaderInitialRec phrec;
2968     int         err;
2969 
2970     /* buffer for TestRequest 32 bit aligned */
2971     uint32_t    msg[_OWP_TEST_REQUEST_PREAMBLE_SIZE / sizeof(uint32_t)];
2972 
2973     hdr_ret->header = 0;
2974 
2975     if(!_OWPReadDataHeaderInitial(ctx,fp,&phrec)){
2976         return 0;
2977     }
2978 
2979     hdr_ret->version = phrec.version;
2980     hdr_ret->sbuf = phrec.sbuf;
2981     hdr_ret->rec_size = phrec.rec_size;
2982 
2983     /*
2984      * Decode the header if present(version 2).
2985      */
2986     if(phrec.version >= 2){
2987 
2988         hdr_ret->finished = phrec.finished;
2989 
2990         /*
2991          * read TestRequestPreamble
2992          */
2993         if(fread(msg,1,_OWP_TEST_REQUEST_PREAMBLE_SIZE,fp) !=
2994                 _OWP_TEST_REQUEST_PREAMBLE_SIZE){
2995             err = errno;
2996             OWPError(ctx,OWPErrFATAL,errno,"fread(): %M");
2997             errno = err;
2998             return 0;
2999         }
3000 
3001         hdr_ret->addr_len = sizeof(hdr_ret->addr_sender);
3002         /*
3003          * Now decode it into the hdr_ret variable.
3004          */
3005         if(_OWPDecodeTestRequestPreamble(ctx,False,msg,
3006                     _OWP_TEST_REQUEST_PREAMBLE_SIZE,
3007                     (struct sockaddr*)&hdr_ret->addr_sender,
3008                     (struct sockaddr*)&hdr_ret->addr_receiver,
3009                     &hdr_ret->addr_len,&hdr_ret->ipvn,
3010                     &hdr_ret->conf_sender,&hdr_ret->conf_receiver,
3011                     hdr_ret->sid,&hdr_ret->test_spec) != OWPErrOK){
3012             /*
3013              * TODO: Check validity of this errno... May need to
3014              * use ENOSYS...
3015              */
3016             errno = EFTYPE;
3017             return 0;
3018         }
3019 
3020         hdr_ret->header = True;
3021     }
3022 
3023     /*
3024      * Forward fp to data records.
3025      */
3026     if(fseeko(fp,phrec.hdr_len,SEEK_SET)){
3027         err = errno;
3028         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fseeko(): %M");
3029         errno = err;
3030         return 0;
3031     }
3032 
3033     /*
3034      * Make sure num_datarecs is not larger than the file allows.
3035      */
3036     if(phrec.num_datarecs > ((phrec.sbuf.st_size - phrec.hdr_len)/
3037                 hdr_ret->rec_size)){
3038         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
3039                 "OWPReadDataHeader: num_datarecs field larger than filesize.");
3040         return 0;
3041     }
3042 
3043     hdr_ret->next_seqno = phrec.next_seqno;
3044     hdr_ret->num_skiprecs = phrec.num_skiprecs;
3045     hdr_ret->oset_skiprecs = phrec.oset_skiprecs;
3046     hdr_ret->oset_datarecs = phrec.oset_datarecs;
3047     if(phrec.finished != OWP_SESSION_FINISHED_NORMAL){
3048         hdr_ret->num_datarecs = (phrec.sbuf.st_size - phrec.hdr_len)/
3049             hdr_ret->rec_size;
3050     }
3051     else{
3052         hdr_ret->num_datarecs = phrec.num_datarecs;
3053     }
3054 
3055     return hdr_ret->num_datarecs;
3056 }
3057 
3058 /*
3059  * Function:        OWPReadDataHeaderSlots
3060  *
3061  * Description:
3062  *         This function will read all the slot records out of the
3063  *         file fp. slots is assumed to be an array of OWPSlot records of
3064  *         length nslots.
3065  *
3066  *         This function will position the fp to the beginning of the data
3067  *         records.
3068  *
3069  * In Args:
3070  *
3071  * Out Args:
3072  *
3073  * Scope:
3074  * Returns:
3075  * Side Effect:
3076  */
3077 OWPBoolean
OWPReadDataHeaderSlots(OWPContext ctx,FILE * fp,uint32_t nslots,OWPSlot * slots)3078 OWPReadDataHeaderSlots(
3079         OWPContext  ctx,
3080         FILE        *fp,
3081         uint32_t   nslots,
3082         OWPSlot     *slots
3083         )
3084 {
3085     int                         err;
3086     _OWPSessionHeaderInitialRec phrec;
3087     uint32_t                    fileslots;
3088     uint32_t                    i;
3089     off_t                       slot_off;
3090     off_t                       hdr_off;
3091 
3092     /* buffer for Slots 32 bit aligned */
3093     uint32_t                    msg[16/sizeof(uint32_t)];
3094     uint32_t                    zero[16/sizeof(uint32_t)];
3095 
3096     /*
3097      * validate array.
3098      */
3099     assert(slots);
3100 
3101     /*
3102      * Stat the file and get the "initial" fields from the header.
3103      */
3104     if(!_OWPReadDataHeaderInitial(ctx,fp,&phrec)){
3105         return False;
3106     }
3107 
3108     /*
3109      * this function is currently only supported for version >=2
3110      */
3111     if(phrec.version < 2){
3112         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
3113                 "OWPReadDataHeaderSlots: Invalid file version (%d)",
3114                 phrec.version);
3115         errno = ENOSYS;
3116         return False;
3117     }
3118     else if(phrec.version == 2){
3119         slot_off = 132; /* see above layout of bytes */
3120         hdr_off = phrec.hdr_len;
3121     }
3122     else{
3123         slot_off = 152; /* see above layout of bytes */
3124 
3125         /*
3126          * Find offset to the end of the "header".
3127          */
3128         hdr_off = MIN(phrec.oset_skiprecs,phrec.oset_datarecs);
3129         if(!hdr_off){
3130             hdr_off = MAX(phrec.oset_skiprecs,phrec.oset_datarecs);
3131         }
3132     }
3133 
3134     /*
3135      * validate nslots passed in with what is in the file.
3136      * hdr_off should point to the offset in the file where the slots
3137      * are finished and the 1 block of zero padding is finished.
3138      */
3139     fileslots = hdr_off - slot_off; /* bytes for slots */
3140 
3141     /*
3142      * bytes for slots/zero padding must be of block size 16
3143      */
3144     if(fileslots%16){
3145         OWPError(ctx,OWPErrFATAL,EFTYPE,
3146                 "OWPReadDataHeaderSlots: Invalid hdr_offset (%" PRIu64 ")",
3147                 hdr_off);
3148         /*
3149          * TODO: Check validity of this errno... May need to
3150          * use ENOSYS...
3151          */
3152         errno = EFTYPE;
3153         return False;
3154     }
3155 
3156     /*
3157      * Convert bytes to number of slots. Divide by block size, then
3158      * subtract 1 for zero integrity block.
3159      */
3160     fileslots/=16;
3161     fileslots--;
3162 
3163     if(fileslots != nslots){
3164         OWPError(ctx,OWPErrFATAL,EINVAL,
3165                 "OWPReadDataHeaderSlots: nslots mismatch with file: fileslots(%d), nslots(%d)",
3166                 fileslots,nslots);
3167         errno = EINVAL;
3168         return False;
3169     }
3170 
3171     /*
3172      * Position fp to beginning of slot records.
3173      */
3174     if(fseeko(fp,slot_off,SEEK_SET)){
3175         err = errno;
3176         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fseeko(): %M");
3177         errno = err;
3178         return False;
3179     }
3180 
3181     for(i=0;i<nslots;i++){
3182 
3183         /*
3184          * Read slot into buffer.
3185          */
3186         if(fread(msg,1,16,fp) != 16){
3187             err = errno;
3188             OWPError(ctx,OWPErrFATAL,errno,"fread(): %M");
3189             errno = err;
3190             return False;
3191         }
3192 
3193         /*
3194          * Decode slot buffer into slot record.
3195          */
3196         if(_OWPDecodeSlot(&slots[i],msg) != OWPErrOK){
3197             OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
3198                     "OWPReadDataHeaderSlots: Invalid Slot record");
3199             errno = EFTYPE;
3200             return False;
3201         }
3202     }
3203 
3204     /*
3205      * Read block of Zero Integrity bytes into buffer.
3206      */
3207     if(fread(msg,1,16,fp) != 16){
3208         err = errno;
3209         OWPError(ctx,OWPErrFATAL,errno,"fread(): %M");
3210         errno = err;
3211         return False;
3212     }
3213 
3214     /*
3215      * check to make sure Zero bytes are zero.
3216      */
3217     memset(zero,0,16);
3218     if(memcmp(zero,msg,16) != 0){
3219         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
3220                 "OWPReadDataHeaderSlots: Invalid zero padding");
3221         errno = EFTYPE;
3222         return False;
3223     }
3224 
3225     return True;
3226 }
3227 
3228 /*
3229  * Function:        OWPParseRecords
3230  *
3231  * Description:
3232  *         Fetch num_rec records from disk calling the record proc function
3233  *         on each record.
3234  *
3235  * In Args:
3236  *
3237  * Out Args:
3238  *
3239  * Scope:
3240  * Returns:
3241  * Side Effect:
3242  */
3243 OWPErrSeverity
OWPParseRecords(OWPContext ctx,FILE * fp,uint32_t num_rec,uint32_t file_version,OWPDoDataRecord proc_rec,void * app_data)3244 OWPParseRecords(
3245         OWPContext      ctx,
3246         FILE            *fp,
3247         uint32_t       num_rec,
3248         uint32_t       file_version,
3249         OWPDoDataRecord proc_rec,
3250         void            *app_data
3251         )
3252 {
3253     size_t      len_rec;
3254     char        rbuf[_OWP_MAXDATAREC_SIZE];
3255     uint32_t    i;
3256     OWPDataRec  rec;
3257     int         rc;
3258 
3259     /*
3260      * This function is used to abstract away the different requirements
3261      * of different versions of the owd data files.
3262      * Currently it supports 0 and 2, (both of which
3263      * require the same 24 octet data records) and 3 which requires
3264      * 25 octets.
3265      */
3266     switch(file_version){
3267         case 0: case 2:
3268             len_rec = _OWP_DATARECV2_SIZE;
3269             break;
3270         case 3:
3271             len_rec = _OWP_DATAREC_SIZE;
3272             break;
3273         default:
3274             OWPError(ctx,OWPErrFATAL,EINVAL,
3275                     "OWPParseRecords: Invalid file version (%d)",
3276                     file_version);
3277             return OWPErrFATAL;
3278     }
3279 
3280     for(i=0;i<num_rec;i++){
3281         if(fread(rbuf,len_rec,1,fp) < 1){
3282             if(ferror(fp)){
3283                 OWPError(ctx,OWPErrFATAL,errno,
3284                         "fread(): STREAM ERROR: offset=%" PRIuPTR ",i=%" PRIu32,
3285                         ftello(fp),i);
3286             }
3287             else if(feof(fp)){
3288                 OWPError(ctx,OWPErrFATAL,errno,
3289                         "fread(): EOF: offset=%" PRIu64,ftello(fp));
3290             }
3291             return OWPErrFATAL;
3292         }
3293         if(!_OWPDecodeDataRecord(file_version,&rec,rbuf)){
3294             errno = EFTYPE;
3295             OWPError(ctx,OWPErrFATAL,errno,
3296                     "OWPParseRecords: Invalid Data Record: %M");
3297             return OWPErrFATAL;
3298         }
3299         rc = proc_rec(&rec,app_data);
3300         if(!rc) continue;
3301         if(rc < 0)
3302             return OWPErrFATAL;
3303         return OWPErrOK;
3304 
3305     }
3306 
3307     return OWPErrOK;
3308 }
3309 
3310 /*
3311  * Function:        OWPReadDataSkips
3312  *
3313  * Description:
3314  *         This function will read all the skip records out of the
3315  *         file fp. skips is assumed to be an array of OWPSkip records of
3316  *         length nskips.
3317  *
3318  * In Args:
3319  *
3320  * Out Args:
3321  *
3322  * Scope:
3323  * Returns:
3324  * Side Effect:
3325  */
3326 OWPBoolean
OWPReadDataSkips(OWPContext ctx,FILE * fp,uint32_t nskips,OWPSkip skips)3327 OWPReadDataSkips(
3328         OWPContext  ctx,
3329         FILE        *fp,
3330         uint32_t   nskips,
3331         OWPSkip     skips
3332         )
3333 {
3334     int                         err;
3335     _OWPSessionHeaderInitialRec phrec;
3336     uint32_t                    i;
3337 
3338     /* buffer for Skips 32 bit aligned */
3339     char                        msg[_OWP_SKIPREC_SIZE];
3340 
3341     /*
3342      * validate array.
3343      */
3344     assert(skips);
3345 
3346     /*
3347      * Stat the file and get the "initial" fields from the header.
3348      */
3349     if(!_OWPReadDataHeaderInitial(ctx,fp,&phrec)){
3350         return False;
3351     }
3352 
3353     /*
3354      * this function is currently only supported for version 2 files.
3355      */
3356     if(phrec.version < 2){
3357         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
3358                 "OWPReadDataSkips: Invalid file version (%d)",
3359                 phrec.version);
3360         errno = ENOSYS;
3361         return False;
3362     }
3363 
3364     if(phrec.num_skiprecs != nskips){
3365         OWPError(ctx,OWPErrFATAL, OWPErrINVALID,
3366                 "OWPReadDataSkips: nskips requested (%lu) doesn't match file (%lu).",
3367                 nskips,phrec.num_skiprecs);
3368 
3369         return False;
3370     }
3371 
3372     /*
3373      * Position fp to beginning of skip records.
3374      */
3375     if(fseeko(fp,phrec.oset_skiprecs,SEEK_SET)){
3376         err = errno;
3377         OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fseeko(): %M");
3378         errno = err;
3379         return False;
3380     }
3381 
3382     for(i=0;i<nskips;i++){
3383 
3384         /*
3385          * Read slot into buffer.
3386          */
3387         if(fread(msg,1,_OWP_SKIPREC_SIZE,fp) != _OWP_SKIPREC_SIZE){
3388             err = errno;
3389             OWPError(ctx,OWPErrFATAL,errno,"fread(): %M");
3390             errno = err;
3391             return False;
3392         }
3393 
3394         /*
3395          * Decode slot buffer into slot record.
3396          */
3397         _OWPDecodeSkipRecord(&skips[i],msg);
3398     }
3399 
3400     return True;
3401 }
3402 
3403 /*
3404  * Function:        OWPTestDiskspace
3405  *
3406  * Description:
3407  *         Returns the size of file a given testspec will require.
3408  *         (Specific to version 3 files - all write functions only
3409  *         support latest version of files.)
3410  *
3411  * In Args:
3412  *
3413  * Out Args:
3414  *
3415  * Scope:
3416  * Returns:
3417  * Side Effect:
3418  */
3419 uint64_t
OWPTestDiskspace(OWPTestSpec * tspec)3420 OWPTestDiskspace(
3421         OWPTestSpec        *tspec
3422         )
3423 {
3424     uint64_t   hdr_len;
3425 
3426     /*
3427      * 56 == 40 for initial portion + 16 for ending IZP
3428      */
3429     hdr_len = 56 + +_OWP_TEST_REQUEST_PREAMBLE_SIZE+
3430         16*(tspec->nslots+1);
3431     return hdr_len + tspec->npackets*_OWP_DATAREC_SIZE;
3432 }
3433 
3434 /*
3435  * Function:        OWPIsLostRecord
3436  *
3437  * Description:
3438  *         Returns true if the given DataRec indicates a "lost" packet. This
3439  *         is determined by looking at the recv timestamp. If it is a string
3440  *         of zero bits, then it is lost.
3441  *
3442  * In Args:
3443  *
3444  * Out Args:
3445  *
3446  * Scope:
3447  * Returns:
3448  * Side Effect:
3449  */
3450 OWPBoolean
OWPIsLostRecord(OWPDataRec * rec)3451 OWPIsLostRecord(
3452         OWPDataRec *rec
3453         )
3454 {
3455     return !rec->recv.owptime;
3456 }
3457