1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/test/tftpd.c Mon Jul 11 16:49:53 2016 +0100
1.3 @@ -0,0 +1,231 @@
1.4 +/*
1.5 + * Copyright (C) 2016 J. Ali Harlow <ali@juiblex.co.uk>
1.6 + *
1.7 + * This program is free software; you can redistribute it and/or modify
1.8 + * it under the terms of the GNU General Public License as published by
1.9 + * the Free Software Foundation; either version 2 of the License, or
1.10 + * (at your option) any later version.
1.11 + *
1.12 + * This program is distributed in the hope that it will be useful,
1.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.15 + * GNU General Public License for more details.
1.16 + *
1.17 + * You should have received a copy of the GNU General Public License along
1.18 + * with this program; if not, write to the Free Software Foundation, Inc.,
1.19 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1.20 + */
1.21 +
1.22 +#include "config.h"
1.23 +
1.24 +#include <stdlib.h>
1.25 +#include <string.h>
1.26 +#include <stdio.h>
1.27 +#include <errno.h>
1.28 +#include <unistd.h>
1.29 +#include <sys/types.h>
1.30 +#include <sys/socket.h>
1.31 +#include <netinet/in.h>
1.32 +#include <netinet/ip.h>
1.33 +#include <arpa/tftp.h>
1.34 +
1.35 +/*
1.36 + * A simple TFTP server suitable only for use on the loopback interface
1.37 + * (it has no support for retransmitting lost packets).
1.38 + */
1.39 +
1.40 +char inbuf[SEGSIZE+4];
1.41 +char outbuf[SEGSIZE+4];
1.42 +
1.43 +int send_error(int s, const struct sockaddr *client, socklen_t client_addr_len,
1.44 + unsigned short code,const char *message)
1.45 +{
1.46 + int r;
1.47 + char *buf;
1.48 + size_t len;
1.49 +
1.50 + len = 4 + strlen(message) + 1;
1.51 + buf = malloc(len);
1.52 + ((unsigned short *)buf)[0] = htons(ERROR);
1.53 + ((unsigned short *)buf)[1] = htons(code);
1.54 + strcpy(buf + 4, message);
1.55 + r = sendto(s, buf, len, 0, client, client_addr_len);
1.56 + free(buf);
1.57 +
1.58 + return r;
1.59 +}
1.60 +
1.61 +int send_file(int s, const struct sockaddr *client, socklen_t client_addr_len,
1.62 + const char *path, const char *mode)
1.63 +{
1.64 + const char *dotdot, *p;
1.65 + FILE *fp;
1.66 + size_t nb;
1.67 + int block = 0;
1.68 +
1.69 + if (strcasecmp(mode, "octet")) {
1.70 + send_error(s, client, client_addr_len, EBADOP, "Bad mode");
1.71 + return -1;
1.72 + }
1.73 +
1.74 + if (!*path || *path == '/') {
1.75 + send_error(s, client, client_addr_len, EACCESS,
1.76 + "Access denied");
1.77 + return -1;
1.78 + }
1.79 +
1.80 + for (p = path; *p;) {
1.81 + dotdot = strstr(p, "..");
1.82 + if (!dotdot)
1.83 + break;
1.84 + if ((dotdot == path || dotdot[-1] == '/') &&
1.85 + (dotdot[2] == '/' || dotdot[2] == '\0')) {
1.86 + send_error(s, client, client_addr_len, EACCESS,
1.87 + "Access denied");
1.88 + return -1;
1.89 + }
1.90 + p = dotdot + 2;
1.91 + }
1.92 +
1.93 + fp = fopen(path, "rb");
1.94 +
1.95 + if (!fp) {
1.96 + if (errno == ENOENT)
1.97 + send_error(s, client, client_addr_len, ENOTFOUND,
1.98 + strerror(errno));
1.99 + else if (errno == EACCES)
1.100 + send_error(s, client, client_addr_len, EACCESS,
1.101 + strerror(errno));
1.102 + else
1.103 + send_error(s, client, client_addr_len, EUNDEF,
1.104 + strerror(errno));
1.105 + return -1;
1.106 + }
1.107 +
1.108 + ((unsigned short *)outbuf)[0] = htons(DATA);
1.109 +
1.110 + while((nb = fread(outbuf + 4, 1, SEGSIZE, fp)) >= 0)
1.111 + {
1.112 + ((unsigned short *)outbuf)[1] = htons(++block);
1.113 + if (sendto(s, outbuf, nb + 4, 0, client, client_addr_len) < 0) {
1.114 + perror("sendto");
1.115 + fclose(fp);
1.116 + return -1;
1.117 + }
1.118 +
1.119 + /* Discard ACKs */
1.120 + (void)recvfrom(s, inbuf, sizeof(inbuf), 0, NULL, NULL);
1.121 +
1.122 + if (nb < SEGSIZE)
1.123 + break;
1.124 + }
1.125 +
1.126 + fclose(fp);
1.127 +
1.128 + return 0;
1.129 +}
1.130 +
1.131 +void serve(int s)
1.132 +{
1.133 + char *filename, *mode;
1.134 + struct sockaddr_storage client;
1.135 + socklen_t client_addr_len;
1.136 + ssize_t nb;
1.137 +
1.138 + for(;;) {
1.139 + client_addr_len = sizeof(client);
1.140 + nb = recvfrom(s, inbuf, sizeof(inbuf), 0,
1.141 + (struct sockaddr *)&client, &client_addr_len);
1.142 +
1.143 + if (nb < 0) {
1.144 + perror("recvfrom");
1.145 + exit(1);
1.146 + }
1.147 +
1.148 + if (nb >= 2) {
1.149 + switch (ntohs(*(unsigned short *)inbuf)) {
1.150 + case RRQ:
1.151 + filename = inbuf + 2;
1.152 + mode = memchr(filename, '\0', nb - 2);
1.153 + if (!mode) {
1.154 + send_error(s,
1.155 + (struct sockaddr *)&client,
1.156 + client_addr_len, EBADOP,
1.157 + "Bad request");
1.158 + break;
1.159 + }
1.160 + mode++;
1.161 + if (!memchr(mode, '\0', nb - (mode - inbuf))) {
1.162 + send_error(s,
1.163 + (struct sockaddr *)&client,
1.164 + client_addr_len, EBADOP,
1.165 + "Bad request");
1.166 + break;
1.167 + }
1.168 + send_file(s, (struct sockaddr *)&client,
1.169 + client_addr_len, filename, mode);
1.170 + break;
1.171 + case WRQ:
1.172 + send_error(s, (struct sockaddr *)&client,
1.173 + client_addr_len, EACCESS,
1.174 + "Access denied");
1.175 + break;
1.176 + case DATA:
1.177 + case ACK:
1.178 + case ERROR:
1.179 + break;
1.180 + }
1.181 + }
1.182 + }
1.183 +}
1.184 +
1.185 +main(int argc, char **argv)
1.186 +{
1.187 + int s;
1.188 + pid_t pid;
1.189 + FILE *fp;
1.190 + in_port_t port;
1.191 + struct sockaddr_in sin = {0,};
1.192 +
1.193 + s = socket(AF_INET, SOCK_DGRAM, 0);
1.194 + if (s < 0) {
1.195 + perror("socket");
1.196 + exit(1);
1.197 + }
1.198 +
1.199 + sin.sin_family = AF_INET;
1.200 + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1.201 +
1.202 + for (port = 0;; port++) {
1.203 + sin.sin_port = htons(IPPORT_USERRESERVED + port);
1.204 + if (!bind(s, (struct sockaddr *)&sin, sizeof(sin)))
1.205 + break;
1.206 + if (errno != EADDRINUSE || port >= 1023) {
1.207 + perror("bind");
1.208 + exit(1);
1.209 + }
1.210 + }
1.211 +
1.212 + if (argc > 2) {
1.213 + fp = fopen(argv[2], "w");
1.214 + if (fp) {
1.215 + fprintf(fp, "%ld\n", (long)ntohs(sin.sin_port));
1.216 + fclose(fp);
1.217 + }
1.218 +
1.219 + pid = fork();
1.220 + if (pid) {
1.221 + fp = fopen(argv[1], "w");
1.222 + if (fp) {
1.223 + fprintf(fp, "%ld\n", (long)pid);
1.224 + fclose(fp);
1.225 + }
1.226 + exit(0);
1.227 + }
1.228 + } else
1.229 + printf("%d\n", ntohs(sin.sin_port));
1.230 +
1.231 + serve(s);
1.232 +
1.233 + exit(0);
1.234 +}