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