]>
Commit | Line | Data |
---|---|---|
ac2f15b3 A |
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 | ||
342c141e A |
34 | #include <sys/cdefs.h> |
35 | ||
ac2f15b3 | 36 | #ifndef lint |
342c141e | 37 | __unused static const char copyright[] = |
ac2f15b3 A |
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> | |
44bd3e4d | 60 | #include <sys/mman.h> |
ac2f15b3 A |
61 | |
62 | #include <netinet/in.h> | |
63 | #include "kdump.h" | |
64 | #include <arpa/inet.h> | |
65 | ||
26c66ce9 | 66 | #include <assert.h> |
44bd3e4d | 67 | #include <stdint.h> |
ac2f15b3 A |
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> | |
9c859447 | 80 | #include <libkern/OSByteOrder.h> |
ac2f15b3 A |
81 | |
82 | #include "kdumpsubs.h" | |
83 | ||
26c66ce9 | 84 | #define DEFAULT_KDUMPD_PORTNO (1069) |
44bd3e4d | 85 | #define TIMEOUT 2 |
ac2f15b3 A |
86 | |
87 | int peer; | |
88 | int rexmtval = TIMEOUT; | |
44bd3e4d | 89 | int maxtimeout = 25 * TIMEOUT; |
ac2f15b3 A |
90 | |
91 | #define PKTSIZE SEGSIZE+6 | |
44bd3e4d A |
92 | |
93 | char buf[MAXIMUM_KDP_PKTSIZE]; | |
94 | char ackbuf[MAXIMUM_KDP_PKTSIZE]; | |
ac2f15b3 | 95 | struct sockaddr_in from; |
b8dff150 | 96 | socklen_t fromlen; |
ac2f15b3 A |
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; | |
26c66ce9 | 115 | static int server_mode = 1; |
ac2f15b3 A |
116 | |
117 | static char *errtomsg __P((int)); | |
118 | static void nak __P((int)); | |
119 | static char * __P(verifyhost(struct sockaddr_in *)); | |
44bd3e4d A |
120 | uint32_t kdp_crashdump_pkt_size = (SEGSIZE + (sizeof(struct kdumphdr))); |
121 | uint32_t kdp_crashdump_seg_size = SEGSIZE; | |
ac2f15b3 | 122 | |
9c859447 | 123 | #define KDP_FEATURE_MASK_STRING "features" |
44bd3e4d A |
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; | |
9c859447 | 128 | |
ac2f15b3 A |
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); | |
26c66ce9 | 143 | while ((ch = getopt(argc, argv, "cClns:u:w")) != -1) { |
ac2f15b3 A |
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; | |
26c66ce9 A |
163 | case 'w': |
164 | server_mode = 0; | |
165 | break; | |
ac2f15b3 A |
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 | ||
26c66ce9 A |
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); | |
ac2f15b3 | 199 | } |
26c66ce9 A |
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"); | |
ac2f15b3 | 205 | exit(1); |
26c66ce9 A |
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 | } | |
ac2f15b3 A |
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); | |
9dc66a05 | 275 | if (((sb.st_mode & S_IFMT ) == S_IFDIR) && |
ac2f15b3 A |
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); | |
26c66ce9 | 290 | } else if (0 != chdir(dirs->name)) { |
ac2f15b3 | 291 | syslog(LOG_ERR, "chdir%s: %m", dirs->name); |
26c66ce9 | 292 | } |
ac2f15b3 A |
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; | |
26c66ce9 A |
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 | ||
ac2f15b3 A |
311 | if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) { |
312 | syslog(LOG_ERR, "bind: %m"); | |
313 | exit(1); | |
314 | } | |
26c66ce9 A |
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 | ||
ac2f15b3 A |
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); | |
9c859447 A |
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; | |
44bd3e4d A |
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); | |
9c859447 | 405 | |
ac2f15b3 A |
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) { | |
44bd3e4d | 415 | syslog(KDUMPD_DEBUG_LEVEL, "%s: %s request for %s: %s", verifyhost(&from), |
ac2f15b3 A |
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 | ||
342c141e | 468 | fd = open(filename, O_RDWR|O_CREAT|O_TRUNC , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); |
ac2f15b3 A |
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 | } | |
ac2f15b3 A |
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 | ||
ac2f15b3 A |
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; | |
9c859447 | 521 | if (block == 0) |
44bd3e4d | 522 | ap->th_opcode = htons((u_short)ACK | ((kdp_feature_large_crashdumps | kdp_feature_large_packets) << 8)); |
9c859447 A |
523 | else |
524 | ap->th_opcode = htons((u_short)ACK); | |
ac2f15b3 A |
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); | |
44bd3e4d | 541 | n = recv(peer, dp, kdp_crashdump_pkt_size, 0); |
ac2f15b3 A |
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); | |
44bd3e4d A |
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 | ||
ac2f15b3 A |
553 | if (dp->th_opcode == ERROR) |
554 | goto abort; | |
44bd3e4d | 555 | |
ac2f15b3 A |
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 | { | |
9c859447 | 565 | off_t crashdump_offset = 0; |
ac2f15b3 | 566 | unsigned int tempoff = 0; |
9c859447 A |
567 | |
568 | if (kdp_feature_large_crashdumps) { | |
569 | crashdump_offset = OSSwapBigToHostInt64((*(uint64_t *)dp->th_data)); | |
9c859447 A |
570 | } |
571 | else { | |
ac2f15b3 | 572 | bcopy (dp->th_data, &tempoff, sizeof(unsigned int)); |
9c859447 A |
573 | crashdump_offset = ntohl(tempoff); |
574 | } | |
44bd3e4d | 575 | |
9c859447 | 576 | #if DEBUG |
44bd3e4d | 577 | syslog(KDUMPD_DEBUG_LEVEL, "Seeking to offset 0x%llx\n", crashdump_offset); |
9c859447 A |
578 | #endif |
579 | errno = 0; | |
580 | lseek(fileno (file), crashdump_offset, SEEK_SET); | |
ac2f15b3 A |
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)) | |
44bd3e4d A |
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 | } | |
ac2f15b3 A |
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 | { | |
44bd3e4d | 602 | syslog (LOG_DAEMON|LOG_ERR, "Retransmitting ack - current block %u, received block %u", block, dp->th_block); |
ac2f15b3 A |
603 | goto send_ack; /* rexmit */ |
604 | } | |
605 | else | |
44bd3e4d | 606 | syslog (LOG_DAEMON|LOG_ERR, "Not retransmitting ack - current block %u, received block %u", block, dp->th_block); |
ac2f15b3 A |
607 | } |
608 | } | |
44bd3e4d A |
609 | #if DEBUG |
610 | syslog(KDUMPD_DEBUG_LEVEL, "Writing block sized %u, current offset 0x%llx\n", n - 6, ftello(file)); | |
611 | #endif | |
ac2f15b3 A |
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 | ||
fdfd5971 A |
642 | /* update if needed, when adding new errmsgs */ |
643 | #define MAXERRMSGLEN 40 | |
644 | ||
ac2f15b3 A |
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 | } | |
fdfd5971 A |
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); | |
ac2f15b3 A |
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"); | |
fdfd5971 A |
710 | |
711 | return; | |
ac2f15b3 A |
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 | } |