Introduction
Introduction Statistics Contact Development Disclaimer Help
tftp.c - sbase - suckless unix tools
git clone git://git.suckless.org/sbase
Log
Files
Refs
README
LICENSE
---
tftp.c (5791B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include <sys/time.h>
3 #include <sys/types.h>
4 #include <sys/socket.h>
5
6 #include <netdb.h>
7 #include <netinet/in.h>
8
9 #include <errno.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14
15 #include "util.h"
16
17 #define BLKSIZE 512
18 #define HDRSIZE 4
19 #define PKTSIZE (BLKSIZE + HDRSIZE)
20
21 #define TIMEOUT_SEC 5
22 /* transfer will time out after NRETRIES * TIMEOUT_SEC */
23 #define NRETRIES 5
24
25 #define RRQ 1
26 #define WWQ 2
27 #define DATA 3
28 #define ACK 4
29 #define ERR 5
30
31 static char *errtext[] = {
32 "Undefined",
33 "File not found",
34 "Access violation",
35 "Disk full or allocation exceeded",
36 "Illegal TFTP operation",
37 "Unknown transfer ID",
38 "File already exists",
39 "No such user"
40 };
41
42 static struct sockaddr_storage to;
43 static socklen_t tolen;
44 static int timeout;
45 static int state;
46 static int s;
47
48 static int
49 packreq(unsigned char *buf, int op, char *path, char *mode)
50 {
51 unsigned char *p = buf;
52
53 *p++ = op >> 8;
54 *p++ = op & 0xff;
55 if (strlen(path) + 1 > 256)
56 eprintf("filename too long\n");
57 memcpy(p, path, strlen(path) + 1);
58 p += strlen(path) + 1;
59 memcpy(p, mode, strlen(mode) + 1);
60 p += strlen(mode) + 1;
61 return p - buf;
62 }
63
64 static int
65 packack(unsigned char *buf, int blkno)
66 {
67 buf[0] = ACK >> 8;
68 buf[1] = ACK & 0xff;
69 buf[2] = blkno >> 8;
70 buf[3] = blkno & 0xff;
71 return 4;
72 }
73
74 static int
75 packdata(unsigned char *buf, int blkno)
76 {
77 buf[0] = DATA >> 8;
78 buf[1] = DATA & 0xff;
79 buf[2] = blkno >> 8;
80 buf[3] = blkno & 0xff;
81 return 4;
82 }
83
84 static int
85 unpackop(unsigned char *buf)
86 {
87 return (buf[0] << 8) | (buf[1] & 0xff);
88 }
89
90 static int
91 unpackblkno(unsigned char *buf)
92 {
93 return (buf[2] << 8) | (buf[3] & 0xff);
94 }
95
96 static int
97 unpackerrc(unsigned char *buf)
98 {
99 int errc;
100
101 errc = (buf[2] << 8) | (buf[3] & 0xff);
102 if (errc < 0 || errc >= LEN(errtext))
103 eprintf("bad error code: %d\n", errc);
104 return errc;
105 }
106
107 static int
108 writepkt(unsigned char *buf, int len)
109 {
110 int n;
111
112 n = sendto(s, buf, len, 0, (struct sockaddr *)&to,
113 tolen);
114 if (n < 0)
115 if (errno != EINTR)
116 eprintf("sendto:");
117 return n;
118 }
119
120 static int
121 readpkt(unsigned char *buf, int len)
122 {
123 int n;
124
125 n = recvfrom(s, buf, len, 0, (struct sockaddr *)&to,
126 &tolen);
127 if (n < 0) {
128 if (errno != EINTR && errno != EWOULDBLOCK)
129 eprintf("recvfrom:");
130 timeout++;
131 if (timeout == NRETRIES)
132 eprintf("transfer timed out\n");
133 } else {
134 timeout = 0;
135 }
136 return n;
137 }
138
139 static void
140 getfile(char *file)
141 {
142 unsigned char buf[PKTSIZE];
143 int n, op, blkno, nextblkno = 1, done = 0;
144
145 state = RRQ;
146 for (;;) {
147 switch (state) {
148 case RRQ:
149 n = packreq(buf, RRQ, file, "octet");
150 writepkt(buf, n);
151 n = readpkt(buf, sizeof(buf));
152 if (n > 0) {
153 op = unpackop(buf);
154 if (op != DATA && op != ERR)
155 eprintf("bad opcode: %d\n", op);
156 state = op;
157 }
158 break;
159 case DATA:
160 n -= HDRSIZE;
161 if (n < 0)
162 eprintf("truncated packet\n");
163 blkno = unpackblkno(buf);
164 if (blkno == nextblkno) {
165 nextblkno++;
166 write(1, &buf[HDRSIZE], n);
167 }
168 if (n < BLKSIZE)
169 done = 1;
170 state = ACK;
171 break;
172 case ACK:
173 n = packack(buf, blkno);
174 writepkt(buf, n);
175 if (done)
176 return;
177 n = readpkt(buf, sizeof(buf));
178 if (n > 0) {
179 op = unpackop(buf);
180 if (op != DATA && op != ERR)
181 eprintf("bad opcode: %d\n", op);
182 state = op;
183 }
184 break;
185 case ERR:
186 eprintf("error: %s\n", errtext[unpackerrc(buf)]);
187 }
188 }
189 }
190
191 static void
192 putfile(char *file)
193 {
194 unsigned char inbuf[PKTSIZE], outbuf[PKTSIZE];
195 int inb, outb, op, blkno, nextblkno = 0, done = 0;
196
197 state = WWQ;
198 for (;;) {
199 switch (state) {
200 case WWQ:
201 outb = packreq(outbuf, WWQ, file, "octet");
202 writepkt(outbuf, outb);
203 inb = readpkt(inbuf, sizeof(inbuf));
204 if (inb > 0) {
205 op = unpackop(inbuf);
206 if (op != ACK && op != ERR)
207 eprintf("bad opcode: %d\n", op);
208 state = op;
209 }
210 break;
211 case DATA:
212 if (blkno == nextblkno) {
213 nextblkno++;
214 packdata(outbuf, nextblkno);
215 outb = read(0, &outbuf[HDRSIZE], BLKSIZE…
216 if (outb < BLKSIZE)
217 done = 1;
218 }
219 writepkt(outbuf, outb + HDRSIZE);
220 inb = readpkt(inbuf, sizeof(inbuf));
221 if (inb > 0) {
222 op = unpackop(inbuf);
223 if (op != ACK && op != ERR)
224 eprintf("bad opcode: %d\n", op);
225 state = op;
226 }
227 break;
228 case ACK:
229 if (inb < HDRSIZE)
230 eprintf("truncated packet\n");
231 blkno = unpackblkno(inbuf);
232 if (blkno == nextblkno)
233 if (done)
234 return;
235 state = DATA;
236 break;
237 case ERR:
238 eprintf("error: %s\n", errtext[unpackerrc(inbuf)…
239 }
240 }
241 }
242
243 static void
244 usage(void)
245 {
246 eprintf("usage: %s -h host [-p port] [-x | -c] file\n", argv0);
247 }
248
249 int
250 main(int argc, char *argv[])
251 {
252 struct addrinfo hints, *res, *r;
253 struct timeval tv;
254 char *host = NULL, *port = "tftp";
255 void (*fn)(char *) = getfile;
256 int ret;
257
258 ARGBEGIN {
259 case 'h':
260 host = EARGF(usage());
261 break;
262 case 'p':
263 port = EARGF(usage());
264 break;
265 case 'x':
266 fn = getfile;
267 break;
268 case 'c':
269 fn = putfile;
270 break;
271 default:
272 usage();
273 } ARGEND
274
275 if (!host || !argc)
276 usage();
277
278 memset(&hints, 0, sizeof(hints));
279 hints.ai_family = AF_UNSPEC;
280 hints.ai_socktype = SOCK_DGRAM;
281 hints.ai_protocol = IPPROTO_UDP;
282 ret = getaddrinfo(host, port, &hints, &res);
283 if (ret)
284 eprintf("getaddrinfo: %s\n", gai_strerror(ret));
285
286 for (r = res; r; r = r->ai_next) {
287 if (r->ai_family != AF_INET &&
288 r->ai_family != AF_INET6)
289 continue;
290 s = socket(r->ai_family, r->ai_socktype,
291 r->ai_protocol);
292 if (s < 0)
293 continue;
294 break;
295 }
296 if (!r)
297 eprintf("cannot create socket\n");
298 memcpy(&to, r->ai_addr, r->ai_addrlen);
299 tolen = r->ai_addrlen;
300 freeaddrinfo(res);
301
302 tv.tv_sec = TIMEOUT_SEC;
303 tv.tv_usec = 0;
304 if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
305 eprintf("setsockopt:");
306
307 fn(argv[0]);
308 return 0;
309 }
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.