r12229: fix the expire time for released records
[samba.git] / source / nbt_server / wins / winsserver.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    core wins server handling
5
6    Copyright (C) Andrew Tridgell        2005
7    Copyright (C) Stefan Metzmacher      2005
8       
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "nbt_server/nbt_server.h"
26 #include "nbt_server/wins/winsdb.h"
27 #include "system/time.h"
28 #include "smbd/service_task.h"
29
30 /*
31   work out the ttl we will use given a client requested ttl
32 */
33 uint32_t wins_server_ttl(struct wins_server *winssrv, uint32_t ttl)
34 {
35         ttl = MIN(ttl, winssrv->config.max_renew_interval);
36         ttl = MAX(ttl, winssrv->config.min_renew_interval);
37         return ttl;
38 }
39
40 static enum wrepl_name_type wrepl_type(uint16_t nb_flags, struct nbt_name *name, BOOL mhomed)
41 {
42         /* this copes with the nasty hack that is the type 0x1c name */
43         if (name->type == NBT_NAME_LOGON) {
44                 return WREPL_TYPE_SGROUP;
45         }
46         if (nb_flags & NBT_NM_GROUP) {
47                 return WREPL_TYPE_GROUP;
48         }
49         if (mhomed) {
50                 return WREPL_TYPE_MHOMED;
51         }
52         return WREPL_TYPE_UNIQUE;
53 }
54
55 /*
56   register a new name with WINS
57 */
58 static uint8_t wins_register_new(struct nbt_name_socket *nbtsock, 
59                                  struct nbt_name_packet *packet, 
60                                  const struct nbt_peer_socket *src,
61                                  enum wrepl_name_type type)
62 {
63         struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, 
64                                                        struct nbtd_interface);
65         struct wins_server *winssrv = iface->nbtsrv->winssrv;
66         struct nbt_name *name = &packet->questions[0].name;
67         uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl);
68         uint16_t nb_flags = packet->additional[0].rdata.netbios.addresses[0].nb_flags;
69         const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
70         struct winsdb_record rec;
71         enum wrepl_name_node node;
72
73 #define WREPL_NODE_NBT_FLAGS(nb_flags) \
74         ((nb_flags & NBT_NM_OWNER_TYPE)>>13)
75
76         node    = WREPL_NODE_NBT_FLAGS(nb_flags);
77
78         rec.name                = name;
79         rec.type                = type;
80         rec.state               = WREPL_STATE_ACTIVE;
81         rec.node                = node;
82         rec.is_static           = False;
83         rec.expire_time         = time(NULL) + ttl;
84         rec.version             = 0; /* will allocated later */
85         rec.wins_owner          = NULL; /* will be set later */
86         rec.registered_by       = src->addr;
87         rec.addresses           = winsdb_addr_list_make(packet);
88         if (rec.addresses == NULL) return NBT_RCODE_SVR;
89
90         rec.addresses     = winsdb_addr_list_add(rec.addresses,
91                                                  address,
92                                                  WINSDB_OWNER_LOCAL,
93                                                  rec.expire_time);
94         if (rec.addresses == NULL) return NBT_RCODE_SVR;
95
96         DEBUG(4,("WINS: accepted registration of %s with address %s\n",
97                  nbt_name_string(packet, name), rec.addresses[0]->address));
98         
99         return winsdb_add(winssrv->wins_db, &rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP);
100 }
101
102
103 /*
104   update the ttl on an existing record
105 */
106 static uint8_t wins_update_ttl(struct nbt_name_socket *nbtsock, 
107                                struct nbt_name_packet *packet, 
108                                struct winsdb_record *rec,
109                                struct winsdb_addr *winsdb_addr,
110                                const struct nbt_peer_socket *src)
111 {
112         struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, 
113                                                        struct nbtd_interface);
114         struct wins_server *winssrv = iface->nbtsrv->winssrv;
115         uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl);
116         const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
117         uint32_t modify_flags = 0;
118
119         rec->expire_time   = time(NULL) + ttl;
120         rec->registered_by = src->addr;
121
122         if (winsdb_addr) {
123                 winsdb_addr->wins_owner  = WINSDB_OWNER_LOCAL;
124                 winsdb_addr->expire_time = rec->expire_time;
125         }
126
127         if (strcmp(WINSDB_OWNER_LOCAL, rec->wins_owner) != 0) {
128                 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
129         }
130
131         DEBUG(5,("WINS: refreshed registration of %s at %s\n",
132                  nbt_name_string(packet, rec->name), address));
133         
134         return winsdb_modify(winssrv->wins_db, rec, modify_flags);
135 }
136
137 /*
138   do a sgroup merge
139 */
140 static uint8_t wins_sgroup_merge(struct nbt_name_socket *nbtsock, 
141                                  struct nbt_name_packet *packet, 
142                                  struct winsdb_record *rec,
143                                  const char *address,
144                                  const struct nbt_peer_socket *src)
145 {
146         struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, 
147                                                        struct nbtd_interface);
148         struct wins_server *winssrv = iface->nbtsrv->winssrv;
149         uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl);
150
151         rec->expire_time   = time(NULL) + ttl;
152         rec->registered_by = src->addr;
153
154         rec->addresses     = winsdb_addr_list_add(rec->addresses,
155                                                   address,
156                                                   WINSDB_OWNER_LOCAL,
157                                                   rec->expire_time);
158         if (rec->addresses == NULL) return NBT_RCODE_SVR;
159
160         DEBUG(5,("WINS: sgroup merge of %s at %s\n",
161                  nbt_name_string(packet, rec->name), address));
162         
163         return winsdb_modify(winssrv->wins_db, rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP);
164 }
165
166 /*
167   register a name
168 */
169 static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock, 
170                                      struct nbt_name_packet *packet, 
171                                      const struct nbt_peer_socket *src)
172 {
173         NTSTATUS status;
174         struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, 
175                                                        struct nbtd_interface);
176         struct wins_server *winssrv = iface->nbtsrv->winssrv;
177         struct nbt_name *name = &packet->questions[0].name;
178         struct winsdb_record *rec;
179         uint8_t rcode = NBT_RCODE_OK;
180         uint16_t nb_flags = packet->additional[0].rdata.netbios.addresses[0].nb_flags;
181         const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
182         BOOL mhomed = ((packet->operation & NBT_OPCODE) == NBT_OPCODE_MULTI_HOME_REG);
183         enum wrepl_name_type new_type = wrepl_type(nb_flags, name, mhomed);
184         struct winsdb_addr *winsdb_addr = NULL;
185
186         /* as a special case, the local master browser name is always accepted
187            for registration, but never stored */
188         if (name->type == NBT_NAME_MASTER) {
189                 rcode = NBT_RCODE_OK;
190                 goto done;
191         }
192
193         /* w2k3 refuses 0x1C names with out marked as group */
194         if (name->type == NBT_NAME_LOGON && !(nb_flags & NBT_NM_GROUP)) {
195                 rcode = NBT_RCODE_RFS;
196                 goto done;
197         }
198
199         status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
200         if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
201                 rcode = wins_register_new(nbtsock, packet, src, new_type);
202                 goto done;
203         } else if (!NT_STATUS_IS_OK(status)) {
204                 rcode = NBT_RCODE_SVR;
205                 goto done;
206         }
207
208         if (rec->type == WREPL_TYPE_GROUP) {
209                 if (new_type != WREPL_TYPE_GROUP) {
210                         DEBUG(2,("WINS: Attempt to register name %s as non normal group(%u)"
211                                  " while a normal group is already there\n",
212                                  nbt_name_string(packet, name), new_type));
213                         rcode = NBT_RCODE_ACT;
214                         goto done;
215                 }
216
217                 if (rec->state == WREPL_STATE_ACTIVE) {
218                         /* TODO: is this correct? */
219                         rcode = wins_update_ttl(nbtsock, packet, rec, NULL, src);
220                         goto done;
221                 }
222
223                 /* TODO: is this correct? */
224                 winsdb_delete(winssrv->wins_db, rec);
225                 rcode = wins_register_new(nbtsock, packet, src, new_type);
226                 goto done;
227         }
228
229         if (rec->state != WREPL_STATE_ACTIVE) {
230                 winsdb_delete(winssrv->wins_db, rec);
231                 rcode = wins_register_new(nbtsock, packet, src, new_type);
232                 goto done;
233         }
234
235         switch (rec->type) {
236         case WREPL_TYPE_UNIQUE:
237         case WREPL_TYPE_MHOMED:
238                 /* 
239                  * if its an active unique name, and the registration is for a group, then
240                  * see if the unique name owner still wants the name
241                  * TODO: is this correct?
242                  */
243                 if (new_type == WREPL_TYPE_GROUP || new_type == WREPL_TYPE_GROUP) {
244                         wins_register_wack(nbtsock, packet, rec, src);
245                         return;
246                 }
247
248                 /* 
249                  * if the registration is for an address that is currently active, then 
250                  * just update the expiry time of the record and the address
251                  * TODO: is this correct?
252                  */
253                 winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
254                 if (winsdb_addr) {
255                         rcode = wins_update_ttl(nbtsock, packet, rec, winsdb_addr, src);
256                         goto done;
257                 }
258
259                 /*
260                  * we have to do a WACK to see if the current owner is willing
261                  * to give up its claim
262                  */
263                 wins_register_wack(nbtsock, packet, rec, src);
264                 return;
265
266         case WREPL_TYPE_GROUP:
267                 /* this should not be reached as normal groups are handled above */
268                 DEBUG(0,("BUG at %s\n",__location__));
269                 rcode = NBT_RCODE_ACT;
270                 goto done;
271
272         case WREPL_TYPE_SGROUP:
273                 /* if the new record isn't also a special group, refuse the registration */ 
274                 if (new_type != WREPL_TYPE_SGROUP) {
275                         DEBUG(2,("WINS: Attempt to register name %s as non special group(%u)"
276                                  " while a special group is already there\n",
277                                  nbt_name_string(packet, name), new_type));
278                         rcode = NBT_RCODE_ACT;
279                         goto done;
280                 }
281
282                 /* 
283                  * if the registration is for an address that is currently active, then 
284                  * just update the expiry time
285                  * just update the expiry time of the record and the address
286                  * TODO: is this correct?
287                  */
288                 winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
289                 if (winsdb_addr) {
290                         rcode = wins_update_ttl(nbtsock, packet, rec, winsdb_addr, src);
291                         goto done;
292                 }
293
294                 rcode = wins_sgroup_merge(nbtsock, packet, rec, address, src);
295                 goto done;
296         }
297
298 done:
299         nbtd_name_registration_reply(nbtsock, packet, src, rcode);
300 }
301
302 /*
303   query a name
304 */
305 static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock, 
306                                   struct nbt_name_packet *packet, 
307                                   const struct nbt_peer_socket *src)
308 {
309         NTSTATUS status;
310         struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, 
311                                                        struct nbtd_interface);
312         struct wins_server *winssrv = iface->nbtsrv->winssrv;
313         struct nbt_name *name = &packet->questions[0].name;
314         struct winsdb_record *rec;
315         const char **addresses;
316         uint16_t nb_flags = 0; /* TODO: ... */
317
318         status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
319         if (!NT_STATUS_IS_OK(status)) {
320                 goto notfound;
321         }
322
323         /*
324          * for group's we always reply with
325          * 255.255.255.255 as address, even if
326          * the record is released or tombstoned
327          */
328         if (rec->type == WREPL_TYPE_GROUP) {
329                 addresses = talloc_array(packet, const char *, 2);
330                 if (addresses == NULL) {
331                         nbtd_negative_name_query_reply(nbtsock, packet, src);
332                         return;
333                 }
334                 addresses[0] = WINSDB_GROUP_ADDRESS;
335                 addresses[1] = NULL;
336                 goto found;
337         }
338
339         if (rec->state != WREPL_STATE_ACTIVE) {
340                 goto notfound;
341         }
342
343         addresses = winsdb_addr_string_list(packet, rec->addresses);
344         if (!addresses) {
345                 goto notfound;
346         }
347 found:
348         nbtd_name_query_reply(nbtsock, packet, src, name, 
349                               0, nb_flags, addresses);
350         return;
351
352 notfound:
353         nbtd_negative_name_query_reply(nbtsock, packet, src);
354 }
355
356 /*
357   release a name
358 */
359 static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock, 
360                                     struct nbt_name_packet *packet, 
361                                     const struct nbt_peer_socket *src)
362 {
363         NTSTATUS status;
364         struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, 
365                                                        struct nbtd_interface);
366         struct wins_server *winssrv = iface->nbtsrv->winssrv;
367         struct nbt_name *name = &packet->questions[0].name;
368         struct winsdb_record *rec;
369         uint32_t modify_flags = 0;
370         uint8_t ret;
371
372         status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
373         if (!NT_STATUS_IS_OK(status)) {
374                 goto done;
375         }
376
377         if (rec->state != WREPL_STATE_ACTIVE) {
378                 goto done;
379         }
380
381         /* 
382          * TODO: do we need to check if
383          *       src->addr matches packet->additional[0].rdata.netbios.addresses[0].ipaddr
384          *       here?
385          */
386
387         /* 
388          * we only allow releases from an owner - other releases are
389          * silently ignored
390          */
391         if (!winsdb_addr_list_check(rec->addresses, src->addr)) {
392                 goto done;
393         }
394
395         DEBUG(4,("WINS: released name %s from %s\n", nbt_name_string(rec, rec->name), src->addr));
396
397         switch (rec->type) {
398         case WREPL_TYPE_UNIQUE:
399                 rec->state = WREPL_STATE_RELEASED;
400                 break;
401
402         case WREPL_TYPE_GROUP:
403                 rec->state = WREPL_STATE_RELEASED;
404                 break;
405
406         case WREPL_TYPE_SGROUP:
407                 winsdb_addr_list_remove(rec->addresses, src->addr);
408                 /* TODO: do we need to take the ownership here? */
409                 if (winsdb_addr_list_length(rec->addresses) == 0) {
410                         rec->state = WREPL_STATE_RELEASED;
411                 }
412                 break;
413
414         case WREPL_TYPE_MHOMED:
415                 winsdb_addr_list_remove(rec->addresses, src->addr);
416                 /* TODO: do we need to take the ownership here? */
417                 if (winsdb_addr_list_length(rec->addresses) == 0) {
418                         rec->state = WREPL_STATE_RELEASED;
419                 }
420                 break;
421         }
422
423         if (rec->state == WREPL_STATE_RELEASED) {
424                 rec->expire_time = time(NULL) + winssrv->config.tombstone_interval;
425         }
426
427         ret = winsdb_modify(winssrv->wins_db, rec, modify_flags);
428         if (ret != NBT_RCODE_OK) {
429                 DEBUG(1,("WINS: FAILED: released name %s at %s: error:%u\n",
430                         nbt_name_string(rec, rec->name), src->addr, ret));
431         }
432 done:
433         /* we match w2k3 by always giving a positive reply to name releases. */
434         nbtd_name_release_reply(nbtsock, packet, src, NBT_RCODE_OK);
435 }
436
437
438 /*
439   answer a name query
440 */
441 void nbtd_winsserver_request(struct nbt_name_socket *nbtsock, 
442                              struct nbt_name_packet *packet, 
443                              const struct nbt_peer_socket *src)
444 {
445         struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, 
446                                                        struct nbtd_interface);
447         struct wins_server *winssrv = iface->nbtsrv->winssrv;
448         if ((packet->operation & NBT_FLAG_BROADCAST) || winssrv == NULL) {
449                 return;
450         }
451
452         switch (packet->operation & NBT_OPCODE) {
453         case NBT_OPCODE_QUERY:
454                 nbtd_winsserver_query(nbtsock, packet, src);
455                 break;
456
457         case NBT_OPCODE_REGISTER:
458         case NBT_OPCODE_REFRESH:
459         case NBT_OPCODE_REFRESH2:
460         case NBT_OPCODE_MULTI_HOME_REG:
461                 nbtd_winsserver_register(nbtsock, packet, src);
462                 break;
463
464         case NBT_OPCODE_RELEASE:
465                 nbtd_winsserver_release(nbtsock, packet, src);
466                 break;
467         }
468
469 }
470
471 /*
472   startup the WINS server, if configured
473 */
474 NTSTATUS nbtd_winsserver_init(struct nbtd_server *nbtsrv)
475 {
476         uint32_t tombstone_interval;
477
478         if (!lp_wins_support()) {
479                 nbtsrv->winssrv = NULL;
480                 return NT_STATUS_OK;
481         }
482
483         nbtsrv->winssrv = talloc_zero(nbtsrv, struct wins_server);
484         NT_STATUS_HAVE_NO_MEMORY(nbtsrv->winssrv);
485
486         nbtsrv->winssrv->config.max_renew_interval = lp_max_wins_ttl();
487         nbtsrv->winssrv->config.min_renew_interval = lp_min_wins_ttl();
488         tombstone_interval = lp_parm_int(-1,"wreplsrv","tombstone_interval", 6*24*60*60);
489         nbtsrv->winssrv->config.tombstone_interval = tombstone_interval;
490
491         nbtsrv->winssrv->wins_db     = winsdb_connect(nbtsrv->winssrv);
492         if (!nbtsrv->winssrv->wins_db) {
493                 return NT_STATUS_INTERNAL_DB_ERROR;
494         }
495
496         irpc_add_name(nbtsrv->task->msg_ctx, "wins_server");
497
498         return NT_STATUS_OK;
499 }