test/tftpd.c
changeset 501 850be6a6885c
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/test/tftpd.c	Fri Jun 08 18:02:49 2018 +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 +}