sntp.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2003 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  * Thanks to Lars H. Andersson, who submitted the first idea of this simple function
00033  */
00034 
00035 
00071 #include <cfg/sntp.h>
00072 
00073 #include <pro/sntp.h>
00074 #include <sys/socket.h>
00075 #include <sys/heap.h>
00076 #include <string.h>
00077 #include "../crt/ctime.h"
00078 #include <stdio.h>
00079 #include <sys/thread.h>
00080 #include <sys/timer.h>
00081 #include <sys/types.h>
00082 #include <netinet/in.h>
00083 
00088 
00089 #ifndef NUT_THREAD_SNTPSTACK
00090 #define NUT_THREAD_SNTPSTACK    256
00091 #endif
00092 
00093 typedef struct _sntpframe sntpframe;
00094 struct _sntpframe {
00095     u_char mode;
00096     u_char stratum;
00097     u_char poll;
00098     u_char precision;
00099     u_long root_delay;
00100     u_long root_dispersion;
00101     u_long reference_identifier;
00102     u_long reference_ts_sec;
00103     u_long reference_ts_frac;
00104     u_long originate_ts_sec;
00105     u_long originate_ts_frac;
00106     u_long receive_ts_sec;
00107     u_long receive_ts_frac;
00108     u_long transmit_ts_sec;
00109     u_long transmit_ts_frac;
00110 };
00111 
00112 
00113 #define NTP_PORT    123
00114 #define SNTP_PORT NTP_PORT
00115 
00116 struct SNTP_resync_args {
00117     u_long server_addr;
00118     u_long interval;
00119 };
00120 
00121 THREAD(SNTP_resync, arg)
00122 {
00123     u_long server_addr = ((struct SNTP_resync_args *) arg)->server_addr;
00124     u_long interval = ((struct SNTP_resync_args *) arg)->interval;
00125     u_long cur_server_addr = server_addr;
00126     int retry = 0;
00127     time_t t;
00128 
00129     NutHeapFree(arg);
00130 
00131     NutThreadSetPriority(63);
00132     for (;;) {
00133         if (NutSNTPGetTime(&cur_server_addr, &t)) {     /* if any error retry */
00134             if (cur_server_addr != server_addr && server_addr == 0xFFFFFFFF) {
00135                 cur_server_addr = server_addr;
00136                 continue;
00137             }
00138 
00139             if (retry++ >= 3) { /* if numer of retries >= 3 wait 30 secs before next retry sequence ... */
00140                 retry = 0;
00141                 NutSleep(30000);
00142             } else              /* ... else wait 5 secs for next retry */
00143                 NutSleep(5000);
00144         } else {                /* no error */
00145             stime(&t);          /* so set the time */
00146             retry = 0;
00147             NutSleep(interval); /* and wait the interval time */
00148         }
00149     }
00150 }
00151 
00152 int NutSNTPGetTime(u_long * server_adr, time_t * t)
00153 {
00154     /*first check the pointers */
00155     u_long rec_addr;
00156     UDPSOCKET *sock = NULL;     /* the udp socket */
00157     sntpframe *data;            /* we're using the heap to save stack space */
00158     u_short port;               /* source port from incoming packet */
00159     int len;
00160     int result = -1;
00161     /* Set UDP input buffer to 256 bytes */
00162     u_short bufsize = 256;
00163 
00164 
00165     if (t == NULL)
00166         return -1;
00167     if (server_adr == NULL)
00168         return -1;
00169 
00170     if ((data = NutHeapAllocClear(sizeof(*data))) == NULL)
00171         goto error;
00172 
00173     sock = NutUdpCreateSocket(0);       /* allocate new udp socket */
00174     if (sock == NULL)
00175         goto error;
00176 
00177     NutUdpSetSockOpt(sock, SO_RCVBUF, &bufsize, sizeof(bufsize));
00178 
00179     data->mode = 0x1B;          /* LI, VN and Mode bit fields (all in u_char mode); */
00180     if (NutUdpSendTo(sock, *server_adr, SNTP_PORT, data, sizeof(*data)))        /* Send packet to server */
00181         goto error;             /* on error return -1 */
00182   retry:
00183     rec_addr = 0;
00184     len = NutUdpReceiveFrom(sock, &rec_addr, &port, data, sizeof(*data), 5000); /* Receive packet with timeout of 5s */
00185     if (len <= 0) {
00186         goto error;             /* error or timeout occured */
00187     } 
00188 
00189     if (port != SNTP_PORT || (data->mode & 0xc0) == 0xc0)       /* if source port is not SNTP_PORT or server is not in sync return */
00190     {
00191         if (*server_adr == 0xFFFFFFFF)
00192             goto retry;         /*  unusable packets will be just ignored. */
00193         else
00194             goto error;
00195     }
00196 
00197     *t = ntohl(data->transmit_ts_sec) - (70 * 365 + _LEAP_YEAR_ADJUST) * _DAY_SEC;
00198     *server_adr = rec_addr;
00199     result = 0;
00200   error:
00201     if (sock)
00202         NutUdpDestroySocket(sock);
00203     if (data)
00204         NutHeapFree(data);
00205     return result;
00206 }
00207 
00208 int NutSNTPStartThread(u_long server_addr, u_long interval)
00209 {
00210     struct SNTP_resync_args *arg = NutHeapAlloc(sizeof(struct SNTP_resync_args));
00211     if (!arg)
00212         return -1;
00213     arg->server_addr = server_addr;
00214     arg->interval = interval;
00215     if (NutThreadCreate("sntpc", SNTP_resync, arg, NUT_THREAD_SNTPSTACK))
00216         return 0;
00217     else {
00218         NutHeapFree(arg);
00219         return -1;
00220     }
00221 }
00222 

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