ali@476: /* ali@476: * Copyright (C) 2016 J. Ali Harlow ali@476: * ali@476: * This program is free software; you can redistribute it and/or modify ali@476: * it under the terms of the GNU General Public License as published by ali@476: * the Free Software Foundation; either version 2 of the License, or ali@476: * (at your option) any later version. ali@476: * ali@476: * This program is distributed in the hope that it will be useful, ali@476: * but WITHOUT ANY WARRANTY; without even the implied warranty of ali@476: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ali@476: * GNU General Public License for more details. ali@476: * ali@476: * You should have received a copy of the GNU General Public License along ali@476: * with this program; if not, write to the Free Software Foundation, Inc., ali@476: * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ali@476: */ ali@476: ali@476: #include "config.h" ali@476: ali@476: #include ali@476: #include ali@476: #include ali@476: #include ali@476: #include ali@476: #include ali@476: #include ali@476: #include ali@476: #include ali@476: #include ali@476: ali@476: /* ali@476: * A simple TFTP server suitable only for use on the loopback interface ali@476: * (it has no support for retransmitting lost packets). ali@476: */ ali@476: ali@476: char inbuf[SEGSIZE+4]; ali@476: char outbuf[SEGSIZE+4]; ali@476: ali@476: int send_error(int s, const struct sockaddr *client, socklen_t client_addr_len, ali@476: unsigned short code,const char *message) ali@476: { ali@476: int r; ali@476: char *buf; ali@476: size_t len; ali@476: ali@476: len = 4 + strlen(message) + 1; ali@476: buf = malloc(len); ali@476: ((unsigned short *)buf)[0] = htons(ERROR); ali@476: ((unsigned short *)buf)[1] = htons(code); ali@476: strcpy(buf + 4, message); ali@476: r = sendto(s, buf, len, 0, client, client_addr_len); ali@476: free(buf); ali@476: ali@476: return r; ali@476: } ali@476: ali@476: int send_file(int s, const struct sockaddr *client, socklen_t client_addr_len, ali@476: const char *path, const char *mode) ali@476: { ali@476: const char *dotdot, *p; ali@476: FILE *fp; ali@476: size_t nb; ali@476: int block = 0; ali@476: ali@476: if (strcasecmp(mode, "octet")) { ali@476: send_error(s, client, client_addr_len, EBADOP, "Bad mode"); ali@476: return -1; ali@476: } ali@476: ali@476: if (!*path || *path == '/') { ali@476: send_error(s, client, client_addr_len, EACCESS, ali@476: "Access denied"); ali@476: return -1; ali@476: } ali@476: ali@476: for (p = path; *p;) { ali@476: dotdot = strstr(p, ".."); ali@476: if (!dotdot) ali@476: break; ali@476: if ((dotdot == path || dotdot[-1] == '/') && ali@476: (dotdot[2] == '/' || dotdot[2] == '\0')) { ali@476: send_error(s, client, client_addr_len, EACCESS, ali@476: "Access denied"); ali@476: return -1; ali@476: } ali@476: p = dotdot + 2; ali@476: } ali@476: ali@476: fp = fopen(path, "rb"); ali@476: ali@476: if (!fp) { ali@476: if (errno == ENOENT) ali@476: send_error(s, client, client_addr_len, ENOTFOUND, ali@476: strerror(errno)); ali@476: else if (errno == EACCES) ali@476: send_error(s, client, client_addr_len, EACCESS, ali@476: strerror(errno)); ali@476: else ali@476: send_error(s, client, client_addr_len, EUNDEF, ali@476: strerror(errno)); ali@476: return -1; ali@476: } ali@476: ali@476: ((unsigned short *)outbuf)[0] = htons(DATA); ali@476: ali@476: while((nb = fread(outbuf + 4, 1, SEGSIZE, fp)) >= 0) ali@476: { ali@476: ((unsigned short *)outbuf)[1] = htons(++block); ali@476: if (sendto(s, outbuf, nb + 4, 0, client, client_addr_len) < 0) { ali@476: perror("sendto"); ali@476: fclose(fp); ali@476: return -1; ali@476: } ali@476: ali@476: /* Discard ACKs */ ali@476: (void)recvfrom(s, inbuf, sizeof(inbuf), 0, NULL, NULL); ali@476: ali@476: if (nb < SEGSIZE) ali@476: break; ali@476: } ali@476: ali@476: fclose(fp); ali@476: ali@476: return 0; ali@476: } ali@476: ali@476: void serve(int s) ali@476: { ali@476: char *filename, *mode; ali@476: struct sockaddr_storage client; ali@476: socklen_t client_addr_len; ali@476: ssize_t nb; ali@476: ali@476: for(;;) { ali@476: client_addr_len = sizeof(client); ali@476: nb = recvfrom(s, inbuf, sizeof(inbuf), 0, ali@476: (struct sockaddr *)&client, &client_addr_len); ali@476: ali@476: if (nb < 0) { ali@476: perror("recvfrom"); ali@476: exit(1); ali@476: } ali@476: ali@476: if (nb >= 2) { ali@476: switch (ntohs(*(unsigned short *)inbuf)) { ali@476: case RRQ: ali@476: filename = inbuf + 2; ali@476: mode = memchr(filename, '\0', nb - 2); ali@476: if (!mode) { ali@476: send_error(s, ali@476: (struct sockaddr *)&client, ali@476: client_addr_len, EBADOP, ali@476: "Bad request"); ali@476: break; ali@476: } ali@476: mode++; ali@476: if (!memchr(mode, '\0', nb - (mode - inbuf))) { ali@476: send_error(s, ali@476: (struct sockaddr *)&client, ali@476: client_addr_len, EBADOP, ali@476: "Bad request"); ali@476: break; ali@476: } ali@476: send_file(s, (struct sockaddr *)&client, ali@476: client_addr_len, filename, mode); ali@476: break; ali@476: case WRQ: ali@476: send_error(s, (struct sockaddr *)&client, ali@476: client_addr_len, EACCESS, ali@476: "Access denied"); ali@476: break; ali@476: case DATA: ali@476: case ACK: ali@476: case ERROR: ali@476: break; ali@476: } ali@476: } ali@476: } ali@476: } ali@476: ali@476: main(int argc, char **argv) ali@476: { ali@476: int s; ali@476: pid_t pid; ali@476: FILE *fp; ali@476: in_port_t port; ali@476: struct sockaddr_in sin = {0,}; ali@476: ali@476: s = socket(AF_INET, SOCK_DGRAM, 0); ali@476: if (s < 0) { ali@476: perror("socket"); ali@476: exit(1); ali@476: } ali@476: ali@476: sin.sin_family = AF_INET; ali@476: sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); ali@476: ali@476: for (port = 0;; port++) { ali@476: sin.sin_port = htons(IPPORT_USERRESERVED + port); ali@476: if (!bind(s, (struct sockaddr *)&sin, sizeof(sin))) ali@476: break; ali@476: if (errno != EADDRINUSE || port >= 1023) { ali@476: perror("bind"); ali@476: exit(1); ali@476: } ali@476: } ali@476: ali@476: if (argc > 2) { ali@476: fp = fopen(argv[2], "w"); ali@476: if (fp) { ali@476: fprintf(fp, "%ld\n", (long)ntohs(sin.sin_port)); ali@476: fclose(fp); ali@476: } ali@476: ali@476: pid = fork(); ali@476: if (pid) { ali@476: fp = fopen(argv[1], "w"); ali@476: if (fp) { ali@476: fprintf(fp, "%ld\n", (long)pid); ali@476: fclose(fp); ali@476: } ali@476: exit(0); ali@476: } ali@476: } else ali@476: printf("%d\n", ntohs(sin.sin_port)); ali@476: ali@476: serve(s); ali@476: ali@476: exit(0); ali@476: }