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