This is *not* a big change (although it looks like one).
[kai/samba.git] / source3 / libsmb / namequery.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    name query routines
5    Copyright (C) Andrew Tridgell 1994-1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20    
21 */
22
23 #include "includes.h"
24
25 extern pstring scope;
26 extern int DEBUGLEVEL;
27
28
29 /****************************************************************************
30 interpret a node status response
31 ****************************************************************************/
32 static void _interpret_node_status(char *p, char *master,char *rname)
33 {
34   int numnames = CVAL(p,0);
35   DEBUG(1,("received %d names\n",numnames));
36
37   if (rname) *rname = 0;
38   if (master) *master = 0;
39
40   p += 1;
41   while (numnames--)
42     {
43       char qname[17];
44       int type;
45       fstring flags;
46       int i;
47       *flags = 0;
48       StrnCpy(qname,p,15);
49       type = CVAL(p,15);
50       p += 16;
51
52       strcat(flags, (p[0] & 0x80) ? "<GROUP> " : "        ");
53       if ((p[0] & 0x60) == 0x00) strcat(flags,"B ");
54       if ((p[0] & 0x60) == 0x20) strcat(flags,"P ");
55       if ((p[0] & 0x60) == 0x40) strcat(flags,"M ");
56       if ((p[0] & 0x60) == 0x60) strcat(flags,"H ");
57       if (p[0] & 0x10) strcat(flags,"<DEREGISTERING> ");
58       if (p[0] & 0x08) strcat(flags,"<CONFLICT> ");
59       if (p[0] & 0x04) strcat(flags,"<ACTIVE> ");
60       if (p[0] & 0x02) strcat(flags,"<PERMANENT> ");
61
62       if (master && !*master && type == 0x1d) {
63         StrnCpy(master,qname,15);
64         trim_string(master,NULL," ");
65       }
66
67       if (rname && !*rname && type == 0x20 && !(p[0]&0x80)) {
68         StrnCpy(rname,qname,15);
69         trim_string(rname,NULL," ");
70       }
71       
72       for (i = strlen( qname) ; --i >= 0 ; ) {
73         if (!isprint(qname[i])) qname[i] = '.';
74       }
75       DEBUG(1,("\t%-15s <%02x> - %s\n",qname,type,flags));
76       p+=2;
77     }
78   DEBUG(1,("num_good_sends=%d num_good_receives=%d\n",
79                IVAL(p,20),IVAL(p,24)));
80 }
81
82
83 /****************************************************************************
84   do a netbios name status query on a host
85
86   the "master" parameter is a hack used for finding workgroups.
87   **************************************************************************/
88 BOOL name_status(int fd,char *name,int name_type,BOOL recurse,
89                  struct in_addr to_ip,char *master,char *rname,
90                  void (*fn)())
91 {
92   BOOL found=False;
93   int retries = 2;
94   int retry_time = 5000;
95   struct timeval tval;
96   struct packet_struct p;
97   struct packet_struct *p2;
98   struct nmb_packet *nmb = &p.packet.nmb;
99   static int name_trn_id = 0;
100
101   bzero((char *)&p,sizeof(p));
102
103   if (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + 
104     (getpid()%(unsigned)100);
105   name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
106
107   nmb->header.name_trn_id = name_trn_id;
108   nmb->header.opcode = 0;
109   nmb->header.response = False;
110   nmb->header.nm_flags.bcast = False;
111   nmb->header.nm_flags.recursion_available = False;
112   nmb->header.nm_flags.recursion_desired = False;
113   nmb->header.nm_flags.trunc = False;
114   nmb->header.nm_flags.authoritative = False;
115   nmb->header.rcode = 0;
116   nmb->header.qdcount = 1;
117   nmb->header.ancount = 0;
118   nmb->header.nscount = 0;
119   nmb->header.arcount = 0;
120
121   make_nmb_name(&nmb->question.question_name,name,name_type,scope);
122
123   nmb->question.question_type = 0x21;
124   nmb->question.question_class = 0x1;
125
126   p.ip = to_ip;
127   p.port = NMB_PORT;
128   p.fd = fd;
129   p.timestamp = time(NULL);
130   p.packet_type = NMB_PACKET;
131
132   GetTimeOfDay(&tval);
133
134   if (!send_packet(&p)) 
135     return(False);
136
137   retries--;
138
139   while (1)
140     {
141       struct timeval tval2;
142       GetTimeOfDay(&tval2);
143       if (TvalDiff(&tval,&tval2) > retry_time) {
144         if (!retries) break;
145         if (!found && !send_packet(&p))
146           return False;
147         GetTimeOfDay(&tval);
148         retries--;
149       }
150
151       if ((p2=receive_packet(fd,NMB_PACKET,90)))
152         {     
153           struct nmb_packet *nmb2 = &p2->packet.nmb;
154       debug_nmb_packet(p2);
155
156           if (nmb->header.name_trn_id != nmb2->header.name_trn_id ||
157               !nmb2->header.response) {
158             /* its not for us - maybe deal with it later */
159             if (fn) 
160               fn(p2);
161             else
162               free_packet(p2);
163             continue;
164           }
165           
166           if (nmb2->header.opcode != 0 ||
167               nmb2->header.nm_flags.bcast ||
168               nmb2->header.rcode ||
169               !nmb2->header.ancount ||
170               nmb2->answers->rr_type != 0x21) {
171             /* XXXX what do we do with this? could be a redirect, but
172                we'll discard it for the moment */
173             free_packet(p2);
174             continue;
175           }
176
177           _interpret_node_status(&nmb2->answers->rdata[0], master,rname);
178           free_packet(p2);
179           return(True);
180         }
181     }
182   
183
184   DEBUG(0,("No status response (this is not unusual)\n"));
185
186   return(False);
187 }
188
189
190 /****************************************************************************
191   do a netbios name query to find someones IP
192   returns an array of IP addresses or NULL if none
193   *count will be set to the number of addresses returned
194   ****************************************************************************/
195 struct in_addr *name_query(int fd,char *name,int name_type, 
196                            BOOL bcast,BOOL recurse,
197                            struct in_addr to_ip, int *count, void (*fn)())
198 {
199   BOOL found=False;
200   int i, retries = 3;
201   int retry_time = bcast?250:2000;
202   struct timeval tval;
203   struct packet_struct p;
204   struct packet_struct *p2;
205   struct nmb_packet *nmb = &p.packet.nmb;
206   static int name_trn_id = 0;
207   struct in_addr *ip_list = NULL;
208
209   bzero((char *)&p,sizeof(p));
210   (*count) = 0;
211
212   if (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + 
213     (getpid()%(unsigned)100);
214   name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
215
216   nmb->header.name_trn_id = name_trn_id;
217   nmb->header.opcode = 0;
218   nmb->header.response = False;
219   nmb->header.nm_flags.bcast = bcast;
220   nmb->header.nm_flags.recursion_available = False;
221   nmb->header.nm_flags.recursion_desired = recurse;
222   nmb->header.nm_flags.trunc = False;
223   nmb->header.nm_flags.authoritative = False;
224   nmb->header.rcode = 0;
225   nmb->header.qdcount = 1;
226   nmb->header.ancount = 0;
227   nmb->header.nscount = 0;
228   nmb->header.arcount = 0;
229
230   make_nmb_name(&nmb->question.question_name,name,name_type,scope);
231
232   nmb->question.question_type = 0x20;
233   nmb->question.question_class = 0x1;
234
235   p.ip = to_ip;
236   p.port = NMB_PORT;
237   p.fd = fd;
238   p.timestamp = time(NULL);
239   p.packet_type = NMB_PACKET;
240
241   GetTimeOfDay(&tval);
242
243   if (!send_packet(&p)) 
244     return NULL;
245
246   retries--;
247
248   while (1)
249     {
250       struct timeval tval2;
251       GetTimeOfDay(&tval2);
252       if (TvalDiff(&tval,&tval2) > retry_time) {
253         if (!retries) break;
254         if (!found && !send_packet(&p))
255           return NULL;
256         GetTimeOfDay(&tval);
257         retries--;
258       }
259
260       if ((p2=receive_packet(fd,NMB_PACKET,90)))
261         {     
262           struct nmb_packet *nmb2 = &p2->packet.nmb;
263           debug_nmb_packet(p2);
264
265           if (nmb->header.name_trn_id != nmb2->header.name_trn_id ||
266               !nmb2->header.response) {
267             /* its not for us - maybe deal with it later 
268                (put it on the queue?) */
269             if (fn) 
270               fn(p2);
271             else
272               free_packet(p2);
273             continue;
274           }
275           
276           if (nmb2->header.opcode != 0 ||
277               nmb2->header.nm_flags.bcast ||
278               nmb2->header.rcode ||
279               !nmb2->header.ancount) {
280             /* XXXX what do we do with this? could be a redirect, but
281                we'll discard it for the moment */
282             free_packet(p2);
283             continue;
284           }
285
286           ip_list = (struct in_addr *)Realloc(ip_list, sizeof(ip_list[0]) * 
287                                               ((*count)+nmb2->answers->rdlength/6));
288           if (ip_list) {
289                   DEBUG(fn?3:2,("Got a positive name query response from %s ( ",
290                                 inet_ntoa(p2->ip)));
291                   for (i=0;i<nmb2->answers->rdlength/6;i++) {
292                           putip((char *)&ip_list[(*count)],&nmb2->answers->rdata[2+i*6]);
293                           DEBUG(fn?3:2,("%s ",inet_ntoa(ip_list[(*count)])));
294                           (*count)++;
295                   }
296                   DEBUG(fn?3:2,(")\n"));
297           }
298           found=True; retries=0;
299           free_packet(p2);
300           if (fn) break;
301         }
302     }
303
304   return ip_list;
305 }