xref: /reactos/dll/win32/netapi32/nbnamecache.c (revision 40462c92)
1 /* Copyright (c) 2003 Juan Lang
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2.1 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
16  *
17  * This implementation uses a linked list, because I don't have a decent
18  * hash table implementation handy.  This is somewhat inefficient, but it's
19  * rather more efficient than not having a name cache at all.
20  */
21 
22 #include "netapi32.h"
23 
24 typedef struct _NBNameCacheNode
25 {
26     DWORD expireTime;
27     NBNameCacheEntry *entry;
28     struct _NBNameCacheNode *next;
29 } NBNameCacheNode;
30 
31 struct NBNameCache
32 {
33     HANDLE heap;
34     CRITICAL_SECTION cs;
35     DWORD entryExpireTimeMS;
36     NBNameCacheNode *head;
37 };
38 
39 /* Unlinks the node pointed to by *prev, and frees any associated memory.
40  * If that node's next pointed to another node, *prev now points to it.
41  * Assumes the caller owns cache's lock.
42  */
43 static void NBNameCacheUnlinkNode(struct NBNameCache *cache,
44  NBNameCacheNode **prev)
45 {
46     if (cache && prev && *prev)
47     {
48         NBNameCacheNode *next = (*prev)->next;
49 
50         HeapFree(cache->heap, 0, (*prev)->entry);
51         HeapFree(cache->heap, 0, *prev);
52         *prev = next;
53     }
54 }
55 
56 /* Walks the list beginning with cache->head looking for the node with name
57  * name.  If the node is found, returns a pointer to the next pointer of the
58  * node _prior_ to the found node (or head if head points to it).  Thus, if the
59  * node's all you want, dereference the return value twice.  If you want to
60  * modify the list, modify the referent of the return value.
61  * While it's at it, deletes nodes whose time has expired (except the node
62  * you're looking for, of course).
63  * Returns NULL if the node isn't found.
64  * Assumes the caller owns cache's lock.
65  */
66 static NBNameCacheNode **NBNameCacheWalk(struct NBNameCache *cache,
67  const char name[NCBNAMSZ])
68 {
69     NBNameCacheNode **ret = NULL;
70 
71     if (cache && cache->head)
72     {
73         NBNameCacheNode **ptr;
74 
75         ptr = &cache->head;
76         while (ptr && *ptr && (*ptr)->entry)
77         {
78             if (!memcmp((*ptr)->entry->name, name, NCBNAMSZ - 1))
79                 ret = ptr;
80             else
81             {
82                 if (GetTickCount() > (*ptr)->expireTime)
83                     NBNameCacheUnlinkNode(cache, ptr);
84             }
85             if (*ptr)
86                 ptr = &(*ptr)->next;
87         }
88     }
89     return ret;
90 }
91 
92 struct NBNameCache *NBNameCacheCreate(HANDLE heap, DWORD entryExpireTimeMS)
93 {
94     struct NBNameCache *cache;
95 
96 
97     if (!heap)
98         heap = GetProcessHeap();
99     cache = HeapAlloc(heap, 0, sizeof(struct NBNameCache));
100     if (cache)
101     {
102         cache->heap = heap;
103         InitializeCriticalSection(&cache->cs);
104         cache->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": NBNameCache.cs");
105         cache->entryExpireTimeMS = entryExpireTimeMS;
106         cache->head = NULL;
107     }
108     return cache;
109 }
110 
111 BOOL NBNameCacheAddEntry(struct NBNameCache *cache, NBNameCacheEntry *entry)
112 {
113     BOOL ret;
114 
115     if (cache && entry)
116     {
117         NBNameCacheNode **node;
118 
119         EnterCriticalSection(&cache->cs);
120         node = NBNameCacheWalk(cache, (char*)entry->name);
121         if (node)
122         {
123             (*node)->expireTime = GetTickCount() +
124              cache->entryExpireTimeMS;
125             HeapFree(cache->heap, 0, (*node)->entry);
126             (*node)->entry = entry;
127             ret = TRUE;
128         }
129         else
130         {
131             NBNameCacheNode *newNode = HeapAlloc(cache->heap, 0, sizeof(NBNameCacheNode));
132             if (newNode)
133             {
134                 newNode->expireTime = GetTickCount() +
135                  cache->entryExpireTimeMS;
136                 newNode->entry = entry;
137                 newNode->next = cache->head;
138                 cache->head = newNode;
139                 ret = TRUE;
140             }
141             else
142                 ret = FALSE;
143         }
144         LeaveCriticalSection(&cache->cs);
145     }
146     else
147         ret = FALSE;
148     return ret;
149 }
150 
151 const NBNameCacheEntry *NBNameCacheFindEntry(struct NBNameCache *cache,
152  const UCHAR name[NCBNAMSZ])
153 {
154     const NBNameCacheEntry *ret;
155     UCHAR printName[NCBNAMSZ];
156 
157     memcpy(printName, name, NCBNAMSZ - 1);
158     printName[NCBNAMSZ - 1] = '\0';
159     if (cache)
160     {
161         NBNameCacheNode **node;
162 
163         EnterCriticalSection(&cache->cs);
164         node = NBNameCacheWalk(cache, (const char *)name);
165         if (node)
166             ret = (*node)->entry;
167         else
168             ret = NULL;
169         LeaveCriticalSection(&cache->cs);
170     }
171     else
172         ret = NULL;
173     return ret;
174 }
175 
176 void NBNameCacheDestroy(struct NBNameCache *cache)
177 {
178     if (cache)
179     {
180         cache->cs.DebugInfo->Spare[0] = 0;
181         DeleteCriticalSection(&cache->cs);
182         while (cache->head)
183             NBNameCacheUnlinkNode(cache, &cache->head);
184         HeapFree(cache->heap, 0, cache);
185     }
186 }
187