r11877: - give winsdb_add/modify/delete() ldb_context as first argument
[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) || rec->state != WREPL_STATE_ACTIVE) {
240                 nbtd_negative_name_query_reply(nbtsock, packet, src);
241                 return;
242         }
243
244         if (rec->type == WREPL_TYPE_GROUP) {
245                 addresses = talloc_array(packet, const char *, 2);
246                 if (addresses == NULL) {
247                         nbtd_negative_name_query_reply(nbtsock, packet, src);
248                         return;
249                 }
250                 addresses[0] = WINSDB_GROUP_ADDRESS;
251                 addresses[1] = NULL;
252         } else {
253                 addresses = winsdb_addr_string_list(packet, rec->addresses);
254                 if (addresses == NULL) {
255                         nbtd_negative_name_query_reply(nbtsock, packet, src);
256                         return; 
257                 }
258         }
259
260         nbtd_name_query_reply(nbtsock, packet, src, name, 
261                               0, nb_flags, addresses);
262 }
263
264 /*
265   release a name
266 */
267 static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock, 
268                                     struct nbt_name_packet *packet, 
269                                     const struct nbt_peer_socket *src)
270 {
271         NTSTATUS status;
272         struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, 
273                                                        struct nbtd_interface);
274         struct wins_server *winssrv = iface->nbtsrv->winssrv;
275         struct nbt_name *name = &packet->questions[0].name;
276         struct winsdb_record *rec;
277
278         status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
279         if (!NT_STATUS_IS_OK(status) || 
280             rec->state != WREPL_STATE_ACTIVE || 
281             rec->type == WREPL_TYPE_GROUP) {
282                 goto done;
283         }
284
285         /* we only allow releases from an owner - other releases are
286            silently ignored */
287         if (winsdb_addr_list_check(rec->addresses, src->addr)) {
288                 const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
289
290                 DEBUG(4,("WINS: released name %s at %s\n", nbt_name_string(rec, rec->name), address));
291                 winsdb_addr_list_remove(rec->addresses, address);
292                 if (rec->addresses[0] == NULL) {
293                         rec->state = WREPL_STATE_RELEASED;
294                 }
295                 winsdb_modify(winssrv->wins_db, rec, 0);
296         }
297
298 done:
299         /* we match w2k3 by always giving a positive reply to name releases. */
300         nbtd_name_release_reply(nbtsock, packet, src, NBT_RCODE_OK);
301 }
302
303
304 /*
305   answer a name query
306 */
307 void nbtd_winsserver_request(struct nbt_name_socket *nbtsock, 
308                              struct nbt_name_packet *packet, 
309                              const struct nbt_peer_socket *src)
310 {
311         struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, 
312                                                        struct nbtd_interface);
313         struct wins_server *winssrv = iface->nbtsrv->winssrv;
314         if ((packet->operation & NBT_FLAG_BROADCAST) || winssrv == NULL) {
315                 return;
316         }
317
318         switch (packet->operation & NBT_OPCODE) {
319         case NBT_OPCODE_QUERY:
320                 nbtd_winsserver_query(nbtsock, packet, src);
321                 break;
322
323         case NBT_OPCODE_REGISTER:
324         case NBT_OPCODE_REFRESH:
325         case NBT_OPCODE_REFRESH2:
326         case NBT_OPCODE_MULTI_HOME_REG:
327                 nbtd_winsserver_register(nbtsock, packet, src);
328                 break;
329
330         case NBT_OPCODE_RELEASE:
331                 nbtd_winsserver_release(nbtsock, packet, src);
332                 break;
333         }
334
335 }
336
337 /*
338   startup the WINS server, if configured
339 */
340 NTSTATUS nbtd_winsserver_init(struct nbtd_server *nbtsrv)
341 {
342         if (!lp_wins_support()) {
343                 nbtsrv->winssrv = NULL;
344                 return NT_STATUS_OK;
345         }
346
347         nbtsrv->winssrv = talloc_zero(nbtsrv, struct wins_server);
348         NT_STATUS_HAVE_NO_MEMORY(nbtsrv->winssrv);
349
350         nbtsrv->winssrv->max_ttl     = lp_max_wins_ttl();
351         nbtsrv->winssrv->min_ttl     = lp_min_wins_ttl();
352
353         nbtsrv->winssrv->wins_db     = winsdb_connect(nbtsrv->winssrv);
354         if (!nbtsrv->winssrv->wins_db) {
355                 return NT_STATUS_INTERNAL_DB_ERROR;
356         }
357
358         irpc_add_name(nbtsrv->task->msg_ctx, "wins_server");
359
360         return NT_STATUS_OK;
361 }