import HEAD into svn+ssh://svn.samba.org/home/svn/samba/trunk
[metze/old/v3-2-winbind-ndr.git] / source / nmbd / nmbd_namequery.c
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
30 static 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
130 static 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
164 static 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
188 BOOL 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
250 BOOL 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 }