1 /* $NetBSD: rshd.c,v 1.50 2012/07/14 15:06:26 darrenr Exp $ */
4 * Copyright (C) 1998 WIDE Project.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by WIDE Project and
19 * 4. Neither the name of the project nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * Copyright (c) 1988, 1989, 1992, 1993, 1994
38 * The Regents of the University of California. All rights reserved.
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
43 * 1. Redistributions of source code must retain the above copyright
44 * notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
48 * 3. Neither the name of the University nor the names of its contributors
49 * may be used to endorse or promote products derived from this software
50 * without specific prior written permission.
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 #include <sys/cdefs.h>
67 __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\
68 The Regents of the University of California. All rights reserved.");
70 static char sccsid
[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94";
72 __RCSID("$NetBSD: rshd.c,v 1.50 2012/07/14 15:06:26 darrenr Exp $");
77 * remote shell server:
84 #include <sys/param.h>
85 #include <sys/ioctl.h>
87 #include <sys/socket.h>
89 #include <netinet/in_systm.h>
90 #include <netinet/in.h>
91 #include <netinet/ip.h>
92 #include <netinet/tcp.h>
93 #include <arpa/inet.h>
108 #include <login_cap.h>
112 #include <security/pam_appl.h>
113 #include <security/openpam.h>
114 #include <sys/wait.h>
116 static struct pam_conv pamc
= { openpam_nullconv
, NULL
};
117 static pam_handle_t
*pamh
;
120 #define PAM_END do { \
121 if ((pam_err = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \
122 syslog(LOG_ERR|LOG_AUTH, "pam_setcred(): %s", \
123 pam_strerror(pamh, pam_err)); \
124 if ((pam_err = pam_close_session(pamh,0)) != PAM_SUCCESS) \
125 syslog(LOG_ERR|LOG_AUTH, "pam_close_session(): %s", \
126 pam_strerror(pamh, pam_err)); \
127 if ((pam_err = pam_end(pamh, pam_err)) != PAM_SUCCESS) \
128 syslog(LOG_ERR|LOG_AUTH, "pam_end(): %s", \
129 pam_strerror(pamh, pam_err)); \
130 } while (/*CONSTCOND*/0)
135 static int keepalive
= 1;
136 static int check_all
;
137 static int log_success
; /* If TRUE, log all successful accesses */
138 static int sent_null
;
140 __dead
static void doit(struct sockaddr
*, struct sockaddr
*);
141 __dead
static void rshd_errx(int, const char *, ...) __printflike(2, 3);
142 static void getstr(char *, int, const char *);
143 static int local_domain(char *);
144 static char *topdomain(char *);
145 __dead
static void usage(void);
147 #define OPTIONS "aLln"
148 extern int __check_rhosts_file
;
149 extern char *__rcmd_errstr
; /* syslog hook from libc/net/rcmd.c. */
150 #if defined(__minix) && defined(USE_PAM)
151 static const char incorrect
[] = "Login incorrect.";
152 #endif /* defined(__minix) && defined(USE_PAM) */
155 main(int argc
, char *argv
[])
157 struct linger linger
;
161 struct sockaddr_storage from
;
162 struct sockaddr_storage local
;
163 struct protoent
*proto
;
165 openlog("rshd", LOG_PID
, LOG_DAEMON
);
168 while ((ch
= getopt(argc
, argv
, OPTIONS
)) != -1)
174 __check_rhosts_file
= 0;
191 fromlen
= sizeof(from
); /* xxx */
192 locallen
= sizeof(local
); /* xxx */
193 if (getpeername(STDIN_FILENO
, (struct sockaddr
*)&from
, &fromlen
) < 0) {
194 syslog(LOG_ERR
, "getpeername: %m");
197 if (getsockname(STDIN_FILENO
, (struct sockaddr
*)&local
,
199 syslog(LOG_ERR
, "getsockname: %m");
203 if (((struct sockaddr
*)&from
)->sa_family
== AF_INET6
&&
204 IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6
*)&from
)->sin6_addr
) &&
205 sizeof(struct sockaddr_in
) <= sizeof(from
)) {
206 struct sockaddr_in sin
;
207 struct sockaddr_in6
*sin6
;
208 const int off
= sizeof(struct sockaddr_in6
) -
209 sizeof(struct sockaddr_in
);
211 sin6
= (struct sockaddr_in6
*)&from
;
212 (void)memset(&sin
, 0, sizeof(sin
));
213 sin
.sin_family
= AF_INET
;
214 sin
.sin_len
= sizeof(struct sockaddr_in
);
215 (void)memcpy(&sin
.sin_addr
, &sin6
->sin6_addr
.s6_addr
[off
],
216 sizeof(sin
.sin_addr
));
217 (void)memcpy(&from
, &sin
, sizeof(sin
));
218 fromlen
= sin
.sin_len
;
221 if (((struct sockaddr
*)&from
)->sa_family
== AF_INET6
&&
222 IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6
*)&from
)->sin6_addr
)) {
223 char hbuf
[NI_MAXHOST
];
224 if (getnameinfo((struct sockaddr
*)&from
, fromlen
, hbuf
,
225 sizeof(hbuf
), NULL
, 0, NI_NUMERICHOST
) != 0) {
226 strlcpy(hbuf
, "invalid", sizeof(hbuf
));
228 syslog(LOG_ERR
, "malformed \"from\" address (v4 mapped, %s)",
234 setsockopt(STDIN_FILENO
, SOL_SOCKET
, SO_KEEPALIVE
, (char *)&on
,
236 syslog(LOG_WARNING
, "setsockopt (SO_KEEPALIVE): %m");
238 linger
.l_linger
= 60; /* XXX */
239 if (setsockopt(STDIN_FILENO
, SOL_SOCKET
, SO_LINGER
, (char *)&linger
,
240 sizeof (linger
)) < 0)
241 syslog(LOG_WARNING
, "setsockopt (SO_LINGER): %m");
242 proto
= getprotobyname("tcp");
243 (void)setsockopt(STDIN_FILENO
, proto
->p_proto
, TCP_NODELAY
, &on
,
245 doit((struct sockaddr
*)&from
, (struct sockaddr
*)&local
);
248 extern char **environ
;
251 doit(struct sockaddr
*fromp
, struct sockaddr
*localp
)
253 struct passwd
*pwd
, pwres
;
255 struct pollfd set
[2];
256 int cc
, pv
[2], pid
, s
= -1; /* XXX gcc */
258 char *hostname
, *errorhost
= NULL
; /* XXX gcc */
260 char sig
, buf
[BUFSIZ
];
261 char cmdbuf
[NCARGS
+1], locuser
[16], remuser
[16];
262 char remotehost
[2 * MAXHOSTNAMELEN
+ 1];
263 char hostnamebuf
[2 * MAXHOSTNAMELEN
+ 1];
267 char naddr
[NI_MAXHOST
];
268 char saddr
[NI_MAXHOST
];
269 char raddr
[NI_MAXHOST
];
270 char pbuf
[NI_MAXSERV
];
271 int af
= fromp
->sa_family
;
273 struct addrinfo hints
, *res
, *res0
;
275 const int niflags
= NI_NUMERICHOST
| NI_NUMERICSERV
;
276 const char *errormsg
= NULL
, *errorstr
= NULL
;
279 (void)signal(SIGINT
, SIG_DFL
);
280 (void)signal(SIGQUIT
, SIG_DFL
);
281 (void)signal(SIGTERM
, SIG_DFL
);
284 int t
= open(_PATH_TTY
, O_RDWR
);
286 ioctl(t
, TIOCNOTTY
, NULL
);
293 portp
= &((struct sockaddr_in
*)fromp
)->sin_port
;
297 portp
= &((struct sockaddr_in6
*)fromp
)->sin6_port
;
301 syslog(LOG_ERR
, "malformed \"from\" address (af %d)", af
);
304 if (getnameinfo(fromp
, fromp
->sa_len
, naddr
, sizeof(naddr
),
305 pbuf
, sizeof(pbuf
), niflags
) != 0) {
306 syslog(LOG_ERR
, "malformed \"from\" address (af %d)", af
);
312 u_char optbuf
[BUFSIZ
/3];
313 socklen_t optsize
= sizeof(optbuf
);
318 if ((ip
= getprotobyname("ip")) != NULL
)
319 ipproto
= ip
->p_proto
;
321 ipproto
= IPPROTO_IP
;
322 if (!getsockopt(0, ipproto
, IP_OPTIONS
, (char *)optbuf
, &optsize
) &&
324 for (i
= 0; i
< optsize
;) {
325 u_char c
= optbuf
[i
];
326 if (c
== IPOPT_LSRR
|| c
== IPOPT_SSRR
) {
328 "Connection refused from %s "
331 (struct sockaddr_in
*)fromp
)->sin_addr
),
332 c
== IPOPT_LSRR
? "LSRR" : "SSRR");
337 i
+= (c
== IPOPT_NOP
) ? 1 : optbuf
[i
+ 1];
342 if (ntohs(*portp
) >= IPPORT_RESERVED
343 || ntohs(*portp
) < IPPORT_RESERVED
/ 2) {
344 syslog(LOG_NOTICE
|LOG_AUTH
,
345 "Connection from %s on illegal port %u",
346 naddr
, ntohs(*portp
));
355 if ((cc
= read(STDIN_FILENO
, &c
, 1)) != 1) {
357 syslog(LOG_ERR
, "read: %m");
358 (void)shutdown(0, SHUT_RDWR
);
363 port
= port
* 10 + c
- '0';
368 int lport
= IPPORT_RESERVED
- 1;
369 s
= rresvport_af_addr(&lport
, af
, localp
);
371 syslog(LOG_ERR
, "can't get stderr port: %m");
374 if (port
>= IPPORT_RESERVED
) {
375 syslog(LOG_ERR
, "2nd port not reserved");
378 *portp
= htons(port
);
379 if (connect(s
, fromp
, fromp
->sa_len
) < 0) {
380 syslog(LOG_ERR
, "connect second port %d: %m", port
);
387 /* from inetd, socket is already on 0, 1, 2 */
388 (void)dup2(f
, STDIN_FILENO
);
389 (void)dup2(f
, STDOUT_FILENO
);
390 (void)dup2(f
, STDERR_FILENO
);
392 if (getnameinfo(fromp
, fromp
->sa_len
, saddr
, sizeof(saddr
),
393 NULL
, 0, NI_NAMEREQD
) == 0) {
395 * If name returned by getnameinfo is in our domain,
396 * attempt to verify that we haven't been fooled by someone
397 * in a remote net; look up the name and check that this
398 * address corresponds to the name.
402 if (check_all
|| local_domain(saddr
)) {
403 (void)strlcpy(remotehost
, saddr
, sizeof(remotehost
));
404 errorhost
= remotehost
;
405 (void)memset(&hints
, 0, sizeof(hints
));
406 hints
.ai_family
= fromp
->sa_family
;
407 hints
.ai_socktype
= SOCK_STREAM
;
408 hints
.ai_flags
= AI_CANONNAME
;
409 gaierror
= getaddrinfo(remotehost
, pbuf
, &hints
, &res0
);
412 "Couldn't look up address for %s: %s",
413 remotehost
, gai_strerror(gaierror
));
415 "Couldn't look up address for your host (%s)\n";
418 for (res
= res0
; res
; res
= res
->ai_next
) {
419 if (res
->ai_family
!= fromp
->sa_family
)
421 if (res
->ai_addrlen
!= fromp
->sa_len
)
423 if (getnameinfo(res
->ai_addr
,
425 raddr
, sizeof(raddr
), NULL
, 0,
427 && strcmp(naddr
, raddr
) == 0) {
428 hostname
= res
->ai_canonname
436 "Host addr %s not listed for host %s",
437 naddr
, res0
->ai_canonname
441 "Host address mismatch for %s\n";
446 (void)strlcpy(hostnamebuf
, hostname
, sizeof(hostnamebuf
));
447 hostname
= hostnamebuf
;
451 (void)strlcpy(hostnamebuf
, naddr
, sizeof(hostnamebuf
));
452 errorhost
= hostname
= hostnamebuf
;
456 getstr(remuser
, sizeof(remuser
), "remuser");
457 getstr(locuser
, sizeof(locuser
), "locuser");
458 getstr(cmdbuf
, sizeof(cmdbuf
), "command");
462 pam_err
= pam_start("rsh", locuser
, &pamc
, &pamh
);
463 if (pam_err
!= PAM_SUCCESS
) {
464 syslog(LOG_ERR
|LOG_AUTH
, "pam_start(): %s",
465 pam_strerror(pamh
, pam_err
));
466 rshd_errx(EXIT_FAILURE
, incorrect
);
469 if ((pam_err
= pam_set_item(pamh
, PAM_RUSER
, remuser
)) != PAM_SUCCESS
||
470 (pam_err
= pam_set_item(pamh
, PAM_RHOST
, hostname
)) != PAM_SUCCESS
){
471 syslog(LOG_ERR
|LOG_AUTH
, "pam_set_item(): %s",
472 pam_strerror(pamh
, pam_err
));
473 rshd_errx(EXIT_FAILURE
, incorrect
);
476 pam_err
= pam_authenticate(pamh
, 0);
477 if (pam_err
== PAM_SUCCESS
) {
478 if ((pam_err
= pam_get_user(pamh
, &cp
, NULL
)) == PAM_SUCCESS
) {
479 (void)strlcpy(locuser
, cp
, sizeof(locuser
));
480 /* XXX truncation! */
482 pam_err
= pam_acct_mgmt(pamh
, 0);
484 if (pam_err
!= PAM_SUCCESS
) {
485 errorstr
= incorrect
;
486 errormsg
= pam_strerror(pamh
, pam_err
);
491 if (getpwnam_r(locuser
, &pwres
, pwbuf
, sizeof(pwbuf
), &pwd
) != 0 ||
493 syslog(LOG_INFO
|LOG_AUTH
,
494 "%s@%s as %s: unknown login. cmd='%.80s'",
495 remuser
, hostname
, locuser
, cmdbuf
);
496 if (errorstr
== NULL
)
497 errorstr
= "Permission denied.";
498 rshd_errx(EXIT_FAILURE
, errorstr
, errorhost
);
501 lc
= login_getclass(pwd
? pwd
->pw_class
: NULL
);
504 if (chdir(pwd
->pw_dir
) < 0) {
507 || login_getcapbool(lc
, "requirehome", pwd
->pw_uid
? 1 : 0)
510 syslog(LOG_INFO
|LOG_AUTH
,
511 "%s@%s as %s: no home directory. cmd='%.80s'",
512 remuser
, hostname
, locuser
, cmdbuf
);
513 rshd_errx(EXIT_SUCCESS
, "No remote home directory.");
519 (pwd
->pw_passwd
!= 0 && *pwd
->pw_passwd
!= '\0' &&
520 iruserok_sa(fromp
, fromp
->sa_len
, pwd
->pw_uid
== 0, remuser
,
522 errormsg
= __rcmd_errstr
? __rcmd_errstr
: "unknown error";
523 if (errorstr
== NULL
)
524 errorstr
= "Permission denied.";
528 if (pwd
->pw_uid
&& !access(_PATH_NOLOGIN
, F_OK
))
529 rshd_errx(EXIT_FAILURE
, "Logins currently disabled.");
534 * PAM modules might add supplementary groups in
535 * pam_setcred(), so initialize them first.
536 * But we need to open the session as root.
538 if (setusercontext(lc
, pwd
, pwd
->pw_uid
, LOGIN_SETGROUP
) != 0) {
539 syslog(LOG_ERR
, "setusercontext: %m");
543 initgroups(pwd
->pw_name
, pwd
->pw_gid
);
547 if ((pam_err
= pam_open_session(pamh
, 0)) != PAM_SUCCESS
) {
548 syslog(LOG_ERR
, "pam_open_session: %s",
549 pam_strerror(pamh
, pam_err
));
550 } else if ((pam_err
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
))
552 syslog(LOG_ERR
, "pam_setcred: %s", pam_strerror(pamh
, pam_err
));
556 (void)write(STDERR_FILENO
, "\0", 1);
561 rshd_errx(EXIT_FAILURE
, "Can't make pipe. (%s)",
565 rshd_errx(EXIT_FAILURE
, "Can't fork. (%s)",
568 (void)close(STDIN_FILENO
);
569 (void)close(STDOUT_FILENO
);
570 (void)close(STDERR_FILENO
);
574 set
[0].events
= POLLIN
;
576 set
[1].events
= POLLIN
;
577 ioctl(pv
[0], FIONBIO
, (char *)&one
);
579 /* should set s nbio! */
582 if (set
[0].events
== 0 && set
[1].events
== 0)
584 #endif /* defined(__minix) */
585 if (poll(set
, 2, INFTIM
) < 0)
587 if (set
[0].revents
& POLLIN
) {
590 ret
= read(s
, &sig
, 1);
596 if (set
[1].revents
& POLLIN
) {
598 cc
= read(pv
[0], buf
, sizeof(buf
));
600 shutdown(s
, SHUT_RDWR
);
603 (void)write(s
, buf
, cc
);
607 } while ((set
[0].revents
| set
[1].revents
) & POLLIN
);
613 (void)dup2(pv
[1], STDERR_FILENO
);
620 rshd_errx(EXIT_FAILURE
, "Can't fork. (%s)",
625 if ((xpid
= waitpid(pid
, &status
, 0)) != pid
) {
626 pam_err
= pam_close_session(pamh
, 0);
627 if (pam_err
!= PAM_SUCCESS
) {
629 "pam_close_session: %s",
630 pam_strerror(pamh
, pam_err
));
635 "wrong PID: %d != %d", pid
, xpid
);
638 "wait pid=%d failed %m", pid
);
647 (void)fcntl(STDERR_FILENO
+ 1, F_CLOSEM
, 0);
649 for (fd
= getdtablesize(); fd
> STDERR_FILENO
; fd
--)
653 syslog(LOG_ERR
, "setsid() failed: %m");
655 if (setlogin(pwd
->pw_name
) < 0)
656 syslog(LOG_ERR
, "setlogin() failed: %m");
658 if (*pwd
->pw_shell
== '\0')
659 pwd
->pw_shell
= __UNCONST(_PATH_BSHELL
);
661 (void)pam_setenv(pamh
, "HOME", pwd
->pw_dir
, 1);
662 (void)pam_setenv(pamh
, "SHELL", pwd
->pw_shell
, 1);
663 (void)pam_setenv(pamh
, "USER", pwd
->pw_name
, 1);
664 (void)pam_setenv(pamh
, "PATH", _PATH_DEFPATH
, 1);
665 environ
= pam_getenvlist(pamh
);
666 (void)pam_end(pamh
, pam_err
);
671 if ((sh
= login_getcapstr(lc
, "shell", NULL
, NULL
))) {
672 if(!(sh
= strdup(sh
))) {
673 syslog(LOG_ERR
, "Cannot alloc mem");
681 static char *envinit
[] = { NULL
};
684 setenv("PATH", _PATH_DEFPATH
, 1);
685 setenv("HOME", pwd
->pw_dir
, 1);
686 setenv("SHELL", pwd
->pw_shell
, 1);
687 setenv("USER", pwd
->pw_name
, 1);
690 cp
= strrchr(pwd
->pw_shell
, '/');
697 if (setusercontext(lc
, pwd
, pwd
->pw_uid
,
698 LOGIN_SETALL
& ~LOGIN_SETGROUP
) < 0) {
699 syslog(LOG_ERR
, "setusercontext(): %m");
704 (void)setgid((gid_t
)pwd
->pw_gid
);
705 (void)setuid((uid_t
)pwd
->pw_uid
);
708 if (log_success
|| pwd
->pw_uid
== 0) {
709 syslog(LOG_INFO
|LOG_AUTH
, "%s@%s as %s: cmd='%.80s'",
710 remuser
, hostname
, locuser
, cmdbuf
);
712 (void)execl(pwd
->pw_shell
, cp
, "-c", cmdbuf
, NULL
);
713 rshd_errx(EXIT_FAILURE
, "%s: %s", pwd
->pw_shell
, strerror(errno
));
715 syslog(LOG_INFO
|LOG_AUTH
,
716 "%s@%s as %s: permission denied (%s). cmd='%.80s'",
717 remuser
, hostname
, locuser
, errormsg
, cmdbuf
);
718 rshd_errx(EXIT_FAILURE
, errorstr
, errorhost
);
722 * Report error to client. Note: can't be used until second socket has
723 * connected to client, or older clients will hang waiting for that
730 rshd_errx(int error
, const char *fmt
, ...)
734 char *bp
, buf
[BUFSIZ
];
737 if (sent_null
== 0) {
742 rv
= vsnprintf(bp
, sizeof(buf
) - 2, fmt
, ap
);
744 (void)write(STDERR_FILENO
, buf
, len
+ rv
);
750 getstr(char *buf
, int cnt
, const char *err
)
755 if (read(STDIN_FILENO
, &c
, 1) != 1)
759 rshd_errx(EXIT_FAILURE
, "%s too long", err
);
764 * Check whether host h is in our local domain,
765 * defined as sharing the last two components of the domain part,
766 * or the entire domain part if the local domain has only one component.
767 * If either name is unqualified (contains no '.'),
768 * assume that the host is local, as it will be
769 * interpreted as such.
772 local_domain(char *h
)
774 char localhost
[MAXHOSTNAMELEN
+ 1];
778 (void)gethostname(localhost
, sizeof(localhost
));
779 localhost
[sizeof(localhost
) - 1] = '\0';
780 p1
= topdomain(localhost
);
782 if (p1
== NULL
|| p2
== NULL
|| !strcasecmp(p1
, p2
))
790 char *p
, *maybe
= NULL
;
793 for (p
= h
+ strlen(h
); p
>= h
; p
--) {
807 syslog(LOG_ERR
, "Usage: %s [-%s]", getprogname(), OPTIONS
);