PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

sys/lib/libsa/tftp.c

http://www.minix3.org/
C | 460 lines | 333 code | 68 blank | 59 comment | 36 complexity | b6d4884d2d9802745dc79b3eb4e0aed1 MD5 | raw file
Possible License(s): MIT, WTFPL, AGPL-1.0, BSD-3-Clause, GPL-3.0, LGPL-2.0, JSON, 0BSD
  1. /* $NetBSD: tftp.c,v 1.34 2011/12/25 06:09:08 tsutsui Exp $ */
  2. /*
  3. * Copyright (c) 1996
  4. * Matthias Drochner. All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  16. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  17. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  18. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  19. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  20. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  21. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  22. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  24. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. *
  26. */
  27. /*
  28. * Simple TFTP implementation for libsa.
  29. * Assumes:
  30. * - socket descriptor (int) at open_file->f_devdata
  31. * - server host IP in global servip
  32. * Restrictions:
  33. * - read only
  34. * - lseek only with SEEK_SET or SEEK_CUR
  35. * - no big time differences between transfers (<tftp timeout)
  36. */
  37. /*
  38. * XXX Does not currently implement:
  39. * XXX
  40. * XXX LIBSA_NO_FS_CLOSE
  41. * XXX LIBSA_NO_FS_SEEK
  42. * XXX LIBSA_NO_FS_WRITE
  43. * XXX LIBSA_NO_FS_SYMLINK (does this even make sense?)
  44. * XXX LIBSA_FS_SINGLECOMPONENT (does this even make sense?)
  45. */
  46. #include <sys/types.h>
  47. #include <sys/stat.h>
  48. #include <netinet/in.h>
  49. #include <netinet/udp.h>
  50. #include <netinet/in_systm.h>
  51. #include <lib/libkern/libkern.h>
  52. #include "stand.h"
  53. #include "net.h"
  54. #include "tftp.h"
  55. extern struct in_addr servip;
  56. static int tftpport = 2000;
  57. #define RSPACE 520 /* max data packet, rounded up */
  58. struct tftp_handle {
  59. struct iodesc *iodesc;
  60. int currblock; /* contents of lastdata */
  61. int islastblock; /* flag */
  62. int validsize;
  63. int off;
  64. const char *path; /* saved for re-requests */
  65. struct {
  66. u_char header[UDP_TOTAL_HEADER_SIZE];
  67. struct tftphdr t;
  68. u_char space[RSPACE];
  69. } lastdata;
  70. };
  71. static const int tftperrors[8] = {
  72. 0, /* ??? */
  73. ENOENT,
  74. EPERM,
  75. ENOSPC,
  76. EINVAL, /* ??? */
  77. EINVAL, /* ??? */
  78. EEXIST,
  79. EINVAL, /* ??? */
  80. };
  81. static ssize_t recvtftp(struct iodesc *, void *, size_t, saseconds_t);
  82. static int tftp_makereq(struct tftp_handle *);
  83. static int tftp_getnextblock(struct tftp_handle *);
  84. #ifndef TFTP_NOTERMINATE
  85. static void tftp_terminate(struct tftp_handle *);
  86. #endif
  87. static ssize_t tftp_size_of_file(struct tftp_handle *tftpfile);
  88. static ssize_t
  89. recvtftp(struct iodesc *d, void *pkt, size_t len, saseconds_t tleft)
  90. {
  91. ssize_t n;
  92. struct tftphdr *t;
  93. errno = 0;
  94. n = readudp(d, pkt, len, tleft);
  95. if (n < 4)
  96. return -1;
  97. t = (struct tftphdr *)pkt;
  98. switch (ntohs(t->th_opcode)) {
  99. case DATA:
  100. if (htons(t->th_block) != d->xid) {
  101. /*
  102. * Expected block?
  103. */
  104. return -1;
  105. }
  106. if (d->xid == 1) {
  107. /*
  108. * First data packet from new port.
  109. */
  110. struct udphdr *uh;
  111. uh = (struct udphdr *)pkt - 1;
  112. d->destport = uh->uh_sport;
  113. } /* else check uh_sport has not changed??? */
  114. return (n - (t->th_data - (char *)t));
  115. case ERROR:
  116. if ((unsigned int)ntohs(t->th_code) >= 8) {
  117. printf("illegal tftp error %d\n", ntohs(t->th_code));
  118. errno = EIO;
  119. } else {
  120. #ifdef DEBUG
  121. printf("tftp-error %d\n", ntohs(t->th_code));
  122. #endif
  123. errno = tftperrors[ntohs(t->th_code)];
  124. }
  125. return -1;
  126. default:
  127. #ifdef DEBUG
  128. printf("tftp type %d not handled\n", ntohs(t->th_opcode));
  129. #endif
  130. return -1;
  131. }
  132. }
  133. /* send request, expect first block (or error) */
  134. static int
  135. tftp_makereq(struct tftp_handle *h)
  136. {
  137. struct {
  138. u_char header[UDP_TOTAL_HEADER_SIZE];
  139. struct tftphdr t;
  140. u_char space[FNAME_SIZE + 6];
  141. } wbuf;
  142. char *wtail;
  143. int l;
  144. ssize_t res;
  145. struct tftphdr *t;
  146. wbuf.t.th_opcode = htons((u_short)RRQ);
  147. wtail = wbuf.t.th_stuff;
  148. l = strlen(h->path);
  149. (void)memcpy(wtail, h->path, l + 1);
  150. wtail += l + 1;
  151. (void)memcpy(wtail, "octet", 6);
  152. wtail += 6;
  153. t = &h->lastdata.t;
  154. /* h->iodesc->myport = htons(--tftpport); */
  155. h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
  156. h->iodesc->destport = htons(IPPORT_TFTP);
  157. h->iodesc->xid = 1; /* expected block */
  158. res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *)&wbuf.t,
  159. recvtftp, t, sizeof(*t) + RSPACE);
  160. if (res == -1)
  161. return errno;
  162. h->currblock = 1;
  163. h->validsize = res;
  164. h->islastblock = 0;
  165. if (res < SEGSIZE)
  166. h->islastblock = 1; /* very short file */
  167. return 0;
  168. }
  169. /* ack block, expect next */
  170. static int
  171. tftp_getnextblock(struct tftp_handle *h)
  172. {
  173. struct {
  174. u_char header[UDP_TOTAL_HEADER_SIZE];
  175. struct tftphdr t;
  176. } wbuf;
  177. char *wtail;
  178. int res;
  179. struct tftphdr *t;
  180. wbuf.t.th_opcode = htons((u_short)ACK);
  181. wbuf.t.th_block = htons((u_short)h->currblock);
  182. wtail = (char *)&wbuf.t.th_data;
  183. t = &h->lastdata.t;
  184. h->iodesc->xid = h->currblock + 1; /* expected block */
  185. res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *)&wbuf.t,
  186. recvtftp, t, sizeof(*t) + RSPACE);
  187. if (res == -1) /* 0 is OK! */
  188. return errno;
  189. h->currblock++;
  190. h->validsize = res;
  191. if (res < SEGSIZE)
  192. h->islastblock = 1; /* EOF */
  193. return 0;
  194. }
  195. #ifndef TFTP_NOTERMINATE
  196. static void
  197. tftp_terminate(struct tftp_handle *h)
  198. {
  199. struct {
  200. u_char header[UDP_TOTAL_HEADER_SIZE];
  201. struct tftphdr t;
  202. } wbuf;
  203. char *wtail;
  204. wtail = (char *)&wbuf.t.th_data;
  205. if (h->islastblock) {
  206. wbuf.t.th_opcode = htons((u_short)ACK);
  207. wbuf.t.th_block = htons((u_short)h->currblock);
  208. } else {
  209. wbuf.t.th_opcode = htons((u_short)ERROR);
  210. wbuf.t.th_code = htons((u_short)ENOSPACE); /* ??? */
  211. *wtail++ = '\0'; /* empty error string */
  212. }
  213. (void)sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t);
  214. }
  215. #endif
  216. __compactcall int
  217. tftp_open(const char *path, struct open_file *f)
  218. {
  219. struct tftp_handle *tftpfile;
  220. struct iodesc *io;
  221. int res;
  222. tftpfile = (struct tftp_handle *)alloc(sizeof(*tftpfile));
  223. if (!tftpfile)
  224. return ENOMEM;
  225. tftpfile->iodesc = io = socktodesc(*(int *)(f->f_devdata));
  226. io->destip = servip;
  227. tftpfile->off = 0;
  228. tftpfile->path = path; /* XXXXXXX we hope it's static */
  229. res = tftp_makereq(tftpfile);
  230. if (res) {
  231. dealloc(tftpfile, sizeof(*tftpfile));
  232. return res;
  233. }
  234. f->f_fsdata = (void *)tftpfile;
  235. fsmod = "nfs";
  236. return 0;
  237. }
  238. __compactcall int
  239. tftp_read(struct open_file *f, void *addr, size_t size, size_t *resid)
  240. {
  241. struct tftp_handle *tftpfile;
  242. #if !defined(LIBSA_NO_TWIDDLE)
  243. static int tc = 0;
  244. #endif
  245. tftpfile = (struct tftp_handle *)f->f_fsdata;
  246. while (size > 0) {
  247. int needblock;
  248. size_t count;
  249. #if !defined(LIBSA_NO_TWIDDLE)
  250. if (!(tc++ % 16))
  251. twiddle();
  252. #endif
  253. needblock = tftpfile->off / SEGSIZE + 1;
  254. if (tftpfile->currblock > needblock) { /* seek backwards */
  255. #ifndef TFTP_NOTERMINATE
  256. tftp_terminate(tftpfile);
  257. #endif
  258. tftp_makereq(tftpfile); /* no error check, it worked
  259. * for open */
  260. }
  261. while (tftpfile->currblock < needblock) {
  262. int res;
  263. res = tftp_getnextblock(tftpfile);
  264. if (res) { /* no answer */
  265. #ifdef DEBUG
  266. printf("tftp: read error (block %d->%d)\n",
  267. tftpfile->currblock, needblock);
  268. #endif
  269. return res;
  270. }
  271. if (tftpfile->islastblock)
  272. break;
  273. }
  274. if (tftpfile->currblock == needblock) {
  275. size_t offinblock, inbuffer;
  276. offinblock = tftpfile->off % SEGSIZE;
  277. if (offinblock > tftpfile->validsize) {
  278. #ifdef DEBUG
  279. printf("tftp: invalid offset %d\n",
  280. tftpfile->off);
  281. #endif
  282. return EINVAL;
  283. }
  284. inbuffer = tftpfile->validsize - offinblock;
  285. count = (size < inbuffer ? size : inbuffer);
  286. (void)memcpy(addr,
  287. tftpfile->lastdata.t.th_data + offinblock,
  288. count);
  289. addr = (char *)addr + count;
  290. tftpfile->off += count;
  291. size -= count;
  292. if ((tftpfile->islastblock) && (count == inbuffer))
  293. break; /* EOF */
  294. } else {
  295. #ifdef DEBUG
  296. printf("tftp: block %d not found\n", needblock);
  297. #endif
  298. return EINVAL;
  299. }
  300. }
  301. if (resid)
  302. *resid = size;
  303. return 0;
  304. }
  305. __compactcall int
  306. tftp_close(struct open_file *f)
  307. {
  308. struct tftp_handle *tftpfile;
  309. tftpfile = (struct tftp_handle *)f->f_fsdata;
  310. #ifdef TFTP_NOTERMINATE
  311. /* let it time out ... */
  312. #else
  313. tftp_terminate(tftpfile);
  314. #endif
  315. dealloc(tftpfile, sizeof(*tftpfile));
  316. return 0;
  317. }
  318. __compactcall int
  319. tftp_write(struct open_file *f, void *start, size_t size, size_t *resid)
  320. {
  321. return EROFS;
  322. }
  323. static ssize_t
  324. tftp_size_of_file(struct tftp_handle *tftpfile)
  325. {
  326. ssize_t filesize;
  327. if (tftpfile->currblock > 1) { /* move to start of file */
  328. #ifndef TFTP_NOTERMINATE
  329. tftp_terminate(tftpfile);
  330. #endif
  331. tftp_makereq(tftpfile); /* no error check, it worked
  332. * for open */
  333. }
  334. /* start with the size of block 1 */
  335. filesize = tftpfile->validsize;
  336. /* and keep adding the sizes till we hit the last block */
  337. while (!tftpfile->islastblock) {
  338. int res;
  339. res = tftp_getnextblock(tftpfile);
  340. if (res) { /* no answer */
  341. #ifdef DEBUG
  342. printf("tftp: read error (block %d)\n",
  343. tftpfile->currblock);
  344. #endif
  345. return -1;
  346. }
  347. filesize += tftpfile->validsize;
  348. }
  349. #ifdef DEBUG
  350. printf("tftp_size_of_file: file is %zu bytes\n", filesize);
  351. #endif
  352. return filesize;
  353. }
  354. __compactcall int
  355. tftp_stat(struct open_file *f, struct stat *sb)
  356. {
  357. struct tftp_handle *tftpfile;
  358. tftpfile = (struct tftp_handle *)f->f_fsdata;
  359. sb->st_mode = 0444;
  360. sb->st_nlink = 1;
  361. sb->st_uid = 0;
  362. sb->st_gid = 0;
  363. sb->st_size = tftp_size_of_file(tftpfile);
  364. return 0;
  365. }
  366. #if defined(LIBSA_ENABLE_LS_OP)
  367. __compactcall void
  368. tftp_ls(struct open_file *f, const char *pattern,
  369. void (*funcp)(char* arg), char* path)
  370. {
  371. printf("Currently ls command is unsupported by tftp\n");
  372. return;
  373. }
  374. #endif
  375. __compactcall off_t
  376. tftp_seek(struct open_file *f, off_t offset, int where)
  377. {
  378. struct tftp_handle *tftpfile;
  379. tftpfile = (struct tftp_handle *)f->f_fsdata;
  380. switch (where) {
  381. case SEEK_SET:
  382. tftpfile->off = offset;
  383. break;
  384. case SEEK_CUR:
  385. tftpfile->off += offset;
  386. break;
  387. default:
  388. errno = EOFFSET;
  389. return -1;
  390. }
  391. return tftpfile->off;
  392. }