1/*
2   Unix SMB/CIFS implementation.
3   NBT netbios routines and daemon - version 2
4   Copyright (C) Andrew Tridgell 1994-1998
5   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
6   Copyright (C) Jeremy Allison 1994-1998
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   Revision History:
23
24*/
25
26#include "includes.h"
27
28extern int ClientNMB;
29extern int ClientDGRAM;
30extern int global_nmb_port;
31
32/* This is the broadcast subnets database. */
33struct subnet_record *subnetlist = NULL;
34
35/* Extra subnets - keep these separate so enumeration code doesn't
36   run onto it by mistake. */
37
38struct subnet_record *unicast_subnet = NULL;
39struct subnet_record *remote_broadcast_subnet = NULL;
40struct subnet_record *wins_server_subnet = NULL;
41
42extern uint16 samba_nb_type; /* Samba's NetBIOS name type. */
43
44/****************************************************************************
45  Add a subnet into the list.
46  **************************************************************************/
47
48static void add_subnet(struct subnet_record *subrec)
49{
50	DLIST_ADD(subnetlist, subrec);
51}
52
53/* ************************************************************************** **
54 * Comparison routine for ordering the splay-tree based namelists assoicated
55 * with each subnet record.
56 *
57 *  Input:  Item  - Pointer to the comparison key.
58 *          Node  - Pointer to a node the splay tree.
59 *
60 *  Output: The return value will be <0 , ==0, or >0 depending upon the
61 *          ordinal relationship of the two keys.
62 *
63 * ************************************************************************** **
64 */
65static int namelist_entry_compare( ubi_trItemPtr Item, ubi_trNodePtr Node )
66{
67	struct name_record *NR = (struct name_record *)Node;
68
69	if( DEBUGLVL( 10 ) ) {
70		struct nmb_name *Iname = (struct nmb_name *)Item;
71
72		Debug1( "nmbd_subnetdb:namelist_entry_compare()\n" );
73		Debug1( "%d == memcmp( \"%s\", \"%s\", %d )\n",
74			memcmp( Item, &(NR->name), sizeof(struct nmb_name) ),
75			nmb_namestr(Iname), nmb_namestr(&NR->name), (int)sizeof(struct nmb_name) );
76	}
77
78	return( memcmp( Item, &(NR->name), sizeof(struct nmb_name) ) );
79}
80
81/****************************************************************************
82stop listening on a subnet
83we don't free the record as we don't have proper reference counting for it
84yet and it may be in use by a response record
85  ****************************************************************************/
86
87void close_subnet(struct subnet_record *subrec)
88{
89	DLIST_REMOVE(subnetlist, subrec);
90
91	if (subrec->dgram_sock != -1) {
92		close(subrec->dgram_sock);
93		subrec->dgram_sock = -1;
94	}
95	if (subrec->nmb_sock != -1) {
96		close(subrec->nmb_sock);
97		subrec->nmb_sock = -1;
98	}
99}
100
101/****************************************************************************
102  Create a subnet entry.
103  ****************************************************************************/
104
105static struct subnet_record *make_subnet(const char *name, enum subnet_type type,
106					 struct in_addr myip, struct in_addr bcast_ip,
107					 struct in_addr mask_ip)
108{
109	struct subnet_record *subrec = NULL;
110	int nmb_sock, dgram_sock;
111
112	/* Check if we are creating a non broadcast subnet - if so don't create
113		sockets.  */
114
115	if(type != NORMAL_SUBNET) {
116		nmb_sock = -1;
117		dgram_sock = -1;
118	} else {
119		/*
120		 * Attempt to open the sockets on port 137/138 for this interface
121		 * and bind them.
122		 * Fail the subnet creation if this fails.
123		 */
124
125		if((nmb_sock = open_socket_in(SOCK_DGRAM, global_nmb_port,0, myip.s_addr,True)) == -1) {
126			if( DEBUGLVL( 0 ) ) {
127				Debug1( "nmbd_subnetdb:make_subnet()\n" );
128				Debug1( "  Failed to open nmb socket on interface %s ", inet_ntoa(myip) );
129				Debug1( "for port %d.  ", global_nmb_port );
130				Debug1( "Error was %s\n", strerror(errno) );
131			}
132			return NULL;
133		}
134
135		if((dgram_sock = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3, myip.s_addr,True)) == -1) {
136			if( DEBUGLVL( 0 ) ) {
137				Debug1( "nmbd_subnetdb:make_subnet()\n" );
138				Debug1( "  Failed to open dgram socket on interface %s ", inet_ntoa(myip) );
139				Debug1( "for port %d.  ", DGRAM_PORT );
140				Debug1( "Error was %s\n", strerror(errno) );
141			}
142			return NULL;
143		}
144
145		/* Make sure we can broadcast from these sockets. */
146		set_socket_options(nmb_sock,"SO_BROADCAST");
147		set_socket_options(dgram_sock,"SO_BROADCAST");
148	}
149
150	subrec = SMB_MALLOC_P(struct subnet_record);
151	if (!subrec) {
152		DEBUG(0,("make_subnet: malloc fail !\n"));
153		close(nmb_sock);
154		close(dgram_sock);
155		return(NULL);
156	}
157
158	memset( (char *)subrec, '\0', sizeof(*subrec) );
159	(void)ubi_trInitTree( subrec->namelist,
160			namelist_entry_compare,
161			ubi_trOVERWRITE );
162
163	if((subrec->subnet_name = SMB_STRDUP(name)) == NULL) {
164		DEBUG(0,("make_subnet: malloc fail for subnet name !\n"));
165		close(nmb_sock);
166		close(dgram_sock);
167		ZERO_STRUCTP(subrec);
168		SAFE_FREE(subrec);
169		return(NULL);
170	}
171
172	DEBUG(2, ("making subnet name:%s ", name ));
173	DEBUG(2, ("Broadcast address:%s ", inet_ntoa(bcast_ip)));
174	DEBUG(2, ("Subnet mask:%s\n", inet_ntoa(mask_ip)));
175
176	subrec->namelist_changed = False;
177	subrec->work_changed = False;
178
179	subrec->bcast_ip = bcast_ip;
180	subrec->mask_ip  = mask_ip;
181	subrec->myip = myip;
182	subrec->type = type;
183	subrec->nmb_sock = nmb_sock;
184	subrec->dgram_sock = dgram_sock;
185
186	return subrec;
187}
188
189/****************************************************************************
190  Create a normal subnet
191**************************************************************************/
192
193struct subnet_record *make_normal_subnet(struct interface *iface)
194{
195	struct subnet_record *subrec;
196
197	subrec = make_subnet(inet_ntoa(iface->ip), NORMAL_SUBNET,
198			     iface->ip, iface->bcast, iface->nmask);
199	if (subrec) {
200		add_subnet(subrec);
201	}
202	return subrec;
203}
204
205/****************************************************************************
206  Create subnet entries.
207**************************************************************************/
208
209BOOL create_subnets(void)
210{
211	int num_interfaces = iface_count();
212	int i;
213	struct in_addr unicast_ip, ipzero;
214	extern struct in_addr loopback_ip;
215
216	if(num_interfaces == 0) {
217		DEBUG(0,("create_subnets: No local interfaces !\n"));
218		DEBUG(0,("create_subnets: Waiting for an interface to appear ...\n"));
219		while (iface_count() == 0) {
220			sleep(5);
221			load_interfaces();
222		}
223	}
224
225	num_interfaces = iface_count();
226
227	/*
228	 * Create subnets from all the local interfaces and thread them onto
229	 * the linked list.
230	 */
231
232	for (i = 0 ; i < num_interfaces; i++) {
233		struct interface *iface = get_interface(i);
234
235		/*
236		 * We don't want to add a loopback interface, in case
237		 * someone has added 127.0.0.1 for smbd, nmbd needs to
238		 * ignore it here. JRA.
239		 */
240
241		if (ip_equal(iface->ip, loopback_ip)) {
242			DEBUG(2,("create_subnets: Ignoring loopback interface.\n" ));
243			continue;
244		}
245
246		if (!make_normal_subnet(iface))
247			return False;
248	}
249
250	if (lp_we_are_a_wins_server()) {
251		/* Pick the first interface ip address as the WINS server ip. */
252		unicast_ip = *iface_n_ip(0);
253	} else {
254		/* note that we do not set the wins server IP here. We just
255			set it at zero and let the wins registration code cope
256			with getting the IPs right for each packet */
257		zero_ip(&unicast_ip);
258	}
259
260	/*
261	 * Create the unicast and remote broadcast subnets.
262	 * Don't put these onto the linked list.
263	 * The ip address of the unicast subnet is set to be
264	 * the WINS server address, if it exists, or ipzero if not.
265	 */
266
267	unicast_subnet = make_subnet( "UNICAST_SUBNET", UNICAST_SUBNET,
268				unicast_ip, unicast_ip, unicast_ip);
269
270	zero_ip(&ipzero);
271
272	remote_broadcast_subnet = make_subnet( "REMOTE_BROADCAST_SUBNET",
273				REMOTE_BROADCAST_SUBNET,
274				ipzero, ipzero, ipzero);
275
276	if((unicast_subnet == NULL) || (remote_broadcast_subnet == NULL))
277		return False;
278
279	/*
280	 * If we are WINS server, create the WINS_SERVER_SUBNET - don't put on
281	 * the linked list.
282	 */
283
284	if (lp_we_are_a_wins_server()) {
285		if( (wins_server_subnet = make_subnet( "WINS_SERVER_SUBNET",
286						WINS_SERVER_SUBNET,
287						ipzero, ipzero, ipzero )) == NULL )
288			return False;
289	}
290
291	return True;
292}
293
294/*******************************************************************
295Function to tell us if we can use the unicast subnet.
296******************************************************************/
297
298BOOL we_are_a_wins_client(void)
299{
300	if (wins_srv_count() > 0) {
301		return True;
302	}
303
304	return False;
305}
306
307/*******************************************************************
308Access function used by NEXT_SUBNET_INCLUDING_UNICAST
309******************************************************************/
310
311struct subnet_record *get_next_subnet_maybe_unicast(struct subnet_record *subrec)
312{
313	if(subrec == unicast_subnet)
314		return NULL;
315	else if((subrec->next == NULL) && we_are_a_wins_client())
316		return unicast_subnet;
317	else
318		return subrec->next;
319}
320
321/*******************************************************************
322 Access function used by retransmit_or_expire_response_records() in
323 nmbd_packets.c. Patch from Andrey Alekseyev <fetch@muffin.arcadia.spb.ru>
324 Needed when we need to enumerate all the broadcast, unicast and
325 WINS subnets.
326******************************************************************/
327
328struct subnet_record *get_next_subnet_maybe_unicast_or_wins_server(struct subnet_record *subrec)
329{
330	if(subrec == unicast_subnet) {
331		if(wins_server_subnet)
332			return wins_server_subnet;
333		else
334			return NULL;
335	}
336
337	if(wins_server_subnet && subrec == wins_server_subnet)
338		return NULL;
339
340	if((subrec->next == NULL) && we_are_a_wins_client())
341		return unicast_subnet;
342	else
343		return subrec->next;
344}
345