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 }
portTASK_FUNCTION( vBasicTFTPServer, pvParameters ) { int lSocket; int lDataLen, lRecvLen; socklen_t lFromLen; struct sockaddr_in sLocalAddr, sFromAddr; portCHAR cData[SEGSIZE+sizeof(struct tftphdr)]; struct tftphdr *sHdr = (struct tftphdr *)cData; // Set up port // Network order in info; host order in server: for (;;) { // Create socket lSocket = socket(AF_INET, SOCK_DGRAM, 0); if (lSocket < 0) { return; } memset((char *)&sLocalAddr, 0, sizeof(sLocalAddr)); sLocalAddr.sin_family = AF_INET; sLocalAddr.sin_len = sizeof(sLocalAddr); sLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY); sLocalAddr.sin_port = htons(TFTP_PORT); if (bind(lSocket, (struct sockaddr *)&sLocalAddr, sizeof(sLocalAddr)) < 0) { // Problem setting up my end close(lSocket); return; } lRecvLen = sizeof(cData); lFromLen = sizeof(sFromAddr); lDataLen = recvfrom(lSocket, sHdr, lRecvLen, 0, (struct sockaddr *)&sFromAddr, &lFromLen); vParTestSetLED( TFTP_LED , pdTRUE ); close(lSocket); // so that other servers can bind to the TFTP socket if ( lDataLen < 0) { } else { switch (ntohs(sHdr->th_opcode)) { case WRQ: tftpd_write_file(sHdr, &sFromAddr, lFromLen); vParTestSetLED( TFTP_LED , pdFALSE ); break; case RRQ: tftpd_read_file(sHdr, &sFromAddr, lFromLen); vParTestSetLED( TFTP_LED , pdFALSE ); break; case ACK: case DATA: case ERROR: vParTestSetLED( TFTP_LED , pdFALSE ); // Ignore break; default: for(;;) { vParTestToggleLED( TFTP_LED ); vTaskDelay(200); } } } } }
static void tftpd_server(cyg_addrword_t p) { struct tftp_server *server = (struct tftp_server *)p; int s; int data_len, recv_len, from_len; struct sockaddr_in local_addr, from_addr; struct servent *server_info; char data[SEGSIZE+sizeof(struct tftphdr)]; struct tftphdr *hdr = (struct tftphdr *)data; #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 // Set up port if (server->port == 0) { server_info = getservbyname("tftp", "udp"); if (server_info == (struct servent *)0) { diag_printf("TFTPD: can't get TFTP service information\n"); return; } // Network order in info; host order in server: server->port = ntohs( server_info->s_port ); } 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_len = sizeof(local_addr); 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 } recv_len = sizeof(data); from_len = sizeof(from_addr); data_len = recvfrom(s, (void*)hdr, recv_len, 0, (struct sockaddr *)&from_addr, (socklen_t *)&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 [%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: 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: 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 } }