r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[tprouty/samba.git] / source / libsmb / namecache.c
index 2252e8e59cebd58e68207e716bd7c7d00cf8b0ba..aeca5673c002d984db5708b45b7002c4f4c89c51 100644 (file)
@@ -1,13 +1,14 @@
 /* 
    Unix SMB/CIFS implementation.
 
-   NetBIOS name cache module.
-
-   Copyright (C) Tim Potter, 2002
+   NetBIOS name cache module on top of gencache mechanism.
+   
+   Copyright (C) Tim Potter         2002
+   Copyright (C) Rafal Szczesniak   2002
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 
-static BOOL done_namecache_init;
-static BOOL enable_namecache;
-static TDB_CONTEXT *namecache_tdb;
+#define NBTKEY_FMT  "NBT/%s#%02X"
 
-struct nc_value {
-       time_t expiry;               /* When entry expires */
-       int count;                   /* Number of addresses */
-       struct in_addr ip_list[1];   /* Address list */
-};
 
-/* Initialise namecache system */
+/**
+ * Initialise namecache system. Function calls gencache
+ * initialisation function to perform necessary actions
+ * 
+ * @return true upon successful initialisation of the cache or
+ *         false on failure
+ **/
 
 BOOL namecache_enable(void)
 {
-       /* Check if we have been here before, or name caching disabled
-           by setting the name cache timeout to zero. */ 
-
-       if (done_namecache_init)
-               return False;
-
-       done_namecache_init = True;
+       /*
+        * Check if name caching disabled by setting the name cache
+        * timeout to zero.
+        */ 
 
        if (lp_name_cache_timeout() == 0) {
-               DEBUG(5, ("namecache_init: disabling netbios name cache\n"));
+               DEBUG(5, ("namecache_enable: disabling netbios name cache\n"));
                return False;
        }
 
-       /* Open namecache tdb in read/write or readonly mode */
-
-       namecache_tdb = tdb_open_log(
-               lock_path("namecache.tdb"), 0,
-               TDB_DEFAULT, O_RDWR | O_CREAT, 0644);
+       /* Init namecache by calling gencache initialisation */
 
-       if (!namecache_tdb) {
-               DEBUG(5, ("namecache_init: could not open %s\n",
-                         lock_path("namecache.tdb")));
+       if (!gencache_init()) {
+               DEBUG(2, ("namecache_enable: Couldn't initialise namecache on top of gencache.\n"));
                return False;
        }
 
-       DEBUG(5, ("namecache_init: enabling netbios namecache, timeout %d "
+       /* I leave it for now, though I don't think we really need this (mimir, 27.09.2002) */
+       DEBUG(5, ("namecache_enable: enabling netbios namecache, timeout %d "
                  "seconds\n", lp_name_cache_timeout()));
 
-       enable_namecache = True;
-
        return True;
 }
 
-/* Return a key for a name and name type.  The caller must free
-   retval.dptr when finished. */
 
-static TDB_DATA namecache_key(const char *name, int name_type)
+/**
+ * Shutdown namecache. Routine calls gencache close function
+ * to safely close gencache file.
+ *
+ * @return true upon successful shutdown of the cache or
+ *         false on failure
+ **/
+BOOL namecache_shutdown(void)
 {
-       TDB_DATA retval;
-       char *keystr;
-
-       asprintf(&keystr, "%s#%02X", strupper_static(name), name_type);
-
-       retval.dsize = strlen(keystr) + 1;
-       retval.dptr = keystr;
-
-       return retval;
+       if (!gencache_shutdown()) {
+               DEBUG(2, ("namecache_shutdown: Couldn't close namecache on top of gencache.\n"));
+               return False;
+       }
+       
+       DEBUG(5, ("namecache_shutdown: netbios namecache closed successfully.\n"));
+       return True;
 }
 
-/* Return a data value for an IP list.  The caller must free
-   retval.dptr when finished. */
-
-static TDB_DATA namecache_value(struct in_addr *ip_list, int num_names, 
-                               time_t expiry)
-{
-       TDB_DATA retval;
-       struct nc_value *value;
-       int size = sizeof(struct nc_value);
-
-       if (num_names > 0)
-               size += sizeof(struct in_addr) * (num_names-1);
-
-       value = (struct nc_value *)malloc(size);
-
-       memset(value, 0, size);
-
-       value->expiry = expiry;
-       value->count = num_names;
 
-       if (ip_list)
-               memcpy(value->ip_list, ip_list, sizeof(struct in_addr) * num_names);
+/**
+ * Generates a key for netbios name lookups on basis of
+ * netbios name and type.
+ * The caller must free returned key string when finished.
+ *
+ * @param name netbios name string (case insensitive)
+ * @param name_type netbios type of the name being looked up
+ *
+ * @return string consisted of uppercased name and appended
+ *         type number
+ */
 
-       retval.dptr = (char *)value;
-       retval.dsize = size;
+static char* namecache_key(const char *name, int name_type)
+{
+       char *keystr;
+       asprintf(&keystr, NBTKEY_FMT, strupper_static(name), name_type);
 
-       return retval;
+       return keystr;
 }
 
-/* Store a name in the name cache */
 
-void namecache_store(const char *name, int name_type,
-                    int num_names, struct in_addr *ip_list)
+/**
+ * Store a name(s) in the name cache
+ *
+ * @param name netbios names array
+ * @param name_type integer netbios name type
+ * @param num_names number of names being stored
+ * @param ip_list array of in_addr structures containing
+ *        ip addresses being stored
+ **/
+
+BOOL namecache_store(const char *name, int name_type,
+                     int num_names, struct ip_service *ip_list)
 {
-       TDB_DATA key, value;
        time_t expiry;
+       char *key, *value_string;
        int i;
+       BOOL ret;
 
-       if (!enable_namecache)
-               return;
-
-       DEBUG(5, ("namecache_store: storing %d address%s for %s#%02x: ",
-                 num_names, num_names == 1 ? "": "es", name, name_type));
+       /*
+        * we use gecache call to avoid annoying debug messages about
+        * initialised namecache again and again...
+        */
+       if (!gencache_init()) return False;
 
-       for (i = 0; i < num_names; i++) 
-               DEBUGADD(5, ("%s%s", inet_ntoa(ip_list[i]),
-                            i == (num_names - 1) ? "" : ", "));
+       if (name_type > 255) {
+               return False; /* Don't store non-real name types. */
+       }
 
-       DEBUGADD(5, ("\n"));
+       if ( DEBUGLEVEL >= 5 ) {
+               DEBUG(5, ("namecache_store: storing %d address%s for %s#%02x: ",
+                       num_names, num_names == 1 ? "": "es", name, name_type));
 
+               for (i = 0; i < num_names; i++) 
+                       DEBUGADD(5, ("%s:%d%s", inet_ntoa(ip_list[i].ip), 
+                               ip_list[i].port, (i == (num_names - 1) ? "" : ",")));
+                       
+               DEBUGADD(5, ("\n"));
+       }
+       
        key = namecache_key(name, name_type);
+       if (!key) {
+               return False;
+       }
 
-       /* Cache pdc location or dc lists for only a little while
-          otherwise if we lock on to a bad DC we can potentially be
-          out of action for the entire cache timeout time! */
-
-       if (name_type != 0x1b || name_type != 0x1c)
-               expiry = time(NULL) + 10;
-       else
-               expiry = time(NULL) + lp_name_cache_timeout();
-
-       value = namecache_value(ip_list, num_names, expiry);
-
-       tdb_store(namecache_tdb, key, value, TDB_REPLACE);
+       expiry = time(NULL) + lp_name_cache_timeout();
 
-       free(key.dptr);
-       free(value.dptr);
+       /*
+        * Generate string representation of ip addresses list
+        * First, store the number of ip addresses and then
+        * place each single ip
+        */
+       if (!ipstr_list_make(&value_string, ip_list, num_names)) {
+               SAFE_FREE(key);
+               SAFE_FREE(value_string);
+               return False;
+       }
+       
+       /* set the entry */
+       ret = gencache_set(key, value_string, expiry);
+       SAFE_FREE(key);
+       SAFE_FREE(value_string);
+       return ret;
 }
 
-/* Look up a name in the name cache.  Return a mallocated list of IP
-   addresses if the name is contained in the cache. */
 
-BOOL namecache_fetch(const char *name, int name_type, struct in_addr **ip_list,
-                    int *num_names)
+/**
+ * Look up a name in the cache.
+ *
+ * @param name netbios name to look up for
+ * @param name_type netbios name type of @param name
+ * @param ip_list mallocated list of IP addresses if found in the cache,
+ *        NULL otherwise
+ * @param num_names number of entries found
+ *
+ * @return true upon successful fetch or
+ *         false if name isn't found in the cache or has expired
+ **/
+
+BOOL namecache_fetch(const char *name, int name_type, struct ip_service **ip_list,
+                     int *num_names)
 {
-       TDB_DATA key, value;
-       struct nc_value *data;
-       time_t now;
-       int i;
+       char *key, *value;
+       time_t timeout;
 
-       *ip_list = NULL;
-       *num_names = 0;
+       /* exit now if null pointers were passed as they're required further */
+       if (!ip_list || !num_names) return False;
 
-       if (!enable_namecache)
+       if (!gencache_init())
                return False;
 
-       /* Read value */
+       if (name_type > 255) {
+               return False; /* Don't fetch non-real name types. */
+       }
 
-       key = namecache_key(name, name_type);
+       *num_names = 0;
 
-       value = tdb_fetch(namecache_tdb, key);
-       
-       if (!value.dptr) {
-               DEBUG(5, ("namecache_fetch: %s#%02x not found\n",
-                         name, name_type));
-               goto done;
+       /* 
+        * Use gencache interface - lookup the key
+        */
+       key = namecache_key(name, name_type);
+       if (!key) {
+               return False;
        }
 
-       data = (struct nc_value *)value.dptr;
+       if (!gencache_get(key, &value, &timeout)) {
+               DEBUG(5, ("no entry for %s#%02X found.\n", name, name_type));
+               SAFE_FREE(key);
+               return False;
+       } else {
+               DEBUG(5, ("name %s#%02X found.\n", name, name_type));
+       }
+       
+       /*
+        * Split up the stored value into the list of IP adresses
+        */
+       *num_names = ipstr_list_parse(value, ip_list);
+       
+       SAFE_FREE(key);
+       SAFE_FREE(value);
+                        
+       return *num_names > 0;          /* true only if some ip has been fetched */
+}
 
-       /* Check expiry time */
+/**
+ * Remove a namecache entry. Needed for site support.
+ *
+ **/
 
-       now = time(NULL);
+BOOL namecache_delete(const char *name, int name_type)
+{
+       BOOL ret;
+       char *key;
 
-       if (now > data->expiry) {
+       if (!gencache_init())
+               return False;
 
-               DEBUG(5, ("namecache_fetch: entry for %s#%02x expired\n",
-                         name, name_type));
+       if (name_type > 255) {
+               return False; /* Don't fetch non-real name types. */
+       }
 
-               tdb_delete(namecache_tdb, key);
+       key = namecache_key(name, name_type);
+       if (!key) {
+               return False;
+       }
+       ret = gencache_del(key);
+       SAFE_FREE(key);
+       return ret;
+}
 
-               value = tdb_null;
+/**
+ * Delete single namecache entry. Look at the
+ * gencache_iterate definition.
+ *
+ **/
 
-               goto done;
-       }
+static void flush_netbios_name(const char* key, const char *value, time_t timeout, void* dptr)
+{
+       gencache_del(key);
+       DEBUG(5, ("Deleting entry %s\n", key));
+}
 
-       if ((data->expiry - now) > lp_name_cache_timeout()) {
 
-               /* Someone may have changed the system time on us */
+/**
+ * Flush all names from the name cache.
+ * It's done by gencache_iterate()
+ *
+ * @return True upon successful deletion or
+ *         False in case of an error
+ **/
 
-               DEBUG(5, ("namecache_fetch: entry for %s#%02x has bad expiry\n",
-                         name, name_type));
+void namecache_flush(void)
+{
+       if (!gencache_init())
+               return;
 
-               tdb_delete(namecache_tdb, key);
+       /* 
+        * iterate through each NBT cache's entry and flush it
+        * by flush_netbios_name function
+        */
+       gencache_iterate(flush_netbios_name, NULL, "NBT/*");
+       DEBUG(5, ("Namecache flushed\n"));
+}
 
-               value = tdb_null;
+/* Construct a name status record key. */
 
-               goto done;
-       }
+static char *namecache_status_record_key(const char *name, int name_type1,
+                               int name_type2, struct in_addr keyip)
+{
+       char *keystr;
 
-       /* Extract and return namelist */
+       asprintf(&keystr, "NBT/%s#%02X.%02X.%s",
+                       strupper_static(name), name_type1, name_type2, inet_ntoa(keyip));
+       return keystr;
+}
 
-       DEBUG(5, ("namecache_fetch: returning %d address%s for %s#%02x: ",
-                 data->count, data->count == 1 ? "" : "es", name, name_type));
+/* Store a name status record. */
 
-       if (data->count) {
+BOOL namecache_status_store(const char *keyname, int keyname_type,
+               int name_type, struct in_addr keyip,
+               const char *srvname)
+{
+       char *key;
+       time_t expiry;
+       BOOL ret;
 
-               *ip_list = (struct in_addr *)malloc(
-                       sizeof(struct in_addr) * data->count);
-               
-               memcpy(*ip_list, data->ip_list, sizeof(struct in_addr) * data->count);
-               
-               *num_names = data->count;
-               
-               for (i = 0; i < *num_names; i++)
-                       DEBUGADD(5, ("%s%s", inet_ntoa((*ip_list)[i]),
-                                    i == (*num_names - 1) ? "" : ", "));
+       if (!gencache_init())
+               return False;
 
-       }
+       key = namecache_status_record_key(keyname, keyname_type, name_type, keyip);
+       if (!key)
+               return False;
 
-       DEBUGADD(5, ("\n"));
+       expiry = time(NULL) + lp_name_cache_timeout();
+       ret = gencache_set(key, srvname, expiry);
 
-done:
-       SAFE_FREE(key.dptr);
-       SAFE_FREE(value.dptr);
+       if (ret)
+               DEBUG(5, ("namecache_status_store: entry %s -> %s\n", key, srvname ));
+       else
+               DEBUG(5, ("namecache_status_store: entry %s store failed.\n", key ));
 
-       return value.dsize > 0;
+       SAFE_FREE(key);
+       return ret;
 }
 
-/* Flush all names from the name cache */
+/* Fetch a name status record. */
 
-void namecache_flush(void)
+BOOL namecache_status_fetch(const char *keyname, int keyname_type,
+                       int name_type, struct in_addr keyip, char *srvname_out)
 {
-       int result;
+       char *key = NULL;
+       char *value = NULL;
+       time_t timeout;
 
-       if (!namecache_tdb)
-               return;
+       if (!gencache_init())
+               return False;
+
+       key = namecache_status_record_key(keyname, keyname_type, name_type, keyip);
+       if (!key)
+               return False;
 
-       result = tdb_traverse(namecache_tdb, tdb_traverse_delete_fn, NULL);
+       if (!gencache_get(key, &value, &timeout)) {
+               DEBUG(5, ("namecache_status_fetch: no entry for %s found.\n", key));
+               SAFE_FREE(key);
+               return False;
+       } else {
+               DEBUG(5, ("namecache_status_fetch: key %s -> %s\n", key, value ));
+       }
 
-       if (result == -1)
-               DEBUG(5, ("namecache_flush: error deleting cache entries\n"));
-       else
-               DEBUG(5, ("namecache_flush: deleted %d cache entr%s\n", 
-                         result, result == 1 ? "y" : "ies"));
+       strlcpy(srvname_out, value, 16);
+       SAFE_FREE(key);
+       SAFE_FREE(value);
+       return True;
 }