Permit DNS SRV record lookups to be used by the Windows afsconf_GetAfsdbInfo
[openafs.git] / src / WINNT / afsd / cm_dns.c
1 /* Copyright 2000, International Business Machines Corporation and others.
2  * All Rights Reserved.
3  *
4  * This software has been released under the terms of the IBM Public
5  * License.  For details, see the LICENSE file in the top-level source
6  * directory or online at http://www.openafs.org/dl/license10.html
7  */
8
9 #ifdef AFS_AFSDB_ENV
10
11 #include <afs/param.h>
12 #include <afs/stds.h>
13 #include <afs/cellconfig.h>
14 #include <windows.h>
15 #include <winsock2.h>
16 #include "cm_dns_private.h"
17 #include "cm_nls.h"
18 #include "cm_dns.h"
19 #include <lwp.h>
20 #include <afs/afsint.h>
21 #if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0500)
22 #include <windns.h>
23 #define DNSAPI_ENV
24 #endif
25 #include <errno.h>
26 #include <strsafe.h>
27
28 /*extern void afsi_log(char *pattern, ...);*/
29
30 static char dns_addr[30];
31 static int cm_dnsEnabled = -1;
32
33 void DNSlowerCase(char *str)
34 {
35     unsigned int i;
36
37     for (i=0; i<strlen(str); i++)
38         /*str[i] = tolower(str[i]);*/
39         if (str[i] >= 'A' && str[i] <= 'Z')
40             str[i] += 'a' - 'A';
41 }
42
43 int cm_InitDNS(int enabled)
44 {
45 #ifndef DNSAPI_ENV
46     char configpath[100];
47     int len;
48     int code;
49     char *addr;
50   
51     if (!enabled) { 
52         fprintf(stderr, "DNS support disabled\n"); 
53         cm_dnsEnabled = 0; 
54         return 0; 
55     }
56
57     /* First try AFS_NS environment var. */
58     addr = getenv("AFS_NS");
59     if (addr && inet_addr(addr) != -1) {
60         strcpy(dns_addr, addr);
61     } else {
62         /* Now check for the AFSDNS.INI file */
63         code = GetWindowsDirectory(configpath, sizeof(configpath));
64         if (code == 0 || code > sizeof(configpath)) return -1;
65         strcat(configpath, "\\afsdns.ini");
66
67         /* Currently we only get (and query) the first nameserver.  Getting
68         list of mult. nameservers should be easy to do. */
69         len = GetPrivateProfileString("AFS Domain Name Servers", "ns1", NULL,
70                                        dns_addr, sizeof(dns_addr),
71                                        configpath);
72   
73         if (len == 0 || inet_addr(dns_addr) == -1) {
74             fprintf(stderr, "No valid name server addresses found, DNS lookup is "
75                      "disabled\n");
76             cm_dnsEnabled = 0;  /* failed */
77             return -1;     /* No name servers defined */
78         }
79         else 
80             fprintf(stderr, "Found DNS server %s\n", dns_addr);
81     }
82 #endif /* DNSAPI_ENV */
83     cm_dnsEnabled = 1;
84     return 0;
85 }
86
87 #ifndef DNSAPI_ENV
88 SOCKADDR_IN setSockAddr(char *server, int port)
89 {
90   SOCKADDR_IN sockAddr;                     
91   int         addrLen = sizeof(SOCKADDR_IN);
92
93 #ifndef WIN32_LEAN_AND_MEAN
94   bzero(&sockAddr,addrLen);
95 #endif /*WIN32_LEAN_AND_MEAN*/
96   sockAddr.sin_family   = AF_INET;
97   sockAddr.sin_port     = htons( port );
98   sockAddr.sin_addr.s_addr = inet_addr( server );
99   /*inet_aton(server, &sockAddr.sin_addr.s_addr);*/
100
101   return (sockAddr);
102 }
103
104 int getRRCount(PDNS_HDR ptr)
105 {
106   return(ntohs(ptr->rr_count));
107 }
108
109
110 int send_DNS_Addr_Query(char* query, 
111                          SOCKET commSock, SOCKADDR_IN sockAddr, char *buffer)
112 {
113   PDNS_HDR    pDNShdr;
114   PDNS_QTAIL  pDNS_qtail;
115
116   int     queryLen = 0;
117   int     res;
118
119 #ifndef WIN32_LEAN_AND_MEAN
120   bzero(buffer,BUFSIZE);
121 #endif /*WIN32_LEAN_AND_MEAN*/
122
123   /*********************************
124    * Build DNS Query Message       *
125    *                               *
126    * hard-coded Adrress (A) query  *
127    *********************************/
128   
129   pDNShdr = (PDNS_HDR)&( buffer[ 0 ] );
130   pDNShdr->id         = htons( 0xDADE );
131   pDNShdr->flags      = htons( DNS_FLAG_RD ); /* do recurse */
132   pDNShdr->q_count    = htons( 1 );           /* one query */
133   pDNShdr->rr_count   = 0;                    /* none in query */
134   pDNShdr->auth_count = 0;                    /* none in query */
135   pDNShdr->add_count  = 0;                    /* none in query */
136   
137   queryLen = putQName( query, &(buffer[ DNS_HDR_LEN ] ) );
138   queryLen += DNS_HDR_LEN; /* query Length is just after the query name and header */
139 #ifdef DEBUG
140   fprintf(stderr, "send_DNS_Addr: query=%s, queryLen=%d\n", query, queryLen);
141 #endif
142   
143   
144   pDNS_qtail = (PDNS_QTAIL) &(buffer[ queryLen ]);
145   pDNS_qtail->qtype = htons(255);/*htons(DNS_RRTYPE_A); */
146   pDNS_qtail->qclass = htons(DNS_RRCLASS_IN); 
147   queryLen +=  DNS_QTAIL_LEN;
148   
149   /**************************
150    * Send DNS Query Message *
151    **************************/
152   
153
154   res = sendto( commSock,
155                 buffer,
156                 queryLen,
157                 0,
158                 (struct sockaddr *) &sockAddr,
159                 sizeof( SOCKADDR_IN ) );
160   
161   /*dumpSbuffer(buffer,queryLen);*/
162
163   if ( res < 0 )
164     {
165 #ifdef DEBUG
166       fprintf(stderr, "send_DNS_Addr_Query: error %d, errno %d\n", res, errno);
167       fprintf(stderr, "sendto() failed \n");
168 #endif
169       return ( -1 );
170     }
171   else
172     {
173     /*printf( "sendto() succeeded\n");*/
174     ;
175     } /* end if */
176   
177   return(0);
178 }
179
180
181 int send_DNS_AFSDB_Query(char* query, 
182                          SOCKET commSock, SOCKADDR_IN sockAddr, char *buffer)
183 {
184   /*static char buffer[BUFSIZE];*/
185
186   PDNS_HDR    pDNShdr;
187   PDNS_QTAIL  pDNS_qtail;
188
189   int     queryLen = 0;
190   int     res;
191
192 #ifndef WIN32_LEAN_AND_MEAN
193   bzero(buffer,BUFSIZE);
194 #endif /*WIN32_LEAN_AND_MEAN*/
195
196   /***************************
197    * Build DNS Query Message *
198    *                         *
199    * hard-coded AFSDB query  *
200    ***************************/
201   
202   pDNShdr = (PDNS_HDR)&( buffer[ 0 ] );
203   pDNShdr->id         = htons( 0xDEAD );
204   pDNShdr->flags      = htons( DNS_FLAG_RD ); /* do recurse */
205   pDNShdr->q_count    = htons( 1 );           /* one query */
206   pDNShdr->rr_count   = 0;                    /* none in query */
207   pDNShdr->auth_count = 0;                    /* none in query */
208   pDNShdr->add_count  = 0;                    /* none in query */
209   
210   queryLen = putQName( query, &(buffer[ DNS_HDR_LEN ] ) );
211   queryLen += DNS_HDR_LEN; /* query Length is just after the query name and header */
212   
213   
214   pDNS_qtail = (PDNS_QTAIL) &(buffer[ queryLen ]);
215   pDNS_qtail->qtype = htons(DNS_RRTYPE_AFSDB); 
216   pDNS_qtail->qclass = htons(DNS_RRCLASS_IN); 
217   queryLen +=  DNS_QTAIL_LEN;
218   
219   /**************************
220    * Send DNS Query Message *
221    **************************/
222   
223   res = sendto( commSock,
224                 buffer,
225                 queryLen,
226                 0,
227                 (struct sockaddr *) &sockAddr,
228                 sizeof( SOCKADDR_IN ) );
229   
230   /*dumpSbuffer(buffer,queryLen);*/
231
232   if ( res < 0 )
233     {
234 #ifdef DEBUG
235       fprintf(stderr, "send_DNS_AFSDB_Query: error %d, errno %d\n", res, errno);
236       fprintf(stderr,  "sendto() failed \n");
237 #endif /* DEBUG */
238       return ( -1 );
239     }
240   else
241     {
242     /*printf( "sendto() succeeded\n");*/
243     ;
244     } /* end if */
245   
246   return(0);
247 }
248
249
250 PDNS_HDR get_DNS_Response(SOCKET commSock, SOCKADDR_IN sockAddr, char *buffer)
251 {
252   /*static char buffer[BUFSIZE];*/
253
254   int         addrLen = sizeof(SOCKADDR_IN);
255   int size;
256
257 #ifndef WIN32_LEAN_AND_MEAN
258   bzero(buffer,BUFSIZE);
259 #endif /*WIN32_LEAN_AND_MEAN*/
260
261   /*****************************
262    * Receive DNS Reply Message *
263    *****************************/
264   
265   /*printf( "calling recvfrom() on connected UDP socket\n" );*/
266   
267   size = recvfrom( commSock,
268                   buffer,
269                   BUFSIZE,
270                   0,
271                   (struct sockaddr *) &sockAddr,
272                   &addrLen );
273   if (size < 0) { fprintf(stderr, "recvfrom error %d\n", errno); return NULL; }
274
275   /*dumpRbuffer(buffer,res);*/
276
277 #ifdef DEBUG
278   fprintf(stderr, "recvfrom returned %d bytes from %s: \n", 
279           size, inet_ntoa( sockAddr.sin_addr ) );
280 #endif /* DEBUG */
281   
282   return((PDNS_HDR)&( buffer[ 0 ] ));
283
284 }
285
286
287 int putQName( char *pHostName, char *pQName )
288 {
289   int     i;
290   char    c;
291   int     j = 0;
292   int     k = 0;
293   
294   DNSlowerCase(pHostName);
295   /*printf( "Hostname: [%s]\n", pHostName );*/
296   
297   for ( i = 0; *( pHostName + i ); i++ )
298     {
299       c = *( pHostName + i );   /* get next character */
300       
301       
302       if ( c == '.' )
303         {
304           /* dot encountered, fill in previous length */
305           if (k!=0){ /*don't process repeated dots*/
306           /*printf( "%c", c );*/
307             *( pQName + j ) = k;
308             j = j+k+1;  /* set index to next counter */
309             k = 0;      /* reset segment length */
310           }
311         }
312       else
313         {
314         /*printf( "%c", c );*/
315           *( pQName + j + k + 1 ) = c;  /* assign to QName */
316           k++;                /* inc count of seg chars */
317         } /* end if */
318     } /* end for loop */
319   
320   *(pQName + j )                  = k;   /* count for final segment */
321
322   *(pQName + j + k + 1 )      = 0;   /* count for trailing NULL segment is 0 */
323   
324   /*printf( "\n" ); */
325   
326   if (c == '.')
327     return ( j + k + 1 );        /* return total length of QName */
328   else
329     return ( j + k + 2 );
330 } /* end putQName() */
331
332
333 u_char * skipRRQName(u_char *pQName)
334 {
335   u_char *ptr;
336   u_char c;
337
338   ptr = pQName;
339   c = *ptr;
340   while (c) {
341     if ( c >= 0xC0 ) {
342     /* skip the 'compression' pointer */
343       ptr = ptr+1;
344       c = '\0';
345     } else {
346       /* skip a normal qname segment */
347       ptr += *ptr;
348       ptr++;
349       c = *ptr;
350     };
351   };
352
353   /* ptr now pointing at terminating zero of query QName,
354      or the pointer for the previous occurrence 
355      (compression)
356    */
357   ptr++;
358
359   return (ptr);
360 } /* end skipRRQName() */
361
362
363
364 u_char * printRRQName( u_char *pQName, PDNS_HDR buffer )
365 {
366   u_short i, k;
367   u_char *buffPtr = (u_char *) buffer;
368   u_char *namePtr;
369   u_char *retPtr;
370   u_char c;
371
372
373   namePtr = pQName;
374   retPtr = 0;
375
376   for ( i = 0; i < BUFSIZE; i++ )
377     {
378       c = *namePtr;
379       if ( c >= 0xC0 ) {
380         c = *(namePtr + 1);
381         retPtr = namePtr+2;
382         namePtr = buffPtr+c; 
383       } else {
384         if ( c == 0 )
385           break;
386         
387         for ( k = 1; k <= c; k++ )
388           {
389             fprintf(stderr, "%c", *( namePtr + k ) );
390           } /* end for loop */
391         fprintf(stderr,".");
392         namePtr += k;
393       }
394     } /* end for loop */
395   fprintf(stderr,"\n");
396   namePtr++; /* skip terminating zero */
397
398   if (retPtr)
399     return(retPtr);
400   else
401     return(namePtr);
402
403 } /* end printRRQName() */
404
405
406 u_char * sPrintRRQName( u_char *pQName, PDNS_HDR buffer, char *str )
407 {
408   u_short i, k;
409   u_char *buffPtr = (u_char *) buffer;
410   u_char *namePtr;
411   u_char *retPtr;
412   u_char c;
413
414   char   section[64];
415
416   strcpy(str,"");
417   namePtr = pQName;
418   retPtr = 0;
419
420   for ( i = 0; i < BUFSIZE; i++ )
421     {
422       c = *namePtr;
423       if ( c >= 0xC0 ) {
424         c = *(namePtr + 1);
425         retPtr = namePtr+2;
426         namePtr = buffPtr+c; 
427       } else {
428         if ( c == 0 )
429           break;
430         
431         for ( k = 1; k <= c; k++ )
432           {
433             sprintf(section,"%c", *( namePtr + k ) );
434             strcat(str,section);
435           } /* end for loop */
436         strcat(str,".");
437         namePtr += k;
438       }
439     } /* end for loop */
440   namePtr++; /* skip terminating zero */
441
442   if (retPtr)
443     return(retPtr);
444   else
445     return(namePtr);
446
447 } /* end sPrintRRQName() */
448
449
450 void printReplyBuffer_AFSDB(PDNS_HDR replyBuff)
451 {
452   u_char *ptr = (u_char *) replyBuff;
453   int    answerCount = ntohs((replyBuff)->rr_count);
454   u_char i;
455   PDNS_AFSDB_RR_HDR 
456          rrPtr;
457
458   ptr += DNS_HDR_LEN;
459
460   /* ptr now pointing at start of QName in query field */
461   ptr = skipRRQName(ptr);
462
463
464   /* skip the query type and class fields */
465   ptr+= DNS_QTAIL_LEN;
466
467   /* ptr should now be at the start of the answer RR sections */
468
469   fprintf(stderr,"---------------------------------\n");
470   for (i=0; i<answerCount ; i++){
471     ptr = skipRRQName(ptr);
472     rrPtr = (PDNS_AFSDB_RR_HDR) ptr;
473     ptr+= DNS_AFSDB_RR_HDR_LEN;
474     if ( ntohs(rrPtr->rr_afsdb_class) == 1) {
475       fprintf(stderr,"AFDB class %d ->  ",ntohs(rrPtr->rr_afsdb_class)); 
476       ptr = printRRQName(ptr,replyBuff); }
477     else
478       ptr = skipRRQName(ptr);
479   };
480   fprintf(stderr,"---------------------------------\n");
481
482
483 };
484
485 void processReplyBuffer_AFSDB(SOCKET commSock, PDNS_HDR replyBuff, int *cellHostAddrs, char cellHostNames[][MAXHOSTCHARS], 
486                               unsigned short ports[], unsigned short ipRanks[], int *numServers, int *ttl)
487   /*PAFS_SRV_LIST (srvList)*/
488 {
489   u_char *ptr = (u_char *) replyBuff;
490   int    answerCount = ntohs((replyBuff)->rr_count);
491   u_char i;
492   PDNS_AFSDB_RR_HDR 
493          rrPtr;
494   int srvCount = 0;
495   char hostName[256];
496   struct in_addr addr;
497   int rc;
498
499   ptr += DNS_HDR_LEN;
500
501   /* ptr now pointing at start of QName in query field */
502   ptr = skipRRQName(ptr);
503
504
505   /* skip the query type and class fields */
506   ptr+= DNS_QTAIL_LEN;
507
508   /* ptr should now be at the start of the answer RR sections */
509
510   answerCount = MIN(answerCount, AFSMAXCELLHOSTS);
511 #ifdef DEBUG
512   fprintf(stderr, "processRep_AFSDB: answerCount=%d\n", answerCount);
513 #endif /* DEBUG */
514
515   for (i=0; i<answerCount ; i++){
516     ptr = skipRRQName(ptr);
517     rrPtr = (PDNS_AFSDB_RR_HDR) ptr;
518     ptr+= DNS_AFSDB_RR_HDR_LEN;
519     if ((ntohs(rrPtr->rr_afsdb_class) == 1) && 
520         (srvCount < MAX_AFS_SRVS)) {
521       /*ptr = sPrintRRQName(ptr,replyBuff,srvList->host[srvList->count]);*/
522       ptr = sPrintRRQName(ptr,replyBuff,hostName);
523       /*ptr = printRRQName(ptr,replyBuff);*/
524       *ttl = ntohl(rrPtr->rr_ttl);
525
526 #ifdef DEBUG
527       fprintf(stderr, "resolving name %s\n", hostName);
528 #endif
529       /* resolve name from DNS query */
530       rc = DNSgetAddr(commSock, hostName, &addr);
531       if (rc < 0)
532         continue;  /* skip this entry */
533 #ifdef DEBUG
534       fprintf(stderr, "processRep_AFSDB: resolved name %s to addr %x\n", hostName, addr);
535 #endif /* DEBUG */
536       memcpy(&cellHostAddrs[srvCount], &addr.s_addr, sizeof(addr.s_addr));
537           strncpy(cellHostNames[srvCount], hostName, CELL_MAXNAMELEN);
538           cellHostNames[srvCount][CELL_MAXNAMELEN-1] = '\0';
539       ipRanks[srvCount] = 0;
540       ports[srvCount] = htons(7003);
541       srvCount++;
542     }
543     else {
544       ptr = skipRRQName(ptr);
545     }
546   }
547
548   *numServers = srvCount;
549
550 }
551
552
553 u_char * processReplyBuffer_Addr(PDNS_HDR replyBuff)
554 {
555   u_char *ptr = (u_char *) replyBuff;
556   int    answerCount = ntohs((replyBuff)->rr_count);
557   PDNS_A_RR_HDR 
558          rrPtr;
559
560 #ifdef DEBUG
561   fprintf(stderr, "processReplyBuffer_Addr: answerCount=%d\n", answerCount);
562 #endif /* DEBUG */
563   if (answerCount == 0) return 0;
564   
565   ptr += DNS_HDR_LEN;
566
567   /* ptr now pointing at start of QName in query field */
568   ptr = skipRRQName(ptr);
569
570
571   /* skip the query type and class fields */
572   ptr+= DNS_QTAIL_LEN;
573
574   /* ptr should now be at the start of the answer RR sections */
575   ptr = skipRRQName(ptr);
576   rrPtr = (PDNS_A_RR_HDR) ptr;
577
578 #ifdef DEBUG
579   fprintf(stderr, "type:%d, class:%d, ttl:%d, rdlength:%d\n",
580          ntohs(rrPtr->rr_type),ntohs(rrPtr->rr_class),
581          ntohl(rrPtr->rr_ttl),ntohs(rrPtr->rr_rdlength));
582   fprintf(stderr, "Count %d\tand Answer %8x\n",answerCount,rrPtr->rr_addr);
583 #endif /* DEBUG */
584
585   ptr += DNS_A_RR_HDR_LEN;
586
587   return (ptr);
588
589 };
590
591 int DNSgetAddr(SOCKET commSock, char *hostName, struct in_addr *iNet)
592 {
593   /* Variables for DNS message parsing and creation */
594   PDNS_HDR  pDNShdr;
595
596   SOCKADDR_IN sockAddr;
597   char buffer[BUFSIZE];
598   u_char *addr;
599   u_long *aPtr;
600   int rc;
601
602   /**********************
603    * Get a DGRAM socket *
604    **********************/
605   
606   sockAddr = setSockAddr(dns_addr, DNS_PORT);
607   
608   rc = send_DNS_Addr_Query(hostName,commSock,sockAddr, buffer);
609   if (rc < 0) return rc;
610   pDNShdr = get_DNS_Response(commSock,sockAddr, buffer);
611   if (pDNShdr == NULL)
612     return -1;
613   
614   addr = processReplyBuffer_Addr(pDNShdr);
615   if (addr == 0)
616     return -1;
617
618   aPtr = (u_long *) addr;
619
620   iNet->s_addr = *aPtr;
621
622   return(0);
623 }
624 #endif /* DNSAPI_ENV */
625
626 int getAFSServer(const char *service, const char *protocol, const char *cellName,
627                  unsigned short afsdbPort,
628                  int *cellHostAddrs, char cellHostNames[][MAXHOSTCHARS],
629                  unsigned short ports[], unsigned short ipRanks[],
630                  int *numServers, int *ttl)
631 {
632 #ifndef DNSAPI_ENV
633     SOCKET commSock;
634     SOCKADDR_IN sockAddr;
635     PDNS_HDR  pDNShdr;
636     char buffer[BUFSIZE];
637     char query[1024];
638     int rc;
639
640 #ifdef DEBUG
641     fprintf(stderr, "getAFSServer: cell %s, cm_dnsEnabled=%d\n", cellName, cm_dnsEnabled);
642 #endif
643
644     *numServers = 0;
645     *ttl = 0;
646
647 #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
648     if (cm_dnsEnabled == -1) { /* not yet initialized, eg when called by klog */
649         cm_InitDNS(1);         /* assume enabled */
650     }
651 #endif
652     if (cm_dnsEnabled == 0) {  /* possibly we failed in cm_InitDNS above */
653         fprintf(stderr, "DNS initialization failed, disabled\n");
654         return -1;
655     }
656   
657     if (service == NULL || protocol == NULL || cellName == NULL) {
658         fprintf(stderr, "invalid input\n");
659         return -1;
660     }
661
662     sockAddr = setSockAddr(dns_addr, DNS_PORT);
663   
664     commSock = socket( AF_INET, SOCK_DGRAM, 0 );
665     if ( commSock < 0 )
666     {
667         /*afsi_log("socket() failed\n");*/
668         fprintf(stderr, "getAFSServer: socket() failed, errno=%d\n", errno);
669         return (-1);
670     } 
671
672     StringCbCopyA(query, sizeof(query), cellName);
673     if (query[strlen(query)-1] != '.') {
674         StringCbCatA(query, sizeof(query), ".");
675     }
676
677     rc = send_DNS_AFSDB_Query(cellName,commSock,sockAddr, buffer);
678     if (rc < 0) {
679         closesocket(commSock);
680         fprintf(stderr,"getAFSServer: send_DNS_AFSDB_Query failed\n");
681         return -1;
682     }
683     
684     pDNShdr = get_DNS_Response(commSock,sockAddr, buffer);
685   
686     /*printReplyBuffer_AFSDB(pDNShdr);*/
687     if (pDNShdr)
688         processReplyBuffer_AFSDB(commSock, pDNShdr, cellHostAddrs, cellHostNames, ports, ipRanks, numServers, ttl);
689
690     closesocket(commSock);
691     if (*numServers == 0)
692         return(-1);
693     else
694         return 0;
695 #else /* DNSAPI_ENV */
696     PDNS_RECORD pDnsCell, pDnsIter, pDnsVol, pDnsVolIter, pDnsCIter;
697     int i;
698     char query[1024];
699
700     *numServers = 0;
701     *ttl = 0;
702
703     if (service == NULL || protocol == NULL || cellName == NULL)
704         return -1;
705
706 #ifdef AFS_FREELANCE_CLIENT
707     if ( cm_stricmp_utf8N(cellName, "Freelance.Local.Root") == 0 )
708         return -1;
709 #endif /* AFS_FREELANCE_CLIENT */
710
711     /* query the SRV _afs3-vlserver._udp records of cell */
712     StringCbPrintf(query, sizeof(query), "_%s._%s.%s", service, protocol, cellName);
713     if (query[strlen(query)-1] != '.') {
714         StringCbCatA(query, sizeof(query), ".");
715     }
716
717     if (DnsQuery_A(query, DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &pDnsCell, NULL) == ERROR_SUCCESS) {
718         /* go through the returned records */
719         for (pDnsIter = pDnsCell;pDnsIter; pDnsIter = pDnsIter->pNext) {
720             /* if we find an SRV record, we found the service */
721             if (pDnsIter->wType == DNS_TYPE_SRV) {
722                 StringCbCopyA(cellHostNames[*numServers], sizeof(cellHostNames[*numServers]),
723                               pDnsIter->Data.SRV.pNameTarget);
724                 ipRanks[*numServers] = pDnsIter->Data.SRV.wPriority;
725                 ports[*numServers] = pDnsIter->Data.SRV.wPort;
726                 (*numServers)++;
727
728                 if (!*ttl) 
729                     *ttl = pDnsIter->dwTtl;
730                 if (*numServers == AFSMAXCELLHOSTS) 
731                     break;
732             }
733         }
734
735         for (i=0;i<*numServers;i++) 
736             cellHostAddrs[i] = 0;
737
738         /* now check if there are any A records in the results */
739         for (pDnsIter = pDnsCell; pDnsIter; pDnsIter = pDnsIter->pNext) {
740             if(pDnsIter->wType == DNS_TYPE_A)
741                 /* check if its for one of the service */
742                 for (i=0;i<*numServers;i++)
743                     if(cm_stricmp_utf8(pDnsIter->pName, cellHostNames[i]) == 0)
744                         cellHostAddrs[i] = pDnsIter->Data.A.IpAddress;
745         }
746
747         for (i=0;i<*numServers;i++) {
748             /* if we don't have an IP yet, then we should try resolving the afs3-vlserver hostname
749             in a separate query. */
750             if (!cellHostAddrs[i]) {
751                 if (DnsQuery_A(cellHostNames[i], DNS_TYPE_A, DNS_QUERY_STANDARD, NULL, &pDnsVol, NULL) == ERROR_SUCCESS) {
752                     for (pDnsVolIter = pDnsVol; pDnsVolIter; pDnsVolIter=pDnsVolIter->pNext) {
753                         /* if we get an A record, keep it */
754                         if (pDnsVolIter->wType == DNS_TYPE_A && cm_stricmp_utf8(cellHostNames[i], pDnsVolIter->pName)==0) {
755                             cellHostAddrs[i] = pDnsVolIter->Data.A.IpAddress;
756                             break;
757                         }
758                         /* if we get a CNAME, look for a corresponding A record */
759                         if (pDnsVolIter->wType == DNS_TYPE_CNAME && cm_stricmp_utf8(cellHostNames[i], pDnsVolIter->pName)==0) {
760                             for (pDnsCIter=pDnsVolIter; pDnsCIter; pDnsCIter=pDnsCIter->pNext) {
761                                 if (pDnsCIter->wType == DNS_TYPE_A && cm_stricmp_utf8(pDnsVolIter->Data.CNAME.pNameHost, pDnsCIter->pName)==0) {
762                                     cellHostAddrs[i] = pDnsCIter->Data.A.IpAddress;
763                                     break;
764                                 }
765                             }
766                             if (cellHostAddrs[i]) 
767                                 break;
768                             /* TODO: if the additional section is missing, then do another lookup for the CNAME */
769                         }
770                     }
771                     /* we are done with the service lookup */
772                     DnsRecordListFree(pDnsVol, DnsFreeRecordListDeep);
773                 }
774             }
775         }
776         DnsRecordListFree(pDnsCell, DnsFreeRecordListDeep);
777     }
778     else {
779         /* query the AFSDB records of cell */
780         StringCbCopyA(query, sizeof(query), cellName);
781         if (query[strlen(query)-1] != '.') {
782             StringCbCatA(query, sizeof(query), ".");
783         }
784
785         if (DnsQuery_A(query, DNS_TYPE_AFSDB, DNS_QUERY_STANDARD, NULL, &pDnsCell, NULL) == ERROR_SUCCESS) {
786             /* go through the returned records */
787             for (pDnsIter = pDnsCell;pDnsIter; pDnsIter = pDnsIter->pNext) {
788                 /* if we find an AFSDB record with Preference set to 1, we found a service instance */
789                 if (pDnsIter->wType == DNS_TYPE_AFSDB && pDnsIter->Data.Afsdb.wPreference == 1) {
790                     StringCbCopyA(cellHostNames[*numServers], sizeof(cellHostNames[*numServers]),
791                                    pDnsIter->Data.Afsdb.pNameExchange);
792                     ipRanks[*numServers] = 0;
793                     ports[*numServers] = htons(afsdbPort);
794                     (*numServers)++;
795
796                     if (!*ttl) 
797                         *ttl = pDnsIter->dwTtl;
798                     if (*numServers == AFSMAXCELLHOSTS) 
799                         break;
800                 }
801             }
802
803             for (i=0;i<*numServers;i++) 
804                 cellHostAddrs[i] = 0;
805
806             /* now check if there are any A records in the results */
807             for (pDnsIter = pDnsCell; pDnsIter; pDnsIter = pDnsIter->pNext) {
808                 if(pDnsIter->wType == DNS_TYPE_A)
809                     /* check if its for one of the service */
810                     for (i=0;i<*numServers;i++)
811                         if(cm_stricmp_utf8(pDnsIter->pName, cellHostNames[i]) == 0)
812                             cellHostAddrs[i] = pDnsIter->Data.A.IpAddress;
813             }
814
815             for (i=0;i<*numServers;i++) {
816                 /* if we don't have an IP yet, then we should try resolving the service hostname
817                 in a separate query. */
818                 if (!cellHostAddrs[i]) {
819                     if (DnsQuery_A(cellHostNames[i], DNS_TYPE_A, DNS_QUERY_STANDARD, NULL, &pDnsVol, NULL) == ERROR_SUCCESS) {
820                         for (pDnsVolIter = pDnsVol; pDnsVolIter; pDnsVolIter=pDnsVolIter->pNext) {
821                             /* if we get an A record, keep it */
822                             if (pDnsVolIter->wType == DNS_TYPE_A && cm_stricmp_utf8(cellHostNames[i], pDnsVolIter->pName)==0) {
823                                 cellHostAddrs[i] = pDnsVolIter->Data.A.IpAddress;
824                                 break;
825                             }
826                             /* if we get a CNAME, look for a corresponding A record */
827                             if (pDnsVolIter->wType == DNS_TYPE_CNAME && cm_stricmp_utf8(cellHostNames[i], pDnsVolIter->pName)==0) {
828                                 for (pDnsCIter=pDnsVolIter; pDnsCIter; pDnsCIter=pDnsCIter->pNext) {
829                                     if (pDnsCIter->wType == DNS_TYPE_A && cm_stricmp_utf8(pDnsVolIter->Data.CNAME.pNameHost, pDnsCIter->pName)==0) {
830                                         cellHostAddrs[i] = pDnsCIter->Data.A.IpAddress;
831                                         break;
832                                     }
833                                 }
834                                 if (cellHostAddrs[i]) 
835                                     break;
836                                 /* TODO: if the additional section is missing, then do another lookup for the CNAME */
837                             }
838                         }
839                         /* we are done with the service lookup */
840                         DnsRecordListFree(pDnsVol, DnsFreeRecordListDeep);
841                     }
842                 }
843             }
844             DnsRecordListFree(pDnsCell, DnsFreeRecordListDeep);
845         }
846     }
847
848     if ( *numServers > 0 )
849         return 0;
850     else        
851         return -1;
852 #endif /* DNSAPI_ENV */
853 }
854
855 int getAFSServerW(const cm_unichar_t *service, const cm_unichar_t *protocol, const cm_unichar_t *cellName,
856                   unsigned short afsdbPort,
857                   int *cellHostAddrs,
858                   cm_unichar_t cellHostNames[][MAXHOSTCHARS], 
859                   unsigned short ports[],
860                   unsigned short ipRanks[],
861                   int *numServers, int *ttl)
862 {
863 #ifdef DNSAPI_ENV
864     PDNS_RECORDW pDnsCell, pDnsIter, pDnsVol,pDnsVolIter, pDnsCIter;
865     int i;
866     cm_unichar_t query[1024];
867
868     *numServers = 0;
869     *ttl = 0;
870
871     if (service == NULL || protocol == NULL || cellName == NULL)
872         return -1;
873
874 #ifdef AFS_FREELANCE_CLIENT
875     if ( cm_stricmp_utf16(cellName, L"Freelance.Local.Root") == 0 )
876         return -1;
877 #endif /* AFS_FREELANCE_CLIENT */
878
879     /* query the SRV _afs3-vlserver._udp records of cell */
880     StringCbPrintfW(query, sizeof(query), L"_%S._%S.%S", service, protocol, cellName);
881     if (query[wcslen(query)-1] != L'.') {
882         StringCbCatW(query, sizeof(query), L".");
883     }
884
885     if (DnsQuery_W(query, DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, (PDNS_RECORD *) &pDnsCell,
886                    NULL) == ERROR_SUCCESS) {
887         /* go through the returned records */
888         for (pDnsIter = pDnsCell; pDnsIter; pDnsIter = pDnsIter->pNext) {
889             /* if we find an SRV record, we found a service instance */
890             if (pDnsIter->wType == DNS_TYPE_SRV) {
891                 StringCbCopyW(cellHostNames[*numServers], sizeof(cellHostNames[*numServers]),
892                               pDnsIter->Data.SRV.pNameTarget);
893                 ipRanks[*numServers] = pDnsIter->Data.SRV.wPriority;
894                 ports[*numServers] = pDnsIter->Data.SRV.wPort;
895                 (*numServers)++;
896                 
897                 if (!*ttl) 
898                     *ttl = pDnsIter->dwTtl;
899                 if (*numServers == AFSMAXCELLHOSTS) 
900                     break;
901             }
902         }
903
904         for (i=0;i<*numServers;i++) 
905             cellHostAddrs[i] = 0;
906
907         /* now check if there are any A records in the results */
908         for (pDnsIter = pDnsCell; pDnsIter; pDnsIter = pDnsIter->pNext) {
909             if(pDnsIter->wType == DNS_TYPE_A)
910                 /* check if its for one of the service instances */
911                 for (i=0;i<*numServers;i++)
912                     if(cm_stricmp_utf16(pDnsIter->pName, cellHostNames[i]) == 0)
913                         cellHostAddrs[i] = pDnsIter->Data.A.IpAddress;
914         }
915
916         for (i=0;i<*numServers;i++) {
917             /* if we don't have an IP yet, then we should try resolving the service hostname
918             in a separate query. */
919             if (!cellHostAddrs[i]) {
920                 if (DnsQuery_W(cellHostNames[i], DNS_TYPE_A, DNS_QUERY_STANDARD, NULL,
921                                (PDNS_RECORD *) &pDnsVol, NULL) == ERROR_SUCCESS) {
922                     for (pDnsVolIter = pDnsVol; pDnsVolIter; pDnsVolIter=pDnsVolIter->pNext) {
923                         /* if we get an A record, keep it */
924                         if (pDnsVolIter->wType == DNS_TYPE_A && cm_stricmp_utf16(cellHostNames[i], pDnsVolIter->pName)==0) {
925                             cellHostAddrs[i] = pDnsVolIter->Data.A.IpAddress;
926                             break;
927                         }
928                         /* if we get a CNAME, look for a corresponding A record */
929                         if (pDnsVolIter->wType == DNS_TYPE_CNAME && cm_stricmp_utf16(cellHostNames[i], pDnsVolIter->pName)==0) {
930                             for (pDnsCIter=pDnsVolIter; pDnsCIter; pDnsCIter=pDnsCIter->pNext) {
931                                 if (pDnsCIter->wType == DNS_TYPE_A && cm_stricmp_utf16(pDnsVolIter->Data.CNAME.pNameHost, pDnsCIter->pName)==0) {
932                                     cellHostAddrs[i] = pDnsCIter->Data.A.IpAddress;
933                                     break;
934                                 }
935                             }
936                             if (cellHostAddrs[i]) 
937                                 break;
938                             /* TODO: if the additional section is missing, then do another lookup for the CNAME */
939                         }
940                     }
941                     /* we are done with the service lookup */
942                     DnsRecordListFree((PDNS_RECORD) pDnsVol, DnsFreeRecordListDeep);
943                 }
944             }
945         }
946         DnsRecordListFree((PDNS_RECORD) pDnsCell, DnsFreeRecordListDeep);
947     }
948     else {
949         /* query the AFSDB records of cell */
950         StringCbCopyW(query, sizeof(query), cellName);
951         if (query[wcslen(query)-1] != L'.') {
952             StringCbCatW(query, sizeof(query), L".");
953         }
954
955         if (DnsQuery_W(query, DNS_TYPE_AFSDB, DNS_QUERY_STANDARD, NULL, (PDNS_RECORD *) &pDnsCell,
956                        NULL) == ERROR_SUCCESS) {
957             /* go through the returned records */
958             for (pDnsIter = pDnsCell;pDnsIter; pDnsIter = pDnsIter->pNext) {
959                 /* if we find an AFSDB record with Preference set to 1, we found a service instance */
960                 if (pDnsIter->wType == DNS_TYPE_AFSDB && pDnsIter->Data.Afsdb.wPreference == 1) {
961                     StringCbCopyW(cellHostNames[*numServers], sizeof(cellHostNames[*numServers]),
962                                   pDnsIter->Data.Afsdb.pNameExchange);
963                     ipRanks[*numServers] = 0;
964                     ports[*numServers] = htons(afsdbPort);
965                     (*numServers)++;
966                 
967                     if (!*ttl) 
968                         *ttl = pDnsIter->dwTtl;
969                     if (*numServers == AFSMAXCELLHOSTS) 
970                         break;
971                 }
972             }
973
974             for (i=0;i<*numServers;i++) 
975                 cellHostAddrs[i] = 0;
976
977             /* now check if there are any A records in the results */
978             for (pDnsIter = pDnsCell; pDnsIter; pDnsIter = pDnsIter->pNext) {
979                 if(pDnsIter->wType == DNS_TYPE_A)
980                     /* check if its for one of the service instances */
981                     for (i=0;i<*numServers;i++)
982                         if(cm_stricmp_utf16(pDnsIter->pName, cellHostNames[i]) == 0)
983                             cellHostAddrs[i] = pDnsIter->Data.A.IpAddress;
984             }
985
986             for (i=0;i<*numServers;i++) {
987                 /* if we don't have an IP yet, then we should try resolving the service hostname
988                 in a separate query. */
989                 if (!cellHostAddrs[i]) {
990                     if (DnsQuery_W(cellHostNames[i], DNS_TYPE_A, DNS_QUERY_STANDARD, NULL,
991                                    (PDNS_RECORD *) &pDnsVol, NULL) == ERROR_SUCCESS) {
992                         for (pDnsVolIter = pDnsVol; pDnsVolIter; pDnsVolIter=pDnsVolIter->pNext) {
993                             /* if we get an A record, keep it */
994                             if (pDnsVolIter->wType == DNS_TYPE_A && cm_stricmp_utf16(cellHostNames[i], pDnsVolIter->pName)==0) {
995                                 cellHostAddrs[i] = pDnsVolIter->Data.A.IpAddress;
996                                 break;
997                             }
998                             /* if we get a CNAME, look for a corresponding A record */
999                             if (pDnsVolIter->wType == DNS_TYPE_CNAME && cm_stricmp_utf16(cellHostNames[i], pDnsVolIter->pName)==0) {
1000                                 for (pDnsCIter=pDnsVolIter; pDnsCIter; pDnsCIter=pDnsCIter->pNext) {
1001                                     if (pDnsCIter->wType == DNS_TYPE_A && cm_stricmp_utf16(pDnsVolIter->Data.CNAME.pNameHost, pDnsCIter->pName)==0) {
1002                                         cellHostAddrs[i] = pDnsCIter->Data.A.IpAddress;
1003                                         break;
1004                                     }
1005                                 }
1006                                 if (cellHostAddrs[i]) 
1007                                     break;
1008                                 /* TODO: if the additional section is missing, then do another lookup for the CNAME */
1009                             }
1010                         }
1011                         /* we are done with the service lookup */
1012                         DnsRecordListFree((PDNS_RECORD) pDnsVol, DnsFreeRecordListDeep);
1013                     }
1014                 }
1015             }
1016             DnsRecordListFree((PDNS_RECORD) pDnsCell, DnsFreeRecordListDeep);
1017         }
1018     }
1019
1020     if ( *numServers > 0 )
1021         return 0;
1022     else        
1023 #endif  /* DNSAPI_ENV */
1024         return -1;
1025 }
1026 #endif /* AFS_AFSDB_ENV */
1027