📄 tftp_server.c
字号:
memset(&hints,0,sizeof(hints));
hints.ai_family = from_addr->sa_family;
hints.ai_flags = AI_PASSIVE;
error = getaddrinfo(NULL,"tftp",&hints, &res);
if (0 != error) {
diag_printf("TFTPD: can't get a suitable local address: %s\n",
gai_strerror(error));
return;
}
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s < 0) {
diag_printf("TFTPD: can't open socket for 'write_file'\n");
freeaddrinfo(res);
return;
}
// We want the stack to pick a free local port number
set_port(res->ai_addr,0);
if (bind(s, res->ai_addr, res->ai_addrlen) < 0) {
// Problem setting up my end
diag_printf("TFTPD: can't bind to reply port for 'write_file'\n");
close(s);
freeaddrinfo(res);
return;
}
if ((fd = (server->ops->open)(hdr->th_stuff, O_RDONLY)) < 0) {
tftpd_send_error(s,reply,TFTP_ENOTFOUND,from_addr, from_len);
close(s);
freeaddrinfo(res);
return;
}
block = 0;
ok = true;
while (ok) {
// Read next chunk of file
len = (server->ops->read)(fd, reply->th_data, SEGSIZE);
reply->th_block = htons(++block); // preincrement
reply->th_opcode = htons(DATA);
for (tries = 0; tries < TFTP_RETRIES_MAX; tries++) {
#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT
tftp_server_instrument.data.send++;
#endif
if (sendto(s, reply, 4+len, 0,
from_addr, from_len) < 0) {
// Something went wrong with the network!
ok = false;
break;
}
repeat_select:
timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
timeout.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(s, &fds);
if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
if (++total_timeouts > TFTP_TIMEOUT_MAX) {
#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT
tftp_server_instrument.err_send++;
#endif
tftpd_send_error(s,reply,TFTP_EBADOP,from_addr, from_len);
ok = false;
break;
}
#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT
tftp_server_instrument.data.resend++;
#endif
continue; // retry the send, using up one retry.
}
data_len = sizeof(data_in);
client_len = sizeof(client_addr);
if ((data_len = recvfrom(s, data_in, data_len, 0,
&client_addr,
&client_len)) < 0) {
// What happened? Maybe someone lied to us...
#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT
tftp_server_instrument.data.resend++;
#endif
continue; // retry the send, using up one retry.
}
if ((ntohs(response->th_opcode) == ACK) &&
(ntohs(response->th_block) < block)) {
#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT
tftp_server_instrument.ack.rx_repeat++;
#endif
// Then it is a repeat ACK for an old block; listen again,
// but do not repeat sending the current block, and do not
// use up a retry count. (we do re-send the data if
// subsequently we time out)
goto repeat_select;
}
if ((ntohs(response->th_opcode) == ACK) &&
(ntohs(response->th_block) == block)) {
#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT
tftp_server_instrument.ack.rx++;
#endif
// Happy! Break out of the retries loop.
break;
}
// Otherwise, we got something we do not understand! So repeat
// sending the current block, and use up a retry count.
#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT
if ( (ntohs(response->th_opcode) == ACK) )
tftp_server_instrument.ack.rx_skip++;
tftp_server_instrument.data.resend++;
#endif
} // End of the retries loop.
if (TFTP_RETRIES_MAX <= tries) {
#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT
tftp_server_instrument.err_send++;
#endif
tftpd_send_error(s,reply,TFTP_EBADOP,from_addr, from_len);
ok = false;
}
if (len < SEGSIZE) {
break; // That's end of file then.
}
}
close(s);
freeaddrinfo(res);
(server->ops->close)(fd);
}
//
// Actual TFTP server
//
static void
tftpd_server(cyg_addrword_t p)
{
struct tftp_server *server = (struct tftp_server *)p;
int max_s = 0;
int data_len, recv_len;
socklen_t from_len;
struct sockaddr from_addr;
char data[SEGSIZE+sizeof(struct tftphdr)];
struct tftphdr *hdr = (struct tftphdr *)data;
struct addrinfo hints;
struct addrinfo *ai;
fd_set readfds;
char name[64];
int error;
int i;
#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT
struct info o = tftp_server_instrument;
#endif
#ifndef CYGPKG_NET_TESTS_USE_RT_TEST_HARNESS
// Otherwise routine printfs fail the test - interrupts disabled too long.
diag_printf("TFTPD [%x]: port %d\n", p, server->port);
#endif
memset(&hints,0,sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_flags = AI_PASSIVE;
error = getaddrinfo(NULL,"tftp",&hints, &server->res);
if (0 != error) {
diag_printf("TFTPD [%d] : can't get a local server address to bind to: %s\n",
p, gai_strerror(error));
return;
}
// If the port is 0, we need to use the default TFTP port. Extrace
// the port number from one of the addresses returned by
// getaddrinfo.
if (server->port == 0) {
switch (server->res->ai_family) {
case AF_INET:
{
struct sockaddr_in *addr = (struct sockaddr_in *)server->res->ai_addr;
server->port = ntohs(addr->sin_port);
break;
}
#ifdef CYGPKG_NET_INET6
case AF_INET6:
{
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)server->res->ai_addr;
server->port = ntohs(addr->sin6_port);
break;
}
#endif
default:
break;
}
}
#ifdef CYGSEM_NET_TFTPD_MULTITHREADED
sem_alloc(server->port);
while (true) {
#endif
// Iterate over the addresses and create a local port to listen for requests
ai = server->res;
memset(server->s,0,sizeof(server->s));
server->num_s = 0;
#ifdef CYGSEM_NET_TFTPD_MULTITHREADED
sem_wait(server->port);
#endif
while (ai && (server->num_s < CYGNUM_NET_MAX_INET_PROTOS)) {
server->s[server->num_s] = socket(ai->ai_family,
ai->ai_socktype,
ai->ai_protocol);
if (server->s[server->num_s] < 0 ) {
diag_printf("TFTPD [%x]: can't open socket\n", p);
freeaddrinfo(server->res);
server->res = NULL;
return;
}
set_port(ai->ai_addr, server->port);
if (bind(server->s[server->num_s],ai->ai_addr, ai->ai_addrlen) < 0) {
// Problem setting up my end
diag_printf("TFTPD [%x]: can't bind to server port\n",p);
close(server->s[server->num_s]);
server->s[server->num_s] = 0;
ai = ai->ai_next;
continue;
}
// We need to know the highest socket number for select.
if (server->s[server->num_s] > max_s)
max_s = server->s[server->num_s];
server->num_s++;
ai = ai->ai_next;
}
#ifndef CYGSEM_NET_TFTPD_MULTITHREADED
while (true) {
#endif
FD_ZERO(&readfds);
for (i=0; i < CYGNUM_NET_MAX_INET_PROTOS; i++) {
if (server->s[i]) {
FD_SET(server->s[i],&readfds);
}
}
error = select(max_s+1,&readfds,NULL,NULL,NULL);
if ( -1 == error) {
diag_printf("TFTPD [%x]: error in select\n",p);
}
for (i=0; i < CYGNUM_NET_MAX_INET_PROTOS; i++) {
if (server->s[i] && FD_ISSET(server->s[i],&readfds)) {
recv_len = sizeof(data);
from_len = sizeof(from_addr);
data_len = recvfrom(server->s[i], hdr, recv_len, 0,
&from_addr, &from_len);
if ( data_len < 0) {
diag_printf("TFTPD [%x]: can't read request\n", p);
} else {
#ifdef CYGSEM_NET_TFTPD_MULTITHREADED
// Close the socket and post on the semaphore some
// another thread can start listening for requests. This
// is not quite right. select could of returned with more than
// one socket with data to read. Here we only deal with one of them
for (i=0; i < CYGNUM_NET_MAX_INET_PROTOS; i++) {
if (server->s[i]) {
close (server->s[i]);
server->s[i] = 0;
}
}
sem_post(server->port);
#endif
#ifndef CYGPKG_NET_TESTS_USE_RT_TEST_HARNESS
getnameinfo(&from_addr,sizeof(from_addr), name, sizeof(name),0,0,0);
diag_printf("TFTPD [%x]: received %x from %s\n", p,
ntohs(hdr->th_opcode), name);
#endif
switch (ntohs(hdr->th_opcode)) {
case WRQ:
tftpd_write_file(server, hdr, &from_addr, from_len);
break;
case RRQ:
tftpd_read_file(server, hdr, &from_addr, from_len);
break;
case ACK:
case DATA:
case ERROR:
// Ignore
break;
default:
getnameinfo(&from_addr,sizeof(from_addr), name, sizeof(name),0,0,0);
diag_printf("TFTPD [%x]: bogus request %x from %s\n", p,
ntohs(hdr->th_opcode),
name);
tftpd_send_error(server->s[i],hdr,TFTP_EBADOP,&from_addr,from_len);
}
#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT
tftp_server_instrument.total_transactions++;
o.data.rx -= tftp_server_instrument.data.rx ;
o.data.rx_repeat -= tftp_server_instrument.data.rx_repeat;
o.data.rx_skip -= tftp_server_instrument.data.rx_skip ;
o.data.send -= tftp_server_instrument.data.send ;
o.data.resend -= tftp_server_instrument.data.resend ;
o.ack.rx -= tftp_server_instrument.ack.rx ;
o.ack.rx_repeat -= tftp_server_instrument.ack.rx_repeat ;
o.ack.rx_skip -= tftp_server_instrument.ack.rx_skip ;
o.ack.send -= tftp_server_instrument.ack.send ;
o.ack.resend -= tftp_server_instrument.ack.resend ;
o.err_send -= tftp_server_instrument.err_send ;
#ifndef CYGPKG_NET_TESTS_USE_RT_TEST_HARNESS
if ( o.data.rx ) diag_printf( "data rx %4d\n", -o.data.rx );
if ( o.data.rx_repeat ) diag_printf( "data rx_repeat%4d\n", -o.data.rx_repeat );
if ( o.data.rx_skip ) diag_printf( "data rx_skip %4d\n", -o.data.rx_skip );
if ( o.data.send ) diag_printf( "data send %4d\n", -o.data.send );
if ( o.data.resend ) diag_printf( "data resend %4d\n", -o.data.resend );
if ( o.ack.rx ) diag_printf( " ack rx %4d\n", -o.ack.rx );
if ( o.ack.rx_repeat ) diag_printf( " ack rx_repeat%4d\n", -o.ack.rx_repeat );
if ( o.ack.rx_skip ) diag_printf( " ack rx_skip %4d\n", -o.ack.rx_skip );
if ( o.ack.send ) diag_printf( " ack send %4d\n", -o.ack.send );
if ( o.ack.resend ) diag_printf( " ack resend %4d\n", -o.ack.resend );
if ( o.err_send ) diag_printf( "*error sends %4d\n", -o.err_send );
#endif // CYGPKG_NET_TESTS_USE_RT_TEST_HARNESS
#endif // CYGOPT_NET_TFTP_SERVER_INSTRUMENT
#ifdef CYGSEM_NET_TFTPD_MULTITHREADED
break;
#endif
}
}
}
// The following looks a little strange, but it keeps emacs's
// auto indention happy.
#ifndef CYGSEM_NET_TFTPD_MULTITHREADED
}
#endif
#ifdef CYGSEM_NET_TFTPD_MULTITHREADED
}
#endif
}
//
// This function is used to create a new server [thread] which supports
// the TFTP protocol on the given port. A server 'id' will be returned
// which can later be used to destroy the server.
//
// Note: all [memory] resources for the server thread will be allocated
// dynamically. If there are insufficient resources, an error will be
// returned.
//
int
tftpd_start(int port, struct tftpd_fileops *ops)
{
struct tftp_server *server;
#ifdef CYGSEM_TFTP_SERVER_MULTITHREADED
static char init = 0;
if ( 0 == init ) {
init++;
cyg_semaphore_init( &tftp_server_sem, 0 );
}
#endif
if ((server = malloc(sizeof(struct tftp_server)))) {
server->tag = TFTP_tag;
server->port = port;
server->ops = ops;
cyg_thread_create(CYGPKG_NET_TFTPD_THREAD_PRIORITY, // Priority
tftpd_server, // entry
(cyg_addrword_t)server, // entry parameter
"TFTP server", // Name
&server->stack[0], // Stack
STACK_SIZE, // Size
&server->thread_handle, // Handle
&server->thread_data // Thread data structure
);
cyg_thread_resume(server->thread_handle); // Start it
}
return (int)server;
}
//
// Destroy a TFTP server, using a previously created server 'id'.
//
int
tftpd_stop(int p)
{
struct tftp_server *server = (struct tftp_server *)p;
// Simple sanity check
if (server->tag == TFTP_tag) {
cyg_thread_kill(server->thread_handle);
cyg_thread_set_priority(server->thread_handle, 0);
cyg_thread_delay(1); // Make sure it gets to die...
if (cyg_thread_delete(server->thread_handle)) {
// Success shutting down the thread. Close all its sockets.
int i;
for (i = 0 ; i < CYGNUM_NET_MAX_INET_PROTOS; i++) {
if (server->s[i]) {
close (server->s[i]);
}
}
freeaddrinfo(server->res);
free(server); // Give up memory
return 1;
}
}
return 0;
}
// EOF tftp_server.c
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -