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