1 /* $NetBSD: bootparamd.c,v 1.44 2004/10/30 15:23:30 dsl Exp $ */
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@nada.kth.se>
8 * Various small changes by Theo de Raadt <deraadt@fsa.ca>
9 * Parser rewritten (adding YP support) by Roland McGrath <roland@frob.com>
12 #include <sys/cdefs.h>
14 __RCSID("$NetBSD: bootparamd.c,v 1.44 2004/10/30 15:23:30 dsl Exp $");
17 #include <sys/types.h>
18 #include <sys/ioctl.h>
20 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
42 #include <rpc/pmap_clnt.h>
43 #include <rpcsvc/bootparam_prot.h>
45 #include <rpcsvc/ypclnt.h>
48 #include "pathnames.h"
52 static char hostname
[MAX_MACHINE_NAME
];
53 static char askname
[MAX_MACHINE_NAME
];
54 static char domain_name
[MAX_MACHINE_NAME
];
56 extern void bootparamprog_1
__P((struct svc_req
*, SVCXPRT
*));
62 struct in_addr route_addr
;
63 struct sockaddr_in my_addr
;
64 const char *bootpfile
= _PATH_BOOTPARAMS
;
67 int main
__P((int, char *[]));
68 int lookup_bootparam
__P((char *, char *, char *, char **, char **));
69 void usage
__P((void));
70 static int get_localaddr
__P((const char *, struct sockaddr_in
*));
86 while ((c
= getopt(argc
, argv
, "di:sr:f:")) != -1)
95 if (isdigit((unsigned char)*optarg
)) {
96 if (inet_aton(optarg
, &route_addr
) != 0)
99 he
= gethostbyname(optarg
);
101 warnx("no such host: %s", optarg
);
104 memmove(&route_addr
.s_addr
, he
->h_addr
, he
->h_length
);
112 openlog("rpc.bootparamd", 0, 0);
114 openlog("rpc.bootparamd", 0, LOG_DAEMON
);
115 setlogmask(LOG_UPTO(LOG_NOTICE
));
122 if (stat(bootpfile
, &buf
))
123 err(1, "%s", bootpfile
);
125 if (route_addr
.s_addr
== 0) {
126 if (get_localaddr(NULL
, &my_addr
) != 0)
127 errx(1, "router address not found");
128 route_addr
.s_addr
= my_addr
.sin_addr
.s_addr
;
132 err(1, "can't detach from terminal");
136 (void) pmap_unset(BOOTPARAMPROG
, BOOTPARAMVERS
);
138 transp
= svcudp_create(RPC_ANYSOCK
);
140 errx(1, "can't create udp service");
142 if (!svc_register(transp
, BOOTPARAMPROG
, BOOTPARAMVERS
, bootparamprog_1
,
145 * Do NOT change the "%u" in the format string below to "%lu". If your
146 * build fails update the "rpcgen" program and use "make cleandir" and
147 * "make includes" in "src/lib/librpcsvc" afterwards.
149 errx(1, "unable to register BOOTPARAMPROG version %u, udp",
153 errx(1, "svc_run returned");
157 bootparamproc_whoami_1_svc(whoami
, rqstp
)
158 bp_whoami_arg
*whoami
;
159 struct svc_req
*rqstp
;
161 static bp_whoami_res res
;
163 struct in_addr haddr
;
167 warnx("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
);
173 syslog(LOG_NOTICE
, "whoami got question for %d.%d.%d.%d",
174 255 & whoami
->client_address
.bp_address_u
.ip_addr
.net
,
175 255 & whoami
->client_address
.bp_address_u
.ip_addr
.host
,
176 255 & whoami
->client_address
.bp_address_u
.ip_addr
.lh
,
177 255 & whoami
->client_address
.bp_address_u
.ip_addr
.impno
);
179 memmove((char *) &haddr
,
180 (char *) &whoami
->client_address
.bp_address_u
.ip_addr
,
182 he
= gethostbyaddr((char *) &haddr
, sizeof(haddr
), AF_INET
);
184 (void)strlcpy(askname
, he
->h_name
, sizeof(askname
));
186 (void)strlcpy(askname
, inet_ntoa(haddr
), sizeof(askname
));
190 warnx("This is host %s", askname
);
192 syslog(LOG_NOTICE
, "This is host %s", askname
);
194 if ((e
= lookup_bootparam(askname
, hostname
, NULL
, NULL
, NULL
)) == 0) {
195 res
.client_name
= hostname
;
196 getdomainname(domain_name
, MAX_MACHINE_NAME
);
197 res
.domain_name
= domain_name
;
199 if (res
.router_address
.address_type
!= IP_ADDR_TYPE
) {
200 res
.router_address
.address_type
= IP_ADDR_TYPE
;
201 memmove(&res
.router_address
.bp_address_u
.ip_addr
,
202 &route_addr
.s_addr
,4);
205 warnx("Returning %s %s %d.%d.%d.%d",
206 res
.client_name
, res
.domain_name
,
207 255 & res
.router_address
.bp_address_u
.ip_addr
.net
,
208 255 & res
.router_address
.bp_address_u
.ip_addr
.host
,
209 255 & res
.router_address
.bp_address_u
.ip_addr
.lh
,
210 255 &res
.router_address
.bp_address_u
.ip_addr
.impno
);
212 syslog(LOG_NOTICE
, "Returning %s %s %d.%d.%d.%d",
213 res
.client_name
, res
.domain_name
,
214 255 & res
.router_address
.bp_address_u
.ip_addr
.net
,
215 255 & res
.router_address
.bp_address_u
.ip_addr
.host
,
216 255 & res
.router_address
.bp_address_u
.ip_addr
.lh
,
217 255 &res
.router_address
.bp_address_u
.ip_addr
.impno
);
223 warn("whoami failed");
225 syslog(LOG_NOTICE
, "whoami failed %m");
231 bootparamproc_getfile_1_svc(getfile
, rqstp
)
232 bp_getfile_arg
*getfile
;
233 struct svc_req
*rqstp
;
235 static bp_getfile_res res
;
240 warnx("getfile got question for \"%s\" and file \"%s\"",
241 getfile
->client_name
, getfile
->file_id
);
245 "getfile got question for \"%s\" and file \"%s\"",
246 getfile
->client_name
, getfile
->file_id
);
249 he
= gethostbyname(getfile
->client_name
);
252 warnx("getfile can't resolve client %s",
253 getfile
->client_name
);
255 syslog(LOG_NOTICE
, "getfile can't resolve client %s",
256 getfile
->client_name
);
260 (void)strlcpy(askname
, he
->h_name
, sizeof(askname
));
261 error
= lookup_bootparam(askname
, NULL
, getfile
->file_id
,
262 &res
.server_name
, &res
.server_path
);
264 he
= gethostbyname(res
.server_name
);
267 warnx("getfile can't resolve server %s for %s",
268 res
.server_name
, getfile
->client_name
);
271 "getfile can't resolve server %s for %s",
272 res
.server_name
, getfile
->client_name
);
276 memmove(&res
.server_address
.bp_address_u
.ip_addr
,
278 res
.server_address
.address_type
= IP_ADDR_TYPE
;
279 } else if (error
== ENOENT
&& !strcmp(getfile
->file_id
, "dump")) {
280 /* Special for dump, answer with null strings. */
281 res
.server_name
[0] = '\0';
282 res
.server_path
[0] = '\0';
283 memset(&res
.server_address
.bp_address_u
.ip_addr
, 0, 4);
286 warnx("getfile lookup failed for %s",
287 getfile
->client_name
);
290 "getfile lookup failed for %s",
291 getfile
->client_name
);
297 "returning server:%s path:%s address: %d.%d.%d.%d",
298 res
.server_name
, res
.server_path
,
299 255 & res
.server_address
.bp_address_u
.ip_addr
.net
,
300 255 & res
.server_address
.bp_address_u
.ip_addr
.host
,
301 255 & res
.server_address
.bp_address_u
.ip_addr
.lh
,
302 255 & res
.server_address
.bp_address_u
.ip_addr
.impno
);
305 "returning server:%s path:%s address: %d.%d.%d.%d",
306 res
.server_name
, res
.server_path
,
307 255 & res
.server_address
.bp_address_u
.ip_addr
.net
,
308 255 & res
.server_address
.bp_address_u
.ip_addr
.host
,
309 255 & res
.server_address
.bp_address_u
.ip_addr
.lh
,
310 255 & res
.server_address
.bp_address_u
.ip_addr
.impno
);
316 lookup_bootparam(client
, client_canonical
, id
, server
, path
)
318 char *client_canonical
;
323 FILE *f
= fopen(bootpfile
, "r");
325 static char *ypbuf
= NULL
;
326 static int ypbuflen
= 0;
328 static char buf
[BUFSIZ
];
329 char *canon
= NULL
, *bp
, *word
= NULL
;
330 size_t idlen
= id
== NULL
? 0 : strlen(id
);
335 return EINVAL
; /* ? */
337 while (fgets(buf
, sizeof buf
, f
)) {
338 int wascontin
= contin
;
339 contin
= buf
[strlen(buf
) - 2] == '\\';
340 bp
= buf
+ strspn(buf
, " \t\n");
344 /* Continuation of uninteresting line */
352 if ((word
= strsep(&bp
, " \t\n")) == NULL
)
355 /* A + in the file means try YP now */
356 if (!strcmp(word
, "+")) {
359 if (yp_get_default_domain(&ypdom
) ||
360 yp_match(ypdom
, "bootparams", client
,
361 strlen(client
), &ypbuf
, &ypbuflen
))
370 warnx("match %s with %s", word
, client
);
372 #define HASGLOB(str) \
373 (strchr(str, '*') != NULL || \
374 strchr(str, '?') != NULL || \
375 strchr(str, '[') != NULL || \
376 strchr(str, ']') != NULL)
378 /* See if this line's client is the one we are
380 if (fnmatch(word
, client
, FNM_CASEFOLD
) == 0) {
382 * Match. The token may be globbed, we
383 * can't just return that as the canonical
384 * name. Check to see if the token has any
385 * globbing characters in it (*, ?, [, ]).
386 * If so, just return the name we already
387 * have. Otherwise, return the token.
396 * If it didn't match, try getting the
397 * canonical host name of the client
398 * on this line, if it's not a glob,
399 * and comparing it to the client we
404 warnx("Skipping non-match: %s",
408 if ((hp
= gethostbyname(word
)) == NULL
) {
411 "Unknown bootparams host %s",
415 "Unknown bootparams host %s",
419 if (strcasecmp(hp
->h_name
, client
) != 0)
429 /* Continued line we want to parse below */
433 assert(canon
!= NULL
);
434 if (client_canonical
)
435 strncpy(client_canonical
, canon
, MAX_MACHINE_NAME
);
437 /* We have found a line for CLIENT */
443 /* Look for a value for the parameter named by ID */
444 while ((word
= strsep(&bp
, " \t\n")) != NULL
) {
445 if (!strncmp(word
, id
, idlen
) && word
[idlen
] == '=') {
446 /* We have found the entry we want */
447 *server
= &word
[idlen
+ 1];
448 *path
= strchr(*server
, ':');
450 /* Malformed entry */
462 return found
? ENOENT
: EPERM
;
469 "usage: %s [-ds] [-i interface] [-r router] [-f bootparamsfile]\n",
475 get_localaddr(ifname
, sin
)
477 struct sockaddr_in
*sin
;
479 struct ifaddrs
*ifap
, *ifa
;
481 if (getifaddrs(&ifap
) != 0)
484 for (ifa
= ifap
; ifa
; ifa
= ifa
->ifa_next
) {
485 if (ifname
&& strcmp(ifname
, ifa
->ifa_name
) != 0)
487 if (ifa
->ifa_addr
->sa_family
!= AF_INET
)
489 if (ifa
->ifa_addr
->sa_len
!= sizeof(*sin
))
492 /* no loopback please */
494 if (ifa
->ifa_flags
& IFF_LOOPBACK
)
497 if (strncmp(ifa
->ifa_name
, "lo", 2) == 0 &&
498 (isdigit(ifa
->ifa_name
[2]) || ifa
->ifa_name
[2] == '\0'))
502 if (!iface
|| strcmp(ifa
->ifa_name
, iface
) == 0)
507 /* candidate found */
508 memcpy(sin
, ifa
->ifa_addr
, ifa
->ifa_addr
->sa_len
);