This commit finally gives us multiple wins server groups. We now
[abartlet/samba.git/.git] / source3 / nsswitch / wins.c
1 /* 
2    Unix SMB/CIFS implementation.
3    a WINS nsswitch module 
4    Copyright (C) Andrew Tridgell 1999
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19    
20 */
21
22 #define NO_SYSLOG
23
24 #include "includes.h"
25 #ifdef HAVE_NS_API_H
26 #undef VOLATILE
27
28 #include <ns_daemon.h>
29 #endif
30
31 #ifndef INADDRSZ
32 #define INADDRSZ 4
33 #endif
34
35 static int initialised;
36
37 extern BOOL AllowDebugChange;
38
39 /* Use our own create socket code so we don't recurse.... */
40
41 static int wins_lookup_open_socket_in(void)
42 {
43         struct sockaddr_in sock;
44         int val=1;
45         int res;
46
47         memset((char *)&sock,'\0',sizeof(sock));
48
49 #ifdef HAVE_SOCK_SIN_LEN
50         sock.sin_len = sizeof(sock);
51 #endif
52         sock.sin_port = 0;
53         sock.sin_family = AF_INET;
54         sock.sin_addr.s_addr = interpret_addr("0.0.0.0");
55         res = socket(AF_INET, SOCK_DGRAM, 0);
56         if (res == -1)
57                 return -1;
58
59         setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val));
60 #ifdef SO_REUSEPORT
61         setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val));
62 #endif /* SO_REUSEPORT */
63
64         /* now we've got a socket - we need to bind it */
65
66         if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) {
67                 close(res);
68                 return(-1);
69         }
70
71         set_socket_options(res,"SO_BROADCAST");
72
73         return res;
74 }
75
76
77 static void nss_wins_init(void)
78 {
79         initialised = 1;
80         DEBUGLEVEL = 0;
81         AllowDebugChange = False;
82
83         /* needed for lp_xx() functions */
84         charset_initialise();
85
86         TimeInit();
87         setup_logging("nss_wins",False);
88         lp_load(dyn_CONFIGFILE,True,False,False);
89         load_interfaces();
90         codepage_initialise(lp_client_code_page());
91 }
92
93 static struct node_status *lookup_byaddr_backend(char *addr, int *count)
94 {
95         int fd;
96         struct in_addr  ip;
97         struct nmb_name nname;
98         struct node_status *status;
99
100         if (!initialised) {
101                 nss_wins_init();
102         }
103
104         fd = wins_lookup_open_socket_in();
105         if (fd == -1)
106                 return NULL;
107
108         make_nmb_name(&nname, "*", 0);
109         ip = *interpret_addr2(addr);
110         status = node_status_query(fd,&nname,ip, count);
111
112         close(fd);
113         return status;
114 }
115
116 static struct in_addr *lookup_byname_backend(const char *name, int *count)
117 {
118         int fd = -1;
119         struct in_addr *ret = NULL;
120         struct in_addr  p;
121         int j, flags = 0;
122
123         if (!initialised) {
124                 nss_wins_init();
125         }
126
127         *count = 0;
128
129         /* always try with wins first */
130         if (resolve_wins(name,0x20,&ret,count)) {
131                 return ret;
132         }
133
134         fd = wins_lookup_open_socket_in();
135         if (fd == -1) {
136                 return NULL;
137         }
138
139         /* uggh, we have to broadcast to each interface in turn */
140         for (j=iface_count() - 1;j >= 0;j--) {
141                 struct in_addr *bcast = iface_n_bcast(j);
142                 ret = name_query(fd,name,0x20,True,True,*bcast,count, &flags, NULL);
143                 if (ret) break;
144         }
145
146 out:
147         close(fd);
148         return ret;
149 }
150
151
152 #ifdef HAVE_NS_API_H
153 /* IRIX version */
154
155 int init(void)
156 {
157         nsd_logprintf(NSD_LOG_MIN, "entering init (wins)\n");
158         nss_wins_init();
159         return NSD_OK;
160 }
161
162 int lookup(nsd_file_t *rq)
163 {
164         char *map;
165         char *key;
166         char *addr;
167         struct in_addr *ip_list;
168         struct node_status *status;
169         int i, count, len, size;
170         char response[1024];
171         BOOL found = False;
172
173         nsd_logprintf(NSD_LOG_MIN, "entering lookup (wins)\n");
174         if (! rq) 
175                 return NSD_ERROR;
176
177         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
178         if (! map) {
179                 rq->f_status = NS_FATAL;
180                 return NSD_ERROR;
181         }
182
183         key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
184         if (! key || ! *key) {
185                 rq->f_status = NS_FATAL;
186                 return NSD_ERROR;
187         }
188
189         response[0] = '\0';
190         len = sizeof(response) - 2;
191
192         /* 
193          * response needs to be a string of the following format
194          * ip_address[ ip_address]*\tname[ alias]*
195          */
196         if (strcasecmp(map,"hosts.byaddr") == 0) {
197                 if ( status = lookup_byaddr_backend(key, &count)) {
198                     size = strlen(key) + 1;
199                     if (size > len) {
200                         free(status);
201                         return NSD_ERROR;
202                     }
203                     len -= size;
204                     strncat(response,key,size);
205                     strncat(response,"\t",1);
206                     for (i = 0; i < count; i++) {
207                         /* ignore group names */
208                         if (status[i].flags & 0x80) continue;
209                         if (status[i].type == 0x20) {
210                                 size = sizeof(status[i].name) + 1;
211                                 if (size > len) {
212                                     free(status);
213                                     return NSD_ERROR;
214                                 }
215                                 len -= size;
216                                 strncat(response, status[i].name, size);
217                                 strncat(response, " ", 1);
218                                 found = True;
219                         }
220                     }
221                     response[strlen(response)-1] = '\n';
222                     free(status);
223                 }
224         } else if (strcasecmp(map,"hosts.byname") == 0) {
225             if (ip_list = lookup_byname_backend(key, &count)) {
226                 for (i = count; i ; i--) {
227                     addr = inet_ntoa(ip_list[i-1]);
228                     size = strlen(addr) + 1;
229                     if (size > len) {
230                         free(ip_list);
231                         return NSD_ERROR;
232                     }
233                     len -= size;
234                     if (i != 0)
235                         response[strlen(response)-1] = ' ';
236                     strncat(response,addr,size);
237                     strncat(response,"\t",1);
238                 }
239                 size = strlen(key) + 1;
240                 if (size > len) {
241                     free(ip_list);
242                     return NSD_ERROR;
243                 }   
244                 strncat(response,key,size);
245                 strncat(response,"\n",1);
246                 found = True;
247                 free(ip_list);
248             }
249         }
250
251         if (found) {
252             nsd_logprintf(NSD_LOG_LOW, "lookup (wins %s) %s\n",map,response);
253             nsd_set_result(rq,NS_SUCCESS,response,strlen(response),VOLATILE);
254             return NSD_OK;
255         }
256         nsd_logprintf(NSD_LOG_LOW, "lookup (wins) not found\n");
257         rq->f_status = NS_NOTFOUND;
258         return NSD_NEXT;
259 }
260
261 #else
262 /****************************************************************************
263 gethostbyname() - we ignore any domain portion of the name and only
264 handle names that are at most 15 characters long
265   **************************************************************************/
266 NSS_STATUS
267 _nss_wins_gethostbyname_r(const char *name, struct hostent *he,
268                           char *buffer, size_t buflen, int *errnop,
269                           int *h_errnop)
270 {
271         char **host_addresses;
272         struct in_addr *ip_list;
273         int i, count;
274         size_t namelen = strlen(name) + 1;
275                 
276         memset(he, '\0', sizeof(*he));
277
278         ip_list = lookup_byname_backend(name, &count);
279         if (!ip_list) {
280                 return NSS_STATUS_NOTFOUND;
281         }
282
283         if (buflen < namelen + (2*count+1)*INADDRSZ) {
284                 /* no ENOMEM error type?! */
285                 return NSS_STATUS_NOTFOUND;
286         }
287
288
289         host_addresses = (char **)buffer;
290         he->h_addr_list = host_addresses;
291         host_addresses[count] = NULL;
292         buffer += (count + 1) * INADDRSZ;
293         buflen += (count + 1) * INADDRSZ;
294         he->h_addrtype = AF_INET;
295         he->h_length = INADDRSZ;
296
297         for (i=0;i<count;i++) {
298                 memcpy(buffer, &ip_list[i].s_addr, INADDRSZ);
299                 *host_addresses = buffer;
300                 buffer += INADDRSZ;
301                 buflen -= INADDRSZ;
302                 host_addresses++;
303         }
304
305         if (ip_list)
306                 free(ip_list);
307
308         memcpy(buffer, name, namelen);
309         he->h_name = buffer;
310
311         return NSS_STATUS_SUCCESS;
312 }
313
314
315 NSS_STATUS
316 _nss_wins_gethostbyname2_r(const char *name, int af, struct hostent *he,
317                                 char *buffer, size_t buflen, int *errnop,
318                                 int *h_errnop)
319 {
320         if(af!=AF_INET) {
321                 *h_errnop = NO_DATA;
322                 *errnop = EAFNOSUPPORT;
323                 return NSS_STATUS_UNAVAIL;
324         }
325
326         return _nss_wins_gethostbyname_r(name,he,buffer,buflen,errnop,h_errnop);
327 }
328 #endif