Mercurial > ecos
diff packages/net/tcpip/current/src/lib/tftp_server.c @ 128:0c2b7be0d798 ecos-sw-2000-10-12
Merge from eCos master repository on 2000-10-12-08:46:24-BST
| author | jlarmour |
|---|---|
| date | Thu, 12 Oct 2000 20:31:43 +0000 |
| parents | 6ed91473a1cd |
| children | 0ae0bc38e387 |
line wrap: on
line diff
--- a/packages/net/tcpip/current/src/lib/tftp_server.c +++ b/packages/net/tcpip/current/src/lib/tftp_server.c @@ -60,6 +60,32 @@ #include <cyg/kernel/kapi.h> #include <stdlib.h> // For malloc +#define nCYGOPT_NET_TFTP_SERVER_INSTRUMENT +#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT + +struct subinfo { + int rx; + int rx_repeat; + int rx_skip; + int send; + int resend; +}; + +struct info { + struct subinfo ack, data; + int err_send; + int total_transactions; +}; + + +static struct info tftp_server_instrument = { + { 0,0,0,0,0 }, + { 0,0,0,0,0 }, + 0, 0, +}; + +#endif // CYGOPT_NET_TFTP_SERVER_INSTRUMENT + #define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL+(3*(SEGSIZE+sizeof(struct tftphdr)))) static char *TFTP_tag = "TFTPD"; struct tftp_server { @@ -72,16 +98,32 @@ struct tftp_server { }; static char * errmsg[] = { - "Undefined error code", - "File not found", - "Access violation", - "Disk full or allocation exceeded", - "Illegal TFTP operation", - "Unknown transfer ID", - "File already exists", - "No such user" + "Undefined error code", // 0 nothing defined + "File not found", // 1 TFTP_ENOTFOUND + "Access violation", // 2 TFTP_EACCESS + "Disk full or allocation exceeded", // 3 TFTP_ENOSPACE + "Illegal TFTP operation", // 4 TFTP_EBADOP + "Unknown transfer ID", // 5 TFTP_EBADID + "File already exists", // 6 TFTP_EEXISTS + "No such user", // 7 TFTP_ENOUSER }; +/* Send an error packet to the client */ +static void +tftpd_send_error(int s, struct tftphdr * reply, int err, + struct sockaddr_in *from_addr, int from_len) +{ + CYG_ASSERT( 0 <= err, "err underflow" ); + CYG_ASSERT( sizeof(errmsg)/sizeof(errmsg[0]) > err, "err overflow" ); + + reply->th_opcode = htons(ERROR); + reply->th_code = htons(err); + if ( (0 > err) || (sizeof(errmsg)/sizeof(errmsg[0]) <= err) ) + err = 0; // Do not copy a random string from hyperspace + strcpy(reply->th_msg, errmsg[err]); + sendto(s, reply, 4+strlen(reply->th_msg)+1, 0, + (struct sockaddr *)from_addr, from_len); +} // // Receive a file from the client @@ -95,7 +137,7 @@ tftpd_write_file(struct tftp_server *ser char data_in[SEGSIZE+sizeof(struct tftphdr)]; struct tftphdr *reply = (struct tftphdr *)data_out; struct tftphdr *response = (struct tftphdr *)data_in; - int fd, block, len, ok, closed, data_len, s; + int fd, block, len, ok, tries, closed, data_len, s; struct timeval timeout; fd_set fds; int total_timeouts = 0; @@ -118,93 +160,113 @@ tftpd_write_file(struct tftp_server *ser return; } if ((fd = (server->ops->open)(hdr->th_stuff, O_WRONLY)) < 0) { - reply->th_opcode = htons(ERROR); - reply->th_code = htons(TFTP_ENOTFOUND); - strcpy(reply->th_msg, errmsg[TFTP_ENOTFOUND]); - sendto(s, reply, 4+strlen(reply->th_msg)+1, 0, - (struct sockaddr *)from_addr, from_len); + tftpd_send_error(s,reply,TFTP_ENOTFOUND,from_addr, from_len); close(s); return; } - // Send ACK telling client he can send data - reply->th_opcode = htons(ACK); - reply->th_block = 0; - sendto(s, reply, 4, 0, (struct sockaddr *)from_addr, from_len); - block = 1; ok = true; closed = false; + block = 0; while (ok) { - 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) { - ok = false; - break; + // Send ACK telling client he can send data + reply->th_opcode = htons(ACK); + reply->th_block = htons(block++); // postincrement + for (tries = 0; tries < TFTP_RETRIES_MAX; tries++) { +#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT + tftp_server_instrument.ack.send++; +#endif + sendto(s, reply, 4, 0, (struct sockaddr *)from_addr, from_len); + 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.ack.resend++; +#endif + continue; // retry the send, using up one retry. } - // Try resending last ACK - sendto(s, reply, 4, 0, (struct sockaddr *)from_addr, from_len); - } else { // Some data has arrived data_len = sizeof(data_in); client_len = sizeof(client_addr); if ((data_len = recvfrom(s, data_in, data_len, 0, - (struct sockaddr *)&client_addr, &client_len)) < 0) { - // What happened? - diag_printf("Can't read client data!\n"); - ok = false; + (struct sockaddr *)&client_addr, &client_len)) < 0) { + // What happened? No data here! +#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT + tftp_server_instrument.ack.resend++; +#endif + continue; // retry the send, using up one retry. + } + if (ntohs(response->th_opcode) == DATA && + ntohs(response->th_block) < block) { +#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT + tftp_server_instrument.data.rx_repeat++; +#endif + // Then it is repeat DATA with an old block; listen again, + // but do not repeat sending the current ack, and do not + // use up a retry count. (we do re-send the ack if + // subsequently we time out) + goto repeat_select; + } + if (ntohs(response->th_opcode) == DATA && + ntohs(response->th_block) == block) { +#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT + tftp_server_instrument.data.rx++; +#endif + // Good data - write to file + len = (server->ops->write)(fd, response->th_data, data_len-4); + if (len < (data_len-4)) { + // File is "full" + tftpd_send_error(s,reply,TFTP_ENOSPACE, + from_addr, from_len); + ok = false; // Give up + break; // out of the retries loop + } + if (data_len < (SEGSIZE+4)) { + // End of file + closed = true; + ok = false; + if ((server->ops->close)(fd) == -1) { + tftpd_send_error(s,reply,TFTP_EACCESS, + from_addr, from_len); + break; // out of the retries loop + } + // Exception to the loop structure: we must ACK the last + // packet, the one that implied EOF: +#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT + tftp_server_instrument.ack.send++; +#endif + reply->th_opcode = htons(ACK); + reply->th_block = htons(block++); // postincrement + sendto(s, reply, 4, 0, (struct sockaddr *)from_addr, from_len); + break; // out of the retries loop + } + // Happy! Break out of the retries loop. break; } - if (ntohs(response->th_opcode) == DATA) { - if (ntohs(response->th_block) == block) { - // Good data - write to file - len = (server->ops->write)(fd, response->th_data, data_len-4); - if (len < (data_len-4)) { - // File is "full" - reply->th_opcode = htons(ERROR); - reply->th_code = htons(TFTP_ENOSPACE); - strcpy(reply->th_msg, errmsg[TFTP_ENOSPACE]); - sendto(s, reply, 4+strlen(reply->th_msg)+1, 0, - (struct sockaddr *)from_addr, from_len); - ok = false; // Give up - } else { - if (data_len < (SEGSIZE+4)) { - // End of file - closed = true; - ok = false; - if ((server->ops->close)(fd) == -1) { - reply->th_opcode = htons(ERROR); - reply->th_code = htons(TFTP_EACCESS); - strcpy(reply->th_msg, errmsg[TFTP_EACCESS]); - sendto(s, reply, 4+strlen(reply->th_msg)+1, 0, - (struct sockaddr *)from_addr, from_len); - } else { - reply->th_opcode = htons(ACK); - reply->th_block = htons(block++); - sendto(s, reply, 4, 0, - (struct sockaddr *)from_addr, from_len); - } - } else { - reply->th_opcode = htons(ACK); - reply->th_block = htons(block++); - sendto(s, reply, 4, 0, - (struct sockaddr *)from_addr, from_len); - } - } - } else { - // Something is wrong - tell client last good block - sendto(s, reply, 4, 0, (struct sockaddr *)from_addr, from_len); - } - } else { - // Client has sent something bogus - bag out! - reply->th_opcode = htons(ERROR); - reply->th_code = htons(TFTP_EBADOP); - strcpy(reply->th_msg, errmsg[TFTP_EBADOP]); - sendto(s, reply, 4+strlen(reply->th_msg)+1, 0, - (struct sockaddr *)from_addr, from_len); - ok = false; - } + // Otherwise, we got something we do not understand! So repeat + // sending the current ACK, and use up a retry count. +#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT + if ( (ntohs(response->th_opcode) == DATA) ) + tftp_server_instrument.data.rx_skip++; + tftp_server_instrument.ack.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; } } close(s); @@ -248,11 +310,7 @@ tftpd_read_file(struct tftp_server *serv return; } if ((fd = (server->ops->open)(hdr->th_stuff, O_RDONLY)) < 0) { - reply->th_opcode = htons(ERROR); - reply->th_code = htons(TFTP_ENOTFOUND); - strcpy(reply->th_msg, errmsg[TFTP_ENOTFOUND]); - sendto(s, reply, 4+strlen(reply->th_msg)+1, 0, - (struct sockaddr *)from_addr, from_len); + tftpd_send_error(s,reply,TFTP_ENOTFOUND,from_addr, from_len); close(s); return; } @@ -261,10 +319,14 @@ tftpd_read_file(struct tftp_server *serv while (ok) { // Read next chunk of file len = (server->ops->read)(fd, reply->th_data, SEGSIZE); - reply->th_block = htons(++block); + reply->th_block = htons(++block); // preincrement reply->th_opcode = htons(DATA); for (tries = 0; tries < TFTP_RETRIES_MAX; tries++) { - if (sendto(s, reply, 4+len, 0, (struct sockaddr *)from_addr, from_len) < 0) { +#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT + tftp_server_instrument.data.send++; +#endif + if (sendto(s, reply, 4+len, 0, + (struct sockaddr *)from_addr, from_len) < 0) { // Something went wrong with the network! ok = false; break; @@ -276,9 +338,16 @@ tftpd_read_file(struct tftp_server *serv 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); @@ -287,23 +356,47 @@ tftpd_read_file(struct tftp_server *serv (struct sockaddr *)&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. + // 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; + break; // That's end of file then. } } close(s); @@ -313,6 +406,11 @@ tftpd_read_file(struct tftp_server *serv // // Actual TFTP server // +#define CYGSEM_TFTP_SERVER_MULTITHREADED +#ifdef CYGSEM_TFTP_SERVER_MULTITHREADED +static cyg_sem_t tftp_server_sem; +#endif + static void tftpd_server(cyg_addrword_t p) { @@ -326,7 +424,7 @@ tftpd_server(cyg_addrword_t p) #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); + diag_printf("TFTPD [%x]: port %d\n", p, server->port); #endif // Set up port @@ -339,33 +437,53 @@ tftpd_server(cyg_addrword_t p) server->port = server_info->s_port; } - // Create socket - s = socket(AF_INET, SOCK_DGRAM, 0); - if (s < 0) { - diag_printf("TFTPD: can't open socket\n"); - return; - } - memset((char *)&local_addr, 0, sizeof(local_addr)); - local_addr.sin_family = AF_INET; - local_addr.sin_addr.s_addr = htonl(INADDR_ANY); - local_addr.sin_port = htons(server->port); - if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) { - // Problem setting up my end - diag_printf("TFTPD: can't bind to service port\n"); - close(s); - return; - } + while (true) { +#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT + struct info o = tftp_server_instrument; +#endif + // Create socket + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + diag_printf("TFTPD [%x]: can't open socket\n", p); + return; + } + memset((char *)&local_addr, 0, sizeof(local_addr)); + local_addr.sin_family = AF_INET; + local_addr.sin_addr.s_addr = htonl(INADDR_ANY); + local_addr.sin_port = htons(server->port); + if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) { + // Problem setting up my end + close(s); +#ifdef CYGSEM_TFTP_SERVER_MULTITHREADED +#ifndef CYGPKG_NET_TESTS_USE_RT_TEST_HARNESS + diag_printf("TFTPD [%x]: waiting to bind to service port\n", p); +#endif + // Wait until the socket is free... + cyg_semaphore_wait( &tftp_server_sem ); + continue; // try re-opening and rebinding the socket. +#else + diag_printf("TFTPD [%x]: can't bind to service port\n", p); + return; +#endif + } - while (true) { recv_len = sizeof(data); from_len = sizeof(from_addr); - if ((data_len = recvfrom(s, hdr, recv_len, 0, - (struct sockaddr *)&from_addr, &from_len)) < 0) { - diag_printf("TFTPD: can't read request\n"); + data_len = recvfrom(s, hdr, recv_len, 0, + (struct sockaddr *)&from_addr, &from_len); + close(s); // so that other servers can bind to the TFTP socket +#ifdef CYGSEM_TFTP_SERVER_MULTITHREADED + // The socket is free... + cyg_semaphore_post( &tftp_server_sem ); +#endif + + if ( data_len < 0) { + diag_printf("TFTPD [%x]: can't read request\n", p); } else { #ifndef CYGPKG_NET_TESTS_USE_RT_TEST_HARNESS - diag_printf("TFTPD: received %x from %s:%d\n", - ntohs(hdr->th_opcode), inet_ntoa(from_addr.sin_addr), from_addr.sin_port); + diag_printf("TFTPD [%x]: received %x from %s:%d\n", p, + ntohs(hdr->th_opcode), inet_ntoa(from_addr.sin_addr), + from_addr.sin_port); #endif switch (ntohs(hdr->th_opcode)) { case WRQ: @@ -380,15 +498,46 @@ tftpd_server(cyg_addrword_t p) // Ignore break; default: - diag_printf("TFTPD: bogus request %x from %s:%d\n", - ntohs(hdr->th_opcode), inet_ntoa(from_addr.sin_addr), from_addr.sin_port); - hdr->th_opcode = htons(ERROR); - hdr->th_code = htons(TFTP_EBADOP); - strcpy(hdr->th_msg, errmsg[TFTP_EBADOP]);; - sendto(s, hdr, 4+strlen(hdr->th_msg)+1, 0, - (struct sockaddr *)&from_addr, from_len); + diag_printf("TFTPD [%x]: bogus request %x from %s:%d\n", p, + ntohs(hdr->th_opcode), + inet_ntoa(from_addr.sin_addr), + from_addr.sin_port ); + tftpd_send_error(s,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 } } @@ -405,6 +554,14 @@ 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;
