Annotation of tftpd/src/srv.c, revision 1.2.2.4
1.1 misho 1: #include "global.h"
1.2 misho 2: #include "exec.h"
3: #include "srv.h"
1.1 misho 4:
5:
6: static void *
7: timeoutSession(sched_task_t *task)
8: {
9: ETRACE();
10:
11: /* drop session */
12: if (cli.fd > 2)
13: close(cli.fd);
14: memset(&cli, 0, sizeof cli);
15:
16: taskExit(task, NULL);
17: }
18:
19: static void *
20: txPkt(sched_task_t *task)
21: {
22: int wlen;
23: rpack_t *pkt = TASK_DATA(task);
24: struct tftp_hdr *tftp = (struct tftp_hdr*) RPACK_BUF(pkt);
25:
26: ETRACE();
27:
28: wlen = sendto(TASK_FD(task), RPACK_BUF(pkt), TASK_DATLEN(task),
29: 0, &cli.addr.sa, cli.addr.sa.sa_len);
30: if (wlen == -1)
31: ESYSERR(0);
32: else if (wlen != TASK_DATLEN(task)) {
33: EERROR(EIO, "Sended %d bytes != packet %d bytes",
34: wlen, TASK_DATLEN(task));
1.2 misho 35: schedEvent(TASK_ROOT(task), execProg, "error", 0, NULL, TFTP_OPC_ERROR);
1.1 misho 36: schedCancelby(TASK_ROOT(task), taskTIMER, CRITERIA_CALL,
37: timeoutSession, NULL);
1.2 misho 38: schedEvent(TASK_ROOT(task), timeoutSession, NULL, 0, NULL, 0);
1.1 misho 39: } else
40: EVERBOSE(2, "Sended %d bytes", wlen);
41: /* on error or argument, drop session */
42: if (TASK_ARG(task) == (void*) -1 || ntohs(tftp->tftp_opc) == TFTP_OPC_ERROR) {
1.2 misho 43: schedEvent(TASK_ROOT(task), execProg, "error", 0, NULL, TFTP_OPC_ERROR);
1.1 misho 44: schedCancelby(TASK_ROOT(task), taskTIMER, CRITERIA_CALL,
45: timeoutSession, NULL);
1.2 misho 46: schedEvent(TASK_ROOT(task), timeoutSession, NULL, 0, NULL, 0);
1.1 misho 47: }
48:
49: taskExit(task, NULL);
50: }
51:
52: static void *
53: txData(sched_task_t *task)
54: {
55: rpack_t *pkt = TASK_DATA(task);
56: u_short code = 0, n = htons(TFTP_OPC_DATA);
57: int len;
58:
59: ETRACE();
60:
61: RPACK_REWIND(pkt);
62: rpack_uint16(pkt, &n, 0);
63: n = htons(cli.seq);
64: rpack_uint16(pkt, &n, 0);
65:
1.2.2.4 ! misho 66: /* max file size check */
! 67: if (cli.tsiz && cli.tsiz <= cli.seq * cli.siz) {
! 68: len = MAX(0, cli.tsiz - (cli.seq - 1) * cli.siz);
! 69: cli.close = 42; /* last sended packet, should be close! */
! 70: } else
! 71: len = cli.siz;
! 72:
! 73: len = pread(cli.fd, RPACK_NEXT(pkt), len, (cli.seq - 1) * cli.siz);
1.1 misho 74: if (len == -1) {
75: ESYSERR(0);
76: code = htole16(3);
77: goto end;
78: } else {
79: rpack_rnext(pkt, len);
80: EVERBOSE(3, "Read from file %s %d bytes", cli.file, len);
81: }
82:
83: if (cli.siz - len)
84: cli.close = 42; /* last sended packet, should be close! */
85:
86: schedEvent(TASK_ROOT(task), txPkt, NULL, TASK_FD(task),
87: TASK_DATA(task), RPACK_OFF(pkt));
88: taskExit(task, NULL);
89: end:
90: RPACK_REWIND(pkt);
91: n = htons(TFTP_OPC_ERROR);
92: rpack_uint16(pkt, &n, 0);
93: rpack_uint16(pkt, &code, 0);
94: rpack_rdata(pkt, errs[le16toh(code)].err_msg, strlen(errs[le16toh(code)].err_msg) + 1);
95:
96: schedEvent(TASK_ROOT(task), txPkt, (void*) -1, TASK_FD(task),
97: TASK_DATA(task), RPACK_OFF(pkt));
98: taskExit(task, NULL);
99: }
100:
101: static void *
102: txAck(sched_task_t *task)
103: {
104: rpack_t *pkt = TASK_DATA(task);
105: u_short n = htons(TFTP_OPC_ACK);
106:
107: ETRACE();
108:
109: RPACK_REWIND(pkt);
110: rpack_uint16(pkt, &n, 0);
111: n = htons(cli.seq);
112: rpack_uint16(pkt, &n, 0);
113:
114: schedEvent(TASK_ROOT(task), txPkt, NULL, TASK_FD(task),
115: TASK_DATA(task), RPACK_OFF(pkt));
116:
117: if (cli.close) {
1.2 misho 118: schedEvent(TASK_ROOT(task), execProg, "complete", 0, NULL, TFTP_OPC_WRQ);
1.1 misho 119: schedCancelby(TASK_ROOT(task), taskTIMER, CRITERIA_CALL, timeoutSession, NULL);
120: schedEvent(TASK_ROOT(task), timeoutSession, NULL, 0, NULL, 0);
121: EVERBOSE(2, "Finish WRQ request");
122: }
123: taskExit(task, NULL);
124: }
125:
1.2.2.1 misho 126: static int
127: getOpts(rpack_t * __restrict pkt)
128: {
1.2.2.2 misho 129: char *opt, *val;
130: int len;
131:
132: do {
133: /* option */
134: len = str_getString(RPACK_NEXT(pkt), RPACK_REMAIN(pkt), NULL);
135: if (len == -1)
136: return -1;
137: opt = (char*) rpack_rnext(pkt, len);
138: if (!opt)
139: return -1;
140: /* value */
141: len = str_getString(RPACK_NEXT(pkt), RPACK_REMAIN(pkt), NULL);
142: if (len == -1)
143: return -1;
144: val = (char*) rpack_rnext(pkt, len);
145: if (!val)
146: return -1;
147:
148: if (!strcasecmp(opt, TFTP_OPT_BLKSIZE)) {
149: len = strtol(val, NULL, 10);
150: if (len > TFTP_LOAD_MAX)
151: cli.siz = len;
152: } else if (!strcasecmp(opt, TFTP_OPT_TSIZE))
153: cli.tsiz = strtoll(val, NULL, 10);
154: else if (!strcasecmp(opt, TFTP_OPT_TIMEOUT))
155: cli.tout = strtol(val, NULL, 10);
156: else
157: return -1;
158: } while (!RPACK_ISEND(pkt));
159:
1.2.2.3 misho 160: EVERBOSE(4, "blksize=%u tsize=%llu timeout=%u", cli.siz, cli.tsiz, cli.tout);
1.2.2.1 misho 161: return 0;
162: }
163:
164: static void *
165: txOack(sched_task_t *task)
166: {
167: rpack_t *pkt = TASK_DATA(task);
168: u_short n = htons(TFTP_OPC_OACK);
169: struct stat sb;
170: char szStr[STRSIZ];
171:
172: ETRACE();
173:
174: RPACK_REWIND(pkt);
175: rpack_uint16(pkt, &n, 0);
176:
177: /* if opcode is RRQ and tsize is 0 then we must return file size to client */
178: if (cli.opc == TFTP_OPC_RRQ && !cli.tsiz && stat(cli.file, &sb) != -1)
179: cli.tsiz = sb.st_size;
180:
181: if (cli.siz > TFTP_LOAD_MAX) {
182: memset(szStr, 0, sizeof szStr);
183: snprintf(szStr, sizeof szStr, "%u", cli.siz);
184: rpack_rdata(pkt, TFTP_OPT_BLKSIZE, strlen(TFTP_OPT_BLKSIZE) + 1);
185: rpack_rdata(pkt, szStr, strlen(szStr) + 1);
186: }
187: if (cli.tsiz) {
188: memset(szStr, 0, sizeof szStr);
189: snprintf(szStr, sizeof szStr, "%llu", cli.tsiz);
190: rpack_rdata(pkt, TFTP_OPT_TSIZE, strlen(TFTP_OPT_TSIZE) + 1);
191: rpack_rdata(pkt, szStr, strlen(szStr) + 1);
192: }
193: if (cli.tout) {
194: memset(szStr, 0, sizeof szStr);
195: snprintf(szStr, sizeof szStr, "%u", cli.tout);
196: rpack_rdata(pkt, TFTP_OPT_TIMEOUT, strlen(TFTP_OPT_TIMEOUT) + 1);
197: rpack_rdata(pkt, szStr, strlen(szStr) + 1);
198: }
199:
200: schedEvent(TASK_ROOT(task), txPkt, NULL, TASK_FD(task),
201: TASK_DATA(task), RPACK_OFF(pkt));
202: taskExit(task, NULL);
203: }
204:
1.1 misho 205: static void *
206: RQ(sched_task_t *task)
207: {
208: rpack_t *pkt = TASK_DATA(task);
209: struct tftp_hdr *tftp = (struct tftp_hdr*) RPACK_BUF(pkt);
210: int len, rlen = TASK_DATLEN(task) - 2;
211: char *str;
212: u_short code = 0;
213:
214: ETRACE();
215:
216: cli.siz = TFTP_LOAD_MAX;
217: len = str_getString(tftp->tftp_data, rlen, &str);
218: if (len == -1)
219: goto end;
220: else {
221: rlen -= len;
222: strlcpy(cli.file, (char*) tftp->tftp_data, sizeof cli.file);
223: }
224: len = str_getString((const u_char*) str, rlen, NULL);
225: if (len == -1)
226: goto end;
227: else {
228: rlen -= len;
229: if (!strcasecmp(str, TFTP_MODE_ASCII))
230: strlcpy(cli.mode, TFTP_MODE_ASCII, sizeof cli.mode);
231: else if (!strcasecmp(str, TFTP_MODE_OCTET))
232: strlcpy(cli.mode, TFTP_MODE_OCTET, sizeof cli.mode);
233: else if (!strcasecmp(str, TFTP_MODE_MAIL)) {
234: strlcpy(cli.mode, TFTP_MODE_MAIL, sizeof cli.mode);
235: code = htole16(4);
236: goto end;
237: } else {
238: code = htole16(1);
239: goto end;
240: }
241: }
242:
243: cli.opc = ntohs(tftp->tftp_opc);
244: switch (cli.opc) {
245: case TFTP_OPC_RRQ:
246: code = O_RDONLY;
247: EVERBOSE(2, "RRQ:: file=%s mode=%s\n", cli.file, cli.mode);
248: break;
249: case TFTP_OPC_WRQ:
250: code = O_WRONLY | O_CREAT;
251: str = (char*) cfg_getAttribute(&cfg, "tftpd", "override");
252: if (!str || tolower(*str) != 'y')
253: code |= O_EXCL;
254: EVERBOSE(2, "WRQ:: file=%s mode=%s\n", cli.file, cli.mode);
255: break;
256: }
1.2.2.1 misho 257:
258: if (!RPACK_ISEND(pkt) && !getOpts(pkt))
259: schedEvent(TASK_ROOT(task), txOack, NULL, TASK_FD(task),
260: TASK_DATA(task), 0);
261:
1.1 misho 262: cli.fd = open(cli.file, code, 0644);
263: if (cli.fd == -1) {
264: if (errno == EACCES)
265: code = htole16(2);
266: else if (errno == ENFILE)
267: code = htole16(3);
268: else if (errno == EEXIST)
269: code = htole16(6);
270: else
271: code = htole16(0);
272: ESYSERR(0);
273: goto end;
274: } else
275: cli.seq = 0;
276:
1.2 misho 277: schedEvent(TASK_ROOT(task), execProg, "request", 0, NULL, cli.opc);
278:
1.1 misho 279: if (cli.opc == TFTP_OPC_WRQ) {
280: /* ack */
281: tftp->tftp_opc = htons(TFTP_OPC_ACK);
282: RPACK_REWIND(pkt);
283: rpack_uint16(pkt, NULL, 0);
284: rpack_uint16(pkt, &cli.seq, 0);
285:
286: schedEvent(TASK_ROOT(task), txPkt, NULL, TASK_FD(task),
287: TASK_DATA(task), RPACK_OFF(pkt));
288: } else
289: schedEvent(TASK_ROOT(task), txData, NULL, TASK_FD(task),
290: TASK_DATA(task), 0);
291:
292: cli.seq = 1; /* 1st ack */
293: taskExit(task, NULL);
294: end:
295: tftp->tftp_opc = htons(TFTP_OPC_ERROR);
296: RPACK_REWIND(pkt);
297: rpack_uint16(pkt, NULL, 0);
298: rpack_uint16(pkt, &code, 0);
299: rpack_rdata(pkt, errs[le16toh(code)].err_msg, strlen(errs[le16toh(code)].err_msg) + 1);
300:
301: schedEvent(TASK_ROOT(task), txPkt, (void*) -1, TASK_FD(task),
302: TASK_DATA(task), RPACK_OFF(pkt));
303: taskExit(task, NULL);
304: }
305:
306: static void *
307: ACK(sched_task_t *task)
308: {
309: rpack_t *pkt = TASK_DATA(task);
310: struct tftp_hdr *tftp = (struct tftp_hdr*) RPACK_BUF(pkt);
311: u_short code;
312:
313: ETRACE();
314:
315: RPACK_REWIND(pkt);
316: code = rpack_uint16(pkt, NULL, 0);
317: if (ntohs(code) != TFTP_OPC_ACK) {
318: code = htole16(5);
319: goto end;
320: }
321:
322: code = rpack_uint16(pkt, NULL, 0);
323: if (ntohs(code) > cli.seq || (ntohs(code) < (cli.seq - 1))) {
324: code = htole16(5);
325: goto end;
326: } else if (ntohs(code) == cli.seq)
327: cli.seq++;
328:
329: EVERBOSE(3, "ACK:: seq=%hu; my new seq=%hu;", ntohs(code), cli.seq);
330:
331: if (!cli.close)
332: schedEvent(TASK_ROOT(task), txData, NULL, TASK_FD(task),
333: TASK_DATA(task), 0);
334: else {
1.2 misho 335: schedEvent(TASK_ROOT(task), execProg, "complete", 0, NULL, TFTP_OPC_RRQ);
1.1 misho 336: schedCancelby(TASK_ROOT(task), taskTIMER, CRITERIA_CALL, timeoutSession, NULL);
337: schedEvent(TASK_ROOT(task), timeoutSession, NULL, 0, NULL, 0);
338: EVERBOSE(2, "Finish RRQ request");
339: }
340: taskExit(task, NULL);
341: end:
342: tftp->tftp_opc = htons(TFTP_OPC_ERROR);
343: RPACK_REWIND(pkt);
344: rpack_uint16(pkt, NULL, 0);
345: rpack_uint16(pkt, &code, 0);
346: rpack_rdata(pkt, errs[le16toh(code)].err_msg, strlen(errs[le16toh(code)].err_msg) + 1);
347:
348: schedEvent(TASK_ROOT(task), txPkt, (void*) -1, TASK_FD(task),
349: TASK_DATA(task), RPACK_OFF(pkt));
350: taskExit(task, NULL);
351: }
352:
353: static void *
354: DATA(sched_task_t *task)
355: {
356: rpack_t *pkt = TASK_DATA(task);
357: struct tftp_hdr *tftp = (struct tftp_hdr*) RPACK_BUF(pkt);
358: u_short code;
359: int len;
360:
361: ETRACE();
362:
363: RPACK_REWIND(pkt);
364: code = rpack_uint16(pkt, NULL, 0);
365: if (ntohs(code) != TFTP_OPC_DATA) {
366: code = htole16(5);
367: goto end;
368: }
369:
370: code = rpack_uint16(pkt, NULL, 0);
371: if (ntohs(code) < cli.seq || ntohs(code) > cli.seq + 1) {
372: code = htole16(5);
373: goto end;
374: } else
375: cli.seq = ntohs(code);
376:
1.2.2.4 ! misho 377: /* max file size check */
1.1 misho 378: len = TASK_DATLEN(task) - RPACK_OFF(pkt);
1.2.2.4 ! misho 379: if (cli.tsiz && cli.tsiz <= cli.seq * cli.siz)
! 380: len = MIN(len, cli.tsiz - (cli.seq - 1) * cli.siz);
1.1 misho 381: if (len < cli.siz)
382: cli.close = 42; /* last received packet, should be close! */
383:
384: EVERBOSE(3, "DATA:: seq=%hu; len=%d", cli.seq, len);
385:
386: len = pwrite(cli.fd, RPACK_NEXT(pkt), len, (cli.seq - 1) * cli.siz);
387: if (len == -1) {
388: ESYSERR(0);
389: code = htole16(3);
390: goto end;
391: } else {
392: rpack_rnext(pkt, len);
393: EVERBOSE(3, "Written to file %s %d bytes", cli.file, len);
394: }
395:
396: schedEvent(TASK_ROOT(task), txAck, NULL, TASK_FD(task), TASK_DATA(task), 0);
397: taskExit(task, NULL);
398: end:
399: tftp->tftp_opc = htons(TFTP_OPC_ERROR);
400: RPACK_REWIND(pkt);
401: rpack_uint16(pkt, NULL, 0);
402: rpack_uint16(pkt, &code, 0);
403: rpack_rdata(pkt, errs[le16toh(code)].err_msg, strlen(errs[le16toh(code)].err_msg) + 1);
404:
405: schedEvent(TASK_ROOT(task), txPkt, (void*) -1, TASK_FD(task),
406: TASK_DATA(task), RPACK_OFF(pkt));
407: taskExit(task, NULL);
408: }
409:
410: void *
411: rxPkt(sched_task_t *task)
412: {
413: sockaddr_t sa;
414: socklen_t salen = sizeof sa;
415: int rlen;
416: rpack_t *pkt = TASK_DATA(task);
417: struct tftp_hdr *tftp = (struct tftp_hdr*) RPACK_BUF(pkt);
418: u_short code;
419:
420: ETRACE();
421:
422: memset(RPACK_BUF(pkt), 0, RPACK_LEN(pkt));
423: rlen = recvfrom(TASK_FD(task), RPACK_BUF(pkt), RPACK_LEN(pkt), 0, &sa.sa, &salen);
424: if (rlen == -1) {
425: ESYSERR(0);
426: goto end;
427: } else if (!cli.addr.sa.sa_len) {
428: cli.addr = sa;
429: switch (ntohs(tftp->tftp_opc)) {
430: case TFTP_OPC_RRQ:
431: case TFTP_OPC_WRQ:
432: schedEvent(TASK_ROOT(task), RQ, NULL, TASK_FD(task),
433: TASK_DATA(task), rlen);
434: break;
435: case TFTP_OPC_ERROR:
436: default:
437: RPACK_REWIND(pkt);
438: code = htons(TFTP_OPC_ERROR);
439: rpack_uint16(pkt, &code, 0);
440: code = htole16(4);
441: rpack_uint16(pkt, &code, 0);
442: rpack_rdata(pkt, errs[4].err_msg, strlen(errs[4].err_msg) + 1);
443:
444: schedEvent(TASK_ROOT(task), txPkt, (void*) -1, TASK_FD(task),
445: TASK_DATA(task), RPACK_OFF(pkt));
446: goto end;
447: }
448: } else if (memcmp(&cli.addr, &sa, salen)) {
449: EERROR(LOG_WARNING, "Packet dropped!!!\n"
450: "Get frame from different address for this session");
451: goto end;
452: } else
453: switch (ntohs(tftp->tftp_opc)) {
454: case TFTP_OPC_ACK:
455: schedEvent(TASK_ROOT(task), ACK, NULL, TASK_FD(task),
456: TASK_DATA(task), rlen);
457: break;
458: case TFTP_OPC_DATA:
459: schedEvent(TASK_ROOT(task), DATA, NULL, TASK_FD(task),
460: TASK_DATA(task), rlen);
461: break;
462: case TFTP_OPC_OACK:
463: ELOGGER(LOG_WARNING, "oack");
464: /*
465: schedEvent(TASK_ROOT(task), OACK, NULL, TASK_FD(task),
466: TASK_DATA(task), rlen);
467: */
468: break;
469: case TFTP_OPC_ERROR:
470: default:
471: RPACK_REWIND(pkt);
472: code = htons(TFTP_OPC_ERROR);
473: rpack_uint16(pkt, &code, 0);
474: code = htole16(4);
475: rpack_uint16(pkt, &code, 0);
476: rpack_rdata(pkt, errs[4].err_msg, strlen(errs[4].err_msg) + 1);
477:
478: schedEvent(TASK_ROOT(task), txPkt, (void*) -1, TASK_FD(task),
479: TASK_DATA(task), RPACK_OFF(pkt));
480: goto end;
481: }
482:
483: schedCancelby(TASK_ROOT(task), taskTIMER, CRITERIA_CALL, timeoutSession, NULL);
484: schedTimer(TASK_ROOT(task), timeoutSession, NULL, timeout, NULL, 0);
485: end:
486: schedReadSelf(task);
487: taskExit(task, NULL);
488: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>