bootparamd.c revision 1.22 1 /* $NetBSD: bootparamd.c,v 1.22 2000/04/14 12:14:40 itojun Exp $ */
2
3 /*
4 * This code is not copyright, and is placed in the public domain.
5 * Feel free to use and modify. Please send modifications and/or
6 * suggestions + bug fixes to Klas Heggemann <klas (at) nada.kth.se>
7 *
8 * Various small changes by Theo de Raadt <deraadt (at) fsa.ca>
9 * Parser rewritten (adding YP support) by Roland McGrath <roland (at) frob.com>
10 */
11
12 #include <sys/cdefs.h>
13 #ifndef lint
14 __RCSID("$NetBSD: bootparamd.c,v 1.22 2000/04/14 12:14:40 itojun Exp $");
15 #endif
16
17 #include <sys/types.h>
18 #include <sys/ioctl.h>
19 #include <sys/stat.h>
20 #include <sys/socket.h>
21
22 #include <ctype.h>
23 #include <errno.h>
24 #include <err.h>
25 #include <netdb.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <syslog.h>
30 #include <unistd.h>
31 #include <util.h>
32 #include <ifaddrs.h>
33
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36
37 #include <rpc/rpc.h>
38 #include <rpc/pmap_clnt.h>
39 #include <rpcsvc/bootparam_prot.h>
40 #include <rpcsvc/ypclnt.h>
41
42 #include "pathnames.h"
43
44 #define MAXLEN 800
45
46 static char hostname[MAX_MACHINE_NAME];
47 static char askname[MAX_MACHINE_NAME];
48 static char domain_name[MAX_MACHINE_NAME];
49
50 extern void bootparamprog_1 __P((struct svc_req *, SVCXPRT *));
51
52 int _rpcsvcdirty = 0;
53 int _rpcpmstart = 0;
54 int debug = 0;
55 int dolog = 0;
56 struct in_addr route_addr;
57 struct sockaddr_in my_addr;
58 extern char *__progname;
59 char *bootpfile = _PATH_BOOTPARAMS;
60
61 int main __P((int, char *[]));
62 int lookup_bootparam __P((char *, char *, char *, char **, char **));
63 void usage __P((void));
64 static int get_localaddr __P((const char *, struct sockaddr_in *));
65
66
67 /*
68 * ever familiar
69 */
70 int
71 main(argc, argv)
72 int argc;
73 char *argv[];
74 {
75 SVCXPRT *transp;
76 struct hostent *he;
77 struct stat buf;
78 int c;
79
80 while ((c = getopt(argc, argv, "dsr:f:")) != -1)
81 switch (c) {
82 case 'd':
83 debug = 1;
84 break;
85 case 'r':
86 if (isdigit(*optarg)) {
87 if (inet_aton(optarg, &route_addr) != 0)
88 break;
89 }
90 he = gethostbyname(optarg);
91 if (he == 0) {
92 warnx("no such host: %s", optarg);
93 usage();
94 }
95 memmove(&route_addr.s_addr, he->h_addr, he->h_length);
96 break;
97 case 'f':
98 bootpfile = optarg;
99 break;
100 case 's':
101 dolog = 1;
102 #ifndef LOG_DAEMON
103 openlog(__progname, 0, 0);
104 #else
105 openlog(__progname, 0, LOG_DAEMON);
106 setlogmask(LOG_UPTO(LOG_NOTICE));
107 #endif
108 break;
109 default:
110 usage();
111 }
112
113 if (stat(bootpfile, &buf))
114 err(1, "%s", bootpfile);
115
116 if (route_addr.s_addr == 0) {
117 if (get_localaddr(NULL, &my_addr) != 0)
118 errx(1, "router address not found");
119 route_addr.s_addr = my_addr.sin_addr.s_addr;
120 }
121 if (!debug) {
122 if (daemon(0, 0))
123 err(1, "can't detach from terminal");
124 }
125 pidfile(NULL);
126
127 (void) pmap_unset(BOOTPARAMPROG, BOOTPARAMVERS);
128
129 transp = svcudp_create(RPC_ANYSOCK);
130 if (transp == NULL)
131 errx(1, "can't create udp service");
132
133 if (!svc_register(transp, BOOTPARAMPROG, BOOTPARAMVERS, bootparamprog_1,
134 IPPROTO_UDP))
135 errx(1, "unable to register BOOTPARAMPROG version %ld, udp",
136 BOOTPARAMVERS);
137
138 svc_run();
139 errx(1, "svc_run returned");
140 }
141
142 bp_whoami_res *
143 bootparamproc_whoami_1_svc(whoami, rqstp)
144 bp_whoami_arg *whoami;
145 struct svc_req *rqstp;
146 {
147 static bp_whoami_res res;
148 struct hostent *he;
149 struct in_addr haddr;
150 int e;
151
152 if (debug)
153 warnx("whoami got question for %d.%d.%d.%d",
154 255 & whoami->client_address.bp_address_u.ip_addr.net,
155 255 & whoami->client_address.bp_address_u.ip_addr.host,
156 255 & whoami->client_address.bp_address_u.ip_addr.lh,
157 255 & whoami->client_address.bp_address_u.ip_addr.impno);
158 if (dolog)
159 syslog(LOG_NOTICE, "whoami got question for %d.%d.%d.%d",
160 255 & whoami->client_address.bp_address_u.ip_addr.net,
161 255 & whoami->client_address.bp_address_u.ip_addr.host,
162 255 & whoami->client_address.bp_address_u.ip_addr.lh,
163 255 & whoami->client_address.bp_address_u.ip_addr.impno);
164
165 memmove((char *) &haddr,
166 (char *) &whoami->client_address.bp_address_u.ip_addr,
167 sizeof(haddr));
168 he = gethostbyaddr((char *) &haddr, sizeof(haddr), AF_INET);
169 if (he) {
170 strncpy(askname, he->h_name, sizeof(askname));
171 askname[sizeof(askname)-1] = 0;
172 } else {
173 strncpy(askname, inet_ntoa(haddr), sizeof(askname));
174 askname[sizeof(askname)-1] = 0;
175 }
176
177 if (debug)
178 warnx("This is host %s", askname);
179 if (dolog)
180 syslog(LOG_NOTICE, "This is host %s", askname);
181
182 if ((e = lookup_bootparam(askname, hostname, NULL, NULL, NULL)) == 0) {
183 res.client_name = hostname;
184 getdomainname(domain_name, MAX_MACHINE_NAME);
185 res.domain_name = domain_name;
186
187 if (res.router_address.address_type != IP_ADDR_TYPE) {
188 res.router_address.address_type = IP_ADDR_TYPE;
189 memmove(&res.router_address.bp_address_u.ip_addr,
190 &route_addr.s_addr,4);
191 }
192 if (debug)
193 warnx("Returning %s %s %d.%d.%d.%d",
194 res.client_name, res.domain_name,
195 255 & res.router_address.bp_address_u.ip_addr.net,
196 255 & res.router_address.bp_address_u.ip_addr.host,
197 255 & res.router_address.bp_address_u.ip_addr.lh,
198 255 &res.router_address.bp_address_u.ip_addr.impno);
199 if (dolog)
200 syslog(LOG_NOTICE, "Returning %s %s %d.%d.%d.%d",
201 res.client_name, res.domain_name,
202 255 & res.router_address.bp_address_u.ip_addr.net,
203 255 & res.router_address.bp_address_u.ip_addr.host,
204 255 & res.router_address.bp_address_u.ip_addr.lh,
205 255 &res.router_address.bp_address_u.ip_addr.impno);
206
207 return (&res);
208 }
209 errno = e;
210 if (debug)
211 warn("whoami failed");
212 if (dolog)
213 syslog(LOG_NOTICE, "whoami failed %m");
214 return (NULL);
215 }
216
217
218 bp_getfile_res *
219 bootparamproc_getfile_1_svc(getfile, rqstp)
220 bp_getfile_arg *getfile;
221 struct svc_req *rqstp;
222 {
223 static bp_getfile_res res;
224 struct hostent *he;
225 int err;
226
227 if (debug)
228 warnx("getfile got question for \"%s\" and file \"%s\"",
229 getfile->client_name, getfile->file_id);
230
231 if (dolog)
232 syslog(LOG_NOTICE,
233 "getfile got question for \"%s\" and file \"%s\"",
234 getfile->client_name, getfile->file_id);
235
236 he = NULL;
237 he = gethostbyname(getfile->client_name);
238 if (!he)
239 goto failed;
240
241 strncpy(askname, he->h_name, sizeof(askname));
242 askname[sizeof(askname)-1] = 0;
243 err = lookup_bootparam(askname, NULL, getfile->file_id,
244 &res.server_name, &res.server_path);
245 if (err == 0) {
246 he = gethostbyname(res.server_name);
247 if (!he)
248 goto failed;
249 memmove(&res.server_address.bp_address_u.ip_addr,
250 he->h_addr, 4);
251 res.server_address.address_type = IP_ADDR_TYPE;
252 } else if (err == ENOENT && !strcmp(getfile->file_id, "dump")) {
253 /* Special for dump, answer with null strings. */
254 res.server_name[0] = '\0';
255 res.server_path[0] = '\0';
256 memset(&res.server_address.bp_address_u.ip_addr, 0, 4);
257 } else {
258 failed:
259 if (debug)
260 warnx("getfile failed for %s",
261 getfile->client_name);
262 if (dolog)
263 syslog(LOG_NOTICE,
264 "getfile failed for %s", getfile->client_name);
265 return (NULL);
266 }
267
268 if (debug)
269 warnx(
270 "returning server:%s path:%s address: %d.%d.%d.%d",
271 res.server_name, res.server_path,
272 255 & res.server_address.bp_address_u.ip_addr.net,
273 255 & res.server_address.bp_address_u.ip_addr.host,
274 255 & res.server_address.bp_address_u.ip_addr.lh,
275 255 & res.server_address.bp_address_u.ip_addr.impno);
276 if (dolog)
277 syslog(LOG_NOTICE,
278 "returning server:%s path:%s address: %d.%d.%d.%d",
279 res.server_name, res.server_path,
280 255 & res.server_address.bp_address_u.ip_addr.net,
281 255 & res.server_address.bp_address_u.ip_addr.host,
282 255 & res.server_address.bp_address_u.ip_addr.lh,
283 255 & res.server_address.bp_address_u.ip_addr.impno);
284 return (&res);
285 }
286
287
288 int
289 lookup_bootparam(client, client_canonical, id, server, path)
290 char *client;
291 char *client_canonical;
292 char *id;
293 char **server;
294 char **path;
295 {
296 FILE *f = fopen(bootpfile, "r");
297 #ifdef YP
298 static char *ypbuf = NULL;
299 static int ypbuflen = 0;
300 #endif
301 static char buf[BUFSIZ];
302 char *bp, *word = NULL;
303 size_t idlen = id == NULL ? 0 : strlen(id);
304 int contin = 0;
305 int found = 0;
306
307 if (f == NULL)
308 return EINVAL; /* ? */
309
310 while (fgets(buf, sizeof buf, f)) {
311 int wascontin = contin;
312 contin = buf[strlen(buf) - 2] == '\\';
313 bp = buf + strspn(buf, " \t\n");
314
315 switch (wascontin) {
316 case -1:
317 /* Continuation of uninteresting line */
318 contin *= -1;
319 continue;
320 case 0:
321 /* New line */
322 contin *= -1;
323 if (*bp == '#')
324 continue;
325 if ((word = strsep(&bp, " \t\n")) == NULL)
326 continue;
327 #ifdef YP
328 /* A + in the file means try YP now */
329 if (!strcmp(word, "+")) {
330 char *ypdom;
331
332 if (yp_get_default_domain(&ypdom) ||
333 yp_match(ypdom, "bootparams", client,
334 strlen(client), &ypbuf, &ypbuflen))
335 continue;
336 bp = ypbuf;
337 word = client;
338 contin *= -1;
339 break;
340 }
341 #endif
342 if (debug)
343 warnx("match %s with %s", word, client);
344 /* See if this line's client is the one we are
345 * looking for */
346 if (strcasecmp(word, client) != 0) {
347 /*
348 * If it didn't match, try getting the
349 * canonical host name of the client
350 * on this line and comparing that to
351 * the client we are looking for
352 */
353 struct hostent *hp = gethostbyname(word);
354 if (hp == NULL ) {
355 if (debug)
356 warnx(
357 "Unknown bootparams host %s", word);
358 if (dolog)
359 syslog(LOG_NOTICE,
360 "Unknown bootparams host %s", word);
361 continue;
362 }
363 if (strcasecmp(hp->h_name, client))
364 continue;
365 }
366 contin *= -1;
367 break;
368 case 1:
369 /* Continued line we want to parse below */
370 break;
371 }
372
373 if (client_canonical)
374 strncpy(client_canonical, word, MAX_MACHINE_NAME);
375
376 /* We have found a line for CLIENT */
377 if (id == NULL) {
378 (void) fclose(f);
379 return 0;
380 }
381
382 /* Look for a value for the parameter named by ID */
383 while ((word = strsep(&bp, " \t\n")) != NULL) {
384 if (!strncmp(word, id, idlen) && word[idlen] == '=') {
385 /* We have found the entry we want */
386 *server = &word[idlen + 1];
387 *path = strchr(*server, ':');
388 if (*path == NULL)
389 /* Malformed entry */
390 continue;
391 *(*path)++ = '\0';
392 (void) fclose(f);
393 return 0;
394 }
395 }
396
397 found = 1;
398 }
399
400 (void) fclose(f);
401 return found ? ENOENT : EPERM;
402 }
403
404 void
405 usage()
406 {
407 fprintf(stderr,
408 "usage: %s [-d] [-s] [-r router] [-f bootparmsfile]\n", __progname);
409 exit(1);
410 }
411
412 static int
413 get_localaddr(ifname, sin)
414 const char *ifname;
415 struct sockaddr_in *sin;
416 {
417 struct ifaddrs *ifap, *ifa;
418
419 if (getifaddrs(&ifap) != 0)
420 return -1;
421
422 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
423 if (ifname && strcmp(ifname, ifa->ifa_name) != 0)
424 continue;
425 if (ifa->ifa_addr->sa_family != AF_INET)
426 continue;
427 if (ifa->ifa_addr->sa_len != sizeof(*sin))
428 continue;
429
430 /* no loopback please */
431 #ifdef IFF_LOOPBACK
432 if (ifa->ifa_flags & IFF_LOOPBACK)
433 continue;
434 #else
435 if (strncmp(ifa->ifa_name, "lo", 2) == 0)
436 continue;
437 #endif
438
439 /* XXX more sanity checks? */
440
441 /* candidate found */
442 memcpy(sin, ifa->ifa_addr, ifa->ifa_addr->sa_len);
443 freeifaddrs(ifap);
444 return 0;
445 }
446
447 /* no candidate */
448 freeifaddrs(ifap);
449 return -1;
450 }
451