* Copyright (c) 1983 Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char sccsid
[] = "@(#)tftp.c 5.10 (Berkeley) 3/1/91";
/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
* TFTP User Program -- Protocol Machines
extern struct sockaddr_in s_in
; /* filled in by main */
extern int f
; /* the opened socket */
#define PKTSIZE SEGSIZE+4
if (timeout
>= maxtimeout
) {
printf("Transfer timed out.\n");
* Send the requested file.
register struct tftphdr
*ap
; /* data and ack packets */
struct tftphdr
*r_init(), *dp
;
register int block
= 0, size
, n
;
register unsigned long amount
= 0;
int convert
; /* true if doing nl->crlf conversion */
startclock(); /* start stat's clock */
dp
= r_init(); /* reset fillbuf/read-ahead code */
ap
= (struct tftphdr
*)ackbuf
;
convert
= !strcmp(mode
, "netascii");
size
= makerequest(WRQ
, name
, dp
, mode
) - 4;
/* size = read(fd, dp->th_data, SEGSIZE); */
size
= readit(file
, &dp
, convert
);
dp
->th_opcode
= htons((u_short
)DATA
);
dp
->th_block
= htons((u_short
)block
);
(void) setjmp(timeoutbuf
);
tpacket("sent", dp
, size
+ 4);
n
= sendto(f
, dp
, size
+ 4, 0,
(struct sockaddr
*)&s_in
, sizeof (s_in
));
read_ahead(file
, convert
);
n
= recvfrom(f
, ackbuf
, sizeof (ackbuf
), 0,
(struct sockaddr
*)&from
, &fromlen
);
perror("tftp: recvfrom");
s_in
.sin_port
= from
.sin_port
; /* added */
tpacket("received", ap
, n
);
/* should verify packet came from server */
ap
->th_opcode
= ntohs(ap
->th_opcode
);
ap
->th_block
= ntohs(ap
->th_block
);
if (ap
->th_opcode
== ERROR
) {
printf("Error code %d: %s\n", ap
->th_code
,
if (ap
->th_opcode
== ACK
) {
if (ap
->th_block
== block
) {
/* On an error, try to synchronize
printf("discarded %d packets\n",
if (ap
->th_block
== (block
-1)) {
} while (size
== SEGSIZE
|| block
== 1);
printstats("Sent", amount
);
register struct tftphdr
*ap
;
struct tftphdr
*dp
, *w_init();
register int block
= 1, n
, size
;
unsigned long amount
= 0;
int fromlen
, firsttrip
= 1;
int convert
; /* true if converting crlf -> lf */
ap
= (struct tftphdr
*)ackbuf
;
convert
= !strcmp(mode
, "netascii");
size
= makerequest(RRQ
, name
, ap
, mode
);
ap
->th_opcode
= htons((u_short
)ACK
);
ap
->th_block
= htons((u_short
)(block
));
(void) setjmp(timeoutbuf
);
tpacket("sent", ap
, size
);
if (sendto(f
, ackbuf
, size
, 0, (struct sockaddr
*)&s_in
,
sizeof (s_in
)) != size
) {
write_behind(file
, convert
);
n
= recvfrom(f
, dp
, PKTSIZE
, 0,
(struct sockaddr
*)&from
, &fromlen
);
perror("tftp: recvfrom");
s_in
.sin_port
= from
.sin_port
; /* added */
tpacket("received", dp
, n
);
/* should verify client address */
dp
->th_opcode
= ntohs(dp
->th_opcode
);
dp
->th_block
= ntohs(dp
->th_block
);
if (dp
->th_opcode
== ERROR
) {
printf("Error code %d: %s\n", dp
->th_code
,
if (dp
->th_opcode
== DATA
) {
if (dp
->th_block
== block
) {
break; /* have next packet */
/* On an error, try to synchronize
printf("discarded %d packets\n", j
);
if (dp
->th_block
== (block
-1)) {
goto send_ack
; /* resend ack */
/* size = write(fd, dp->th_data, n - 4); */
size
= writeit(file
, &dp
, n
- 4, convert
);
} while (size
== SEGSIZE
);
abort
: /* ok to ack, since user */
ap
->th_opcode
= htons((u_short
)ACK
); /* has seen err msg */
ap
->th_block
= htons((u_short
)block
);
(void) sendto(f
, ackbuf
, 4, 0, (struct sockaddr
*)&s_in
, sizeof (s_in
));
write_behind(file
, convert
); /* flush last buffer */
printstats("Received", amount
);
makerequest(request
, name
, tp
, mode
)
tp
->th_opcode
= htons((u_short
)request
);
return (cp
- (char *)tp
);
{ EUNDEF
, "Undefined error code" },
{ ENOTFOUND
, "File not found" },
{ EACCESS
, "Access violation" },
{ ENOSPACE
, "Disk full or allocation exceeded" },
{ EBADOP
, "Illegal TFTP operation" },
{ EBADID
, "Unknown transfer ID" },
{ EEXISTS
, "File already exists" },
{ ENOUSER
, "No such user" },
* Send a nak packet (error message).
* Error code passed in is one of the
* standard TFTP codes, or a UNIX errno
register struct errmsg
*pe
;
register struct tftphdr
*tp
;
tp
= (struct tftphdr
*)ackbuf
;
tp
->th_opcode
= htons((u_short
)ERROR
);
tp
->th_code
= htons((u_short
)error
);
for (pe
= errmsgs
; pe
->e_code
>= 0; pe
++)
pe
->e_msg
= strerror(error
- 100);
strcpy(tp
->th_msg
, pe
->e_msg
);
length
= strlen(pe
->e_msg
) + 4;
tpacket("sent", tp
, length
);
if (sendto(f
, ackbuf
, length
, 0, (struct sockaddr
*)&s_in
,
sizeof (s_in
)) != length
)
{ "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
register char *cp
, *file
;
u_short op
= ntohs(tp
->th_opcode
);
if (op
< RRQ
|| op
> ERROR
)
printf("%s opcode=%x ", s
, op
);
printf("%s %s ", s
, opcodes
[op
]);
file
= cp
= tp
->th_stuff
;
printf("<file=%s, mode=%s>\n", file
, cp
+ 1);
printf("<block=%d, %d bytes>\n", ntohs(tp
->th_block
), n
- 4);
printf("<block=%d>\n", ntohs(tp
->th_block
));
printf("<code=%d, msg=%s>\n", ntohs(tp
->th_code
), tp
->th_msg
);
gettimeofday(&tstart
, &zone
);
gettimeofday(&tstop
, &zone
);
printstats(direction
, amount
)
/* compute delta in 1/10's second units */
delta
= ((tstop
.tv_sec
*10.)+(tstop
.tv_usec
/100000)) -
((tstart
.tv_sec
*10.)+(tstart
.tv_usec
/100000));
delta
= delta
/10.; /* back to seconds */
printf("%s %d bytes in %.1f seconds", direction
, amount
, delta
);
printf(" [%.0f bits/sec]", (amount
*8.)/delta
);