/* * WRQ - receive a file from the client */ void tftp_wrq(int peer, char *recvbuffer, ssize_t size) { char *cp; int has_options = 0, ecode; char *filename, *mode; char fnbuf[PATH_MAX]; cp = parse_header(peer, recvbuffer, size, &filename, &mode); size -= (cp - recvbuffer) + 1; strlcpy(fnbuf, filename, sizeof(fnbuf)); reduce_path(fnbuf); filename = fnbuf; if (size > 0) { if (options_rfc_enabled) has_options = !parse_options(peer, cp, size); else tftp_log(LOG_INFO, "Options found but not enabled"); } ecode = validate_access(peer, &filename, WRQ); if (ecode == 0) { if (has_options) send_oack(peer); else send_ack(peer, 0); } if (logging) { tftp_log(LOG_INFO, "%s: write request for %s: %s", peername, filename, errtomsg(ecode)); } if (ecode) { send_error(peer, ecode); exit(1); } tftp_recvfile(peer, mode); exit(0); }
/* Handle initial connection protocol */ void tftp( Client *cl ) { struct tftphdr *tp; char *cp, *cp2, *ep; int ecode; char *mode; char file[MAXPATHLEN]; tp = cl->tp; tp->th_opcode = ntohs((u_short)tp->th_opcode); if (tp->th_opcode != RRQ) { // nak( cl, EBADOP ); return; } errstr = NULL; cp = (char *)&tp->th_block; ep = (char *)cl->tp + cl->tpcc; if ( (ep - cp) > MAXPATHLEN ) { stats.badfilename++; nak( cl, EBADOP ); return; } if ( *cp == '/' ) { cp++; } /* Extract filename */ cp2 = file; while (cp < ep && *cp != '\0') *cp2++ = *cp++; if (cp >= ep) { ++stats.badfilename; nak(cl, EBADOP); return; } *cp2 = '\0'; ++cp; /* Make sure mode is in packet */ mode = cp; while (cp < ep && *cp != '\0') cp++; if (cp >= ep) { stats.badfilemode++; nak( cl, EBADOP ); return; } if (strcasecmp(mode, "octet") != 0) { stats.badfilemode++; nak( cl, EBADOP ); return; } ecode = validate_access( cl, file ); if (ecode) { my_syslog( MLOG_TFTP, "%s %s \"%s\" DENIED (%s)", inet_ntoa(cl->sin.sin_addr), tp->th_opcode == WRQ ? "write" : "read", file, errstr != NULL ? errstr : errtomsg(ecode)); cl->proc = CP_DONE; return; } if (cl->dynam == 0) my_TFTPlog( file, cl->sin.sin_addr.s_addr ); /* my_syslog( MLOG_TFTP, "%s-%s req for \"%s\" from %s", tp->th_opcode == WRQ ? "write" : "read", mode, file, inet_ntoa(cl->sin.sin_addr)); */ cl->state = ST_INIT; cl->tp = NULL; cl->tpcc = 0; cl->proc = CP_SENDFILE; // sendfile( cl ); }
/* * RRQ - send a file to the client */ void tftp_rrq(int peer, char *recvbuffer, ssize_t size) { char *cp; int has_options = 0, ecode; char *filename, *mode; char fnbuf[PATH_MAX]; cp = parse_header(peer, recvbuffer, size, &filename, &mode); size -= (cp - recvbuffer) + 1; strcpy(fnbuf, filename); reduce_path(fnbuf); filename = fnbuf; if (size > 0) { if (options_rfc_enabled) has_options = !parse_options(peer, cp, size); else tftp_log(LOG_INFO, "Options found but not enabled"); } ecode = validate_access(peer, &filename, RRQ); if (ecode == 0) { if (has_options) { int n; char lrecvbuffer[MAXPKTSIZE]; struct tftphdr *rp = (struct tftphdr *)lrecvbuffer; send_oack(peer); n = receive_packet(peer, lrecvbuffer, MAXPKTSIZE, NULL, timeoutpacket); if (n < 0) { if (debug&DEBUG_SIMPLE) tftp_log(LOG_DEBUG, "Aborting: %s", rp_strerror(n)); return; } if (rp->th_opcode != ACK) { if (debug&DEBUG_SIMPLE) tftp_log(LOG_DEBUG, "Expected ACK, got %s on OACK", packettype(rp->th_opcode)); return; } } } if (logging) tftp_log(LOG_INFO, "%s: read request for %s: %s", peername, filename, errtomsg(ecode)); if (ecode) { /* * Avoid storms of naks to a RRQ broadcast for a relative * bootfile pathname from a diskless Sun. */ if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) exit(0); send_error(peer, ecode); exit(1); } tftp_xmitfile(peer, mode); }
/* * Handle initial connection protocol. */ void tftp(struct tftphdr *tp, int size) { char *cp; size_t i; int first = 1, has_options = 0, ecode; struct formats *pf; char *filename, *mode = NULL, *option, *ccp; char fnbuf[MAXPATHLEN]; cp = tp->th_stuff; again: while (cp < buf + size) { if (*cp == '\0') break; cp++; } if (*cp != '\0') { nak(EBADOP); exit(1); } i = cp - tp->th_stuff; if (i >= sizeof(fnbuf)) { nak(EBADOP); exit(1); } memcpy(fnbuf, tp->th_stuff, i); fnbuf[i] = '\0'; filename = fnbuf; if (first) { mode = ++cp; first = 0; goto again; } for (cp = mode; *cp; cp++) if (isupper(*cp)) *cp = tolower(*cp); for (pf = formats; pf->f_mode; pf++) if (strcmp(pf->f_mode, mode) == 0) break; if (pf->f_mode == NULL) { nak(EBADOP); exit(1); } while (++cp < buf + size) { for (i = 2, ccp = cp; i > 0; ccp++) { if (ccp >= buf + size) { /* * Don't reject the request, just stop trying * to parse the option and get on with it. * Some Apple OpenFirmware versions have * trailing garbage on the end of otherwise * valid requests. */ goto option_fail; } else if (*ccp == '\0') i--; } for (option = cp; *cp; cp++) if (isupper(*cp)) *cp = tolower(*cp); for (i = 0; options[i].o_type != NULL; i++) if (strcmp(option, options[i].o_type) == 0) { options[i].o_request = ++cp; has_options = 1; } cp = ccp-1; } option_fail: if (options[OPT_TIMEOUT].o_request) { int to = atoi(options[OPT_TIMEOUT].o_request); if (to < 1 || to > 255) { nak(EBADOP); exit(1); } else if (to <= max_rexmtval) options[OPT_TIMEOUT].o_reply = rexmtval = to; else options[OPT_TIMEOUT].o_request = NULL; } ecode = (*pf->f_validate)(&filename, tp->th_opcode); if (has_options) oack(); if (logging) { char hbuf[NI_MAXHOST]; getnameinfo((struct sockaddr *)&from, from.ss_len, hbuf, sizeof(hbuf), NULL, 0, NI_WITHSCOPEID); syslog(LOG_INFO, "%s: %s request for %s: %s", hbuf, tp->th_opcode == WRQ ? "write" : "read", filename, errtomsg(ecode)); } if (ecode) { /* * Avoid storms of naks to a RRQ broadcast for a relative * bootfile pathname from a diskless Sun. */ if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) exit(0); nak(ecode); exit(1); } if (tp->th_opcode == WRQ) (*pf->f_recv)(pf); else (*pf->f_send)(pf); exit(0); }
/* * Handle initial connection protocol. */ void tftp (struct tftphdr *tp, int size) { register char *cp; int first = 1, ecode; register struct formats *pf; char *filename, *mode; filename = cp = tp->th_stuff; again: while (cp < buf + size) { if (*cp == '\0') break; cp++; } if (*cp != '\0') { nak (EBADOP); exit (1); } if (first) { mode = ++cp; first = 0; goto again; } for (cp = mode; *cp; cp++) if (isupper (*cp)) *cp = tolower (*cp); for (pf = formats; pf->f_mode; pf++) if (strcmp (pf->f_mode, mode) == 0) break; if (pf->f_mode == 0) { nak (EBADOP); exit (1); } ecode = (*pf->f_validate) (&filename, tp->th_opcode); if (logging) { syslog (LOG_INFO, "%s: %s request for %s: %s", verifyhost (&from), tp->th_opcode == WRQ ? "write" : "read", filename, errtomsg (ecode)); } if (ecode) { /* * Avoid storms of naks to a RRQ broadcast for a relative * bootfile pathname from a diskless Sun. */ if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) exit (0); nak (ecode); exit (1); } if (tp->th_opcode == WRQ) (*pf->f_recv) (pf); else (*pf->f_send) (pf); exit (0); }