Nut/OS  4.10.3
API Reference
ftpserv.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2009 by egnite GmbH
00003  * Copyright (C) 2005 by egnite Software GmbH
00004  * 
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions
00009  * are met:
00010  *
00011  * 1. Redistributions of source code must retain the above copyright
00012  *    notice, this list of conditions and the following disclaimer.
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in the
00015  *    documentation and/or other materials provided with the distribution.
00016  * 3. Neither the name of the copyright holders nor the names of
00017  *    contributors may be used to endorse or promote products derived
00018  *    from this software without specific prior written permission.
00019  *
00020  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00027  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00028  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00029  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00030  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00031  * SUCH DAMAGE.
00032  *
00033  * For additional information see http://www.ethernut.de/
00034  */
00035 
00040 #include <stdio.h>
00041 #include <fcntl.h>
00042 #include <io.h>
00043 
00044 #include <dev/board.h>
00045 #include <dev/pnut.h>
00046 #include <dev/sbimmc.h>
00047 #include <fs/phatfs.h>
00048 
00049 #include <sys/confnet.h>
00050 #include <sys/version.h>
00051 #include <sys/heap.h>
00052 #include <sys/thread.h>
00053 #include <sys/socket.h>
00054 
00055 #include <arpa/inet.h>
00056 #include <netinet/tcp.h>
00057 #include <net/route.h>
00058 #include <pro/dhcp.h>
00059 #include <pro/ftpd.h>
00060 #include <pro/wins.h>
00061 #include <pro/sntp.h>
00062 #include <pro/discover.h>
00063 
00064 /* Determine the compiler. */
00065 #if defined(__IMAGECRAFT__)
00066 #if defined(__AVR__)
00067 #define CC_STRING   "ICCAVR"
00068 #else
00069 #define CC_STRING   "ICC"
00070 #endif
00071 #elif defined(__GNUC__)
00072 #if defined(__AVR__)
00073 #define CC_STRING   "AVRGCC"
00074 #elif defined(__arm__)
00075 #define CC_STRING   "ARMGCC"
00076 #else
00077 #define CC_STRING   "GCC"
00078 #endif
00079 #else
00080 #define CC_STRING   "Compiler unknown"
00081 #endif
00082 
00092 /* 
00093  * Baudrate for debug output. 
00094  */
00095 #ifndef DBG_BAUDRATE
00096 #define DBG_BAUDRATE 115200
00097 #endif
00098 
00099 /*
00100  * Wether we should use DHCP.
00101  */
00102 #define USE_DHCP
00103 
00104 /*
00105  * Wether we should run a discovery responder.
00106  */
00107 #if defined(__arm__)
00108 #define USE_DISCOVERY
00109 #endif
00110 
00111 /* 
00112  * Unique MAC address of the Ethernut Board. 
00113  *
00114  * Ignored if EEPROM contains a valid configuration.
00115  */
00116 #define MY_MAC { 0x00, 0x06, 0x98, 0x30, 0x00, 0x35 }
00117 
00118 /* 
00119  * Unique IP address of the Ethernut Board. 
00120  *
00121  * Ignored if DHCP is used. 
00122  */
00123 #define MY_IPADDR "192.168.192.35"
00124 
00125 /* 
00126  * IP network mask of the Ethernut Board.
00127  *
00128  * Ignored if DHCP is used. 
00129  */
00130 #define MY_IPMASK "255.255.255.0"
00131 
00132 /* 
00133  * Gateway IP address for the Ethernut Board.
00134  *
00135  * Ignored if DHCP is used. 
00136  */
00137 #define MY_IPGATE "192.168.192.1"
00138 
00139 /* 
00140  * NetBIOS name.
00141  *
00142  * Use a symbolic name with Win32 Explorer.
00143  */
00144 //#define MY_WINSNAME "ETHERNUT"
00145 
00146 /*
00147  * FTP port number.
00148  */
00149 #define FTP_PORTNUM 21
00150 
00151 /*
00152  * FTP timeout.
00153  *
00154  * The server will terminate the session, if no new command is received
00155  * within the specified number of milliseconds.
00156  */
00157 #define FTPD_TIMEOUT 600000
00158 
00159 /*
00160  * TCP buffer size.
00161  */
00162 #define TCPIP_BUFSIZ 5840
00163 
00164 /*
00165  * Maximum segment size. 
00166  *
00167  * Choose 536 up to 1460. Note, that segment sizes above 536 may result 
00168  * in fragmented packets. Remember, that Ethernut doesn't support TCP 
00169  * fragmentation.
00170  */
00171 #define TCPIP_MSS 1460
00172 
00173 #if defined(ETHERNUT3)
00174 
00175 /* Ethernut 3 file system. */
00176 #define FSDEV       devPhat0
00177 #define FSDEV_NAME  "PHAT0" 
00178 
00179 /* Ethernut 3 block device interface. */
00180 #define BLKDEV      devNplMmc0
00181 #define BLKDEV_NAME "MMC0"
00182 
00183 #elif defined(AT91SAM7X_EK)
00184 
00185 /* SAM7X-EK file system. */
00186 #define FSDEV       devPhat0
00187 #define FSDEV_NAME  "PHAT0" 
00188 
00189 /* SAM7X-EK block device interface. */
00190 #define BLKDEV      devAt91SpiMmc0
00191 #define BLKDEV_NAME "MMC0"
00192 
00193 #elif defined(AT91SAM9260_EK)
00194 
00195 /* SAM9260-EK file system. */
00196 #define FSDEV       devPhat0
00197 #define FSDEV_NAME  "PHAT0" 
00198 
00199 /* SAM9260-EK block device interface. */
00200 #define BLKDEV      devAt91Mci0
00201 #define BLKDEV_NAME "MCI0"
00202 
00203 #elif defined(ETHERNUT2)
00204 
00205 /*
00206  * Ethernut 2 File system
00207  */
00208 #define FSDEV       devPnut
00209 #define FSDEV_NAME  "PNUT" 
00210 
00211 #else
00212 
00213 #define FSDEV_NAME  "NONE" 
00214 
00215 #endif
00216 
00218 #define MYTZ    -1
00219 
00221 #if defined(DEV_ETHER)
00222 #define MYTIMED "130.149.17.21"
00223 #endif
00224 
00225 #ifdef ETHERNUT3
00226 
00227 #define X12RTC_DEV
00228 #endif
00229 
00230 /*
00231  * FTP service.
00232  *
00233  * This function waits for client connect, processes the FTP request 
00234  * and disconnects. Nut/Net doesn't support a server backlog. If one 
00235  * client has established a connection, further connect attempts will 
00236  * be rejected. 
00237  *
00238  * Some FTP clients, like the Win32 Explorer, open more than one 
00239  * connection for background processing. So we run this routine by
00240  * several threads.
00241  */
00242 static void FtpService(void)
00243 {
00244 #if defined(DEV_ETHER)
00245     TCPSOCKET *sock;
00246 
00247     /*
00248      * Create a socket.
00249      */
00250     if ((sock = NutTcpCreateSocket()) != 0) {
00251 
00252         /* 
00253          * Set specified socket options. 
00254          */
00255 #ifdef TCPIP_MSS
00256         {
00257             uint16_t mss = TCPIP_MSS;
00258             NutTcpSetSockOpt(sock, TCP_MAXSEG, &mss, sizeof(mss));
00259         }
00260 #endif
00261 #ifdef FTPD_TIMEOUT
00262         {
00263             uint32_t tmo = FTPD_TIMEOUT;
00264             NutTcpSetSockOpt(sock, SO_RCVTIMEO, &tmo, sizeof(tmo));
00265         }
00266 #endif
00267 #ifdef TCPIP_BUFSIZ
00268         {
00269             uint16_t siz = TCPIP_BUFSIZ;
00270             NutTcpSetSockOpt(sock, SO_RCVBUF, &siz, sizeof(siz));
00271         }
00272 #endif
00273 
00274         /*
00275          * Listen on our port. If we return, we got a client.
00276          */
00277         printf("\nWaiting for an FTP client...");
00278         if (NutTcpAccept(sock, FTP_PORTNUM) == 0) {
00279             printf("%s connected, %u bytes free\n", inet_ntoa(sock->so_remote_addr), (unsigned int)NutHeapAvailable());
00280             NutFtpServerSession(sock);
00281             printf("%s disconnected, %u bytes free\n", inet_ntoa(sock->so_remote_addr), (unsigned int)NutHeapAvailable());
00282         } else {
00283             puts("Accept failed");
00284         }
00285 
00286         /*
00287          * Close our socket.
00288          */
00289         NutTcpCloseSocket(sock);
00290     }
00291 #endif
00292 }
00293 
00294 /*
00295  * FTP service thread.
00296  */
00297 THREAD(FtpThread, arg)
00298 {
00299     /* Loop endless for connections. */
00300     for (;;) {
00301         FtpService();
00302     }
00303 }
00304 
00305 /*
00306  * Assign stdout to the UART device.
00307  */
00308 void InitDebugDevice(void)
00309 {
00310     uint32_t baud = DBG_BAUDRATE;
00311 
00312     NutRegisterDevice(&DEV_CONSOLE, 0, 0);
00313     freopen(DEV_CONSOLE_NAME, "w", stdout);
00314     _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
00315 }
00316 
00317 /*
00318  * Setup the ethernet device. Try DHCP first. If this is
00319  * the first time boot with empty EEPROM and no DHCP server
00320  * was found, use hardcoded values.
00321  */
00322 static int InitEthernetDevice(void)
00323 {
00324 #if defined(DEV_ETHER)
00325     uint32_t ip_addr = inet_addr(MY_IPADDR);
00326     uint32_t ip_mask = inet_addr(MY_IPMASK);
00327     uint32_t ip_gate = inet_addr(MY_IPGATE);
00328     uint8_t mac[6] = MY_MAC;
00329 
00330     if (NutRegisterDevice(&DEV_ETHER, 0x8300, 5) == 0) {
00331         printf("Configure %s...", DEV_ETHER_NAME);
00332 #ifdef USE_DHCP
00333         if (NutDhcpIfConfig(DEV_ETHER_NAME, 0, 60000) == 0) {
00334             puts("OK");
00335             return 0;
00336         }
00337         printf("initial boot...");
00338         if (NutDhcpIfConfig(DEV_ETHER_NAME, mac, 60000) == 0) {
00339             puts("OK");
00340             return 0;
00341         }
00342 #endif
00343         printf("No DHCP...");
00344         NutNetIfConfig(DEV_ETHER_NAME, mac, ip_addr, ip_mask);
00345         /* Without DHCP we had to set the default gateway manually.*/
00346         if(ip_gate) {
00347             printf("hard coded gate...");
00348             NutIpRouteAdd(0, 0, ip_gate, &DEV_ETHER);
00349         }
00350         puts("OK");
00351         return 0;
00352     }
00353 #endif /* DEV_ETHER */
00354     puts("No Ethernet Device");
00355 
00356     return -1;
00357 }
00358 
00359 /*
00360  * Query a time server and optionally update the hardware clock.
00361  */
00362 static int QueryTimeServer(void)
00363 {
00364     int rc = -1;
00365 
00366 #ifdef MYTIMED
00367     {
00368         time_t now;
00369         uint32_t timeserver = inet_addr(MYTIMED);
00370 
00371         /* Query network time service and set the system time. */
00372         printf("Query time from %s...", MYTIMED);
00373         if(NutSNTPGetTime(&timeserver, &now) == 0) {
00374             puts("OK");
00375             rc = 0;
00376             stime(&now);
00377 #ifdef X12RTC_DEV
00378             /* If RTC hardware is available, update it. */
00379             {
00380                 struct _tm *gmt = gmtime(&now);
00381 
00382                 if (X12RtcSetClock(gmt)) {
00383                     puts("RTC update failed");
00384                 }
00385             }
00386 #endif
00387         }
00388         else {
00389             puts("failed");
00390         }
00391     }
00392 #endif
00393 
00394     return rc;
00395 }
00396 
00397 /*
00398  * Try to get initial date and time from the hardware clock or a time server.
00399  */
00400 static int InitTimeAndDate(void)
00401 {
00402     int rc = -1;
00403 
00404     /* Set the local time zone. */
00405     _timezone = MYTZ * 60L * 60L;
00406 
00407 #ifdef X12RTC_DEV
00408     /* Query RTC hardware if available. */
00409     {
00410         uint32_t rs;
00411 
00412         /* Query the status. If it fails, we do not have an RTC. */
00413         if (X12RtcGetStatus(&rs)) {
00414             puts("No hardware RTC");
00415             rc = QueryTimeServer();
00416         }
00417         else {
00418             /* RTC hardware seems to be available. Check for power failure. */
00419             //rs = RTC_STATUS_PF;
00420             if ((rs & RTC_STATUS_PF) == RTC_STATUS_PF) {
00421                 puts("RTC power fail detected");
00422                 rc = QueryTimeServer();
00423             }
00424 
00425             /* RTC hardware status is fine, update our system clock. */
00426             else {
00427                 struct _tm gmt;
00428 
00429                 /* Assume that RTC is running at GMT. */
00430                 if (X12RtcGetClock(&gmt) == 0) {
00431                     time_t now = _mkgmtime(&gmt);
00432 
00433                     if (now != -1) {
00434                         stime(&now);
00435                         rc = 0;
00436                     }
00437                 }
00438             }
00439         }
00440     }
00441 #else
00442     /* No hardware RTC, query the time server if available. */
00443     rc = QueryTimeServer();
00444 #endif
00445     return rc;
00446 }
00447 
00448 /*
00449  * Main application routine. 
00450  *
00451  * Nut/OS automatically calls this entry after initialization.
00452  */
00453 int main(void)
00454 {
00455     int volid;
00456     uint32_t ipgate;
00457 
00458     /* Initialize a debug output device and print a banner. */
00459     InitDebugDevice();
00460     printf("\n\nFTP Server Sample - Nut/OS %s - " CC_STRING "\n", NutVersionString());
00461 
00462     /* Initialize the Ethernet device and print our IP address. */
00463     if (InitEthernetDevice()) {
00464         for(;;);
00465     }
00466 #if defined(DEV_ETHER)
00467     printf("IP Addr: %s\n", inet_ntoa(confnet.cdn_ip_addr));
00468     printf("IP Mask: %s\n", inet_ntoa(confnet.cdn_ip_mask));
00469     NutIpRouteQuery(0, &ipgate);
00470     printf("IP Gate: %s\n", inet_ntoa(ipgate));
00471 #endif
00472 
00473 #ifdef USE_DISCOVERY
00474     /* Register a discovery responder. */
00475     printf("Start Responder...");
00476     if (NutRegisterDiscovery((uint32_t)-1, 0, DISF_INITAL_ANN)) {
00477         puts("failed");
00478     }
00479     else {
00480         puts("OK");
00481     }
00482 #endif
00483 
00484     /* Initialize system clock and calendar. */
00485     if (InitTimeAndDate() == 0) {
00486         time_t now = time(0);
00487         struct _tm *lot = localtime(&now);
00488         printf("Date: %02u.%02u.%u\n", lot->tm_mday, lot->tm_mon + 1, 1900 + lot->tm_year);
00489         printf("Time: %02u:%02u:%02u\n", lot->tm_hour, lot->tm_min, lot->tm_sec);
00490     }
00491 
00492 #ifdef FSDEV
00493     /* Initialize the file system. */
00494     printf("Register file system...");
00495     if (NutRegisterDevice(&FSDEV, 0, 0)) {
00496         puts("failed");
00497         for (;;);
00498     }
00499     puts("OK");
00500 #endif
00501 
00502 #ifdef BLKDEV
00503     /* Register block device. */
00504     printf("Register block device...");
00505     if (NutRegisterDevice(&BLKDEV, 0, 0)) {
00506         puts("failed");
00507         for (;;);
00508     }
00509     puts("OK");
00510 
00511     /* Mount partition. */
00512     printf("Mounting partition...");
00513     if ((volid = _open(BLKDEV_NAME ":1/" FSDEV_NAME, _O_RDWR | _O_BINARY)) == -1) {
00514         puts("failed");
00515         for (;;);
00516     }
00517     puts("OK");
00518 #else
00519     volid = 0;
00520 #endif
00521 
00522 #if defined(DEV_ETHER)
00523     /* Register root path. */
00524     printf("Register FTP root...");
00525     if (NutRegisterFtpRoot(FSDEV_NAME ":")) {
00526         puts("failed");
00527         for (;;);
00528     }
00529     puts("OK");
00530 #endif
00531 
00532     /* Start two additional server threads. */
00533     NutThreadCreate("ftpd0", FtpThread, 0, 1640);
00534     NutThreadCreate("ftpd1", FtpThread, 0, 1640);
00535 
00536     /* Main server thread. */
00537     for (;;) {
00538 #ifdef MY_WINSNAME
00539         NutWinsNameQuery(MY_WINSNAME, confnet.cdn_ip_addr);
00540 #endif
00541         FtpService();
00542     }
00543     return 0;
00544 }