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

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