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