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