]> git.cameronkatri.com Git - apple_cmds.git/blob - network_cmds/kdumpd.tproj/kdumpd.c
Makefile.inc: Compress with zstd by default
[apple_cmds.git] / network_cmds / kdumpd.tproj / kdumpd.c
1 /*
2 * Copyright (c) 1983, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #include <sys/cdefs.h>
35
36 #ifndef lint
37 __unused static const char copyright[] =
38 "@(#) Copyright (c) 1983, 1993\n\
39 The Regents of the University of California. All rights reserved.\n";
40 #endif /* not lint */
41
42 /* Mac OS X kernel core dump server, based on the BSD trivial file
43 * transfer protocol server (FreeBSD distribution), with several
44 * modifications. This server is *not* compatible with tftp, as the
45 * protocol has changed considerably.
46 */
47
48 /*
49 * Based on the trivial file transfer protocol server.
50 *
51 * The original version included many modifications by Jim Guyton
52 * <guyton@rand-unix>.
53 */
54
55 #include <sys/param.h>
56 #include <sys/ioctl.h>
57 #include <sys/stat.h>
58 #include <sys/socket.h>
59 #include <sys/types.h>
60 #include <sys/mman.h>
61
62 #include <netinet/in.h>
63 #include "kdump.h"
64 #include <arpa/inet.h>
65
66 #include <assert.h>
67 #include <stdint.h>
68 #include <ctype.h>
69 #include <errno.h>
70 #include <fcntl.h>
71 #include <netdb.h>
72 #include <pwd.h>
73 #include <setjmp.h>
74 #include <signal.h>
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <string.h>
78 #include <syslog.h>
79 #include <unistd.h>
80 #include <libkern/OSByteOrder.h>
81
82 #include "kdumpsubs.h"
83
84 #define DEFAULT_KDUMPD_PORTNO (1069)
85 #define TIMEOUT 2
86
87 int peer;
88 int rexmtval = TIMEOUT;
89 int maxtimeout = 25 * TIMEOUT;
90
91 #define PKTSIZE SEGSIZE+6
92
93 char buf[MAXIMUM_KDP_PKTSIZE];
94 char ackbuf[MAXIMUM_KDP_PKTSIZE];
95 struct sockaddr_in from;
96 socklen_t fromlen;
97
98 void kdump __P((struct kdumphdr *, int));
99
100 /*
101 * Null-terminated directory prefix list for absolute pathname requests and
102 * search list for relative pathname requests.
103 *
104 * MAXDIRS should be at least as large as the number of arguments that
105 * inetd allows (currently 20).
106 */
107 #define MAXDIRS 20
108 static struct dirlist {
109 char *name;
110 int len;
111 } dirs[MAXDIRS+1];
112 static int suppress_naks;
113 static int logging = 1;
114 static int ipchroot;
115 static int server_mode = 1;
116
117 static char *errtomsg __P((int));
118 static void nak __P((int));
119 static char * __P(verifyhost(struct sockaddr_in *));
120 uint32_t kdp_crashdump_pkt_size = (SEGSIZE + (sizeof(struct kdumphdr)));
121 uint32_t kdp_crashdump_seg_size = SEGSIZE;
122
123 #define KDP_FEATURE_MASK_STRING "features"
124 enum {KDP_FEATURE_LARGE_CRASHDUMPS = 1, KDP_FEATURE_LARGE_PKT_SIZE = 2};
125
126 uint32_t kdp_crashdump_feature_mask;
127 uint32_t kdp_feature_large_crashdumps, kdp_feature_large_packets;
128
129 int
130 main(argc, argv)
131 int argc;
132 char *argv[];
133 {
134 register struct kdumphdr *tp;
135 register int n;
136 int ch, on;
137 struct sockaddr_in sin;
138 char *chroot_dir = NULL;
139 struct passwd *nobody;
140 char *chuser = "nobody";
141
142 openlog("kdumpd", LOG_PID | LOG_NDELAY, LOG_FTP);
143 while ((ch = getopt(argc, argv, "cClns:u:w")) != -1) {
144 switch (ch) {
145 case 'c':
146 ipchroot = 1;
147 break;
148 case 'C':
149 ipchroot = 2;
150 break;
151 case 'l':
152 logging = 1;
153 break;
154 case 'n':
155 suppress_naks = 1;
156 break;
157 case 's':
158 chroot_dir = optarg;
159 break;
160 case 'u':
161 chuser = optarg;
162 break;
163 case 'w':
164 server_mode = 0;
165 break;
166 default:
167 syslog(LOG_WARNING, "ignoring unknown option -%c", ch);
168 }
169 }
170
171 if (optind < argc) {
172 struct dirlist *dirp;
173
174 /* Get list of directory prefixes. Skip relative pathnames. */
175 for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS];
176 optind++) {
177 if (argv[optind][0] == '/') {
178 dirp->name = argv[optind];
179 dirp->len = strlen(dirp->name);
180 dirp++;
181 }
182 }
183 }
184 else if (chroot_dir) {
185 dirs->name = "/";
186 dirs->len = 1;
187 }
188 if (ipchroot && chroot_dir == NULL) {
189 syslog(LOG_ERR, "-c requires -s");
190 exit(1);
191 }
192
193 /* If we are not in server mode, skip the whole 'inetd' logic below. */
194 if (server_mode) {
195 on = 1;
196 if (ioctl(0, FIONBIO, &on) < 0) {
197 syslog(LOG_ERR, "ioctl(FIONBIO): %m");
198 exit(1);
199 }
200 fromlen = sizeof (from);
201 n = recvfrom(0, buf, sizeof (buf), 0,
202 (struct sockaddr *)&from, &fromlen);
203 if (n < 0) {
204 syslog(LOG_ERR, "recvfrom: %m");
205 exit(1);
206 }
207 /*
208 * Now that we have read the message out of the UDP
209 * socket, we fork and exit. Thus, inetd will go back
210 * to listening to the kdump port, and the next request
211 * to come in will start up a new instance of kdumpd.
212 *
213 * We do this so that inetd can run kdumpd in "wait" mode.
214 * The problem with kdumpd running in "nowait" mode is that
215 * inetd may get one or more successful "selects" on the
216 * kdump port before we do our receive, so more than one
217 * instance of kdumpd may be started up. Worse, if kdumpd
218 * breaks before doing the above "recvfrom", inetd would
219 * spawn endless instances, clogging the system.
220 */
221 {
222 int pid;
223 int i;
224 socklen_t j;
225
226 for (i = 1; i < 20; i++) {
227 pid = fork();
228 if (pid < 0) {
229 sleep(i);
230 /*
231 * flush out to most recently sent request.
232 *
233 * This may drop some requests, but those
234 * will be resent by the clients when
235 * they timeout. The positive effect of
236 * this flush is to (try to) prevent more
237 * than one kdumpd being started up to service
238 * a single request from a single client.
239 */
240 j = sizeof from;
241 i = recvfrom(0, buf, sizeof (buf), 0,
242 (struct sockaddr *)&from, &j);
243 if (i > 0) {
244 n = i;
245 fromlen = j;
246 }
247 } else {
248 break;
249 }
250 }
251 if (pid < 0) {
252 syslog(LOG_ERR, "fork: %m");
253 exit(1);
254 } else if (pid != 0) {
255 exit(0);
256 }
257 }
258 }
259
260 /*
261 * Since we exit here, we should do that only after the above
262 * recvfrom to keep inetd from constantly forking should there
263 * be a problem. See the above comment about system clogging.
264 */
265 if (chroot_dir) {
266 if (ipchroot) {
267 char tempchroot[MAXPATHLEN];
268 char *tempaddr;
269 struct stat sb;
270 int statret;
271
272 tempaddr = inet_ntoa(from.sin_addr);
273 snprintf(tempchroot, sizeof(tempchroot), "%s/%s", chroot_dir, tempaddr);
274 statret = stat(tempchroot, &sb);
275 if (((sb.st_mode & S_IFMT ) == S_IFDIR) &&
276 (statret == 0 || (statret == -1 && ipchroot == 1)))
277 chroot_dir = tempchroot;
278 }
279 /* Must get this before chroot because /etc might go away */
280 if ((nobody = getpwnam(chuser)) == NULL) {
281 syslog(LOG_ERR, "%s: no such user", chuser);
282 exit(1);
283 }
284 if (chroot(chroot_dir)) {
285 syslog(LOG_ERR, "chroot: %s: %m", chroot_dir);
286 exit(1);
287 }
288 chdir( "/" );
289 setuid(nobody->pw_uid);
290 } else if (0 != chdir(dirs->name)) {
291 syslog(LOG_ERR, "chdir%s: %m", dirs->name);
292 }
293
294 from.sin_family = AF_INET;
295 alarm(0);
296 close(0);
297 close(1);
298 peer = socket(AF_INET, SOCK_DGRAM, 0);
299 if (peer < 0) {
300 syslog(LOG_ERR, "socket: %m");
301 exit(1);
302 }
303 memset(&sin, 0, sizeof(sin));
304 sin.sin_family = AF_INET;
305
306 if (!server_mode) {
307 sin.sin_addr.s_addr = htonl(INADDR_ANY);
308 sin.sin_port = htons((uint16_t) DEFAULT_KDUMPD_PORTNO);
309 }
310
311 if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
312 syslog(LOG_ERR, "bind: %m");
313 exit(1);
314 }
315
316 if (!server_mode) {
317 /*
318 * Wait for an incoming message from a remote peer, note that we need to
319 * populate n since kdump() expect the first message to be in buf
320 * already.
321 */
322 socklen_t slen = sizeof(from);
323 n = recvfrom(peer, buf, sizeof(buf), 0,
324 (struct sockaddr *) &from, &slen);
325 if (n <= 0) {
326 syslog(LOG_ERR, "recvfrom: %m");
327 exit(1);
328 }
329 }
330
331 if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
332 syslog(LOG_ERR, "connect: %m");
333 exit(1);
334 }
335 tp = (struct kdumphdr *)buf;
336 tp->th_opcode = ntohs(tp->th_opcode);
337 if (tp->th_opcode == WRQ)
338 kdump(tp, n);
339 exit(1);
340 }
341
342 struct formats;
343 int validate_access __P((char **, int));
344
345 void recvfile __P((struct formats *));
346
347 struct formats {
348 char *f_mode;
349 int (*f_validate) __P((char **, int));
350
351 void (*f_recv) __P((struct formats *));
352 int f_convert;
353 } formats[] = {
354 { "netascii", validate_access, recvfile, 1 },
355 { "octet", validate_access, recvfile, 0 },
356 { 0 }
357 };
358
359 /*
360 * Handle initial connection protocol.
361 */
362 void
363 kdump(tp, size)
364 struct kdumphdr *tp;
365 int size;
366 {
367 register char *cp;
368 int first = 1, ecode;
369 register struct formats *pf;
370 char *filename, *mode = NULL;
371
372 filename = cp = tp->th_stuff;
373 again:
374 while (cp < buf + size) {
375 if (*cp == '\0')
376 break;
377 cp++;
378 }
379 if (*cp != '\0') {
380 nak(EBADOP);
381 exit(1);
382 }
383 if (first) {
384 mode = ++cp;
385 first = 0;
386 goto again;
387 }
388 for (cp = mode; *cp; cp++)
389 if (isupper(*cp))
390 *cp = tolower(*cp);
391
392 cp++;
393 if (strncmp(KDP_FEATURE_MASK_STRING, cp, sizeof(KDP_FEATURE_MASK_STRING)) == 0) {
394 kdp_crashdump_feature_mask = ntohl(*(uint32_t *) (cp + sizeof(KDP_FEATURE_MASK_STRING)));
395 kdp_feature_large_crashdumps = kdp_crashdump_feature_mask & KDP_FEATURE_LARGE_CRASHDUMPS;
396 kdp_feature_large_packets = kdp_crashdump_feature_mask & KDP_FEATURE_LARGE_PKT_SIZE;
397
398 if (kdp_feature_large_packets) {
399 kdp_crashdump_pkt_size = KDP_LARGE_CRASHDUMP_PKT_SIZE;
400 kdp_crashdump_seg_size = kdp_crashdump_pkt_size - sizeof(struct kdumphdr);
401 }
402 syslog(KDUMPD_DEBUG_LEVEL, "Received feature mask %s:0x%x", cp, kdp_crashdump_feature_mask);
403 } else
404 syslog(KDUMPD_DEBUG_LEVEL, "Unable to locate feature mask, mode: %s", mode);
405
406 for (pf = formats; pf->f_mode; pf++)
407 if (strcmp(pf->f_mode, mode) == 0)
408 break;
409 if (pf->f_mode == 0) {
410 nak(EBADOP);
411 exit(1);
412 }
413 ecode = (*pf->f_validate)(&filename, tp->th_opcode);
414 if (logging) {
415 syslog(KDUMPD_DEBUG_LEVEL, "%s: %s request for %s: %s", verifyhost(&from),
416 tp->th_opcode == WRQ ? "write" : "read",
417 filename, errtomsg(ecode));
418 }
419 if (ecode) {
420 /*
421 * Avoid storms of naks to a RRQ broadcast for a relative
422 * bootfile pathname from a diskless Sun.
423 */
424 if (suppress_naks && *filename != '/' && ecode == ENOTFOUND)
425 exit(0);
426 nak(ecode);
427 exit(1);
428 }
429 if (tp->th_opcode == WRQ)
430 (*pf->f_recv)(pf);
431
432 exit(0);
433 }
434
435
436 FILE *file;
437
438 /*
439 * Validate file access. We only allow storage of files that do not already
440 * exist, and that do not include directory specifiers in their pathnames.
441 * This is because kernel coredump filenames should always be of the form
442 * "core-version-IP as dotted quad-random string" as in :
443 * core-custom-17.202.40.204-a75b4eec
444 * The file is written to the directory supplied as the first argument
445 * in inetd.conf
446 */
447
448 int
449 validate_access(char **filep, int mode)
450 {
451 struct stat stbuf;
452 int fd;
453 char *filename = *filep;
454 static char pathname[MAXPATHLEN];
455
456 if (strstr(filename, "/") || strstr(filename, ".."))
457 return (EACCESS);
458
459 snprintf(pathname, sizeof(pathname), "./%s", filename);
460
461 if (0 == stat(pathname, &stbuf))
462 return (EEXIST);
463
464 if (errno != ENOENT)
465 return (errno);
466
467
468 fd = open(filename, O_RDWR|O_CREAT|O_TRUNC , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
469
470 if (fd < 0)
471 return (errno + 100);
472
473 file = fdopen(fd, (mode == RRQ)? "r":"w");
474 if (file == NULL) {
475 return errno+100;
476 }
477
478 return (0);
479 }
480
481 int timeout;
482 jmp_buf timeoutbuf;
483
484 void
485 timer()
486 {
487
488 timeout += rexmtval;
489 if (timeout >= maxtimeout)
490 {
491 longjmp(timeoutbuf, 2);
492 }
493 longjmp(timeoutbuf, 1);
494 }
495
496 void
497 justquit()
498 {
499 exit(0);
500 }
501
502 /*
503 * Receive a file.
504 */
505 void
506 recvfile(pf)
507 struct formats *pf;
508 {
509 struct kdumphdr *dp, *w_init();
510 register struct kdumphdr *ap; /* ack buffer */
511 register int n, size;
512 volatile unsigned int block;
513 volatile unsigned int jmpval = 0;
514
515 signal(SIGALRM, timer);
516 dp = w_init();
517 ap = (struct kdumphdr *)ackbuf;
518 block = 0;
519 do {
520 send_seek_ack: timeout = 0;
521 if (block == 0)
522 ap->th_opcode = htons((u_short)ACK | ((kdp_feature_large_crashdumps | kdp_feature_large_packets) << 8));
523 else
524 ap->th_opcode = htons((u_short)ACK);
525 ap->th_block = htonl((unsigned int)block);
526 block++;
527 jmpval = setjmp(timeoutbuf);
528 if (2 == jmpval)
529 {
530 syslog (LOG_ERR, "Timing out and flushing file to disk");
531 goto flushfile;
532 }
533 send_ack:
534 if (send(peer, ackbuf, 6 , 0) != 6) {
535 syslog(LOG_ERR, "write: %m");
536 goto abort;
537 }
538 write_behind(file, pf->f_convert);
539 for ( ; ; ) {
540 alarm(rexmtval);
541 n = recv(peer, dp, kdp_crashdump_pkt_size, 0);
542 alarm(0);
543 if (n < 0) { /* really? */
544 syslog(LOG_ERR, "read: %m");
545 goto abort;
546 }
547 dp->th_opcode = ntohs((u_short)dp->th_opcode);
548 dp->th_block = ntohl((unsigned int)dp->th_block);
549 #if DEBUG
550 syslog(KDUMPD_DEBUG_LEVEL, "Received packet type %u, block %u\n", (unsigned)dp->th_opcode, (unsigned)dp->th_block);
551 #endif
552
553 if (dp->th_opcode == ERROR)
554 goto abort;
555
556 if (dp->th_opcode == KDP_EOF)
557 {
558 syslog (LOG_ERR, "Received last panic dump packet");
559 goto final_ack;
560 }
561 if (dp->th_opcode == KDP_SEEK)
562 {
563 if (dp->th_block == block)
564 {
565 off_t crashdump_offset = 0;
566 unsigned int tempoff = 0;
567
568 if (kdp_feature_large_crashdumps) {
569 crashdump_offset = OSSwapBigToHostInt64((*(uint64_t *)dp->th_data));
570 }
571 else {
572 bcopy (dp->th_data, &tempoff, sizeof(unsigned int));
573 crashdump_offset = ntohl(tempoff);
574 }
575
576 #if DEBUG
577 syslog(KDUMPD_DEBUG_LEVEL, "Seeking to offset 0x%llx\n", crashdump_offset);
578 #endif
579 errno = 0;
580 lseek(fileno (file), crashdump_offset, SEEK_SET);
581 if (errno)
582 syslog (LOG_ERR, "lseek: %m");
583
584 goto send_seek_ack;
585 }
586 (void) synchnet(peer);
587 if (dp->th_block == (block-1))
588 {
589 syslog (LOG_DAEMON|LOG_ERR, "Retransmitting seek ack - current block %u, received block %u", block, dp->th_block);
590 goto send_ack; /* rexmit */
591 }
592 }
593
594 if (dp->th_opcode == DATA) {
595 if (dp->th_block == block) {
596 break; /* normal */
597 }
598 /* Re-synchronize with the other side */
599 (void) synchnet(peer);
600 if (dp->th_block == (block-1))
601 {
602 syslog (LOG_DAEMON|LOG_ERR, "Retransmitting ack - current block %u, received block %u", block, dp->th_block);
603 goto send_ack; /* rexmit */
604 }
605 else
606 syslog (LOG_DAEMON|LOG_ERR, "Not retransmitting ack - current block %u, received block %u", block, dp->th_block);
607 }
608 }
609 #if DEBUG
610 syslog(KDUMPD_DEBUG_LEVEL, "Writing block sized %u, current offset 0x%llx\n", n - 6, ftello(file));
611 #endif
612 size = writeit(file, &dp, n - 6, pf->f_convert);
613 if (size != (n-6)) { /* ahem */
614 if (size < 0) nak(errno + 100);
615 else nak(ENOSPACE);
616 goto abort;
617 }
618 } while (dp->th_opcode != KDP_EOF);
619
620 final_ack:
621 ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */
622 ap->th_block = htonl((unsigned int) (block));
623 (void) send(peer, ackbuf, 6, 0);
624 flushfile:
625 write_behind(file, pf->f_convert);
626 (void) fclose(file); /* close data file */
627 syslog (LOG_ERR, "file closed, sending final ACK\n");
628
629 signal(SIGALRM, justquit); /* just quit on timeout */
630 alarm(rexmtval);
631 n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
632 alarm(0);
633 if (n >= 6 && /* if read some data */
634 dp->th_opcode == DATA && /* and got a data block */
635 block == dp->th_block) { /* then my last ack was lost */
636 (void) send(peer, ackbuf, 6, 0); /* resend final ack */
637 }
638 abort:
639 return;
640 }
641
642 /* update if needed, when adding new errmsgs */
643 #define MAXERRMSGLEN 40
644
645 struct errmsg {
646 int e_code;
647 char *e_msg;
648 } errmsgs[] = {
649 { EUNDEF, "Undefined error code" },
650 { ENOTFOUND, "File not found" },
651 { EACCESS, "Access violation" },
652 { ENOSPACE, "Disk full or allocation exceeded" },
653 { EBADOP, "Illegal KDUMP operation" },
654 { EBADID, "Unknown transfer ID" },
655 { EEXISTS, "File already exists" },
656 { ENOUSER, "No such user" },
657 { -1, 0 }
658 };
659
660 static char *
661 errtomsg(error)
662 int error;
663 {
664 static char buf[20];
665 register struct errmsg *pe;
666 if (error == 0)
667 return "success";
668 for (pe = errmsgs; pe->e_code >= 0; pe++)
669 if (pe->e_code == error)
670 return pe->e_msg;
671 snprintf(buf, sizeof(buf), "error %d", error);
672 return buf;
673 }
674
675 /*
676 * Send a nak packet (error message).
677 * Error code passed in is one of the
678 * standard KDUMP codes, or a UNIX errno
679 * offset by 100.
680 */
681 static void
682 nak(error)
683 int error;
684 {
685 register struct kdumphdr *tp;
686 int length;
687 register struct errmsg *pe;
688
689 tp = (struct kdumphdr *)buf;
690 tp->th_opcode = htons((u_short)ERROR);
691 tp->th_code = htons((unsigned int)error);
692 for (pe = errmsgs; pe->e_code >= 0; pe++)
693 if (pe->e_code == error)
694 break;
695 if (pe->e_code < 0) {
696 pe->e_msg = strerror(error - 100);
697 tp->th_code = EUNDEF; /* set 'undef' errorcode */
698 }
699 if (strlen(pe->e_msg) > MAXERRMSGLEN) {
700 syslog(LOG_ERR, "nak: error msg too long");
701 return;
702 }
703
704 strlcpy(tp->th_msg, pe->e_msg, MAXERRMSGLEN);
705 length = strlen(pe->e_msg);
706 tp->th_msg[length] = '\0';
707 length += 5;
708 if (send(peer, buf, length, 0) != length)
709 syslog(LOG_ERR, "nak: %m");
710
711 return;
712 }
713
714 static char *
715 verifyhost(fromp)
716 struct sockaddr_in *fromp;
717 {
718 struct hostent *hp;
719
720 hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof(fromp->sin_addr),
721 fromp->sin_family);
722 if(hp)
723 return hp->h_name;
724 else
725 return inet_ntoa(fromp->sin_addr);
726 }