ftpserv.c

Go to the documentation of this file.
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 
00065 #include <stdio.h>
00066 #include <fcntl.h>
00067 #include <io.h>
00068 
00069 #include <dev/board.h>
00070 #include <dev/lanc111.h>
00071 #include <dev/debug.h>
00072 #include <dev/pnut.h>
00073 #include <dev/nplmmc.h>
00074 #include <dev/sbimmc.h>
00075 #include <dev/spimmc_at91.h>
00076 #include <dev/at91_mci.h>
00077 #include <dev/x12rtc.h>
00078 #include <fs/phatfs.h>
00079 
00080 #include <sys/confnet.h>
00081 #include <sys/version.h>
00082 #include <sys/heap.h>
00083 #include <sys/thread.h>
00084 #include <sys/socket.h>
00085 
00086 #include <arpa/inet.h>
00087 #include <netinet/tcp.h>
00088 #include <net/route.h>
00089 #include <pro/dhcp.h>
00090 #include <pro/ftpd.h>
00091 #include <pro/wins.h>
00092 #include <pro/sntp.h>
00093 #include <pro/discover.h>
00094 
00095 /* Determine the compiler. */
00096 #if defined(__IMAGECRAFT__)
00097 #if defined(__AVR__)
00098 #define CC_STRING   "ICCAVR"
00099 #else
00100 #define CC_STRING   "ICC"
00101 #endif
00102 #elif defined(__GNUC__)
00103 #if defined(__AVR__)
00104 #define CC_STRING   "AVRGCC"
00105 #elif defined(__arm__)
00106 #define CC_STRING   "ARMGCC"
00107 #else
00108 #define CC_STRING   "GCC"
00109 #endif
00110 #else
00111 #define CC_STRING   "Compiler unknown"
00112 #endif
00113 
00123 /* 
00124  * Baudrate for debug output. 
00125  */
00126 #ifndef DBG_BAUDRATE
00127 #define DBG_BAUDRATE 115200
00128 #endif
00129 
00130 /*
00131  * Wether we should use DHCP.
00132  */
00133 #define USE_DHCP
00134 
00135 /*
00136  * Wether we should run a discovery responder.
00137  */
00138 #if defined(__arm__)
00139 #define USE_DISCOVERY
00140 #endif
00141 
00142 /* 
00143  * Unique MAC address of the Ethernut Board. 
00144  *
00145  * Ignored if EEPROM contains a valid configuration.
00146  */
00147 #define MY_MAC { 0x00, 0x06, 0x98, 0x30, 0x00, 0x35 }
00148 
00149 /* 
00150  * Unique IP address of the Ethernut Board. 
00151  *
00152  * Ignored if DHCP is used. 
00153  */
00154 #define MY_IPADDR "192.168.192.35"
00155 
00156 /* 
00157  * IP network mask of the Ethernut Board.
00158  *
00159  * Ignored if DHCP is used. 
00160  */
00161 #define MY_IPMASK "255.255.255.0"
00162 
00163 /* 
00164  * Gateway IP address for the Ethernut Board.
00165  *
00166  * Ignored if DHCP is used. 
00167  */
00168 #define MY_IPGATE "192.168.192.1"
00169 
00170 /* 
00171  * NetBIOS name.
00172  *
00173  * Use a symbolic name with Win32 Explorer.
00174  */
00175 //#define MY_WINSNAME "ETHERNUT"
00176 
00177 /*
00178  * FTP port number.
00179  */
00180 #define FTP_PORTNUM 21
00181 
00182 /*
00183  * FTP timeout.
00184  *
00185  * The server will terminate the session, if no new command is received
00186  * within the specified number of milliseconds.
00187  */
00188 #define FTPD_TIMEOUT 600000
00189 
00190 /*
00191  * TCP buffer size.
00192  */
00193 #define TCPIP_BUFSIZ 5840
00194 
00195 /*
00196  * Maximum segment size. 
00197  *
00198  * Choose 536 up to 1460. Note, that segment sizes above 536 may result 
00199  * in fragmented packets. Remember, that Ethernut doesn't support TCP 
00200  * fragmentation.
00201  */
00202 #define TCPIP_MSS 1460
00203 
00204 #if defined(ETHERNUT3)
00205 
00206 /* Ethernut 3 file system. */
00207 #define FSDEV       devPhat0
00208 #define FSDEV_NAME  "PHAT0" 
00209 
00210 /* Ethernut 3 block device interface. */
00211 #define BLKDEV      devNplMmc0
00212 #define BLKDEV_NAME "MMC0"
00213 
00214 #elif defined(AT91SAM7X_EK)
00215 
00216 /* SAM7X-EK file system. */
00217 #define FSDEV       devPhat0
00218 #define FSDEV_NAME  "PHAT0" 
00219 
00220 /* SAM7X-EK block device interface. */
00221 #define BLKDEV      devAt91SpiMmc0
00222 #define BLKDEV_NAME "MMC0"
00223 
00224 #elif defined(AT91SAM9260_EK)
00225 
00226 /* SAM9260-EK file system. */
00227 #define FSDEV       devPhat0
00228 #define FSDEV_NAME  "PHAT0" 
00229 
00230 /* SAM9260-EK block device interface. */
00231 #define BLKDEV      devAt91Mci0
00232 #define BLKDEV_NAME "MCI0"
00233 
00234 #elif defined(ETHERNUT2)
00235 
00236 /*
00237  * Ethernut 2 File system
00238  */
00239 #define FSDEV       devPnut
00240 #define FSDEV_NAME  "PNUT" 
00241 
00242 #else
00243 
00244 #define FSDEV_NAME  "NONE" 
00245 
00246 #endif
00247 
00249 #define MYTZ    -1
00250 
00252 #define MYTIMED "130.149.17.21"
00253 
00254 #ifdef ETHERNUT3
00255 
00256 #define X12RTC_DEV
00257 #endif
00258 
00259 /*
00260  * FTP service.
00261  *
00262  * This function waits for client connect, processes the FTP request 
00263  * and disconnects. Nut/Net doesn't support a server backlog. If one 
00264  * client has established a connection, further connect attempts will 
00265  * be rejected. 
00266  *
00267  * Some FTP clients, like the Win32 Explorer, open more than one 
00268  * connection for background processing. So we run this routine by
00269  * several threads.
00270  */
00271 void FtpService(void)
00272 {
00273     TCPSOCKET *sock;
00274 
00275     /*
00276      * Create a socket.
00277      */
00278     if ((sock = NutTcpCreateSocket()) != 0) {
00279 
00280         /* 
00281          * Set specified socket options. 
00282          */
00283 #ifdef TCPIP_MSS
00284         {
00285             u_short mss = TCPIP_MSS;
00286             NutTcpSetSockOpt(sock, TCP_MAXSEG, &mss, sizeof(mss));
00287         }
00288 #endif
00289 #ifdef FTPD_TIMEOUT
00290         {
00291             u_long tmo = FTPD_TIMEOUT;
00292             NutTcpSetSockOpt(sock, SO_RCVTIMEO, &tmo, sizeof(tmo));
00293         }
00294 #endif
00295 #ifdef TCPIP_BUFSIZ
00296         {
00297             u_short siz = TCPIP_BUFSIZ;
00298             NutTcpSetSockOpt(sock, SO_RCVBUF, &siz, sizeof(siz));
00299         }
00300 #endif
00301 
00302         /*
00303          * Listen on our port. If we return, we got a client.
00304          */
00305         printf("\nWaiting for an FTP client...");
00306         if (NutTcpAccept(sock, FTP_PORTNUM) == 0) {
00307             printf("%s connected, %u bytes free\n", inet_ntoa(sock->so_remote_addr), (u_int)NutHeapAvailable());
00308             NutFtpServerSession(sock);
00309             printf("%s disconnected, %u bytes free\n", inet_ntoa(sock->so_remote_addr), (u_int)NutHeapAvailable());
00310         } else {
00311             puts("Accept failed");
00312         }
00313 
00314         /*
00315          * Close our socket.
00316          */
00317         NutTcpCloseSocket(sock);
00318     }
00319 }
00320 
00321 /*
00322  * FTP service thread.
00323  */
00324 THREAD(FtpThread, arg)
00325 {
00326     /* Loop endless for connections. */
00327     for (;;) {
00328         FtpService();
00329     }
00330 }
00331 
00332 /*
00333  * Assign stdout to the UART device.
00334  */
00335 void InitDebugDevice(void)
00336 {
00337     u_long baud = DBG_BAUDRATE;
00338 
00339     NutRegisterDevice(&DEV_DEBUG, 0, 0);
00340     freopen(DEV_DEBUG_NAME, "w", stdout);
00341     _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
00342 }
00343 
00344 /*
00345  * Setup the ethernet device. Try DHCP first. If this is
00346  * the first time boot with empty EEPROM and no DHCP server
00347  * was found, use hardcoded values.
00348  */
00349 int InitEthernetDevice(void)
00350 {
00351     u_long ip_addr = inet_addr(MY_IPADDR);
00352     u_long ip_mask = inet_addr(MY_IPMASK);
00353     u_long ip_gate = inet_addr(MY_IPGATE);
00354     u_char mac[6] = MY_MAC;
00355 
00356     if (NutRegisterDevice(&DEV_ETHER, 0x8300, 5)) {
00357         puts("No Ethernet Device");
00358         return -1;
00359     }
00360 
00361     printf("Configure %s...", DEV_ETHER_NAME);
00362 #ifdef USE_DHCP
00363     if (NutDhcpIfConfig(DEV_ETHER_NAME, 0, 60000) == 0) {
00364         puts("OK");
00365         return 0;
00366     }
00367     printf("initial boot...");
00368     if (NutDhcpIfConfig(DEV_ETHER_NAME, mac, 60000) == 0) {
00369         puts("OK");
00370         return 0;
00371     }
00372 #endif
00373     printf("No DHCP...");
00374     NutNetIfConfig(DEV_ETHER_NAME, mac, ip_addr, ip_mask);
00375     /* Without DHCP we had to set the default gateway manually.*/
00376     if(ip_gate) {
00377         printf("hard coded gate...");
00378         NutIpRouteAdd(0, 0, ip_gate, &DEV_ETHER);
00379     }
00380     puts("OK");
00381 
00382     return 0;
00383 }
00384 
00385 /*
00386  * Query a time server and optionally update the hardware clock.
00387  */
00388 static int QueryTimeServer(void)
00389 {
00390     int rc = -1;
00391 
00392 #ifdef MYTIMED
00393     {
00394         time_t now;
00395         u_long timeserver = inet_addr(MYTIMED);
00396 
00397         /* Query network time service and set the system time. */
00398         printf("Query time from %s...", MYTIMED);
00399         if(NutSNTPGetTime(&timeserver, &now) == 0) {
00400             puts("OK");
00401             rc = 0;
00402             stime(&now);
00403 #ifdef X12RTC_DEV
00404             /* If RTC hardware is available, update it. */
00405             {
00406                 struct _tm *gmt = gmtime(&now);
00407 
00408                 if (X12RtcSetClock(gmt)) {
00409                     puts("RTC update failed");
00410                 }
00411             }
00412 #endif
00413         }
00414         else {
00415             puts("failed");
00416         }
00417     }
00418 #endif
00419 
00420     return rc;
00421 }
00422 
00423 /*
00424  * Try to get initial date and time from the hardware clock or a time server.
00425  */
00426 static int InitTimeAndDate(void)
00427 {
00428     int rc = -1;
00429 
00430     /* Set the local time zone. */
00431     _timezone = MYTZ * 60L * 60L;
00432 
00433 #ifdef X12RTC_DEV
00434     /* Query RTC hardware if available. */
00435     {
00436         u_long rs;
00437 
00438         /* Query the status. If it fails, we do not have an RTC. */
00439         if (X12RtcGetStatus(&rs)) {
00440             puts("No hardware RTC");
00441             rc = QueryTimeServer();
00442         }
00443         else {
00444             /* RTC hardware seems to be available. Check for power failure. */
00445             //rs = RTC_STATUS_PF;
00446             if ((rs & RTC_STATUS_PF) == RTC_STATUS_PF) {
00447                 puts("RTC power fail detected");
00448                 rc = QueryTimeServer();
00449             }
00450 
00451             /* RTC hardware status is fine, update our system clock. */
00452             else {
00453                 struct _tm gmt;
00454 
00455                 /* Assume that RTC is running at GMT. */
00456                 if (X12RtcGetClock(&gmt) == 0) {
00457                     time_t now = _mkgmtime(&gmt);
00458 
00459                     if (now != -1) {
00460                         stime(&now);
00461                         rc = 0;
00462                     }
00463                 }
00464             }
00465         }
00466     }
00467 #else
00468     /* No hardware RTC, query the time server if available. */
00469     rc = QueryTimeServer();
00470 #endif
00471     return rc;
00472 }
00473 
00474 /*
00475  * Main application routine. 
00476  *
00477  * Nut/OS automatically calls this entry after initialization.
00478  */
00479 int main(void)
00480 {
00481     int volid;
00482     u_long ipgate;
00483 
00484     /* Initialize a debug output device and print a banner. */
00485     InitDebugDevice();
00486     printf("\n\nFTP Server Sample - Nut/OS %s - " CC_STRING "\n", NutVersionString());
00487 
00488     /* Initialize the Ethernet device and print our IP address. */
00489     if (InitEthernetDevice()) {
00490         for(;;);
00491     }
00492     printf("IP Addr: %s\n", inet_ntoa(confnet.cdn_ip_addr));
00493     printf("IP Mask: %s\n", inet_ntoa(confnet.cdn_ip_mask));
00494     NutIpRouteQuery(0, &ipgate);
00495     printf("IP Gate: %s\n", inet_ntoa(ipgate));
00496 
00497 #ifdef USE_DISCOVERY
00498     /* Register a discovery responder. */
00499     printf("Start Responder...");
00500     if (NutRegisterDiscovery((u_long)-1, 0, DISF_INITAL_ANN)) {
00501         puts("failed");
00502     }
00503     else {
00504         puts("OK");
00505     }
00506 #endif
00507 
00508     /* Initialize system clock and calendar. */
00509     if (InitTimeAndDate() == 0) {
00510         time_t now = time(0);
00511         struct _tm *lot = localtime(&now);
00512         printf("Date: %02u.%02u.%u\n", lot->tm_mday, lot->tm_mon + 1, 1900 + lot->tm_year);
00513         printf("Time: %02u:%02u:%02u\n", lot->tm_hour, lot->tm_min, lot->tm_sec);
00514     }
00515 
00516 #ifdef FSDEV
00517     /* Initialize the file system. */
00518     printf("Register file system...");
00519     if (NutRegisterDevice(&FSDEV, 0, 0)) {
00520         puts("failed");
00521         for (;;);
00522     }
00523     puts("OK");
00524 #endif
00525 
00526 #ifdef BLKDEV
00527     /* Register block device. */
00528     printf("Register block device...");
00529     if (NutRegisterDevice(&BLKDEV, 0, 0)) {
00530         puts("failed");
00531         for (;;);
00532     }
00533     puts("OK");
00534 
00535     /* Mount partition. */
00536     printf("Mounting partition...");
00537     if ((volid = _open(BLKDEV_NAME ":1/" FSDEV_NAME, _O_RDWR | _O_BINARY)) == -1) {
00538         puts("failed");
00539         for (;;);
00540     }
00541     puts("OK");
00542 #else
00543     volid = 0;
00544 #endif
00545 
00546     /* Register root path. */
00547     printf("Register FTP root...");
00548     if (NutRegisterFtpRoot(FSDEV_NAME ":")) {
00549         puts("failed");
00550         for (;;);
00551     }
00552     puts("OK");
00553 
00554     /* Start two additional server threads. */
00555     NutThreadCreate("ftpd0", FtpThread, 0, 1640);
00556     NutThreadCreate("ftpd1", FtpThread, 0, 1640);
00557 
00558     /* Main server thread. */
00559     for (;;) {
00560 #ifdef MY_WINSNAME
00561         NutWinsNameQuery(MY_WINSNAME, confnet.cdn_ip_addr);
00562 #endif
00563         FtpService();
00564     }
00565 }

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