1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2015 Gary Mills
24 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27
28 /*
29 * DESCRIPTION: Contains utilities relating to TTL calculation.
30 */
31 #include <unistd.h>
32 #include <syslog.h>
33 #include <errno.h>
34 #include <strings.h>
35 #include <ndbm.h>
36 #include "ypsym.h"
37 #include "ypdefs.h"
38 #include "shim.h"
39 #include "yptol.h"
40 #include "../ldap_util.h"
41
42 /*
43 * Constants used in time calculations
44 */
45 #define MILLION 1000000
46
47 /*
48 * Decs
49 */
50 suc_code is_greater_timeval(struct timeval *, struct timeval *);
51 suc_code add_to_timeval(struct timeval *, int);
52
53 /*
54 * FUNCTION: has_entry_expired()
55 *
56 * DESCRIPTION: Determines if an individual entry has expired.
57 *
58 * INPUTS: Map control structure for an open map
59 * Entry key
60 *
61 * OUTPUTS: TRUE = Entry has expired or cannot be found this will cause
62 * missing entries to be pulled out of the DIT.
63 * FALSE = Entry has not expired
64 *
65 */
66 bool_t
has_entry_expired(map_ctrl * map,datum * key)67 has_entry_expired(map_ctrl *map, datum *key)
68 {
69 datum ttl;
70 struct timeval now;
71 struct timeval old_time;
72 char *key_name;
73 char *myself = "has_entry_expired";
74
75 if ((map == NULL) || (map->ttl == NULL))
76 return (FALSE);
77
78 /* Get expiry time entry for key */
79 ttl = dbm_fetch(map->ttl, *key);
80
81 if (NULL == ttl.dptr) {
82 /*
83 * If we failed to get a map expiry key, which must always be
84 * present, then something is seriously wrong. Try to recreate
85 * the map.
86 */
87 if ((key->dsize == strlen(MAP_EXPIRY_KEY)) &&
88 (0 == strncmp(key->dptr, MAP_EXPIRY_KEY, key->dsize))) {
89 logmsg(MSG_NOTIMECHECK, LOG_ERR, "Cannot find %s TTL "
90 "for map %s. Will attempt to recreate map",
91 MAP_EXPIRY_KEY, map->map_name);
92 return (TRUE);
93 }
94
95 /*
96 * Not a problem just no TTL entry for this entry. Maybe it has
97 * not yet been downloaded. Maybe it will be handled by a
98 * service other than NIS. Check if the entire map has expired.
99 * This prevents repeated LDAP reads when requests are made for
100 * nonexistant entries.
101 */
102 if (has_map_expired(map)) {
103 /* Kick of a map update */
104 update_map_if_required(map, FALSE);
105 }
106
107 /* Don't update the entry */
108 return (FALSE);
109 }
110
111 if (ttl.dsize != sizeof (struct timeval)) {
112 /*
113 * Need to malloc some memory before can syslog the key name
114 * but this may fail. Solution log a simple message first THEn
115 * a more detailed one if it works.
116 */
117 logmsg(MSG_NOTIMECHECK, LOG_ERR,
118 "Invalid TTL key in map %s. error %d",
119 map->map_name, dbm_error(map->ttl));
120
121 /* Log the key name */
122 key_name = (char *)am(myself, key->dsize + 1);
123 if (NULL == key_name) {
124 logmsg(MSG_NOMEM, LOG_ERR,
125 "Could not alloc memory for keyname");
126 } else {
127 strncpy(key_name, key->dptr, key->dsize);
128 key_name[key->dsize] = '\0';
129 logmsg(MSG_NOTIMECHECK, LOG_ERR,
130 "Key name was %s", key_name);
131 sfree(key_name);
132 }
133 /* Update it Anyway */
134 return (TRUE);
135 }
136
137 /* Get current time */
138 gettimeofday(&now, NULL);
139
140 /*
141 * Because dptr may not be int aligned need to build an int
142 * out of what it points to or will get a bus error
143 */
144 bcopy(ttl.dptr, &old_time, sizeof (struct timeval));
145
146 return (is_greater_timeval(&now, &old_time));
147 }
148
149 /*
150 * FUNCTION: has_map_expired()
151 *
152 * DESCRIPTION: Determines if an entire map has expire
153 *
154 * INPUTS: Map control structure for an open map
155 *
156 * OUTPUTS: TRUE = Map has expired
157 * FALSE Map has not expired
158 *
159 */
160 bool_t
has_map_expired(map_ctrl * map)161 has_map_expired(map_ctrl *map)
162 {
163 datum key;
164
165 /* Set up datum with magic expiry key */
166 key.dsize = strlen(MAP_EXPIRY_KEY);
167 key.dptr = MAP_EXPIRY_KEY;
168
169 /* Call has_entry_expired() with magic map expiry key */
170 return (has_entry_expired(map, &key));
171 }
172
173 /*
174 * FUNCTION: update_entry_ttl()
175 *
176 * DESCRIPTION: Updates the TTL for one map entry
177 *
178 * INPUTS: Map control structure for an open map
179 * Entry key
180 * Flag indication if TTL should be max, min or random
181 *
182 * OUTPUTS: SUCCESS = TTL updated
183 * FAILURE = TTL not updated
184 *
185 */
186
187 suc_code
update_entry_ttl(map_ctrl * map,datum * key,TTL_TYPE type)188 update_entry_ttl(map_ctrl *map, datum *key, TTL_TYPE type)
189 {
190 datum expire;
191 struct timeval now;
192 int ttl;
193
194 /* Get current time */
195 gettimeofday(&now, NULL);
196
197 /* Get TTL from mapping file */
198 ttl = get_ttl_value(map, type);
199
200 if (FAILURE == add_to_timeval(&now, ttl))
201 return (FAILURE);
202
203 /* Convert time into a datum */
204 expire.dsize = sizeof (struct timeval);
205 expire.dptr = (char *)&now;
206
207 /* Set expiry time entry for key */
208 errno = 0;
209 if (0 > dbm_store(map->ttl, *key, expire, DBM_REPLACE)) {
210 logmsg(MSG_NOTIMECHECK, LOG_ERR, "Could not write TTL entry "
211 "(errno=%d)", errno);
212 return (FAILURE);
213 }
214
215 return (SUCCESS);
216 }
217
218 /*
219 * FUNCTION: update_map_ttl()
220 *
221 * DESCRIPTION: Updates the TTL for entire map. This can be called either with
222 * the map open (map_ctrl DBM pointer set up) or the map closed
223 * (map_ctrl DBM pointers not set). The latter case will occur
224 * when we have just created a new map.
225 *
226 * This function must open the TTL map but, in either case, must
227 * return with the map_ctrl in it's original state.
228 *
229 * INPUTS: Map control structure for an open map
230 *
231 * OUTPUTS: SUCCESS = TTL updated
232 * FAILURE = TTL not updated
233 *
234 */
235 suc_code
update_map_ttl(map_ctrl * map)236 update_map_ttl(map_ctrl *map)
237 {
238 datum key;
239 bool_t map_was_open = TRUE;
240 suc_code ret;
241
242 /* Set up datum with magic expiry key */
243 key.dsize = strlen(MAP_EXPIRY_KEY);
244 key.dptr = MAP_EXPIRY_KEY;
245
246 /* If TTL not open open it */
247 if (NULL == map->ttl) {
248 map->ttl = dbm_open(map->ttl_path, O_RDWR, 0644);
249 if (NULL == map->ttl)
250 return (FAILURE);
251 map_was_open = FALSE;
252 }
253
254 /* Call update_entry_ttl() with magic map expiry key */
255 ret = update_entry_ttl(map, &key, TTL_MIN);
256
257 /* If we had to open TTL file close it */
258 if (!map_was_open) {
259 dbm_close(map->ttl);
260 map->ttl_path = NULL;
261 }
262
263 return (ret);
264 }
265
266 /*
267 * FUNCTION: add_to_timeval()
268 *
269 * DESCRIPTION: Adds an int to a timeval
270 *
271 * NOTE : Seems strange that there is not a library function to do this
272 * if one exists then this function can be removed.
273 *
274 * NOTE : Does not handle UNIX clock wrap round but this is a much bigger
275 * problem.
276 *
277 * INPUTS: Time value to add to
278 * Time value to add in seconds
279 *
280 * OUTPUTS: SUCCESS = Addition successful
281 * FAILURE = Addition failed (probably wrapped)
282 *
283 */
284 suc_code
add_to_timeval(struct timeval * t1,int t2)285 add_to_timeval(struct timeval *t1, int t2)
286 {
287 struct timeval oldval;
288
289 oldval.tv_sec = t1->tv_sec;
290
291 /* Add seconds part */
292 t1->tv_sec += t2;
293
294 /* Check for clock wrap */
295 if (!(t1->tv_sec >= oldval.tv_sec)) {
296 logmsg(MSG_NOTIMECHECK, LOG_ERR,
297 "Wrap when adding %d to %d", t2, oldval.tv_sec);
298 return (FAILURE);
299 }
300
301 return (SUCCESS);
302 }
303
304 /*
305 * FUNCTION: is_greater_timeval()
306 *
307 * DESCRIPTION: Compares two timevals
308 *
309 * NOTE : Seems strange that there is not a library function to do this
310 * if one exists then this function can be removed.
311 *
312 * INPUTS: First time value
313 * Time value to compare it with
314 *
315 * OUTPUTS: TRUE t1 > t2
316 * FALSE t1 <= t2
317 *
318 */
319 suc_code
is_greater_timeval(struct timeval * t1,struct timeval * t2)320 is_greater_timeval(struct timeval *t1, struct timeval *t2)
321 {
322 if (t1->tv_sec > t2->tv_sec)
323 return (TRUE);
324
325 if (t1->tv_sec == t2->tv_sec) {
326 if (t1->tv_usec > t2->tv_usec)
327 return (TRUE);
328 else
329 return (FALSE);
330 }
331
332 return (FALSE);
333 }
334