Commit | Line | Data |
---|---|---|
524aa063 SL |
1 | #ifndef lint |
2 | static char sccsid[] = "@(#)tftp.c 4.7 (Berkeley) %G%"; | |
3 | #endif | |
d3fc13cc SL |
4 | |
5 | /* | |
6 | * TFTP User Program -- Protocol Machines | |
7 | */ | |
8 | #include <sys/types.h> | |
d3fc13cc | 9 | #include <sys/socket.h> |
de3b21e8 SL |
10 | |
11 | #include <netinet/in.h> | |
12 | ||
86fe57d4 SL |
13 | #include <arpa/tftp.h> |
14 | ||
d3fc13cc SL |
15 | #include <signal.h> |
16 | #include <stdio.h> | |
17 | #include <errno.h> | |
18 | #include <setjmp.h> | |
de3b21e8 | 19 | |
d3fc13cc SL |
20 | extern int errno; |
21 | extern struct sockaddr_in sin; | |
22 | extern char mode[]; | |
23 | int f; | |
24 | int trace; | |
25 | int verbose; | |
26 | int connected; | |
27 | char buf[BUFSIZ]; | |
61ad9979 SL |
28 | int rexmtval; |
29 | int maxtimeout; | |
d3fc13cc SL |
30 | int timeout; |
31 | jmp_buf toplevel; | |
61ad9979 | 32 | jmp_buf timeoutbuf; |
d3fc13cc SL |
33 | |
34 | timer() | |
35 | { | |
61ad9979 SL |
36 | |
37 | timeout += rexmtval; | |
38 | if (timeout >= maxtimeout) { | |
d3fc13cc SL |
39 | printf("Transfer timed out.\n"); |
40 | longjmp(toplevel, -1); | |
41 | } | |
61ad9979 | 42 | longjmp(timeoutbuf, 1); |
d3fc13cc SL |
43 | } |
44 | ||
45 | /* | |
46 | * Send the requested file. | |
47 | */ | |
48 | sendfile(fd, name) | |
49 | int fd; | |
50 | char *name; | |
51 | { | |
52 | register struct tftphdr *tp = (struct tftphdr *)buf; | |
53 | register int block = 0, size, n, amount = 0; | |
54 | struct sockaddr_in from; | |
55 | time_t start = time(0), delta; | |
de3b21e8 | 56 | int fromlen; |
d3fc13cc | 57 | |
61ad9979 | 58 | signal(SIGALRM, timer); |
d3fc13cc | 59 | do { |
61ad9979 SL |
60 | if (block == 0) |
61 | size = makerequest(WRQ, name) - 4; | |
62 | else { | |
d3fc13cc SL |
63 | size = read(fd, tp->th_data, SEGSIZE); |
64 | if (size < 0) { | |
65 | nak(errno + 100); | |
66 | break; | |
67 | } | |
68 | tp->th_opcode = htons((u_short)DATA); | |
69 | tp->th_block = htons((u_short)block); | |
70 | } | |
71 | timeout = 0; | |
61ad9979 | 72 | (void) setjmp(timeoutbuf); |
d3fc13cc SL |
73 | if (trace) |
74 | tpacket("sent", tp, size + 4); | |
f9519a15 | 75 | n = sendto(f, buf, size + 4, 0, (caddr_t)&sin, sizeof (sin)); |
de3b21e8 | 76 | if (n != size + 4) { |
61ad9979 SL |
77 | perror("tftp: sendto"); |
78 | goto abort; | |
d3fc13cc | 79 | } |
61ad9979 SL |
80 | do { |
81 | alarm(rexmtval); | |
82 | do { | |
83 | fromlen = sizeof (from); | |
84 | n = recvfrom(f, buf, sizeof (buf), 0, | |
85 | (caddr_t)&from, &fromlen); | |
86 | } while (n <= 0); | |
d3fc13cc | 87 | alarm(0); |
61ad9979 SL |
88 | if (n < 0) { |
89 | perror("tftp: recvfrom"); | |
90 | goto abort; | |
91 | } | |
92 | if (trace) | |
93 | tpacket("received", tp, n); | |
94 | /* should verify packet came from server */ | |
95 | tp->th_opcode = ntohs(tp->th_opcode); | |
96 | tp->th_block = ntohs(tp->th_block); | |
97 | if (tp->th_opcode == ERROR) { | |
98 | printf("Error code %d: %s\n", tp->th_code, | |
99 | tp->th_msg); | |
100 | goto abort; | |
101 | } | |
102 | } while (tp->th_opcode != ACK && block != tp->th_block); | |
d3fc13cc SL |
103 | if (block > 0) |
104 | amount += size; | |
105 | block++; | |
106 | } while (size == SEGSIZE || block == 1); | |
61ad9979 | 107 | abort: |
d3fc13cc SL |
108 | (void) close(fd); |
109 | if (amount > 0) { | |
110 | delta = time(0) - start; | |
111 | printf("Sent %d bytes in %d seconds.\n", amount, delta); | |
112 | } | |
113 | } | |
114 | ||
115 | /* | |
116 | * Receive a file. | |
117 | */ | |
118 | recvfile(fd, name) | |
119 | int fd; | |
120 | char *name; | |
121 | { | |
122 | register struct tftphdr *tp = (struct tftphdr *)buf; | |
123 | register int block = 1, n, size, amount = 0; | |
124 | struct sockaddr_in from; | |
125 | time_t start = time(0), delta; | |
61ad9979 | 126 | int fromlen, firsttrip = 1; |
d3fc13cc | 127 | |
61ad9979 | 128 | signal(SIGALRM, timer); |
d3fc13cc | 129 | do { |
61ad9979 SL |
130 | if (firsttrip) { |
131 | size = makerequest(RRQ, name); | |
132 | firsttrip = 0; | |
133 | } else { | |
134 | tp->th_opcode = htons((u_short)ACK); | |
135 | tp->th_block = htons((u_short)(block)); | |
136 | size = 4; | |
137 | block++; | |
138 | } | |
d3fc13cc | 139 | timeout = 0; |
61ad9979 | 140 | (void) setjmp(timeoutbuf); |
d3fc13cc SL |
141 | if (trace) |
142 | tpacket("sent", tp, size); | |
61ad9979 SL |
143 | if (sendto(f, buf, size, 0, (caddr_t)&sin, |
144 | sizeof (sin)) != size) { | |
d3fc13cc | 145 | alarm(0); |
61ad9979 SL |
146 | perror("tftp: sendto"); |
147 | goto abort; | |
d3fc13cc | 148 | } |
61ad9979 SL |
149 | do { |
150 | alarm(rexmtval); | |
151 | do | |
152 | n = recvfrom(f, buf, sizeof (buf), 0, | |
153 | (caddr_t)&from, &fromlen); | |
154 | while (n <= 0); | |
155 | alarm(0); | |
156 | if (n < 0) { | |
157 | perror("tftp: recvfrom"); | |
158 | goto abort; | |
159 | } | |
160 | if (trace) | |
161 | tpacket("received", tp, n); | |
162 | /* should verify client address */ | |
163 | tp->th_opcode = ntohs(tp->th_opcode); | |
164 | tp->th_block = ntohs(tp->th_block); | |
165 | if (tp->th_opcode == ERROR) { | |
166 | printf("Error code %d: %s\n", tp->th_code, | |
167 | tp->th_msg); | |
168 | goto abort; | |
169 | } | |
170 | } while (tp->th_opcode != DATA && block != tp->th_block); | |
d3fc13cc SL |
171 | size = write(fd, tp->th_data, n - 4); |
172 | if (size < 0) { | |
173 | nak(errno + 100); | |
174 | break; | |
175 | } | |
176 | amount += size; | |
177 | } while (size == SEGSIZE); | |
61ad9979 | 178 | abort: |
d3fc13cc SL |
179 | tp->th_opcode = htons((u_short)ACK); |
180 | tp->th_block = htons((u_short)block); | |
f9519a15 | 181 | (void) sendto(f, buf, 4, 0, &sin, sizeof (sin)); |
d3fc13cc SL |
182 | (void) close(fd); |
183 | if (amount > 0) { | |
184 | delta = time(0) - start; | |
185 | printf("Received %d bytes in %d seconds.\n", amount, delta); | |
186 | } | |
187 | } | |
188 | ||
189 | makerequest(request, name) | |
190 | int request; | |
191 | char *name; | |
192 | { | |
193 | register struct tftphdr *tp; | |
194 | int size; | |
195 | register char *cp; | |
196 | ||
197 | tp = (struct tftphdr *)buf; | |
198 | tp->th_opcode = htons((u_short)request); | |
199 | strcpy(tp->th_stuff, name); | |
200 | size = strlen(name); | |
201 | cp = tp->th_stuff + strlen(name); | |
202 | *cp++ = '\0'; | |
203 | strcpy(cp, mode); | |
204 | cp += sizeof ("netascii") - 1; | |
205 | *cp++ = '\0'; | |
206 | return (cp - buf); | |
207 | } | |
208 | ||
209 | struct errmsg { | |
210 | int e_code; | |
211 | char *e_msg; | |
212 | } errmsgs[] = { | |
213 | { EUNDEF, "Undefined error code" }, | |
214 | { ENOTFOUND, "File not found" }, | |
215 | { EACCESS, "Access violation" }, | |
216 | { ENOSPACE, "Disk full or allocation exceeded" }, | |
217 | { EBADOP, "Illegal TFTP operation" }, | |
218 | { EBADID, "Unknown transfer ID" }, | |
219 | { EEXISTS, "File already exists" }, | |
220 | { ENOUSER, "No such user" }, | |
221 | { -1, 0 } | |
222 | }; | |
223 | ||
224 | /* | |
225 | * Send a nak packet (error message). | |
226 | * Error code passed in is one of the | |
227 | * standard TFTP codes, or a UNIX errno | |
228 | * offset by 100. | |
229 | */ | |
230 | nak(error) | |
231 | int error; | |
232 | { | |
233 | register struct tftphdr *tp; | |
234 | int length; | |
235 | register struct errmsg *pe; | |
236 | extern char *sys_errlist[]; | |
237 | ||
238 | tp = (struct tftphdr *)buf; | |
239 | tp->th_opcode = htons((u_short)ERROR); | |
240 | tp->th_code = htons((u_short)error); | |
241 | for (pe = errmsgs; pe->e_code >= 0; pe++) | |
242 | if (pe->e_code == error) | |
243 | break; | |
244 | if (pe->e_code < 0) | |
245 | pe->e_msg = sys_errlist[error - 100]; | |
246 | strcpy(tp->th_msg, pe->e_msg); | |
247 | length = strlen(pe->e_msg) + 4; | |
248 | if (trace) | |
249 | tpacket("sent", tp, length); | |
250 | if (send(f, &sin, buf, length) != length) | |
251 | perror("nak"); | |
252 | } | |
253 | ||
254 | tpacket(s, tp, n) | |
255 | struct tftphdr *tp; | |
256 | int n; | |
257 | { | |
258 | static char *opcodes[] = | |
259 | { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; | |
260 | register char *cp, *file; | |
261 | u_short op = ntohs(tp->th_opcode); | |
262 | char *index(); | |
263 | ||
264 | if (op < RRQ || op > ERROR) | |
265 | printf("%s opcode=%x ", s, op); | |
266 | else | |
267 | printf("%s %s ", s, opcodes[op]); | |
268 | switch (op) { | |
269 | ||
270 | case RRQ: | |
271 | case WRQ: | |
272 | n -= 2; | |
273 | file = cp = tp->th_stuff; | |
274 | cp = index(cp, '\0'); | |
275 | printf("<file=%s, mode=%s>\n", file, cp + 1); | |
276 | break; | |
277 | ||
278 | case DATA: | |
279 | printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); | |
280 | break; | |
281 | ||
282 | case ACK: | |
283 | printf("<block=%d>\n", ntohs(tp->th_block)); | |
284 | break; | |
285 | ||
286 | case ERROR: | |
287 | printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); | |
288 | break; | |
289 | } | |
290 | } |