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

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