home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.urbanrage.com
/
2015-02-07.ftp.urbanrage.com.tar
/
ftp.urbanrage.com
/
pub
/
tftp.c
< prev
next >
Wrap
C/C++ Source or Header
|
2007-02-15
|
11KB
|
345 lines
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <poll.h>
#include <time.h>
#include "tftp.h"
#define MAX_ACTIVE 100
int setup_udp_listener(void) {
int sfd;
int option_on = 1;
int error;
int slen;
struct sockaddr_in saddr;
sfd = socket(PF_INET, SOCK_DGRAM, 0);
if (sfd < 0) {
perror("Couldn't open a socket");
exit(0);
}
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&option_on,
sizeof(option_on));
/* set up sock_addr structure */
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
saddr.sin_port = htons(6943);
error = bind(sfd, (struct sockaddr*)&saddr, sizeof(saddr));
if (error < 0) {
perror("couldn't bind socket to address");
close(sfd);
exit(0);
}
return sfd;
}
int find_free(active_t* active) {
int i;
for (i=0; i < MAX_ACTIVE; i++) {
if (active[i].fd == 0) {
return i;
}
}
return -1;
}
int find_active(active_t* active, struct sockaddr_in from) {
int i;
for (i=0; i < MAX_ACTIVE; i++) {
if ((active[i].ip == from.sin_addr.s_addr) &&
(active[i].port == from.sin_port)) {
return i;
}
}
return -1;
}
int write_block(int sfd, active_t* aptr, tftp_t* tptr, int size,
struct sockaddr_in from) {
int n;
off_t fpos;
socklen_t flen;
size -= 4;
if (size < 1) {
return -1;
}
memset(&aptr->last.ack, 0, sizeof(tf_ack_t));
fpos = lseek(aptr->fd, tptr->data.block*512, SEEK_SET);
if (fpos == -1) {
return 1;
}
n = write(aptr->fd, tptr->data.data, size);
if (n <= 0) {
return -1;
}
aptr->last.ack.op = ACK;
aptr->last.ack.block = tptr->data.block;
flen = sizeof(from);
sendto(sfd, &aptr->last.ack, sizeof(tf_ack_t), 0, (struct sockaddr*)&from, flen);
aptr->drop = time(0)+5;
aptr->retransmit = aptr->drop - 3;
aptr->last.size = sizeof(tf_ack_t);
return 0;
}
int send_block(int sfd, active_t* aptr, struct sockaddr_in from) {
int n;
off_t fpos;
socklen_t flen;
memset(&aptr->last.data, 0, sizeof(tf_data_t));
fpos = lseek(aptr->fd, aptr->block*512, SEEK_SET);
if (fpos == -1) {
return 1;
}
aptr->last.data.op = DATA;
aptr->last.data.block = (unsigned short)(aptr->block & 0xffff);
n = read(aptr->fd, aptr->last.data.data, 512);
if (n <= 0) {
return 1;
}
flen = sizeof(from);
sendto(sfd, &aptr->last.data, n+4, 0, (struct sockaddr*)&from, flen);
aptr->drop = time(0)+5;
aptr->retransmit = aptr->drop - 3;
aptr->last.size = n+4;
return 0;
}
void send_error(int sfd, unsigned short error, char* errmsg, struct sockaddr_in from) {
tf_err_t err;
socklen_t flen;
memset(&err, 0, sizeof(err));
err.op = ERR;
err.error = error;
if (errmsg != NULL) {
snprintf(err.errmsg, sizeof(err.errmsg), "%s", errmsg);
}
flen = sizeof(from);
sendto(sfd, &err, 4+strlen(err.errmsg), 0, (struct sockaddr*)&from, flen);
}
char* inet_ntoa_r(in_addr_t ip, char* buffer) {
char* ptr = (char*)&ip;
unsigned char a = ptr[0];
unsigned char b = ptr[1];
unsigned char c = ptr[2];
unsigned char d = ptr[3];
if (buffer != NULL) {
memset(buffer, 0, 16);
snprintf(buffer, 16, "%d.%d.%d.%d", a, b, c, d);
}
return buffer;
}
void client_request(int sfd, active_t* active, int* active_count) {
int tfd;
int err;
int n;
int flags;
ssize_t size;
char buffer[8192];
char filename[4096];
char ip[16];
struct sockaddr_in from;
socklen_t flen;
tftp_t* tptr;
tf_ack_t ack;
memset(&from, 0, sizeof(from));
flen = sizeof(from);
size = recvfrom(sfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&from, &flen);
if (size > 0) {
buffer[size] = 0;
tptr = (tftp_t*)&buffer[0];
switch (tptr->op) {
case WRQ : flags = O_WRONLY | O_TRUNC; /* no CREAT so mode ignored */
case RRQ : {
if (*active_count < MAX_ACTIVE) { /* a new request can be serviced */
n = find_free(active);
if (n >= 0) { /* open slot */
snprintf(filename, sizeof(filename), "/tmp/%s",
tptr->req.buffer);
if (tptr->op == RRQ) {
flags = O_RDONLY;
}
tfd = open(filename, flags);
if (tfd >= 0) {
active[n].ip = from.sin_addr.s_addr;
active[n].port = from.sin_port;
active[n].fd = tfd;
active[n].block = 0;
active[n].retransmit = 0;
active[n].drop = time(0) + 5; /* give the client some initial time */
/* here we'd return the OACK instead of the first block or ack
if we handle tftp option extensions */
if (tptr->op == RRQ) {
err = send_block(sfd, &active[n], from);
if (err != 0) {
close(tfd);
memset(&active[n], 0, sizeof(active_t));
} else {
fprintf(stderr, "started transfer of %s to [%s:%d]\n",
filename, inet_ntoa_r(from.sin_addr.s_addr, ip), from.sin_port);
*active_count++;
}
} else {
fprintf(stderr, "started transfer of %s from [%s:%d]\n",
filename, inet_ntoa_r(from.sin_addr.s_addr, ip), from.sin_port);
*active_count++;
memset(&active[n].last.ack, 0, sizeof(ack));
active[n].last.ack.op = ACK;
active[n].last.ack.block = 0;
flen = sizeof(from);
sendto(sfd, &active[n].last.ack, sizeof(tf_ack_t), 0,
(struct sockaddr*)&from, flen);
active[n].retransmit = time(0) + 2;
active[n].last.size = sizeof(tf_ack_t);
}
} else {
err = errno; /* preserve err */
snprintf(buffer, sizeof(buffer), "Couldn't open file %s",
filename);
perror(buffer);
if (err == ENOENT) {
send_error(sfd, 1, "file not found", from);
} else {
send_error(sfd, 2, "permission denied", from); break;
}
}
}
} else {
fprintf(stderr, "handling as many clients as I can\n");
send_error(sfd, 3, "too many clients", from);
/* send error #3 */
}
} break;
case DATA : {
n = find_active(active, from);
if (n >= 0) {
err = write_block(sfd, active, tptr, size, from);
if (err < 0) {
send_error(sfd, 0, "write error", from);
}
if (err < 512) { /* small packet or error, write finished */
*active_count--;
close(active[n].fd);
memset(&active[n], 0, sizeof(active_t));
fprintf(stderr, "finished transfer from client [%s:%d]\n",
inet_ntoa_r(from.sin_addr.s_addr, ip), from.sin_port);
}
} else { /* got a retransmit on something we already closed */
send_error(sfd, 5, "unknown transfer id", from);
}
}; break;
case ACK : {
n = find_active(active, from);
if (n >= 0) {
active[n].block++;
if (active[n].block == 0) {
fprintf(stderr, "block wrap around, do what?\n");
/* send error */
} else {
err = send_block(sfd, &active[n], from);
if (err != 0) { /* done with client */
*active_count--;
close(active[n].fd);
memset(&active[n], 0, sizeof(active_t));
fprintf(stderr, "finished transfer to client [%s:%d]\n",
inet_ntoa_r(from.sin_addr.s_addr, ip), from.sin_port);
}
}
} else {
fprintf(stderr, "Got an ack but no active connection [%s:%d]\n",
inet_ntoa_r(from.sin_addr.s_addr, ip), from.sin_port);
send_error(sfd, 5, "unknown transfer id", from);
}
}; break;
default : {
fprintf(stderr, "received error or unknown packet from [%s:%d]\n",
inet_ntoa_r(from.sin_addr.s_addr, ip), from.sin_port);
n = find_active(active, from);
if (n >= 0) {
*active_count--;
close(active[n].fd);
memset(&active[n], 0, sizeof (active_t));
}
}; break;
};
}
}
int main (int argc, char* argv[]) {
int sfd;
int n;
int active_count = 0;
active_t active[MAX_ACTIVE];
struct pollfd fds[1];
struct sockaddr_in from;
time_t tdrop;
char ip[16];
sfd = setup_udp_listener();
memset(active, 0, sizeof(active));
memset(fds, 0, sizeof(fds));
fds[0].fd = sfd;
fds[0].events = POLLIN;
while(1) {
n = poll(fds, 1, 1);
if ((n > 0) && ((fds[0].revents & POLLIN) == POLLIN)) {
client_request(sfd, active, &active_count);
}
tdrop = time(0);
for (n=0; n < MAX_ACTIVE; n++) {
if ((active[n].fd != 0) && (active[n].drop < tdrop)) {
memset(&from, 0, sizeof(from));
from.sin_family = AF_INET;
from.sin_addr.s_addr = active[n].ip;
from.sin_port = active[n].port;
send_error(sfd, 0, "transaction timed out", from);
fprintf(stderr, "transaction with client [%s:%d] timed out\n",
inet_ntoa_r(active[n].ip, ip), active[n].port);
close(active[n].fd);
memset(&active[n], 0, sizeof (active_t));
}
if ((active[n].fd != 0) && (active[n].retransmit) &&
(active[n].retransmit < tdrop)) {
active[n].retransmit = 0;
memset(&from, 0, sizeof(from));
from.sin_family = AF_INET;
from.sin_addr.s_addr = active[n].ip;
from.sin_port = active[n].port;
fprintf(stderr, "retransmitting [%d] to client [%s:%d]\n",
active[n].last.op, inet_ntoa_r(active[n].ip, ip), active[n].port);
sendto(sfd, &active[n].last, active[n].last.size, 0,
(struct sockaddr*)&from, sizeof(from));
}
}
}
}