xref: /freebsd/crypto/krb5/src/lib/krb5/os/c_ustime.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/os/c_ustime.c */
3 /*
4  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 
27 #include "k5-int.h"
28 #include "k5-thread.h"
29 
30 k5_mutex_t krb5int_us_time_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
31 
32 struct time_now {
33     krb5_timestamp sec;
34     krb5_int32 usec;
35 };
36 
37 #if defined(_WIN32)
38 
39 /* Microsoft Windows NT and 95   (32bit)  */
40 /* This one works for WOW (Windows on Windows, ntvdm on Win-NT) */
41 
42 #include <time.h>
43 #include <sys/timeb.h>
44 #include <string.h>
45 
46 static krb5_error_code
get_time_now(struct time_now * n)47 get_time_now(struct time_now *n)
48 {
49     struct _timeb timeptr;
50     _ftime(&timeptr);
51     n->sec = timeptr.time;
52     n->usec = timeptr.millitm * 1000;
53     return 0;
54 }
55 
56 #else
57 
58 /* Everybody else is UNIX, right?  POSIX 1996 doesn't give us
59    gettimeofday, but what real OS doesn't?  */
60 
61 static krb5_error_code
get_time_now(struct time_now * n)62 get_time_now(struct time_now *n)
63 {
64     struct timeval tv;
65 
66     if (gettimeofday(&tv, (struct timezone *)0) == -1)
67         return errno;
68 
69     n->sec = tv.tv_sec;
70     n->usec = tv.tv_usec;
71     return 0;
72 }
73 
74 #endif
75 
76 krb5_error_code
k5_us_timeofday(krb5_timestamp * seconds,krb5_int32 * microseconds)77 k5_us_timeofday(krb5_timestamp *seconds, krb5_int32 *microseconds)
78 {
79     struct time_now now;
80     krb5_error_code err;
81 
82     err = get_time_now(&now);
83     if (err)
84         return err;
85 
86     *seconds = now.sec;
87     *microseconds = now.usec;
88     return 0;
89 }
90 
91 static struct time_now last_time;
92 
93 krb5_error_code
krb5_crypto_us_timeofday(krb5_timestamp * seconds,krb5_int32 * microseconds)94 krb5_crypto_us_timeofday(krb5_timestamp *seconds, krb5_int32 *microseconds)
95 {
96     struct time_now now;
97     krb5_error_code err;
98 
99     now.sec = now.usec = 0;
100     err = get_time_now(&now);
101     if (err)
102         return err;
103 
104     /* It would probably be more efficient to remove this mutex and use
105        thread-local storage for last_time.  But that could result in
106        different threads getting the same value for time, which may be
107        a technical violation of spec. */
108 
109     k5_mutex_lock(&krb5int_us_time_mutex);
110     /* Just guessing: If the number of seconds hasn't changed, yet the
111        microseconds are moving backwards, we probably just got a third
112        instance of returning the same clock value from the system, so
113        the saved value was artificially incremented.
114 
115        On Windows, where we get millisecond accuracy currently, that's
116        quite likely.  On UNIX, it appears that we always get new
117        microsecond values, so this case should never trigger.  */
118 
119     /* Check for case where previously usec rollover caused bump in sec,
120        putting now.sec in the past.  But don't just use '<' because we
121        need to properly handle the case where the administrator intentionally
122        adjusted time backwards. */
123     if (now.sec == ts_incr(last_time.sec, -1) ||
124         (now.sec == last_time.sec && now.usec <= last_time.usec)) {
125         /* Correct 'now' to be exactly one microsecond later than 'last_time'.
126            Note that _because_ we perform this hack, 'now' may be _earlier_
127            than 'last_time', even though the system time is monotonically
128            increasing. */
129 
130         now.sec = last_time.sec;
131         now.usec = last_time.usec + 1;
132         if (now.usec >= 1000000) {
133             now.sec = ts_incr(now.sec, 1);
134             now.usec = 0;
135         }
136     }
137     last_time.sec = now.sec;    /* Remember for next time */
138     last_time.usec = now.usec;
139     k5_mutex_unlock(&krb5int_us_time_mutex);
140 
141     *seconds = now.sec;
142     *microseconds = now.usec;
143     return 0;
144 }
145