namequery.c: Fixed SGI IRIX 5.x compiler problem.
[tprouty/samba.git] / source / 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 /* nmbd.c sets this to True. */
29 BOOL global_in_nmbd = False;
30
31 /****************************************************************************
32 interpret a node status response
33 ****************************************************************************/
34 static void _interpret_node_status(char *p, char *master,char *rname)
35 {
36   int numnames = CVAL(p,0);
37   DEBUG(1,("received %d names\n",numnames));
38
39   if (rname) *rname = 0;
40   if (master) *master = 0;
41
42   p += 1;
43   while (numnames--)
44     {
45       char qname[17];
46       int type;
47       fstring flags;
48       int i;
49       *flags = 0;
50       StrnCpy(qname,p,15);
51       type = CVAL(p,15);
52       p += 16;
53
54       fstrcat(flags, (p[0] & 0x80) ? "<GROUP> " : "        ");
55       if ((p[0] & 0x60) == 0x00) fstrcat(flags,"B ");
56       if ((p[0] & 0x60) == 0x20) fstrcat(flags,"P ");
57       if ((p[0] & 0x60) == 0x40) fstrcat(flags,"M ");
58       if ((p[0] & 0x60) == 0x60) fstrcat(flags,"H ");
59       if (p[0] & 0x10) fstrcat(flags,"<DEREGISTERING> ");
60       if (p[0] & 0x08) fstrcat(flags,"<CONFLICT> ");
61       if (p[0] & 0x04) fstrcat(flags,"<ACTIVE> ");
62       if (p[0] & 0x02) fstrcat(flags,"<PERMANENT> ");
63
64       if (master && !*master && type == 0x1d) {
65         StrnCpy(master,qname,15);
66         trim_string(master,NULL," ");
67       }
68
69       if (rname && !*rname && type == 0x20 && !(p[0]&0x80)) {
70         StrnCpy(rname,qname,15);
71         trim_string(rname,NULL," ");
72       }
73       
74       for (i = strlen( qname) ; --i >= 0 ; ) {
75         if (!isprint(qname[i])) qname[i] = '.';
76       }
77       DEBUG(1,("\t%-15s <%02x> - %s\n",qname,type,flags));
78       p+=2;
79     }
80   DEBUG(1,("num_good_sends=%d num_good_receives=%d\n",
81                IVAL(p,20),IVAL(p,24)));
82 }
83
84
85 /****************************************************************************
86   do a netbios name status query on a host
87
88   the "master" parameter is a hack used for finding workgroups.
89   **************************************************************************/
90 BOOL name_status(int fd,char *name,int name_type,BOOL recurse,
91                  struct in_addr to_ip,char *master,char *rname,
92                  void (*fn)(struct packet_struct *))
93 {
94   BOOL found=False;
95   int retries = 2;
96   int retry_time = 5000;
97   struct timeval tval;
98   struct packet_struct p;
99   struct packet_struct *p2;
100   struct nmb_packet *nmb = &p.packet.nmb;
101   static int name_trn_id = 0;
102
103   bzero((char *)&p,sizeof(p));
104
105   if (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + 
106     (getpid()%(unsigned)100);
107   name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
108
109   nmb->header.name_trn_id = name_trn_id;
110   nmb->header.opcode = 0;
111   nmb->header.response = False;
112   nmb->header.nm_flags.bcast = False;
113   nmb->header.nm_flags.recursion_available = False;
114   nmb->header.nm_flags.recursion_desired = False;
115   nmb->header.nm_flags.trunc = False;
116   nmb->header.nm_flags.authoritative = False;
117   nmb->header.rcode = 0;
118   nmb->header.qdcount = 1;
119   nmb->header.ancount = 0;
120   nmb->header.nscount = 0;
121   nmb->header.arcount = 0;
122
123   make_nmb_name(&nmb->question.question_name,name,name_type,scope);
124
125   nmb->question.question_type = 0x21;
126   nmb->question.question_class = 0x1;
127
128   p.ip = to_ip;
129   p.port = NMB_PORT;
130   p.fd = fd;
131   p.timestamp = time(NULL);
132   p.packet_type = NMB_PACKET;
133
134   GetTimeOfDay(&tval);
135
136   if (!send_packet(&p)) 
137     return(False);
138
139   retries--;
140
141   while (1)
142     {
143       struct timeval tval2;
144       GetTimeOfDay(&tval2);
145       if (TvalDiff(&tval,&tval2) > retry_time) {
146         if (!retries) break;
147         if (!found && !send_packet(&p))
148           return False;
149         GetTimeOfDay(&tval);
150         retries--;
151       }
152
153       if ((p2=receive_packet(fd,NMB_PACKET,90)))
154         {     
155           struct nmb_packet *nmb2 = &p2->packet.nmb;
156       debug_nmb_packet(p2);
157
158           if (nmb->header.name_trn_id != nmb2->header.name_trn_id ||
159               !nmb2->header.response) {
160             /* its not for us - maybe deal with it later */
161             if (fn) 
162               fn(p2);
163             else
164               free_packet(p2);
165             continue;
166           }
167           
168           if (nmb2->header.opcode != 0 ||
169               nmb2->header.nm_flags.bcast ||
170               nmb2->header.rcode ||
171               !nmb2->header.ancount ||
172               nmb2->answers->rr_type != 0x21) {
173             /* XXXX what do we do with this? could be a redirect, but
174                we'll discard it for the moment */
175             free_packet(p2);
176             continue;
177           }
178
179           _interpret_node_status(&nmb2->answers->rdata[0], master,rname);
180           free_packet(p2);
181           return(True);
182         }
183     }
184   
185
186   DEBUG(0,("No status response (this is not unusual)\n"));
187
188   return(False);
189 }
190
191
192 /****************************************************************************
193   do a netbios name query to find someones IP
194   returns an array of IP addresses or NULL if none
195   *count will be set to the number of addresses returned
196   ****************************************************************************/
197 struct in_addr *name_query(int fd,char *name,int name_type, BOOL bcast,BOOL recurse,
198          struct in_addr to_ip, int *count, void (*fn)(struct packet_struct *))
199 {
200   BOOL found=False;
201   int i, retries = 3;
202   int retry_time = bcast?250:2000;
203   struct timeval tval;
204   struct packet_struct p;
205   struct packet_struct *p2;
206   struct nmb_packet *nmb = &p.packet.nmb;
207   static int name_trn_id = 0;
208   struct in_addr *ip_list = NULL;
209
210   bzero((char *)&p,sizeof(p));
211   (*count) = 0;
212
213   if (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + 
214     (getpid()%(unsigned)100);
215   name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
216
217   nmb->header.name_trn_id = name_trn_id;
218   nmb->header.opcode = 0;
219   nmb->header.response = False;
220   nmb->header.nm_flags.bcast = bcast;
221   nmb->header.nm_flags.recursion_available = False;
222   nmb->header.nm_flags.recursion_desired = recurse;
223   nmb->header.nm_flags.trunc = False;
224   nmb->header.nm_flags.authoritative = False;
225   nmb->header.rcode = 0;
226   nmb->header.qdcount = 1;
227   nmb->header.ancount = 0;
228   nmb->header.nscount = 0;
229   nmb->header.arcount = 0;
230
231   make_nmb_name(&nmb->question.question_name,name,name_type,scope);
232
233   nmb->question.question_type = 0x20;
234   nmb->question.question_class = 0x1;
235
236   p.ip = to_ip;
237   p.port = NMB_PORT;
238   p.fd = fd;
239   p.timestamp = time(NULL);
240   p.packet_type = NMB_PACKET;
241
242   GetTimeOfDay(&tval);
243
244   if (!send_packet(&p)) 
245     return NULL;
246
247   retries--;
248
249   while (1)
250     {
251       struct timeval tval2;
252       GetTimeOfDay(&tval2);
253       if (TvalDiff(&tval,&tval2) > retry_time) {
254         if (!retries) break;
255         if (!found && !send_packet(&p))
256           return NULL;
257         GetTimeOfDay(&tval);
258         retries--;
259       }
260
261       if ((p2=receive_packet(fd,NMB_PACKET,90)))
262         {     
263           struct nmb_packet *nmb2 = &p2->packet.nmb;
264           debug_nmb_packet(p2);
265
266           if (nmb->header.name_trn_id != nmb2->header.name_trn_id ||
267               !nmb2->header.response) {
268             /* its not for us - maybe deal with it later 
269                (put it on the queue?) */
270             if (fn) 
271               fn(p2);
272             else
273               free_packet(p2);
274             continue;
275           }
276           
277           if (nmb2->header.opcode != 0 ||
278               nmb2->header.nm_flags.bcast ||
279               nmb2->header.rcode ||
280               !nmb2->header.ancount) {
281             /* XXXX what do we do with this? could be a redirect, but
282                we'll discard it for the moment */
283             free_packet(p2);
284             continue;
285           }
286
287           ip_list = (struct in_addr *)Realloc(ip_list, sizeof(ip_list[0]) * 
288                                               ((*count)+nmb2->answers->rdlength/6));
289           if (ip_list) {
290                   DEBUG(fn?3:2,("Got a positive name query response from %s ( ",
291                                 inet_ntoa(p2->ip)));
292                   for (i=0;i<nmb2->answers->rdlength/6;i++) {
293                           putip((char *)&ip_list[(*count)],&nmb2->answers->rdata[2+i*6]);
294                           DEBUG(fn?3:2,("%s ",inet_ntoa(ip_list[(*count)])));
295                           (*count)++;
296                   }
297                   DEBUG(fn?3:2,(")\n"));
298           }
299           found=True; retries=0;
300           free_packet(p2);
301           if (fn) break;
302         }
303     }
304
305   return ip_list;
306 }
307
308 /********************************************************
309  Start parsing the lmhosts file.
310 *********************************************************/
311
312 FILE *startlmhosts(char *fname)
313 {
314   FILE *fp = fopen(fname,"r");
315   if (!fp) {
316     DEBUG(2,("startlmhosts: Can't open lmhosts file %s. Error was %s\n",
317              fname, strerror(errno)));
318     return NULL;
319   }
320   return fp;
321 }
322
323 /********************************************************
324  Parse the next line in the lmhosts file.
325 *********************************************************/
326
327 BOOL getlmhostsent( FILE *fp, char *name, int *name_type, struct in_addr *ipaddr)
328 {
329   pstring line;
330
331   while(!feof(fp) && !ferror(fp)) {
332     pstring ip,flags,extra;
333     char *ptr;
334     int count = 0;
335
336     *name_type = -1;
337
338     if (!fgets_slash(line,sizeof(pstring),fp))
339       continue;
340
341     if (*line == '#')
342       continue;
343
344     pstrcpy(ip,"");
345     pstrcpy(name,"");
346     pstrcpy(flags,"");
347
348     ptr = line;
349
350     if (next_token(&ptr,ip   ,NULL))
351       ++count;
352     if (next_token(&ptr,name ,NULL))
353       ++count;
354     if (next_token(&ptr,flags,NULL))
355       ++count;
356     if (next_token(&ptr,extra,NULL))
357       ++count;
358
359     if (count <= 0)
360       continue;
361
362     if (count > 0 && count < 2)
363     {
364       DEBUG(0,("getlmhostsent: Ill formed hosts line [%s]\n",line));
365       continue;
366     }
367
368     if (count >= 4)
369     {
370       DEBUG(0,("getlmhostsent: too many columns in lmhosts file (obsolete syntax)\n"));
371       continue;
372     }
373
374     DEBUG(4, ("getlmhostsent: lmhost entry: %s %s %s\n", ip, name, flags));
375
376     if (strchr(flags,'G') || strchr(flags,'S'))
377     {
378       DEBUG(0,("getlmhostsent: group flag in lmhosts ignored (obsolete)\n"));
379       continue;
380     }
381
382     *ipaddr = *interpret_addr2(ip);
383
384     /* Extra feature. If the name ends in '#XX', where XX is a hex number,
385        then only add that name type. */
386     if((ptr = strchr(name, '#')) != NULL)
387     {
388       char *endptr;
389
390       ptr++;
391       *name_type = (int)strtol(ptr, &endptr,0);
392
393       if(!*ptr || (endptr == ptr))
394       {
395         DEBUG(0,("getlmhostsent: invalid name %s containing '#'.\n", name));
396         continue;
397       }
398
399       *(--ptr) = '\0'; /* Truncate at the '#' */
400     }
401
402     return True;
403   }
404
405   return False;
406 }
407
408 /********************************************************
409  Finish parsing the lmhosts file.
410 *********************************************************/
411
412 void endlmhosts(FILE *fp)
413 {
414   fclose(fp);
415 }
416
417 /********************************************************
418  Resolve a name into an IP address. Use this function if
419  the string is either an IP address, DNS or host name
420  or NetBIOS name. This uses the name switch in the
421  smb.conf to determine the order of name resolution.
422 *********************************************************/
423
424 BOOL resolve_name(char *name, struct in_addr *return_ip)
425 {
426   int i;
427   BOOL pure_address = True;
428   pstring name_resolve_list;
429   fstring tok;
430   char *ptr;
431
432   if (strcmp(name,"0.0.0.0") == 0) {
433     return_ip->s_addr = 0;
434     return True;
435   }
436   if (strcmp(name,"255.255.255.255") == 0) {
437     return_ip->s_addr = 0xFFFFFFFF;
438     return True;
439   }
440    
441   for (i=0; pure_address && name[i]; i++)
442     if (!(isdigit(name[i]) || name[i] == '.'))
443       pure_address = False;
444    
445   /* if it's in the form of an IP address then get the lib to interpret it */
446   if (pure_address) {
447     return_ip->s_addr = inet_addr(name);
448     return True;
449   }
450
451   pstrcpy(name_resolve_list, lp_name_resolve_order());
452   ptr = name_resolve_list;
453   if (!ptr || !*ptr) ptr = "host";
454
455   while (next_token(&ptr, tok, LIST_SEP)) {
456     if(strequal(tok, "host") || strequal(tok, "hosts")) {
457
458       /*
459        * "host" means do a localhost, or dns lookup.
460        */
461
462       struct hostent *hp;
463
464       DEBUG(3,("resolve_name: Attempting host lookup for name %s\n", name));
465
466       if (((hp = Get_Hostbyname(name)) != NULL) && (hp->h_addr != NULL)) {
467         putip((char *)return_ip,(char *)hp->h_addr);
468         return True;
469       }
470
471     } else if(strequal( tok, "lmhosts")) {
472
473       /*
474        * "lmhosts" means parse the local lmhosts file.
475        */
476
477       FILE *fp;
478       pstring lmhost_name;
479       int name_type;
480
481       DEBUG(3,("resolve_name: Attempting lmhosts lookup for name %s\n", name));
482
483       fp = startlmhosts( LMHOSTSFILE );
484       if(fp) {
485         while( getlmhostsent(fp, lmhost_name, &name_type, return_ip ) ) {
486           if( strequal(name, lmhost_name )) {
487             endlmhosts(fp);
488             return True; 
489           }
490         }
491         endlmhosts(fp);
492       }
493
494     } else if(strequal( tok, "wins")) {
495
496       int sock;
497
498       /*
499        * "wins" means do a unicast lookup to the WINS server.
500        * Ignore if there is no WINS server specified or if the
501        * WINS server is one of our interfaces (if we're being
502        * called from within nmbd - we can't do this call as we
503        * would then block).
504        */
505
506       DEBUG(3,("resolve_name: Attempting wins lookup for name %s<0x20>\n", name));
507
508       if(*lp_wins_server()) {
509         struct in_addr wins_ip = *interpret_addr2(lp_wins_server());
510         BOOL wins_ismyip = ismyip(wins_ip);
511
512         if((wins_ismyip && !global_in_nmbd) || !wins_ismyip) {
513           sock = open_socket_in( SOCK_DGRAM, 0, 3,
514                                interpret_addr(lp_socket_address()) );
515
516           if (sock != -1) {
517             struct in_addr *iplist = NULL;
518             int count;
519             iplist = name_query(sock, name, 0x20, False, True, wins_ip, &count, NULL);
520             if(iplist != NULL) {
521               *return_ip = iplist[0];
522               free((char *)iplist);
523               close(sock);
524               return True;
525             }
526             close(sock);
527           }
528         }
529       } else {
530         DEBUG(3,("resolve_name: WINS server resolution selected and no WINS server present.\n"));
531       }
532     } else if(strequal( tok, "bcast")) {
533
534       int sock;
535
536       /*
537        * "bcast" means do a broadcast lookup on all the local interfaces.
538        */
539
540       DEBUG(3,("resolve_name: Attempting broadcast lookup for name %s<0x20>\n", name));
541
542       sock = open_socket_in( SOCK_DGRAM, 0, 3,
543                              interpret_addr(lp_socket_address()) );
544
545       if (sock != -1) {
546         struct in_addr *iplist = NULL;
547         int count;
548         int num_interfaces = iface_count();
549         set_socket_options(sock,"SO_BROADCAST");
550         /*
551          * Lookup the name on all the interfaces, return on
552          * the first successful match.
553          */
554         for( i = 0; i < num_interfaces; i++) {
555           struct in_addr sendto_ip;
556           /* Done this way to fix compiler error on IRIX 5.x */
557           sendto_ip = *iface_bcast(*iface_n_ip(i));
558           iplist = name_query(sock, name, 0x20, True, False, sendto_ip, &count, NULL);
559           if(iplist != NULL) {
560             *return_ip = iplist[0];
561             free((char *)iplist);
562             close(sock);
563             return True;
564           }
565         }
566         close(sock);
567       }
568
569     } else {
570       DEBUG(0,("resolve_name: unknown name switch type %s\n", tok));
571     }
572   }
573
574   return False;
575 }