r12425: match w2k3 and store 0x1D names when they're registered as group name,
[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         /*
187          * as a special case, the local master browser name is always accepted
188          * for registration, but never stored, but w2k3 stores it if it's registered
189          * as a group name, (but a query for the 0x1D name still returns not found!)
190          */
191         if (name->type == NBT_NAME_MASTER && !(nb_flags & NBT_NM_GROUP)) {
192                 rcode = NBT_RCODE_OK;
193                 goto done;
194         }
195
196         /* w2k3 refuses 0x1C names with out marked as group */
197         if (name->type == NBT_NAME_LOGON && !(nb_flags & NBT_NM_GROUP)) {
198                 rcode = NBT_RCODE_RFS;
199                 goto done;
200         }
201
202         /* w2k3 refuses 0x1E names with out marked as group */
203         if (name->type == NBT_NAME_BROWSER && !(nb_flags & NBT_NM_GROUP)) {
204                 rcode = NBT_RCODE_RFS;
205                 goto done;
206         }
207
208         status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
209         if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
210                 rcode = wins_register_new(nbtsock, packet, src, new_type);
211                 goto done;
212         } else if (!NT_STATUS_IS_OK(status)) {
213                 rcode = NBT_RCODE_SVR;
214                 goto done;
215         } else if (rec->is_static) {
216                 if (rec->type == WREPL_TYPE_GROUP || rec->type == WREPL_TYPE_SGROUP) {
217                         rcode = NBT_RCODE_OK;
218                         goto done;
219                 }
220                 rcode = NBT_RCODE_ACT;
221                 goto done;
222         }
223
224         if (rec->type == WREPL_TYPE_GROUP) {
225                 if (new_type != WREPL_TYPE_GROUP) {
226                         DEBUG(2,("WINS: Attempt to register name %s as non normal group(%u)"
227                                  " while a normal group is already there\n",
228                                  nbt_name_string(packet, name), new_type));
229                         rcode = NBT_RCODE_ACT;
230                         goto done;
231                 }
232
233                 if (rec->state == WREPL_STATE_ACTIVE) {
234                         /* TODO: is this correct? */
235                         rcode = wins_update_ttl(nbtsock, packet, rec, NULL, src);
236                         goto done;
237                 }
238
239                 /* TODO: is this correct? */
240                 winsdb_delete(winssrv->wins_db, rec);
241                 rcode = wins_register_new(nbtsock, packet, src, new_type);
242                 goto done;
243         }
244
245         if (rec->state != WREPL_STATE_ACTIVE) {
246                 winsdb_delete(winssrv->wins_db, rec);
247                 rcode = wins_register_new(nbtsock, packet, src, new_type);
248                 goto done;
249         }
250
251         switch (rec->type) {
252         case WREPL_TYPE_UNIQUE:
253         case WREPL_TYPE_MHOMED:
254                 /* 
255                  * if its an active unique name, and the registration is for a group, then
256                  * see if the unique name owner still wants the name
257                  * TODO: is this correct?
258                  */
259                 if (new_type == WREPL_TYPE_GROUP || new_type == WREPL_TYPE_GROUP) {
260                         wins_register_wack(nbtsock, packet, rec, src);
261                         return;
262                 }
263
264                 /* 
265                  * if the registration is for an address that is currently active, then 
266                  * just update the expiry time of the record and the address
267                  * TODO: is this correct?
268                  */
269                 winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
270                 if (winsdb_addr) {
271                         rcode = wins_update_ttl(nbtsock, packet, rec, winsdb_addr, src);
272                         goto done;
273                 }
274
275                 /*
276                  * we have to do a WACK to see if the current owner is willing
277                  * to give up its claim
278                  */
279                 wins_register_wack(nbtsock, packet, rec, src);
280                 return;
281
282         case WREPL_TYPE_GROUP:
283                 /* this should not be reached as normal groups are handled above */
284                 DEBUG(0,("BUG at %s\n",__location__));
285                 rcode = NBT_RCODE_ACT;
286                 goto done;
287
288         case WREPL_TYPE_SGROUP:
289                 /* if the new record isn't also a special group, refuse the registration */ 
290                 if (new_type != WREPL_TYPE_SGROUP) {
291                         DEBUG(2,("WINS: Attempt to register name %s as non special group(%u)"
292                                  " while a special group is already there\n",
293                                  nbt_name_string(packet, name), new_type));
294                         rcode = NBT_RCODE_ACT;
295                         goto done;
296                 }
297
298                 /* 
299                  * if the registration is for an address that is currently active, then 
300                  * just update the expiry time
301                  * just update the expiry time of the record and the address
302                  * TODO: is this correct?
303                  */
304                 winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
305                 if (winsdb_addr) {
306                         rcode = wins_update_ttl(nbtsock, packet, rec, winsdb_addr, src);
307                         goto done;
308                 }
309
310                 rcode = wins_sgroup_merge(nbtsock, packet, rec, address, src);
311                 goto done;
312         }
313
314 done:
315         nbtd_name_registration_reply(nbtsock, packet, src, rcode);
316 }
317
318 /*
319   query a name
320 */
321 static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock, 
322                                   struct nbt_name_packet *packet, 
323                                   const struct nbt_peer_socket *src)
324 {
325         NTSTATUS status;
326         struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, 
327                                                        struct nbtd_interface);
328         struct wins_server *winssrv = iface->nbtsrv->winssrv;
329         struct nbt_name *name = &packet->questions[0].name;
330         struct winsdb_record *rec;
331         const char **addresses;
332         uint16_t nb_flags = 0; /* TODO: ... */
333
334         if (name->type == NBT_NAME_MASTER) {
335                 goto notfound;
336         }
337
338         status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
339         if (!NT_STATUS_IS_OK(status)) {
340                 goto notfound;
341         }
342
343         /*
344          * for group's we always reply with
345          * 255.255.255.255 as address, even if
346          * the record is released or tombstoned
347          */
348         if (rec->type == WREPL_TYPE_GROUP) {
349                 addresses = talloc_array(packet, const char *, 2);
350                 if (addresses == NULL) {
351                         nbtd_negative_name_query_reply(nbtsock, packet, src);
352                         return;
353                 }
354                 addresses[0] = WINSDB_GROUP_ADDRESS;
355                 addresses[1] = NULL;
356                 goto found;
357         }
358
359         if (rec->state != WREPL_STATE_ACTIVE) {
360                 goto notfound;
361         }
362
363         addresses = winsdb_addr_string_list(packet, rec->addresses);
364         if (!addresses) {
365                 goto notfound;
366         }
367 found:
368         nbtd_name_query_reply(nbtsock, packet, src, name, 
369                               0, nb_flags, addresses);
370         return;
371
372 notfound:
373         nbtd_negative_name_query_reply(nbtsock, packet, src);
374 }
375
376 /*
377   release a name
378 */
379 static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock, 
380                                     struct nbt_name_packet *packet, 
381                                     const struct nbt_peer_socket *src)
382 {
383         NTSTATUS status;
384         struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, 
385                                                        struct nbtd_interface);
386         struct wins_server *winssrv = iface->nbtsrv->winssrv;
387         struct nbt_name *name = &packet->questions[0].name;
388         struct winsdb_record *rec;
389         uint32_t modify_flags = 0;
390         uint8_t ret;
391
392         status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
393         if (!NT_STATUS_IS_OK(status)) {
394                 goto done;
395         }
396
397         if (rec->is_static) {
398                 if (rec->type == WREPL_TYPE_UNIQUE || rec->type == WREPL_TYPE_MHOMED) {
399                         goto done;
400                 }
401                 nbtd_name_release_reply(nbtsock, packet, src, NBT_RCODE_ACT);
402                 return;
403         }
404
405         if (rec->state != WREPL_STATE_ACTIVE) {
406                 goto done;
407         }
408
409         /* 
410          * TODO: do we need to check if
411          *       src->addr matches packet->additional[0].rdata.netbios.addresses[0].ipaddr
412          *       here?
413          */
414
415         /* 
416          * we only allow releases from an owner - other releases are
417          * silently ignored
418          */
419         if (!winsdb_addr_list_check(rec->addresses, src->addr)) {
420                 goto done;
421         }
422
423         DEBUG(4,("WINS: released name %s from %s\n", nbt_name_string(rec, rec->name), src->addr));
424
425         switch (rec->type) {
426         case WREPL_TYPE_UNIQUE:
427                 rec->state = WREPL_STATE_RELEASED;
428                 break;
429
430         case WREPL_TYPE_GROUP:
431                 rec->state = WREPL_STATE_RELEASED;
432                 break;
433
434         case WREPL_TYPE_SGROUP:
435                 winsdb_addr_list_remove(rec->addresses, src->addr);
436                 /* TODO: do we need to take the ownership here? */
437                 if (winsdb_addr_list_length(rec->addresses) == 0) {
438                         rec->state = WREPL_STATE_RELEASED;
439                 }
440                 break;
441
442         case WREPL_TYPE_MHOMED:
443                 winsdb_addr_list_remove(rec->addresses, src->addr);
444                 /* TODO: do we need to take the ownership here? */
445                 if (winsdb_addr_list_length(rec->addresses) == 0) {
446                         rec->state = WREPL_STATE_RELEASED;
447                 }
448                 break;
449         }
450
451         if (rec->state == WREPL_STATE_RELEASED) {
452                 rec->expire_time = time(NULL) + winssrv->config.tombstone_interval;
453         }
454
455         ret = winsdb_modify(winssrv->wins_db, rec, modify_flags);
456         if (ret != NBT_RCODE_OK) {
457                 DEBUG(1,("WINS: FAILED: released name %s at %s: error:%u\n",
458                         nbt_name_string(rec, rec->name), src->addr, ret));
459         }
460 done:
461         /* we match w2k3 by always giving a positive reply to name releases. */
462         nbtd_name_release_reply(nbtsock, packet, src, NBT_RCODE_OK);
463 }
464
465
466 /*
467   answer a name query
468 */
469 void nbtd_winsserver_request(struct nbt_name_socket *nbtsock, 
470                              struct nbt_name_packet *packet, 
471                              const struct nbt_peer_socket *src)
472 {
473         struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, 
474                                                        struct nbtd_interface);
475         struct wins_server *winssrv = iface->nbtsrv->winssrv;
476         if ((packet->operation & NBT_FLAG_BROADCAST) || winssrv == NULL) {
477                 return;
478         }
479
480         switch (packet->operation & NBT_OPCODE) {
481         case NBT_OPCODE_QUERY:
482                 nbtd_winsserver_query(nbtsock, packet, src);
483                 break;
484
485         case NBT_OPCODE_REGISTER:
486         case NBT_OPCODE_REFRESH:
487         case NBT_OPCODE_REFRESH2:
488         case NBT_OPCODE_MULTI_HOME_REG:
489                 nbtd_winsserver_register(nbtsock, packet, src);
490                 break;
491
492         case NBT_OPCODE_RELEASE:
493                 nbtd_winsserver_release(nbtsock, packet, src);
494                 break;
495         }
496
497 }
498
499 /*
500   startup the WINS server, if configured
501 */
502 NTSTATUS nbtd_winsserver_init(struct nbtd_server *nbtsrv)
503 {
504         uint32_t tombstone_interval;
505
506         if (!lp_wins_support()) {
507                 nbtsrv->winssrv = NULL;
508                 return NT_STATUS_OK;
509         }
510
511         nbtsrv->winssrv = talloc_zero(nbtsrv, struct wins_server);
512         NT_STATUS_HAVE_NO_MEMORY(nbtsrv->winssrv);
513
514         nbtsrv->winssrv->config.max_renew_interval = lp_max_wins_ttl();
515         nbtsrv->winssrv->config.min_renew_interval = lp_min_wins_ttl();
516         tombstone_interval = lp_parm_int(-1,"wreplsrv","tombstone_interval", 6*24*60*60);
517         nbtsrv->winssrv->config.tombstone_interval = tombstone_interval;
518
519         nbtsrv->winssrv->wins_db     = winsdb_connect(nbtsrv->winssrv);
520         if (!nbtsrv->winssrv->wins_db) {
521                 return NT_STATUS_INTERNAL_DB_ERROR;
522         }
523
524         irpc_add_name(nbtsrv->task->msg_ctx, "wins_server");
525
526         return NT_STATUS_OK;
527 }