nsswitch: depend on dl
[samba.git] / 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 3 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, see <http://www.gnu.org/licenses/>.
18
19 */
20
21 #include "includes.h"
22 #include "nsswitch/winbind_nss.h"
23
24 #ifdef HAVE_NS_API_H
25
26 #include <ns_daemon.h>
27 #endif
28
29 #if HAVE_PTHREAD_H
30 #include <pthread.h>
31 #endif
32
33 #if HAVE_PTHREAD
34 static pthread_mutex_t wins_nss_mutex = PTHREAD_MUTEX_INITIALIZER;
35 #endif
36
37 #ifndef INADDRSZ
38 #define INADDRSZ 4
39 #endif
40
41 static int initialised;
42
43 NSS_STATUS _nss_wins_gethostbyname_r(const char *hostname, struct hostent *he,
44                           char *buffer, size_t buflen, int *h_errnop);
45 NSS_STATUS _nss_wins_gethostbyname2_r(const char *name, int af, struct hostent *he,
46                            char *buffer, size_t buflen, int *h_errnop);
47
48 static void nss_wins_init(void)
49 {
50         initialised = 1;
51         load_case_tables();
52         lp_set_cmdline("log level", "0");
53
54         TimeInit();
55         setup_logging("nss_wins",False);
56         lp_load(get_dyn_CONFIGFILE(),True,False,False,True);
57         load_interfaces();
58 }
59
60 static struct in_addr *lookup_byname_backend(const char *name, int *count)
61 {
62         struct ip_service *address = NULL;
63         struct in_addr *ret = NULL;
64         int j;
65
66         if (!initialised) {
67                 nss_wins_init();
68         }
69
70         *count = 0;
71
72         /* always try with wins first */
73         if (NT_STATUS_IS_OK(resolve_wins(name,0x00,&address,count))) {
74                 if ( (ret = SMB_MALLOC_P(struct in_addr)) == NULL ) {
75                         free( address );
76                         return NULL;
77                 }
78                 if (address[0].ss.ss_family != AF_INET) {
79                         free(address);
80                         free(ret);
81                         return NULL;
82                 }
83                 *ret = ((struct sockaddr_in *)(void *)&address[0].ss)
84                         ->sin_addr;
85                 free( address );
86                 return ret;
87         }
88
89         /* uggh, we have to broadcast to each interface in turn */
90         for (j=iface_count() - 1;j >= 0;j--) {
91                 const struct in_addr *bcast = iface_n_bcast_v4(j);
92                 struct sockaddr_storage ss;
93                 struct sockaddr_storage *pss;
94                 NTSTATUS status;
95
96                 if (!bcast) {
97                         continue;
98                 }
99                 in_addr_to_sockaddr_storage(&ss, *bcast);
100                 status = name_query(name, 0x00, True, True, &ss,
101                                     NULL, &pss, count, NULL);
102                 if (pss) {
103                         if ((ret = SMB_MALLOC_P(struct in_addr)) == NULL) {
104                                 return NULL;
105                         }
106                         *ret = ((struct sockaddr_in *)pss)->sin_addr;
107                         TALLOC_FREE(pss);
108                         break;
109                 }
110         }
111
112         return ret;
113 }
114
115 #ifdef HAVE_NS_API_H
116
117 /* Use our own create socket code so we don't recurse.... */
118
119 static int wins_lookup_open_socket_in(void)
120 {
121         struct sockaddr_in sock;
122         int val=1;
123         int res;
124
125         memset((char *)&sock,'\0',sizeof(sock));
126
127 #ifdef HAVE_SOCK_SIN_LEN
128         sock.sin_len = sizeof(sock);
129 #endif
130         sock.sin_port = 0;
131         sock.sin_family = AF_INET;
132         sock.sin_addr.s_addr = interpret_addr("0.0.0.0");
133         res = socket(AF_INET, SOCK_DGRAM, 0);
134         if (res == -1)
135                 return -1;
136
137         if (setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val)) != 0) {
138                 close(res);
139                 return -1;
140         }
141 #ifdef SO_REUSEPORT
142         if (setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val)) != 0) {
143                 close(res);
144                 return -1;
145         }
146 #endif /* SO_REUSEPORT */
147
148         /* now we've got a socket - we need to bind it */
149
150         if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) {
151                 close(res);
152                 return(-1);
153         }
154
155         set_socket_options(res,"SO_BROADCAST");
156
157         return res;
158 }
159
160 static struct node_status *lookup_byaddr_backend(char *addr, int *count)
161 {
162         int fd;
163         struct sockaddr_storage ss;
164         struct nmb_name nname;
165         struct node_status *status;
166
167         if (!initialised) {
168                 nss_wins_init();
169         }
170
171         fd = wins_lookup_open_socket_in();
172         if (fd == -1)
173                 return NULL;
174
175         make_nmb_name(&nname, "*", 0);
176         if (!interpret_string_addr(&ss, addr, AI_NUMERICHOST)) {
177                 return NULL;
178         }
179         status = node_status_query(fd, &nname, &ss, count, NULL);
180
181         close(fd);
182         return status;
183 }
184
185 /* IRIX version */
186
187 int init(void)
188 {
189         nsd_logprintf(NSD_LOG_MIN, "entering init (wins)\n");
190         nss_wins_init();
191         return NSD_OK;
192 }
193
194 int lookup(nsd_file_t *rq)
195 {
196         char *map;
197         char *key;
198         char *addr;
199         struct in_addr *ip_list;
200         struct node_status *status;
201         int i, count, len, size;
202         char response[1024];
203         bool found = False;
204
205         nsd_logprintf(NSD_LOG_MIN, "entering lookup (wins)\n");
206         if (! rq)
207                 return NSD_ERROR;
208
209         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
210         if (! map) {
211                 rq->f_status = NS_FATAL;
212                 return NSD_ERROR;
213         }
214
215         key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
216         if (! key || ! *key) {
217                 rq->f_status = NS_FATAL;
218                 return NSD_ERROR;
219         }
220
221         response[0] = '\0';
222         len = sizeof(response) - 2;
223
224         /*
225          * response needs to be a string of the following format
226          * ip_address[ ip_address]*\tname[ alias]*
227          */
228         if (StrCaseCmp(map,"hosts.byaddr") == 0) {
229                 if ( status = lookup_byaddr_backend(key, &count)) {
230                     size = strlen(key) + 1;
231                     if (size > len) {
232                         free(status);
233                         return NSD_ERROR;
234                     }
235                     len -= size;
236                     strncat(response,key,size);
237                     strncat(response,"\t",1);
238                     for (i = 0; i < count; i++) {
239                         /* ignore group names */
240                         if (status[i].flags & 0x80) continue;
241                         if (status[i].type == 0x20) {
242                                 size = sizeof(status[i].name) + 1;
243                                 if (size > len) {
244                                     free(status);
245                                     return NSD_ERROR;
246                                 }
247                                 len -= size;
248                                 strncat(response, status[i].name, size);
249                                 strncat(response, " ", 1);
250                                 found = True;
251                         }
252                     }
253                     response[strlen(response)-1] = '\n';
254                     free(status);
255                 }
256         } else if (StrCaseCmp(map,"hosts.byname") == 0) {
257             if (ip_list = lookup_byname_backend(key, &count)) {
258                 for (i = count; i ; i--) {
259                     addr = inet_ntoa(ip_list[i-1]);
260                     size = strlen(addr) + 1;
261                     if (size > len) {
262                         free(ip_list);
263                         return NSD_ERROR;
264                     }
265                     len -= size;
266                     if (i != 0)
267                         response[strlen(response)-1] = ' ';
268                     strncat(response,addr,size);
269                     strncat(response,"\t",1);
270                 }
271                 size = strlen(key) + 1;
272                 if (size > len) {
273                     free(ip_list);
274                     return NSD_ERROR;
275                 }
276                 strncat(response,key,size);
277                 strncat(response,"\n",1);
278                 found = True;
279                 free(ip_list);
280             }
281         }
282
283         if (found) {
284             nsd_logprintf(NSD_LOG_LOW, "lookup (wins %s) %s\n",map,response);
285             nsd_set_result(rq,NS_SUCCESS,response,strlen(response),VOLATILE);
286             return NSD_OK;
287         }
288         nsd_logprintf(NSD_LOG_LOW, "lookup (wins) not found\n");
289         rq->f_status = NS_NOTFOUND;
290         return NSD_NEXT;
291 }
292
293 #else
294
295 /* Allocate some space from the nss static buffer.  The buffer and buflen
296    are the pointers passed in by the C library to the _nss_*_*
297    functions. */
298
299 static char *get_static(char **buffer, size_t *buflen, int len)
300 {
301         char *result;
302
303         /* Error check.  We return false if things aren't set up right, or
304            there isn't enough buffer space left. */
305
306         if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
307                 return NULL;
308         }
309
310         /* Return an index into the static buffer */
311
312         result = *buffer;
313         *buffer += len;
314         *buflen -= len;
315
316         return result;
317 }
318
319 /****************************************************************************
320 gethostbyname() - we ignore any domain portion of the name and only
321 handle names that are at most 15 characters long
322   **************************************************************************/
323 NSS_STATUS
324 _nss_wins_gethostbyname_r(const char *hostname, struct hostent *he,
325                           char *buffer, size_t buflen, int *h_errnop)
326 {
327         NSS_STATUS nss_status = NSS_STATUS_SUCCESS;
328         struct in_addr *ip_list;
329         int i, count;
330         fstring name;
331         size_t namelen;
332         TALLOC_CTX *frame;
333
334 #if HAVE_PTHREAD
335         pthread_mutex_lock(&wins_nss_mutex);
336 #endif
337
338         frame = talloc_stackframe();
339
340         memset(he, '\0', sizeof(*he));
341         fstrcpy(name, hostname);
342
343         /* Do lookup */
344
345         ip_list = lookup_byname_backend(name, &count);
346
347         if (!ip_list) {
348                 nss_status = NSS_STATUS_NOTFOUND;
349                 goto out;
350         }
351
352         /* Copy h_name */
353
354         namelen = strlen(name) + 1;
355
356         if ((he->h_name = get_static(&buffer, &buflen, namelen)) == NULL) {
357                 free(ip_list);
358                 nss_status = NSS_STATUS_TRYAGAIN;
359                 goto out;
360         }
361
362         memcpy(he->h_name, name, namelen);
363
364         /* Copy h_addr_list, align to pointer boundary first */
365
366         if ((i = (unsigned long)(buffer) % sizeof(char*)) != 0)
367                 i = sizeof(char*) - i;
368
369         if (get_static(&buffer, &buflen, i) == NULL) {
370                 free(ip_list);
371                 nss_status = NSS_STATUS_TRYAGAIN;
372                 goto out;
373         }
374
375         if ((he->h_addr_list = (char **)get_static(
376                      &buffer, &buflen, (count + 1) * sizeof(char *))) == NULL) {
377                 free(ip_list);
378                 nss_status = NSS_STATUS_TRYAGAIN;
379                 goto out;
380         }
381
382         for (i = 0; i < count; i++) {
383                 if ((he->h_addr_list[i] = get_static(&buffer, &buflen,
384                                                      INADDRSZ)) == NULL) {
385                         free(ip_list);
386                         nss_status = NSS_STATUS_TRYAGAIN;
387                         goto out;
388                 }
389                 memcpy(he->h_addr_list[i], &ip_list[i], INADDRSZ);
390         }
391
392         he->h_addr_list[count] = NULL;
393
394         free(ip_list);
395
396         /* Set h_addr_type and h_length */
397
398         he->h_addrtype = AF_INET;
399         he->h_length = INADDRSZ;
400
401         /* Set h_aliases */
402
403         if ((i = (unsigned long)(buffer) % sizeof(char*)) != 0)
404                 i = sizeof(char*) - i;
405
406         if (get_static(&buffer, &buflen, i) == NULL) {
407                 nss_status = NSS_STATUS_TRYAGAIN;
408                 goto out;
409         }
410
411         if ((he->h_aliases = (char **)get_static(
412                      &buffer, &buflen, sizeof(char *))) == NULL) {
413                 nss_status = NSS_STATUS_TRYAGAIN;
414                 goto out;
415         }
416
417         he->h_aliases[0] = NULL;
418
419         nss_status = NSS_STATUS_SUCCESS;
420
421   out:
422
423         TALLOC_FREE(frame);
424
425 #if HAVE_PTHREAD
426         pthread_mutex_unlock(&wins_nss_mutex);
427 #endif
428         return nss_status;
429 }
430
431
432 NSS_STATUS
433 _nss_wins_gethostbyname2_r(const char *name, int af, struct hostent *he,
434                            char *buffer, size_t buflen, int *h_errnop)
435 {
436         NSS_STATUS nss_status;
437
438         if(af!=AF_INET) {
439                 *h_errnop = NO_DATA;
440                 nss_status = NSS_STATUS_UNAVAIL;
441         } else {
442                 nss_status = _nss_wins_gethostbyname_r(
443                                 name, he, buffer, buflen, h_errnop);
444         }
445         return nss_status;
446 }
447 #endif