ypbind.c revision 1.1 1 #include <sys/param.h>
2 #include <sys/types.h>
3 #include <sys/ioctl.h>
4 #include <sys/signal.h>
5 #include <sys/socket.h>
6 #include <sys/file.h>
7 #include <sys/fcntl.h>
8 #include <sys/syslog.h>
9 #include <stdio.h>
10 #include <errno.h>
11 #include <ctype.h>
12 #include <dirent.h>
13 #include <netdb.h>
14 #include <string.h>
15 #include <rpc/rpc.h>
16 #include <rpc/xdr.h>
17 #include <net/if.h>
18 #include <arpa/inet.h>
19 #include <rpc/pmap_clnt.h>
20 #include <rpc/pmap_prot.h>
21 #include <rpc/pmap_rmt.h>
22 #include <unistd.h>
23 #include <rpcsvc/yp_prot.h>
24 #include <rpcsvc/ypclnt.h>
25
26 #ifndef BINDINGDIR
27 #define BINDINGDIR "/var/yp/binding"
28 #endif
29
30 struct _dom_binding {
31 struct _dom_binding *dom_pnext;
32 char dom_domain[YPMAXDOMAIN + 1];
33 struct sockaddr_in dom_server_addr;
34 unsigned short int dom_server_port;
35 int dom_socket;
36 CLIENT *dom_client;
37 long int dom_vers;
38 time_t dom_check_t;
39 int dom_lockfd;
40 int dom_alive;
41 };
42
43 extern bool_t xdr_domainname(), xdr_ypbind_resp();
44 extern bool_t xdr_ypreq_key(), xdr_ypresp_val();
45 extern bool_t xdr_ypbind_setdom();
46
47 char *domainname;
48
49 struct _dom_binding *ypbindlist;
50 int check;
51
52 #define YPSET_NO 0
53 #define YPSET_LOCAL 1
54 #define YPSET_ALL 2
55 int ypsetmode = YPSET_NO;
56
57 int rpcsock;
58 struct rmtcallargs rmtca;
59 struct rmtcallres rmtcr;
60 char rmtcr_outval;
61 u_long rmtcr_port;
62
63 void *
64 ypbindproc_null_2(transp, argp, clnt)
65 SVCXPRT *transp;
66 void *argp;
67 CLIENT *clnt;
68 {
69 static char res;
70
71 bzero((char *)&res, sizeof(res));
72 return (void *)&res;
73 }
74
75 struct ypbind_resp *
76 ypbindproc_domain_2(transp, argp, clnt)
77 SVCXPRT *transp;
78 char *argp;
79 CLIENT *clnt;
80 {
81 static struct ypbind_resp res;
82 struct _dom_binding *ypdb;
83 char path[MAXPATHLEN];
84
85 bzero((char *)&res, sizeof res);
86 res.ypbind_status = YPBIND_FAIL_VAL;
87
88 for(ypdb=ypbindlist; ypdb; ypdb=ypdb->dom_pnext)
89 if( strcmp(ypdb->dom_domain, argp) == 0)
90 break;
91
92 if(ypdb==NULL) {
93 ypdb = (struct _dom_binding *)malloc(sizeof *ypdb);
94 bzero((char *)ypdb, sizeof *ypdb);
95 strncpy(ypdb->dom_domain, argp, sizeof ypdb->dom_domain);
96 ypdb->dom_vers = YPVERS;
97 ypdb->dom_alive = 0;
98 ypdb->dom_lockfd = -1;
99 sprintf(path, "%s/%s.%d", BINDINGDIR, ypdb->dom_domain, ypdb->dom_vers);
100 unlink(path);
101 ypdb->dom_pnext = ypbindlist;
102 ypbindlist = ypdb;
103 check++;
104 return NULL;
105 }
106
107 if(ypdb->dom_alive==0)
108 return NULL;
109
110 #if 0
111 delta = ypdb->dom_check_t - ypdb->dom_ask_t;
112 if( !(ypdb->dom_ask_t==0 || delta > 5)) {
113 ypdb->dom_ask_t = time(NULL);
114 /*
115 * Hmm. More than 2 requests in 5 seconds have indicated that my
116 * binding is possibly incorrect. Ok, make myself unalive, and
117 * find out what the actual state is.
118 */
119 if(ypdb->dom_lockfd!=-1)
120 close(ypdb->dom_lockfd);
121 ypdb->dom_lockfd = -1;
122 ypdb->dom_alive = 0;
123 ypdb->dom_lockfd = -1;
124 sprintf(path, "%s/%s.%d", BINDINGDIR, ypdb->dom_domain, ypdb->dom_vers);
125 unlink(path);
126 check++;
127 return NULL;
128 }
129 #endif
130
131 answer:
132 res.ypbind_status = YPBIND_SUCC_VAL;
133 res.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr.s_addr =
134 ypdb->dom_server_addr.sin_addr.s_addr;
135 res.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port =
136 ypdb->dom_server_port;
137 /*printf("domain %s at %s/%d\n", ypdb->dom_domain,
138 inet_ntoa(ypdb->dom_server_addr.sin_addr),
139 ntohs(ypdb->dom_server_addr.sin_port));*/
140 return &res;
141 }
142
143 bool_t *
144 ypbindproc_setdom_2(transp, argp, clnt)
145 SVCXPRT *transp;
146 struct ypbind_setdom *argp;
147 CLIENT *clnt;
148 {
149 struct sockaddr_in *fromsin, bindsin;
150 char res;
151
152 bzero((char *)&res, sizeof(res));
153 fromsin = svc_getcaller(transp);
154
155 switch(ypsetmode) {
156 case YPSET_LOCAL:
157 if( fromsin->sin_addr.s_addr != htonl(INADDR_LOOPBACK))
158 return (void *)NULL;
159 break;
160 case YPSET_ALL:
161 break;
162 case YPSET_NO:
163 default:
164 return (void *)NULL;
165 }
166
167 if(argp->ypsetdom_vers != YPVERS)
168 return (void *)&res;
169
170 bzero((char *)&bindsin, sizeof bindsin);
171 bindsin.sin_family = AF_INET;
172 bindsin.sin_addr.s_addr = argp->ypsetdom_addr.s_addr;
173 bindsin.sin_port = argp->ypsetdom_port;
174 rpc_received(argp->ypsetdom_domain, &bindsin, 1);
175
176 res = 1;
177 return (void *)&res;
178 }
179
180 static void
181 ypbindprog_2(rqstp, transp)
182 struct svc_req *rqstp;
183 register SVCXPRT *transp;
184 {
185 union {
186 char ypbindproc_domain_2_arg[MAXHOSTNAMELEN];
187 struct ypbind_setdom ypbindproc_setdom_2_arg;
188 } argument;
189 struct authunix_parms *creds;
190 char *result;
191 bool_t (*xdr_argument)(), (*xdr_result)();
192 char *(*local)();
193
194 switch (rqstp->rq_proc) {
195 case YPBINDPROC_NULL:
196 xdr_argument = xdr_void;
197 xdr_result = xdr_void;
198 local = (char *(*)()) ypbindproc_null_2;
199 break;
200
201 case YPBINDPROC_DOMAIN:
202 xdr_argument = xdr_domainname;
203 xdr_result = xdr_ypbind_resp;
204 local = (char *(*)()) ypbindproc_domain_2;
205 break;
206
207 case YPBINDPROC_SETDOM:
208 switch(rqstp->rq_cred.oa_flavor) {
209 case AUTH_UNIX:
210 creds = (struct authunix_parms *)rqstp->rq_clntcred;
211 if( creds->aup_uid != 0) {
212 svcerr_auth(transp, AUTH_BADCRED);
213 return;
214 }
215 break;
216 default:
217 svcerr_auth(transp, AUTH_TOOWEAK);
218 return;
219 }
220
221 xdr_argument = xdr_ypbind_setdom;
222 xdr_result = xdr_void;
223 local = (char *(*)()) ypbindproc_setdom_2;
224 break;
225
226 default:
227 svcerr_noproc(transp);
228 return;
229 }
230 bzero((char *)&argument, sizeof(argument));
231 if (!svc_getargs(transp, xdr_argument, &argument)) {
232 svcerr_decode(transp);
233 return;
234 }
235 result = (*local)(transp, &argument, rqstp);
236 if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
237 svcerr_systemerr(transp);
238 }
239 return;
240 }
241
242 main(argc, argv)
243 char **argv;
244 {
245 char path[MAXPATHLEN];
246 struct timeval tv;
247 SVCXPRT *transp;
248 fd_set fdsr;
249 int width;
250 int i;
251
252 yp_get_default_domain(&domainname);
253 if( domainname[0] == '\0') {
254 fprintf(stderr, "domainname not set. Aborting.\n");
255 exit(1);
256 }
257
258 for(i=1; i<argc; i++) {
259 if( strcmp("-ypset", argv[i]) == 0)
260 ypsetmode = YPSET_ALL;
261 else if (strcmp("-ypsetme", argv[i]) == 0)
262 ypsetmode = YPSET_LOCAL;
263 }
264
265 /* blow away everything in BINDINGDIR */
266
267
268
269 #ifdef DAEMON
270 switch(fork()) {
271 case 0:
272 break;
273 case -1:
274 perror("fork");
275 exit(1);
276 default:
277 exit(0);
278 }
279 setsid();
280 #endif
281
282 pmap_unset(YPBINDPROG, YPBINDVERS);
283
284 transp = svcudp_create(RPC_ANYSOCK);
285 if (transp == NULL) {
286 fprintf(stderr, "cannot create udp service.");
287 exit(1);
288 }
289 if (!svc_register(transp, YPBINDPROG, YPBINDVERS, ypbindprog_2, IPPROTO_UDP)) {
290 fprintf(stderr, "unable to register (YPBINDPROG, YPBINDVERS, udp).");
291 exit(1);
292 }
293
294 transp = svctcp_create(RPC_ANYSOCK, 0, 0);
295 if (transp == NULL) {
296 fprintf(stderr, "cannot create tcp service.");
297 exit(1);
298 }
299
300 if (!svc_register(transp, YPBINDPROG, YPBINDVERS, ypbindprog_2, IPPROTO_TCP)) {
301 fprintf(stderr, "unable to register (YPBINDPROG, YPBINDVERS, tcp).");
302 exit(1);
303 }
304
305 if( (rpcsock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
306 perror("socket");
307 return -1;
308 }
309 fcntl(rpcsock, F_SETFL, fcntl(rpcsock, F_GETFL, 0) | FNDELAY);
310 i = 1;
311 setsockopt(rpcsock, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i));
312 rmtca.prog = YPPROG;
313 rmtca.vers = YPVERS;
314 rmtca.proc = YPPROC_DOMAIN_NONACK;
315 rmtca.xdr_args = NULL; /* set at call time */
316 rmtca.args_ptr = NULL; /* set at call time */
317 rmtcr.port_ptr = &rmtcr_port;
318 rmtcr.xdr_results = xdr_bool;
319 rmtcr.results_ptr = (caddr_t)&rmtcr_outval;
320
321 /* build initial domain binding, make it "unsuccessful" */
322 ypbindlist = (struct _dom_binding *)malloc(sizeof *ypbindlist);
323 bzero((char *)ypbindlist, sizeof *ypbindlist);
324 strncpy(ypbindlist->dom_domain, domainname, sizeof ypbindlist->dom_domain);
325 ypbindlist->dom_vers = YPVERS;
326 ypbindlist->dom_alive = 0;
327 ypbindlist->dom_lockfd = -1;
328 sprintf(path, "%s/%s.%d", BINDINGDIR, ypbindlist->dom_domain,
329 ypbindlist->dom_vers);
330 (void)unlink(path);
331
332 width = getdtablesize();
333 while(1) {
334 fdsr = svc_fdset;
335 FD_SET(rpcsock, &fdsr);
336 tv.tv_sec = 1;
337 tv.tv_usec = 0;
338
339 switch(select(width, &fdsr, NULL, NULL, &tv)) {
340 case 0:
341 checkwork();
342 break;
343 case -1:
344 perror("select\n");
345 break;
346 default:
347 if(FD_ISSET(rpcsock, &fdsr)) {
348 FD_CLR(rpcsock, &fdsr);
349 handle_replies();
350 }
351 svc_getreqset(&fdsr);
352 if(check)
353 checkwork();
354 break;
355 }
356 }
357 }
358
359 /*
360 * change to do something like this:
361 *
362 * STATE TIME ACTION NEWTIME NEWSTATE
363 * no binding t==* broadcast t=2 no binding
364 * binding t==60 check server t=10 binding
365 * binding t=10 broadcast t=2 no binding
366 */
367 checkwork()
368 {
369 struct _dom_binding *ypdb;
370 time_t t;
371
372 check = 0;
373
374 time(&t);
375 for(ypdb=ypbindlist; ypdb; ypdb=ypdb->dom_pnext) {
376 if(ypdb->dom_alive==0 || ypdb->dom_check_t < t) {
377 broadcast(ypdb->dom_domain);
378 time(&t);
379 ypdb->dom_check_t = t + 5;
380 }
381 }
382 }
383
384 broadcast(dom)
385 char *dom;
386 {
387 struct rpc_msg rpcmsg;
388 char buf[1400], inbuf[8192];
389 enum clnt_stat st;
390 struct timeval tv;
391 int outlen, i, sock, len;
392 struct sockaddr_in bsin;
393 struct ifconf ifc;
394 struct ifreq ifreq, *ifr;
395 struct in_addr in;
396 AUTH *rpcua;
397 XDR rpcxdr;
398
399 rmtca.xdr_args = xdr_domainname;
400 rmtca.args_ptr = dom;
401
402 bzero((char *)&bsin, sizeof bsin);
403 bsin.sin_family = AF_INET;
404 bsin.sin_port = htons(PMAPPORT);
405
406 bzero((char *)&rpcxdr, sizeof rpcxdr);
407 bzero((char *)&rpcmsg, sizeof rpcmsg);
408
409 rpcua = authunix_create_default();
410 if( rpcua == (AUTH *)NULL) {
411 /*printf("cannot get unix auth\n");*/
412 return RPC_SYSTEMERROR;
413 }
414 rpcmsg.rm_direction = CALL;
415 rpcmsg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
416 rpcmsg.rm_call.cb_prog = PMAPPROG;
417 rpcmsg.rm_call.cb_vers = PMAPVERS;
418 rpcmsg.rm_call.cb_proc = PMAPPROC_CALLIT;
419 rpcmsg.rm_call.cb_cred = rpcua->ah_cred;
420 rpcmsg.rm_call.cb_verf = rpcua->ah_verf;
421
422 gettimeofday(&tv, (struct timezone *)0);
423 rpcmsg.rm_xid = (int)dom;
424 tv.tv_usec = 0;
425 xdrmem_create(&rpcxdr, buf, sizeof buf, XDR_ENCODE);
426 if( (!xdr_callmsg(&rpcxdr, &rpcmsg)) ) {
427 st = RPC_CANTENCODEARGS;
428 AUTH_DESTROY(rpcua);
429 return st;
430 }
431 if( (!xdr_rmtcall_args(&rpcxdr, &rmtca)) ) {
432 st = RPC_CANTENCODEARGS;
433 AUTH_DESTROY(rpcua);
434 return st;
435 }
436 outlen = (int)xdr_getpos(&rpcxdr);
437 xdr_destroy(&rpcxdr);
438 if(outlen<1) {
439 AUTH_DESTROY(rpcua);
440 return st;
441 }
442 AUTH_DESTROY(rpcua);
443
444 /* find all networks and send the RPC packet out them all */
445 if( (sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
446 perror("socket");
447 return -1;
448 }
449
450 ifc.ifc_len = sizeof inbuf;
451 ifc.ifc_buf = inbuf;
452 if( ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
453 close(sock);
454 perror("ioctl(SIOCGIFCONF)");
455 return -1;
456 }
457 ifr = ifc.ifc_req;
458 ifreq.ifr_name[0] = '\0';
459 for(i=0; i<ifc.ifc_len; i+=len, ifr=(struct ifreq *)((caddr_t)ifr+len)) {
460 #if defined(BSD) && BSD >= 199103
461 len = sizeof ifr->ifr_name + ifr->ifr_addr.sa_len;
462 #else
463 len = sizeof ifc.ifc_len / sizeof(struct ifreq);
464 #endif
465 ifreq = *ifr;
466 if( ifreq.ifr_addr.sa_family != AF_INET)
467 continue;
468 if( ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) {
469 perror("ioctl(SIOCGIFFLAGS)");
470 continue;
471 }
472 if( (ifreq.ifr_flags & IFF_UP) == 0)
473 continue;
474
475 ifreq.ifr_flags &= (IFF_LOOPBACK | IFF_BROADCAST);
476 if( ifreq.ifr_flags==IFF_BROADCAST ) {
477 if( ioctl(sock, SIOCGIFBRDADDR, &ifreq) < 0 ) {
478 perror("ioctl(SIOCGIFBRDADDR)");
479 continue;
480 }
481 } else if( ifreq.ifr_flags==IFF_LOOPBACK ) {
482 if( ioctl(sock, SIOCGIFADDR, &ifreq) < 0 ) {
483 perror("ioctl(SIOCGIFADDR)");
484 continue;
485 }
486 } else
487 continue;
488
489 in = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
490 bsin.sin_addr = in;
491 if( sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bsin,
492 sizeof bsin) < 0 )
493 perror("sendto");
494 }
495 close(sock);
496 return 0;
497 }
498
499 /*enum clnt_stat*/
500 handle_replies()
501 {
502 char buf[1400];
503 int fromlen, inlen;
504 struct sockaddr_in raddr;
505 struct rpc_msg msg;
506 XDR xdr;
507
508 recv_again:
509 bzero((char *)&xdr, sizeof(xdr));
510 bzero((char *)&msg, sizeof(msg));
511 msg.acpted_rply.ar_verf = _null_auth;
512 msg.acpted_rply.ar_results.where = (caddr_t)&rmtcr;
513 msg.acpted_rply.ar_results.proc = xdr_rmtcallres;
514
515 try_again:
516 fromlen = sizeof (struct sockaddr);
517 inlen = recvfrom(rpcsock, buf, sizeof buf, 0,
518 (struct sockaddr *)&raddr, &fromlen);
519 if(inlen<0) {
520 if(errno==EINTR)
521 goto try_again;
522 return RPC_CANTRECV;
523 }
524 if(inlen<sizeof(u_long))
525 goto recv_again;
526
527 /*
528 * see if reply transaction id matches sent id.
529 * If so, decode the results.
530 */
531 xdrmem_create(&xdr, buf, (u_int)inlen, XDR_DECODE);
532 if( xdr_replymsg(&xdr, &msg)) {
533 if( (msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
534 (msg.acpted_rply.ar_stat == SUCCESS)) {
535 raddr.sin_port = htons((u_short)rmtcr_port);
536 rpc_received(msg.rm_xid, &raddr, 0);
537 }
538 }
539 xdr.x_op = XDR_FREE;
540 msg.acpted_rply.ar_results.proc = xdr_void;
541 xdr_destroy(&xdr);
542
543 return RPC_SUCCESS;
544 }
545
546 /*
547 * LOOPBACK IS MORE IMPORTANT: PUT IN HACK
548 */
549 rpc_received(dom, raddrp, force)
550 char *dom;
551 struct sockaddr_in *raddrp;
552 int force;
553 {
554 struct _dom_binding *ypdb;
555 char path[MAXPATHLEN];
556 int fd;
557
558 /*printf("returned from %s about %s\n", inet_ntoa(raddrp->sin_addr), dom);*/
559
560 if(dom==NULL)
561 return;
562
563 for(ypdb=ypbindlist; ypdb; ypdb=ypdb->dom_pnext)
564 if( strcmp(ypdb->dom_domain, dom) == 0)
565 break;
566
567 if(ypdb==NULL) {
568 if(force==0)
569 return;
570 ypdb = (struct _dom_binding *)malloc(sizeof *ypdb);
571 bzero((char *)ypdb, sizeof *ypdb);
572 strncpy(ypdb->dom_domain, dom, sizeof ypdb->dom_domain);
573 ypdb->dom_lockfd = -1;
574 ypdb->dom_pnext = ypbindlist;
575 ypbindlist = ypdb;
576 }
577
578 /* soft update, alive, less than 30 seconds old */
579 if(ypdb->dom_alive==1 && force==0 && ypdb->dom_check_t<time(NULL)+30)
580 return;
581
582 bcopy((char *)raddrp, (char *)&ypdb->dom_server_addr,
583 sizeof ypdb->dom_server_addr);
584 ypdb->dom_check_t = time(NULL) + 60; /* recheck binding in 60 seconds */
585 ypdb->dom_vers = YPVERS;
586 ypdb->dom_alive = 1;
587
588 if(ypdb->dom_lockfd != -1)
589 close(ypdb->dom_lockfd);
590
591 sprintf(path, "%s/%s.%d", BINDINGDIR,
592 ypdb->dom_domain, ypdb->dom_vers);
593 #ifdef O_SHLOCK
594 if( (fd=open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1) {
595 (void)mkdir(BINDINGDIR, 0755);
596 if( (fd=open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1)
597 return;
598 }
599 #else
600 if( (fd=open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1) {
601 (void)mkdir(BINDINGDIR, 0755);
602 if( (fd=open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1)
603 return;
604 }
605 flock(fd, LOCK_SH);
606
607 /*
608 * ok, if BINDINGDIR exists, and we can create the binding file,
609 * then write to it..
610 */
611 ypdb->dom_lockfd = fd;
612 if( write(ypdb->dom_lockfd, raddrp, sizeof *raddrp) != sizeof *raddrp) {
613 perror("write");
614 close(ypdb->dom_lockfd);
615 ypdb->dom_lockfd = -1;
616 return;
617 }
618 #endif
619 }
620