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-2003
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
24#include "includes.h"
25
26/****************************************************************************
27 Deal with a response packet when querying a name.
28****************************************************************************/
29
30static void query_name_response( struct subnet_record   *subrec,
31                                 struct response_record *rrec,
32                                 struct packet_struct   *p)
33{
34	struct nmb_packet *nmb = &p->packet.nmb;
35	BOOL success = False;
36	struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
37	struct in_addr answer_ip;
38
39	zero_ip(&answer_ip);
40
41	/* Ensure we don't retry the query but leave the response record cleanup
42		to the timeout code. We may get more answer responses in which case
43		we should mark the name in conflict.. */
44	rrec->repeat_count = 0;
45
46	if(rrec->num_msgs == 1) {
47		/* This is the first response. */
48
49		if(nmb->header.opcode == NMB_WACK_OPCODE) {
50			/* WINS server is telling us to wait. Pretend we didn't get
51				the response but don't send out any more query requests. */
52
53			if( DEBUGLVL( 5 ) ) {
54				dbgtext( "query_name_response: " );
55				dbgtext( "WACK from WINS server %s ", inet_ntoa(p->ip) );
56				dbgtext( "in querying name %s ", nmb_namestr(question_name) );
57				dbgtext( "on subnet %s.\n", subrec->subnet_name );
58			}
59
60			rrec->repeat_count = 0;
61			/* How long we should wait for. */
62			rrec->repeat_time = p->timestamp + nmb->answers->ttl;
63			rrec->num_msgs--;
64			return;
65		} else if(nmb->header.rcode != 0) {
66
67			success = False;
68
69			if( DEBUGLVL( 5 ) ) {
70				dbgtext( "query_name_response: On subnet %s ", subrec->subnet_name );
71				dbgtext( "- negative response from IP %s ", inet_ntoa(p->ip) );
72				dbgtext( "for name %s. ", nmb_namestr(question_name) );
73				dbgtext( "Error code was %d.\n", nmb->header.rcode );
74			}
75		} else {
76			if (!nmb->answers) {
77				dbgtext( "query_name_response: On subnet %s ", subrec->subnet_name );
78				dbgtext( "IP %s ", inet_ntoa(p->ip) );
79				dbgtext( "returned a success response with no answer\n" );
80				return;
81			}
82
83			success = True;
84
85			putip((char *)&answer_ip,&nmb->answers->rdata[2]);
86
87			if( DEBUGLVL( 5 ) ) {
88				dbgtext( "query_name_response: On subnet %s ", subrec->subnet_name );
89				dbgtext( "- positive response from IP %s ", inet_ntoa(p->ip) );
90				dbgtext( "for name %s.  ", nmb_namestr(question_name) );
91				dbgtext( "IP of that name is %s\n", inet_ntoa(answer_ip) );
92			}
93
94			/* Interestingly, we could add these names to our namelists, and
95				change nmbd to a model that checked its own name cache first,
96				before sending out a query. This is a task for another day, though.
97			*/
98		}
99	} else if( rrec->num_msgs > 1) {
100
101		if( DEBUGLVL( 0 ) ) {
102			if (nmb->answers)
103				putip( (char *)&answer_ip, &nmb->answers->rdata[2] );
104			dbgtext( "query_name_response: " );
105			dbgtext( "Multiple (%d) responses ", rrec->num_msgs );
106			dbgtext( "received for a query on subnet %s ", subrec->subnet_name );
107			dbgtext( "for name %s.\nThis response ", nmb_namestr(question_name) );
108			dbgtext( "was from IP %s, reporting ", inet_ntoa(p->ip) );
109			dbgtext( "an IP address of %s.\n", inet_ntoa(answer_ip) );
110		}
111
112		/* We have already called the success or fail function, so we
113			don't call again here. Leave the response record around in
114			case we get more responses. */
115
116		return;
117	}
118
119	if(success && rrec->success_fn)
120		(*(query_name_success_function)rrec->success_fn)(subrec, rrec->userdata, question_name, answer_ip, nmb->answers);
121	else if( rrec->fail_fn)
122		(*(query_name_fail_function)rrec->fail_fn)(subrec, rrec, question_name, nmb->header.rcode);
123
124}
125
126/****************************************************************************
127 Deal with a timeout when querying a name.
128****************************************************************************/
129
130static void query_name_timeout_response(struct subnet_record *subrec,
131                       struct response_record *rrec)
132{
133	struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
134	/* We can only fail here, never succeed. */
135	BOOL failed = True;
136	struct nmb_name *question_name = &sent_nmb->question.question_name;
137
138	if(rrec->num_msgs != 0) {
139		/* We got at least one response, and have called the success/fail
140			function already. */
141
142		failed = False;
143	}
144
145	if(failed) {
146		if( DEBUGLVL( 5 ) ) {
147			dbgtext( "query_name_timeout_response: No response to " );
148			dbgtext( "query for name %s ", nmb_namestr(question_name) );
149			dbgtext( "on subnet %s.\n", subrec->subnet_name );
150		}
151
152		if(rrec->fail_fn)
153			(*(query_name_fail_function)rrec->fail_fn)(subrec, rrec, question_name, 0);
154	}
155
156	remove_response_record(subrec, rrec);
157}
158
159/****************************************************************************
160 Lookup a name on our local namelists. We check the lmhosts file first. If the
161 name is not there we look for the name on the given subnet.
162****************************************************************************/
163
164static BOOL query_local_namelists(struct subnet_record *subrec, struct nmb_name *nmbname,
165                                  struct name_record **namerecp)
166{
167	struct name_record *namerec;
168
169	*namerecp = NULL;
170
171	if(find_name_in_lmhosts(nmbname, namerecp))
172		return True;
173
174	if((namerec = find_name_on_subnet(subrec, nmbname, FIND_ANY_NAME))==NULL)
175		return False;
176
177	if( NAME_IS_ACTIVE(namerec) && ( (namerec->data.source == SELF_NAME) || (namerec->data.source == LMHOSTS_NAME) ) ) {
178		*namerecp = namerec;
179		return True;
180	}
181	return False;
182}
183
184/****************************************************************************
185 Try and query for a name.
186****************************************************************************/
187
188BOOL query_name(struct subnet_record *subrec, const char *name, int type,
189                   query_name_success_function success_fn,
190                   query_name_fail_function fail_fn,
191                   struct userdata_struct *userdata)
192{
193	struct nmb_name nmbname;
194	struct name_record *namerec;
195
196	make_nmb_name(&nmbname, name, type);
197
198	/*
199	 * We need to check our local namelists first.
200	 * It may be an magic name, lmhosts name or just
201	 * a name we have registered.
202	 */
203
204	if(query_local_namelists(subrec, &nmbname, &namerec) == True) {
205		struct res_rec rrec;
206		int i;
207
208		memset((char *)&rrec, '\0', sizeof(struct res_rec));
209
210		/* Fake up the needed res_rec just in case it's used. */
211		rrec.rr_name = nmbname;
212		rrec.rr_type = RR_TYPE_NB;
213		rrec.rr_class = RR_CLASS_IN;
214		rrec.ttl = PERMANENT_TTL;
215		rrec.rdlength = namerec->data.num_ips * 6;
216		if(rrec.rdlength > MAX_DGRAM_SIZE) {
217			if( DEBUGLVL( 0 ) ) {
218				dbgtext( "query_name: nmbd internal error - " );
219				dbgtext( "there are %d ip addresses ", namerec->data.num_ips );
220				dbgtext( "for name %s.\n", nmb_namestr(&nmbname) );
221			}
222			return False;
223		}
224
225		for( i = 0; i < namerec->data.num_ips; i++) {
226			set_nb_flags( &rrec.rdata[i*6], namerec->data.nb_flags );
227			putip( &rrec.rdata[(i*6) + 2], (char *)&namerec->data.ip[i]);
228		}
229
230		/* Call the success function directly. */
231		if(success_fn)
232			(*(query_name_success_function)success_fn)(subrec, userdata, &nmbname, namerec->data.ip[0], &rrec);
233		return False;
234	}
235
236	if(queue_query_name( subrec, query_name_response, query_name_timeout_response, success_fn, fail_fn, userdata, &nmbname) == NULL) {
237		if( DEBUGLVL( 0 ) ) {
238			dbgtext( "query_name: Failed to send packet " );
239			dbgtext( "trying to query name %s\n", nmb_namestr(&nmbname) );
240		}
241		return True;
242	}
243	return False;
244}
245
246/****************************************************************************
247 Try and query for a name from nmbd acting as a WINS server.
248****************************************************************************/
249
250BOOL query_name_from_wins_server(struct in_addr ip_to,
251                   const char *name, int type,
252                   query_name_success_function success_fn,
253                   query_name_fail_function fail_fn,
254                   struct userdata_struct *userdata)
255{
256	struct nmb_name nmbname;
257
258	make_nmb_name(&nmbname, name, type);
259
260	if(queue_query_name_from_wins_server( ip_to, query_name_response, query_name_timeout_response, success_fn, fail_fn, userdata, &nmbname) == NULL) {
261		if( DEBUGLVL( 0 ) ) {
262			dbgtext( "query_name_from_wins_server: Failed to send packet " );
263			dbgtext( "trying to query name %s\n", nmb_namestr(&nmbname) );
264		}
265		return True;
266	}
267	return False;
268}
269