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