ftpd/ftpserv.c

FTP server sample.

This application requires the PNUT file system, which is not available on Ethernut 1.x or Charon II.

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

© 2000-2006 by egnite Software GmbH - visit http://www.ethernut.de/