9bb2d6755cb576682d3a926988e35360daaed650
[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         TimeInit();
84         setup_logging("nss_wins",False);
85         lp_load(dyn_CONFIGFILE,True,False,False);
86         load_interfaces();
87 }
88
89 static struct node_status *lookup_byaddr_backend(char *addr, int *count)
90 {
91         int fd;
92         struct in_addr  ip;
93         struct nmb_name nname;
94         struct node_status *status;
95
96         if (!initialised) {
97                 nss_wins_init();
98         }
99
100         fd = wins_lookup_open_socket_in();
101         if (fd == -1)
102                 return NULL;
103
104         make_nmb_name(&nname, "*", 0);
105         ip = *interpret_addr2(addr);
106         status = node_status_query(fd,&nname,ip, count);
107
108         close(fd);
109         return status;
110 }
111
112 static struct in_addr *lookup_byname_backend(const char *name, int *count)
113 {
114         int fd = -1;
115         struct in_addr *ret = NULL;
116         struct in_addr  p;
117         int j, flags = 0;
118
119         if (!initialised) {
120                 nss_wins_init();
121         }
122
123         *count = 0;
124
125         /* always try with wins first */
126         if (resolve_wins(name,0x20,&ret,count)) {
127                 return ret;
128         }
129
130         fd = wins_lookup_open_socket_in();
131         if (fd == -1) {
132                 return NULL;
133         }
134
135         /* uggh, we have to broadcast to each interface in turn */
136         for (j=iface_count() - 1;j >= 0;j--) {
137                 struct in_addr *bcast = iface_n_bcast(j);
138                 ret = name_query(fd,name,0x20,True,True,*bcast,count, &flags, NULL);
139                 if (ret) break;
140         }
141
142 out:
143         close(fd);
144         return ret;
145 }
146
147
148 #ifdef HAVE_NS_API_H
149 /* IRIX version */
150
151 int init(void)
152 {
153         nsd_logprintf(NSD_LOG_MIN, "entering init (wins)\n");
154         nss_wins_init();
155         return NSD_OK;
156 }
157
158 int lookup(nsd_file_t *rq)
159 {
160         char *map;
161         char *key;
162         char *addr;
163         struct in_addr *ip_list;
164         struct node_status *status;
165         int i, count, len, size;
166         char response[1024];
167         BOOL found = False;
168
169         nsd_logprintf(NSD_LOG_MIN, "entering lookup (wins)\n");
170         if (! rq) 
171                 return NSD_ERROR;
172
173         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
174         if (! map) {
175                 rq->f_status = NS_FATAL;
176                 return NSD_ERROR;
177         }
178
179         key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
180         if (! key || ! *key) {
181                 rq->f_status = NS_FATAL;
182                 return NSD_ERROR;
183         }
184
185         response[0] = '\0';
186         len = sizeof(response) - 2;
187
188         /* 
189          * response needs to be a string of the following format
190          * ip_address[ ip_address]*\tname[ alias]*
191          */
192         if (strcasecmp(map,"hosts.byaddr") == 0) {
193                 if ( status = lookup_byaddr_backend(key, &count)) {
194                     size = strlen(key) + 1;
195                     if (size > len) {
196                         free(status);
197                         return NSD_ERROR;
198                     }
199                     len -= size;
200                     strncat(response,key,size);
201                     strncat(response,"\t",1);
202                     for (i = 0; i < count; i++) {
203                         /* ignore group names */
204                         if (status[i].flags & 0x80) continue;
205                         if (status[i].type == 0x20) {
206                                 size = sizeof(status[i].name) + 1;
207                                 if (size > len) {
208                                     free(status);
209                                     return NSD_ERROR;
210                                 }
211                                 len -= size;
212                                 strncat(response, status[i].name, size);
213                                 strncat(response, " ", 1);
214                                 found = True;
215                         }
216                     }
217                     response[strlen(response)-1] = '\n';
218                     free(status);
219                 }
220         } else if (strcasecmp(map,"hosts.byname") == 0) {
221             if (ip_list = lookup_byname_backend(key, &count)) {
222                 for (i = count; i ; i--) {
223                     addr = inet_ntoa(ip_list[i-1]);
224                     size = strlen(addr) + 1;
225                     if (size > len) {
226                         free(ip_list);
227                         return NSD_ERROR;
228                     }
229                     len -= size;
230                     if (i != 0)
231                         response[strlen(response)-1] = ' ';
232                     strncat(response,addr,size);
233                     strncat(response,"\t",1);
234                 }
235                 size = strlen(key) + 1;
236                 if (size > len) {
237                     free(ip_list);
238                     return NSD_ERROR;
239                 }   
240                 strncat(response,key,size);
241                 strncat(response,"\n",1);
242                 found = True;
243                 free(ip_list);
244             }
245         }
246
247         if (found) {
248             nsd_logprintf(NSD_LOG_LOW, "lookup (wins %s) %s\n",map,response);
249             nsd_set_result(rq,NS_SUCCESS,response,strlen(response),VOLATILE);
250             return NSD_OK;
251         }
252         nsd_logprintf(NSD_LOG_LOW, "lookup (wins) not found\n");
253         rq->f_status = NS_NOTFOUND;
254         return NSD_NEXT;
255 }
256
257 #else
258 /****************************************************************************
259 gethostbyname() - we ignore any domain portion of the name and only
260 handle names that are at most 15 characters long
261   **************************************************************************/
262 NSS_STATUS
263 _nss_wins_gethostbyname_r(const char *name, struct hostent *he,
264                           char *buffer, size_t buflen, int *errnop,
265                           int *h_errnop)
266 {
267         char **host_addresses;
268         struct in_addr *ip_list;
269         int i, count;
270         size_t namelen = strlen(name) + 1;
271                 
272         memset(he, '\0', sizeof(*he));
273
274         ip_list = lookup_byname_backend(name, &count);
275         if (!ip_list) {
276                 return NSS_STATUS_NOTFOUND;
277         }
278
279         if (buflen < namelen + (2*count+1)*INADDRSZ) {
280                 /* no ENOMEM error type?! */
281                 return NSS_STATUS_NOTFOUND;
282         }
283
284
285         host_addresses = (char **)buffer;
286         he->h_addr_list = host_addresses;
287         host_addresses[count] = NULL;
288         buffer += (count + 1) * INADDRSZ;
289         buflen += (count + 1) * INADDRSZ;
290         he->h_addrtype = AF_INET;
291         he->h_length = INADDRSZ;
292
293         for (i=0;i<count;i++) {
294                 memcpy(buffer, &ip_list[i].s_addr, INADDRSZ);
295                 *host_addresses = buffer;
296                 buffer += INADDRSZ;
297                 buflen -= INADDRSZ;
298                 host_addresses++;
299         }
300
301         if (ip_list)
302                 free(ip_list);
303
304         memcpy(buffer, name, namelen);
305         he->h_name = buffer;
306
307         return NSS_STATUS_SUCCESS;
308 }
309
310
311 NSS_STATUS
312 _nss_wins_gethostbyname2_r(const char *name, int af, struct hostent *he,
313                                 char *buffer, size_t buflen, int *errnop,
314                                 int *h_errnop)
315 {
316         if(af!=AF_INET) {
317                 *h_errnop = NO_DATA;
318                 *errnop = EAFNOSUPPORT;
319                 return NSS_STATUS_UNAVAIL;
320         }
321
322         return _nss_wins_gethostbyname_r(name,he,buffer,buflen,errnop,h_errnop);
323 }
324 #endif