test/tftpd.c
author J. Ali Harlow <ali@juiblex.co.uk>
Fri Jun 08 18:02:33 2018 +0100 (2018-06-08)
changeset 500 f98d77376544
permissions -rw-r--r--
Release 0.7
ali@476
     1
/*
ali@476
     2
 * Copyright (C) 2016  J. Ali Harlow <ali@juiblex.co.uk>
ali@476
     3
 *
ali@476
     4
 * This program is free software; you can redistribute it and/or modify
ali@476
     5
 * it under the terms of the GNU General Public License as published by
ali@476
     6
 * the Free Software Foundation; either version 2 of the License, or
ali@476
     7
 * (at your option) any later version.
ali@476
     8
 *
ali@476
     9
 * This program is distributed in the hope that it will be useful,
ali@476
    10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
ali@476
    11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
ali@476
    12
 * GNU General Public License for more details.
ali@476
    13
 *
ali@476
    14
 * You should have received a copy of the GNU General Public License along
ali@476
    15
 * with this program; if not, write to the Free Software Foundation, Inc.,
ali@476
    16
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
ali@476
    17
 */
ali@476
    18
ali@476
    19
#include "config.h"
ali@476
    20
ali@476
    21
#include <stdlib.h>
ali@476
    22
#include <string.h>
ali@476
    23
#include <stdio.h>
ali@476
    24
#include <errno.h>
ali@476
    25
#include <unistd.h>
ali@476
    26
#include <sys/types.h>
ali@476
    27
#include <sys/socket.h>
ali@476
    28
#include <netinet/in.h>
ali@476
    29
#include <netinet/ip.h>
ali@476
    30
#include <arpa/tftp.h>
ali@476
    31
ali@476
    32
/*
ali@476
    33
 * A simple TFTP server suitable only for use on the loopback interface
ali@476
    34
 * (it has no support for retransmitting lost packets).
ali@476
    35
 */
ali@476
    36
ali@476
    37
char inbuf[SEGSIZE+4];
ali@476
    38
char outbuf[SEGSIZE+4];
ali@476
    39
ali@476
    40
int send_error(int s, const struct sockaddr *client, socklen_t client_addr_len,
ali@476
    41
	       unsigned short code,const char *message)
ali@476
    42
{
ali@476
    43
	int r;
ali@476
    44
	char *buf;
ali@476
    45
	size_t len;
ali@476
    46
ali@476
    47
	len = 4 + strlen(message) + 1;
ali@476
    48
	buf = malloc(len);
ali@476
    49
	((unsigned short *)buf)[0] = htons(ERROR);
ali@476
    50
	((unsigned short *)buf)[1] = htons(code);
ali@476
    51
	strcpy(buf + 4, message);
ali@476
    52
	r = sendto(s, buf, len, 0, client, client_addr_len);
ali@476
    53
	free(buf);
ali@476
    54
ali@476
    55
	return r;
ali@476
    56
}
ali@476
    57
ali@476
    58
int send_file(int s, const struct sockaddr *client, socklen_t client_addr_len,
ali@476
    59
	       const char *path, const char *mode)
ali@476
    60
{
ali@476
    61
	const char *dotdot, *p;
ali@476
    62
	FILE *fp;
ali@476
    63
	size_t nb;
ali@476
    64
	int block = 0;
ali@476
    65
ali@476
    66
	if (strcasecmp(mode, "octet")) {
ali@476
    67
		send_error(s, client, client_addr_len, EBADOP, "Bad mode");
ali@476
    68
		return -1;
ali@476
    69
	}
ali@476
    70
ali@476
    71
	if (!*path || *path == '/') {
ali@476
    72
		send_error(s, client, client_addr_len, EACCESS,
ali@476
    73
			   "Access denied");
ali@476
    74
		return -1;
ali@476
    75
	}
ali@476
    76
ali@476
    77
	for (p = path; *p;) {
ali@476
    78
		dotdot = strstr(p, "..");
ali@476
    79
		if (!dotdot)
ali@476
    80
			break;
ali@476
    81
		if ((dotdot == path || dotdot[-1] == '/') &&
ali@476
    82
		    (dotdot[2] == '/' || dotdot[2] == '\0')) {
ali@476
    83
			send_error(s, client, client_addr_len, EACCESS,
ali@476
    84
				   "Access denied");
ali@476
    85
			return -1;
ali@476
    86
		}
ali@476
    87
		p = dotdot + 2;
ali@476
    88
	}
ali@476
    89
ali@476
    90
	fp = fopen(path, "rb");
ali@476
    91
ali@476
    92
	if (!fp) {
ali@476
    93
		if (errno == ENOENT)
ali@476
    94
			send_error(s, client, client_addr_len, ENOTFOUND,
ali@476
    95
				   strerror(errno));
ali@476
    96
		else if (errno == EACCES)
ali@476
    97
			send_error(s, client, client_addr_len, EACCESS,
ali@476
    98
				   strerror(errno));
ali@476
    99
		else
ali@476
   100
			send_error(s, client, client_addr_len, EUNDEF,
ali@476
   101
				   strerror(errno));
ali@476
   102
		return -1;
ali@476
   103
	}
ali@476
   104
ali@476
   105
	((unsigned short *)outbuf)[0] = htons(DATA);
ali@476
   106
ali@476
   107
	while((nb = fread(outbuf + 4, 1, SEGSIZE, fp)) >= 0)
ali@476
   108
	{
ali@476
   109
		((unsigned short *)outbuf)[1] = htons(++block);
ali@476
   110
		if (sendto(s, outbuf, nb + 4, 0, client, client_addr_len) < 0) {
ali@476
   111
			perror("sendto");
ali@476
   112
			fclose(fp);
ali@476
   113
			return -1;
ali@476
   114
		}
ali@476
   115
ali@476
   116
		/* Discard ACKs */
ali@476
   117
		(void)recvfrom(s, inbuf, sizeof(inbuf), 0, NULL, NULL);
ali@476
   118
		
ali@476
   119
		if (nb < SEGSIZE)
ali@476
   120
			break;
ali@476
   121
	}
ali@476
   122
ali@476
   123
	fclose(fp);
ali@476
   124
ali@476
   125
	return 0;
ali@476
   126
}
ali@476
   127
ali@476
   128
void serve(int s)
ali@476
   129
{
ali@476
   130
	char *filename, *mode;
ali@476
   131
	struct sockaddr_storage client;
ali@476
   132
	socklen_t client_addr_len;
ali@476
   133
	ssize_t nb;
ali@476
   134
ali@476
   135
	for(;;) {
ali@476
   136
		client_addr_len = sizeof(client);
ali@476
   137
		nb = recvfrom(s, inbuf, sizeof(inbuf), 0,
ali@476
   138
			      (struct sockaddr *)&client, &client_addr_len);
ali@476
   139
ali@476
   140
		if (nb < 0) {
ali@476
   141
			perror("recvfrom");
ali@476
   142
			exit(1);
ali@476
   143
		}
ali@476
   144
ali@476
   145
		if (nb >= 2) {
ali@476
   146
			switch (ntohs(*(unsigned short *)inbuf)) {
ali@476
   147
			case RRQ:
ali@476
   148
				filename = inbuf + 2;
ali@476
   149
				mode = memchr(filename, '\0', nb - 2);
ali@476
   150
				if (!mode) {
ali@476
   151
					send_error(s,
ali@476
   152
						   (struct sockaddr *)&client,
ali@476
   153
						   client_addr_len, EBADOP,
ali@476
   154
						   "Bad request");
ali@476
   155
					break;
ali@476
   156
				}
ali@476
   157
				mode++;
ali@476
   158
				if (!memchr(mode, '\0', nb - (mode - inbuf))) {
ali@476
   159
					send_error(s,
ali@476
   160
						   (struct sockaddr *)&client,
ali@476
   161
						   client_addr_len, EBADOP,
ali@476
   162
						   "Bad request");
ali@476
   163
					break;
ali@476
   164
				}
ali@476
   165
				send_file(s, (struct sockaddr *)&client,
ali@476
   166
					  client_addr_len, filename, mode);
ali@476
   167
				break;
ali@476
   168
			case WRQ:
ali@476
   169
				send_error(s, (struct sockaddr *)&client,
ali@476
   170
					   client_addr_len, EACCESS,
ali@476
   171
					   "Access denied");
ali@476
   172
				break;
ali@476
   173
			case DATA:
ali@476
   174
			case ACK:
ali@476
   175
			case ERROR:
ali@476
   176
				break;
ali@476
   177
			}
ali@476
   178
		}
ali@476
   179
	}
ali@476
   180
}
ali@476
   181
ali@476
   182
main(int argc, char **argv)
ali@476
   183
{
ali@476
   184
	int s;
ali@476
   185
	pid_t pid;
ali@476
   186
	FILE *fp;
ali@476
   187
	in_port_t port;
ali@476
   188
	struct sockaddr_in sin = {0,};
ali@476
   189
ali@476
   190
	s = socket(AF_INET, SOCK_DGRAM, 0);
ali@476
   191
	if (s < 0) {
ali@476
   192
		perror("socket");
ali@476
   193
		exit(1);
ali@476
   194
	}
ali@476
   195
ali@476
   196
	sin.sin_family = AF_INET;
ali@476
   197
	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
ali@476
   198
ali@476
   199
	for (port = 0;; port++) {
ali@476
   200
		sin.sin_port = htons(IPPORT_USERRESERVED + port);
ali@476
   201
		if (!bind(s, (struct sockaddr *)&sin, sizeof(sin)))
ali@476
   202
			break;
ali@476
   203
		if (errno != EADDRINUSE || port >= 1023) {
ali@476
   204
			perror("bind");
ali@476
   205
			exit(1);
ali@476
   206
		}
ali@476
   207
	}
ali@476
   208
ali@476
   209
	if (argc > 2) {
ali@476
   210
		fp = fopen(argv[2], "w");
ali@476
   211
		if (fp) {
ali@476
   212
			fprintf(fp, "%ld\n", (long)ntohs(sin.sin_port));
ali@476
   213
			fclose(fp);
ali@476
   214
		}
ali@476
   215
ali@476
   216
		pid = fork();
ali@476
   217
		if (pid) {
ali@476
   218
			fp = fopen(argv[1], "w");
ali@476
   219
			if (fp) {
ali@476
   220
				fprintf(fp, "%ld\n", (long)pid);
ali@476
   221
				fclose(fp);
ali@476
   222
			}
ali@476
   223
			exit(0);
ali@476
   224
		}
ali@476
   225
	} else
ali@476
   226
		printf("%d\n", ntohs(sin.sin_port));
ali@476
   227
ali@476
   228
	serve(s);
ali@476
   229
ali@476
   230
	exit(0);
ali@476
   231
}