r5294: - added a separate NBT-WINS test for WINS operations (register, refresh, relea...
[samba.git] / source / nbt_server / interfaces.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    NBT interface 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 "dlinklist.h"
25 #include "nbt_server/nbt_server.h"
26 #include "smbd/service_task.h"
27
28
29 /*
30   receive an incoming request and dispatch it to the right place
31 */
32 static void nbtd_request_handler(struct nbt_name_socket *nbtsock, 
33                                  struct nbt_name_packet *packet, 
34                                  const char *src_address, int src_port)
35 {
36         /* if its a WINS query then direct to our WINS server if we
37            are running one */
38         if ((packet->operation & NBT_FLAG_RECURSION_DESIRED) &&
39             !(packet->operation & NBT_FLAG_BROADCAST) &&
40             lp_wins_support()) {
41                 nbtd_query_wins(nbtsock, packet, src_address, src_port);
42                 return;
43         }
44
45         /* see if its from one of our own interfaces - if so, then ignore it */
46         if (nbtd_self_packet(nbtsock, packet, src_address, src_port)) {
47                 DEBUG(10,("Ignoring self packet from %s:%d\n", src_address, src_port));
48                 return;
49         }
50
51         /* the request is to us in our role as a B node */
52         switch ((enum nbt_opcode)(packet->operation & NBT_OPCODE)) {
53         case NBT_OPCODE_QUERY:
54                 nbtd_request_query(nbtsock, packet, src_address, src_port);
55                 break;
56
57         case NBT_OPCODE_REGISTER:
58         case NBT_OPCODE_REFRESH:
59         case NBT_OPCODE_REFRESH2:
60                 nbtd_request_defense(nbtsock, packet, src_address, src_port);
61                 break;
62
63         default:
64                 nbtd_bad_packet(packet, src_address, "Unexpected opcode");
65                 break;
66         }
67 }
68
69
70
71 /*
72   find a registered name on an interface
73 */
74 struct nbtd_iface_name *nbtd_find_iname(struct nbtd_interface *iface, 
75                                         struct nbt_name *name, 
76                                         uint16_t nb_flags)
77 {
78         struct nbtd_iface_name *iname;
79         for (iname=iface->names;iname;iname=iname->next) {
80                 if (iname->name.type == name->type &&
81                     StrCaseCmp(name->name, iname->name.name) == 0 &&
82                     ((iname->nb_flags & nb_flags) == nb_flags)) {
83                         return iname;
84                 }
85         }
86         return NULL;
87 }
88
89 /*
90   start listening on the given address
91 */
92 static NTSTATUS nbtd_add_socket(struct nbtd_server *nbtsrv, 
93                                 const char *bind_address, 
94                                 const char *address, 
95                                 const char *bcast, 
96                                 const char *netmask)
97 {
98         struct nbtd_interface *iface;
99         NTSTATUS status;
100         struct nbt_name_socket *bcast_nbtsock;
101
102         /*
103           we actually create two sockets. One listens on the broadcast address
104           for the interface, and the other listens on our specific address. This
105           allows us to run with "bind interfaces only" while still receiving 
106           broadcast addresses, and also simplifies matching incoming requests 
107           to interfaces
108         */
109
110         iface = talloc(nbtsrv, struct nbtd_interface);
111         NT_STATUS_HAVE_NO_MEMORY(iface);
112
113         iface->nbtsrv        = nbtsrv;
114         iface->bcast_address = talloc_steal(iface, bcast);
115         iface->ip_address    = talloc_steal(iface, address);
116         iface->netmask       = talloc_steal(iface, netmask);
117         iface->names         = NULL;
118
119         if (strcmp(netmask, "0.0.0.0") != 0) {
120                 bcast_nbtsock = nbt_name_socket_init(iface, nbtsrv->task->event_ctx);
121                 NT_STATUS_HAVE_NO_MEMORY(bcast_nbtsock);
122
123                 status = socket_listen(bcast_nbtsock->sock, bcast, lp_nbt_port(), 0, 0);
124                 if (!NT_STATUS_IS_OK(status)) {
125                         DEBUG(0,("Failed to bind to %s:%d - %s\n", 
126                                  bcast, lp_nbt_port(), nt_errstr(status)));
127                         talloc_free(iface);
128                         return status;
129                 }
130
131                 nbt_set_incoming_handler(bcast_nbtsock, nbtd_request_handler, iface);
132         }
133
134         iface->nbtsock = nbt_name_socket_init(iface, nbtsrv->task->event_ctx);
135         NT_STATUS_HAVE_NO_MEMORY(iface->ip_address);
136
137         status = socket_listen(iface->nbtsock->sock, bind_address, lp_nbt_port(), 0, 0);
138         if (!NT_STATUS_IS_OK(status)) {
139                 DEBUG(0,("Failed to bind to %s:%d - %s\n", 
140                          address, lp_nbt_port(), nt_errstr(status)));
141                 talloc_free(iface);
142                 return status;
143         }
144
145         nbt_set_incoming_handler(iface->nbtsock, nbtd_request_handler, iface);
146
147         if (strcmp(netmask, "0.0.0.0") == 0) {
148                 DLIST_ADD(nbtsrv->bcast_interface, iface);
149         } else {
150                 DLIST_ADD(nbtsrv->interfaces, iface);
151         }
152
153         return NT_STATUS_OK;
154 }
155
156
157 /*
158   setup a socket for talking to our WINS servers
159 */
160 static NTSTATUS nbtd_add_wins_socket(struct nbtd_server *nbtsrv)
161 {
162         struct nbtd_interface *iface;
163
164         iface = talloc_zero(nbtsrv, struct nbtd_interface);
165         NT_STATUS_HAVE_NO_MEMORY(iface);
166
167         iface->nbtsrv        = nbtsrv;
168
169         DLIST_ADD(nbtsrv->wins_interface, iface);
170
171         return NT_STATUS_OK;
172 }
173
174
175 /*
176   setup our listening sockets on the configured network interfaces
177 */
178 NTSTATUS nbtd_startup_interfaces(struct nbtd_server *nbtsrv)
179 {
180         int num_interfaces = iface_count();
181         int i;
182         TALLOC_CTX *tmp_ctx = talloc_new(nbtsrv);
183         NTSTATUS status;
184
185         /* if we are allowing incoming packets from any address, then
186            we also need to bind to the wildcard address */
187         if (!lp_bind_interfaces_only()) {
188                 const char *primary_address;
189
190                 /* the primary address is the address we will return
191                    for non-WINS queries not made on a specific
192                    interface */
193                 if (num_interfaces > 0) {
194                         primary_address = iface_n_ip(0);
195                 } else {
196                         primary_address = sys_inet_ntoa(interpret_addr2(
197                                                                 lp_netbios_name()));
198                 }
199                 primary_address = talloc_strdup(tmp_ctx, primary_address);
200                 NT_STATUS_HAVE_NO_MEMORY(primary_address);
201
202                 status = nbtd_add_socket(nbtsrv, 
203                                          "0.0.0.0",
204                                          primary_address,
205                                          talloc_strdup(tmp_ctx, "255.255.255.255"),
206                                          talloc_strdup(tmp_ctx, "0.0.0.0"));
207                 NT_STATUS_NOT_OK_RETURN(status);
208         }
209
210         for (i=0; i<num_interfaces; i++) {
211                 const char *address = talloc_strdup(tmp_ctx, iface_n_ip(i));
212                 const char *bcast   = talloc_strdup(tmp_ctx, iface_n_bcast(i));
213                 const char *netmask = talloc_strdup(tmp_ctx, iface_n_netmask(i));
214
215                 status = nbtd_add_socket(nbtsrv, address, address, bcast, netmask);
216                 NT_STATUS_NOT_OK_RETURN(status);
217         }
218
219         if (lp_wins_server_list()) {
220                 status = nbtd_add_wins_socket(nbtsrv);
221                 NT_STATUS_NOT_OK_RETURN(status);
222         }
223
224         talloc_free(tmp_ctx);
225
226         return NT_STATUS_OK;
227 }
228
229
230 /*
231   form a list of addresses that we should use in name query replies
232   we always place the IP in the given interface first
233 */
234 const char **nbtd_address_list(struct nbtd_interface *iface, TALLOC_CTX *mem_ctx)
235 {
236         struct nbtd_server *nbtsrv = iface->nbtsrv;
237         const char **ret = NULL;
238         struct nbtd_interface *iface2;
239         int count = 0;
240
241         if (iface->ip_address) {
242                 ret = talloc_array(mem_ctx, const char *, 2);
243                 if (ret == NULL) goto failed;
244
245                 ret[0] = talloc_strdup(ret, iface->ip_address);
246                 if (ret[0] == NULL) goto failed;
247                 ret[1] = NULL;
248
249                 count = 1;
250         }
251
252         for (iface2=nbtsrv->interfaces;iface2;iface2=iface2->next) {
253                 const char **ret2;
254
255                 if (iface->ip_address &&
256                     strcmp(iface2->ip_address, iface->ip_address) == 0) {
257                         continue;
258                 }
259
260                 ret2 = talloc_realloc(mem_ctx, ret, const char *, count+2);
261                 if (ret2 == NULL) goto failed;
262                 ret = ret2;
263                 ret[count] = talloc_strdup(ret, iface2->ip_address);
264                 if (ret[count] == NULL) goto failed;
265                 count++;
266         }
267         ret[count] = NULL;
268         return ret;
269
270 failed:
271         talloc_free(ret);
272         return NULL;
273 }