lots of changes to nmbd
[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-1995
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 level = (master||rname)?4:0;
35   int numnames = CVAL(p,0);
36   DEBUG(level,("received %d names\n",numnames));
37
38   if (rname) *rname = 0;
39   if (master) *master = 0;
40
41   p += 1;
42   while (numnames--)
43     {
44       char qname[17];
45       int type;
46       fstring flags;
47       int i;
48       *flags = 0;
49       StrnCpy(qname,p,15);
50       type = CVAL(p,15);
51       p += 16;
52
53       strcat(flags, (p[0] & 0x80) ? "<GROUP> " : "        ");
54       if ((p[0] & 0x60) == 0x00) strcat(flags,"B ");
55       if ((p[0] & 0x60) == 0x20) strcat(flags,"P ");
56       if ((p[0] & 0x60) == 0x40) strcat(flags,"M ");
57       if ((p[0] & 0x60) == 0x60) strcat(flags,"_ ");
58       if (p[0] & 0x10) strcat(flags,"<DEREGISTERING> ");
59       if (p[0] & 0x08) strcat(flags,"<CONFLICT> ");
60       if (p[0] & 0x04) strcat(flags,"<ACTIVE> ");
61       if (p[0] & 0x02) strcat(flags,"<PERMANENT> ");
62
63       if (master && !*master && type == 0x1d) {
64         StrnCpy(master,qname,15);
65         trim_string(master,NULL," ");
66       }
67
68       if (rname && !*rname && type == 0x20 && !(p[0]&0x80)) {
69         StrnCpy(rname,qname,15);
70         trim_string(rname,NULL," ");
71       }
72       
73       for (i = strlen( qname) ; --i >= 0 ; ) {
74         if (!isprint(qname[i])) qname[i] = '.';
75       }
76       DEBUG(level,("\t%-15s <%02x> - %s\n",qname,type,flags));
77       p+=2;
78     }
79   DEBUG(level,("num_good_sends=%d num_good_receives=%d\n",
80                IVAL(p,20),IVAL(p,24)));
81 }
82
83
84 /****************************************************************************
85   do a netbios name status query on a host
86
87   the "master" parameter is a hack used for finding workgroups.
88   **************************************************************************/
89 BOOL name_status(int fd,char *name,int name_type,BOOL recurse,
90                  struct in_addr to_ip,char *master,char *rname,
91                  void (*fn)())
92 {
93   BOOL found=False;
94   int retries = 2;
95   int retry_time = 5000;
96   struct timeval tval;
97   struct packet_struct p;
98   struct packet_struct *p2;
99   struct nmb_packet *nmb = &p.packet.nmb;
100   static int name_trn_id = 0;
101
102   bzero((char *)&p,sizeof(p));
103
104   if (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + 
105     (getpid()%(unsigned)100);
106   name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
107
108   nmb->header.name_trn_id = name_trn_id;
109   nmb->header.opcode = 0;
110   nmb->header.response = False;
111   nmb->header.nm_flags.bcast = False;
112   nmb->header.nm_flags.recursion_available = 0;
113   nmb->header.nm_flags.recursion_desired = 1;
114   nmb->header.nm_flags.trunc = False;
115   nmb->header.nm_flags.authoritative = False;
116   nmb->header.rcode = 0;
117   nmb->header.qdcount = 1;
118   nmb->header.ancount = 0;
119   nmb->header.nscount = 0;
120   nmb->header.arcount = 0;
121
122   make_nmb_name(&nmb->question.question_name,name,name_type,scope);
123
124   nmb->question.question_type = 0x21;
125   nmb->question.question_class = 0x1;
126
127   p.ip = to_ip;
128   p.port = NMB_PORT;
129   p.fd = fd;
130   p.timestamp = time(NULL);
131   p.packet_type = NMB_PACKET;
132
133   GetTimeOfDay(&tval);
134
135   if (!send_packet(&p)) 
136     return(False);
137
138   retries--;
139
140   while (1)
141     {
142       struct timeval tval2;
143       GetTimeOfDay(&tval2);
144       if (TvalDiff(&tval,&tval2) > retry_time) {
145         if (!retries) break;
146         if (!found && !send_packet(&p))
147           return False;
148         GetTimeOfDay(&tval);
149         retries--;
150       }
151
152       if ((p2=receive_packet(fd,NMB_PACKET,90)))
153         {     
154           struct nmb_packet *nmb2 = &p2->packet.nmb;
155           if (nmb->header.name_trn_id != nmb2->header.name_trn_id ||
156               !nmb2->header.response) {
157             /* its not for us - maybe deal with it later */
158             if (fn) 
159               fn(p2);
160             else
161               free_packet(p2);
162             continue;
163           }
164           
165           if (nmb2->header.opcode != 0 ||
166               nmb2->header.nm_flags.bcast ||
167               nmb2->header.rcode ||
168               !nmb2->header.ancount ||
169               nmb2->answers->rr_type != 0x21) {
170             /* XXXX what do we do with this? could be a redirect, but
171                we'll discard it for the moment */
172             free_packet(p2);
173             continue;
174           }
175
176       debug_nmb_packet(p2);
177
178           _interpret_node_status(&nmb2->answers->rdata[0], master,rname);
179           free_packet(p2);
180           return(True);
181         }
182     }
183   
184
185   DEBUG(0,("No status response (this is not unusual)\n"));
186
187   return(False);
188 }
189
190
191 /****************************************************************************
192   do a netbios name query to find someones IP
193   ****************************************************************************/
194 BOOL name_query(int fd,char *name,int name_type, 
195                 BOOL bcast,BOOL recurse,
196                 struct in_addr to_ip, struct in_addr *ip,void (*fn)())
197 {
198   BOOL found=False;
199   int retries = 3;
200   int retry_time = bcast?250:2000;
201   struct timeval tval;
202   struct packet_struct p;
203   struct packet_struct *p2;
204   struct nmb_packet *nmb = &p.packet.nmb;
205   static int name_trn_id = 0;
206
207   bzero((char *)&p,sizeof(p));
208
209   if (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + 
210     (getpid()%(unsigned)100);
211   name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
212
213   nmb->header.name_trn_id = name_trn_id;
214   nmb->header.opcode = 0;
215   nmb->header.response = False;
216   nmb->header.nm_flags.bcast = bcast;
217   nmb->header.nm_flags.recursion_available = 0;
218   nmb->header.nm_flags.recursion_desired = 1;
219   nmb->header.nm_flags.trunc = False;
220   nmb->header.nm_flags.authoritative = False;
221   nmb->header.rcode = 0;
222   nmb->header.qdcount = 1;
223   nmb->header.ancount = 0;
224   nmb->header.nscount = 0;
225   nmb->header.arcount = 0;
226
227   make_nmb_name(&nmb->question.question_name,name,name_type,scope);
228
229   nmb->question.question_type = 0x20;
230   nmb->question.question_class = 0x1;
231
232   p.ip = to_ip;
233   p.port = NMB_PORT;
234   p.fd = fd;
235   p.timestamp = time(NULL);
236   p.packet_type = NMB_PACKET;
237
238   GetTimeOfDay(&tval);
239
240   if (!send_packet(&p)) 
241     return(False);
242
243   retries--;
244
245   while (1)
246     {
247       struct timeval tval2;
248       GetTimeOfDay(&tval2);
249       if (TvalDiff(&tval,&tval2) > retry_time) {
250         if (!retries) break;
251         if (!found && !send_packet(&p))
252           return False;
253         GetTimeOfDay(&tval);
254         retries--;
255       }
256
257       if ((p2=receive_packet(fd,NMB_PACKET,90)))
258         {     
259           struct nmb_packet *nmb2 = &p2->packet.nmb;
260           if (nmb->header.name_trn_id != nmb2->header.name_trn_id ||
261               !nmb2->header.response) {
262             /* its not for us - maybe deal with it later 
263                (put it on the queue?) */
264             if (fn) 
265               fn(p2);
266             else
267               free_packet(p2);
268             continue;
269           }
270           
271       debug_nmb_packet(p2);
272
273           if (nmb2->header.opcode != 0 ||
274               nmb2->header.nm_flags.bcast ||
275               nmb2->header.rcode ||
276               !nmb2->header.ancount) {
277             /* XXXX what do we do with this? could be a redirect, but
278                we'll discard it for the moment */
279             free_packet(p2);
280             continue;
281           }
282
283           if (ip) {
284             putip((char *)ip,&nmb2->answers->rdata[2]);
285             DEBUG(fn?3:2,("Got a positive name query response from %s",
286                           inet_ntoa(p2->ip)));
287             DEBUG(fn?3:2,(" (%s)\n",inet_ntoa(*ip)));
288           }
289           found=True; retries=0;
290           free_packet(p2);
291           if (fn) break;
292         }
293     }
294
295   return(found);
296 }