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
  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
 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
 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
 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
 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
 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 }