Commit 9e517fde authored by Alexander Alashkin's avatar Alexander Alashkin Committed by Cesanta Bot

Implement SNTP client

PUBLISHED_FROM=ac54bcbc81a9ee688e8b90e261172be76a9fbacd
parent 1ff61837
PROG = sntp_client
MODULE_CFLAGS = -DMG_ENABLE_SNTP
include ../examples.mk
/*
* Copyright (c) 2016 Cesanta Software Limited
* All rights reserved
*/
#include "mongoose.h"
static int s_exit_flag = 0;
static const char *s_default_server = "pool.ntp.org";
static void ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_sntp_message *sm = (struct mg_sntp_message *) ev_data;
time_t t;
(void) c;
switch (ev) {
case MG_SNTP_REPLY:
t = time(NULL);
fprintf(stdout, "Local time: %s", ctime(&t));
t = (time_t) sm->time;
fprintf(stdout, "Time from %s: %s", s_default_server, ctime(&t));
s_exit_flag = 1;
break;
case MG_SNTP_FAILED:
fprintf(stderr, "Failed to get time\n");
s_exit_flag = 1;
break;
}
}
int main() {
struct mg_mgr mgr;
struct mg_connection *c;
mg_mgr_init(&mgr, NULL);
c = mg_sntp_get_time(&mgr, ev_handler, s_default_server);
if (c == NULL) {
fprintf(stderr, "Failed to connect to %s\n", s_default_server);
return -1;
}
while (s_exit_flag == 0) {
mg_mgr_poll(&mgr, 1000);
}
mg_mgr_free(&mgr);
return 0;
}
......@@ -165,6 +165,16 @@ MG_INTERNAL int mg_get_errno(void);
MG_INTERNAL void mg_close_conn(struct mg_connection *conn);
MG_INTERNAL int mg_http_common_url_parse(const char *url, const char *schema,
const char *schema_tls, int *use_ssl,
char **user, char **pass, char **addr,
int *port_i, const char **path);
#if MG_ENABLE_SNTP
MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len,
struct mg_sntp_message *msg);
#endif
#endif /* CS_MONGOOSE_SRC_INTERNAL_H_ */
#ifdef MG_MODULE_LINES
#line 1 "common/cs_dbg.h"
......@@ -11463,6 +11473,288 @@ void mg_tun_send_frame(struct mg_connection *ws, uint32_t stream_id,
#endif /* MG_ENABLE_TUN */
#ifdef MG_MODULE_LINES
#line 1 "mongoose/src/sntp.c"
#endif
/*
* Copyright (c) 2016 Cesanta Software Limited
* All rights reserved
*/
/* Amalgamated: #include "mongoose/src/internal.h" */
/* Amalgamated: #include "mongoose/src/sntp.h" */
/* Amalgamated: #include "mongoose/src/util.h" */
#if MG_ENABLE_SNTP
#define SNTP_TIME_OFFSET 2208988800
#ifndef SNTP_TIMEOUT
#define SNTP_TIMEOUT 10
#endif
#ifndef SNTP_ATTEMPTS
#define SNTP_ATTEMPTS 3
#endif
static uint64_t mg_get_sec(uint64_t val) {
return (val & 0xFFFFFFFF00000000) >> 32;
}
static uint64_t mg_get_usec(uint64_t val) {
uint64_t tmp = (val & 0x00000000FFFFFFFF);
tmp *= 1000000;
tmp >>= 32;
return tmp;
}
static void mg_ntp_to_tv(uint64_t val, struct timeval *tv) {
uint64_t tmp;
tmp = mg_get_sec(val);
tmp -= SNTP_TIME_OFFSET;
tv->tv_sec = tmp;
tv->tv_usec = mg_get_usec(val);
}
static void mg_get_ntp_ts(const char *ntp, uint64_t *val) {
uint32_t tmp;
memcpy(&tmp, ntp, sizeof(tmp));
tmp = ntohl(tmp);
*val = (uint64_t) tmp << 32;
memcpy(&tmp, ntp + 4, sizeof(tmp));
tmp = ntohl(tmp);
*val |= tmp;
}
void mg_sntp_send_request(struct mg_connection *c) {
char buf[48] = {0};
/*
* header - 8 bit:
* LI (2 bit) - 3 (not in sync), VN (3 bit) - 4 (version),
* mode (3 bit) - 3 (client)
*/
buf[0] = (3 << 6) | (4 << 3) | 3;
/*
* Next fields should be empty in client request
* stratum, 8 bit
* poll interval, 8 bit
* rrecision, 8 bit
* root delay, 32 bit
* root dispersion, 32 bit
* ref id, 32 bit
* ref timestamp, 64 bit
* originate Timestamp, 64 bit
* receive Timestamp, 64 bit
*/
/*
* convert time to sntp format (sntp starts from 00:00:00 01.01.1900)
* according to rfc868 it is 2208988800L sec
* this information is used to correct roundtrip delay
* but if local clock is absolutely broken (and doesn't work even
* as simple timer), it is better to disable it
*/
#ifndef MG_SNMP_NO_DELAY_CORRECTION
uint32_t sec;
sec = htonl(mg_time() + SNTP_TIME_OFFSET);
memcpy(&buf[40], &sec, sizeof(sec));
#endif
mg_send(c, buf, sizeof(buf));
}
#ifndef MG_SNMP_NO_DELAY_CORRECTION
static uint64_t mg_calculate_delay(uint64_t t1, uint64_t t2, uint64_t t3) {
/* roundloop delay = (T4 - T1) - (T3 - T2) */
uint64_t d1 = ((mg_time() + SNTP_TIME_OFFSET) * 1000000) -
(mg_get_sec(t1) * 1000000 + mg_get_usec(t1));
uint64_t d2 = (mg_get_sec(t3) * 1000000 + mg_get_usec(t3)) -
(mg_get_sec(t2) * 1000000 + mg_get_usec(t2));
return (d1 > d2) ? d1 - d2 : 0;
}
#endif
MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len,
struct mg_sntp_message *msg) {
uint8_t hdr;
uint64_t orig_ts_T1, recv_ts_T2, trsm_ts_T3, delay = 0;
int mode;
struct timeval tv;
(void) orig_ts_T1;
(void) recv_ts_T2;
if (len < 48) {
return -1;
}
hdr = buf[0];
if ((hdr & 0x38) >> 3 != 4) {
/* Wrong version */
return -1;
}
mode = hdr & 0x7;
if (mode != 4 && mode != 5) {
/* Not a server reply */
return -1;
}
memset(msg, 0, sizeof(*msg));
msg->kiss_of_death = (buf[1] == 0); /* Server asks to not send requests */
mg_get_ntp_ts(&buf[40], &trsm_ts_T3);
#ifndef MG_SNMP_NO_DELAY_CORRECTION
mg_get_ntp_ts(&buf[24], &orig_ts_T1);
mg_get_ntp_ts(&buf[32], &recv_ts_T2);
delay = mg_calculate_delay(orig_ts_T1, recv_ts_T2, trsm_ts_T3);
#endif
mg_ntp_to_tv(trsm_ts_T3, &tv);
msg->time = (double) tv.tv_sec + (((double) tv.tv_usec + delay) / 1000000.0);
return 0;
}
static void mg_sntp_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mbuf *io = &c->recv_mbuf;
struct mg_sntp_message msg;
c->handler(c, ev, ev_data);
switch (ev) {
case MG_EV_RECV: {
if (mg_sntp_parse_reply(io->buf, io->len, &msg) < 0) {
DBG(("Invalid SNTP packet received (%d)", (int) io->len));
c->handler(c, MG_SNTP_MALFORMED_REPLY, NULL);
} else {
c->handler(c, MG_SNTP_REPLY, (void *) &msg);
}
mbuf_remove(io, io->len);
break;
}
}
}
int mg_set_protocol_sntp(struct mg_connection *c) {
if ((c->flags & MG_F_UDP) == 0) {
return -1;
}
c->proto_handler = mg_sntp_handler;
return 0;
}
struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr,
mg_event_handler_t event_handler,
const char *sntp_server_name) {
struct mg_connection *c = NULL;
char url[100], *p_url = url;
const char *proto = "", *port = "", *tmp;
/* If port is not specified, use default (123) */
tmp = strchr(sntp_server_name, ':');
if (tmp != NULL && *(tmp + 1) == '/') {
tmp = strchr(tmp + 1, ':');
}
if (tmp == NULL) {
port = ":123";
}
/* Add udp:// if needed */
if (strncmp(sntp_server_name, "udp://", 6) != 0) {
proto = "udp://";
}
mg_asprintf(&p_url, sizeof(url), "%s%s%s", proto, sntp_server_name, port);
c = mg_connect(mgr, p_url, event_handler);
if (c == NULL) {
goto cleanup;
}
mg_set_protocol_sntp(c);
cleanup:
if (p_url != url) {
MG_FREE(p_url);
}
return c;
}
struct sntp_data {
mg_event_handler_t hander;
int count;
};
static void mg_sntp_util_ev_handler(struct mg_connection *c, int ev,
void *ev_data) {
struct sntp_data *sd = (struct sntp_data *) c->user_data;
switch (ev) {
case MG_EV_CONNECT:
if (*(int *) ev_data != 0) {
mg_call(c, sd->hander, MG_SNTP_FAILED, NULL);
break;
}
/* fallthrough */
case MG_EV_TIMER:
if (sd->count <= SNTP_ATTEMPTS) {
mg_sntp_send_request(c);
mg_set_timer(c, mg_time() + 10);
sd->count++;
} else {
mg_call(c, sd->hander, MG_SNTP_FAILED, NULL);
c->flags |= MG_F_CLOSE_IMMEDIATELY;
}
break;
case MG_SNTP_MALFORMED_REPLY:
mg_call(c, sd->hander, MG_SNTP_FAILED, NULL);
c->flags |= MG_F_CLOSE_IMMEDIATELY;
break;
case MG_SNTP_REPLY:
mg_call(c, sd->hander, MG_SNTP_REPLY, ev_data);
c->flags |= MG_F_CLOSE_IMMEDIATELY;
break;
case MG_EV_CLOSE:
MG_FREE(c->user_data);
c->user_data = NULL;
break;
}
}
struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr,
mg_event_handler_t event_handler,
const char *sntp_server_name) {
struct mg_connection *c;
struct sntp_data *sd = (struct sntp_data *) MG_CALLOC(1, sizeof(*sd));
if (sd == NULL) {
return NULL;
}
c = mg_sntp_connect(mgr, mg_sntp_util_ev_handler, sntp_server_name);
if (c == NULL) {
MG_FREE(sd);
return NULL;
}
sd->hander = event_handler;
c->user_data = sd;
return c;
}
#endif /* MG_ENABLE_SNTP */
#ifdef MG_MODULE_LINES
#line 1 "common/platforms/cc3200/cc3200_libc.c"
#endif
/*
......
......@@ -2812,6 +2812,10 @@ struct { \
#define MG_ENABLE_TUN MG_ENABLE_HTTP_WEBSOCKET
#endif
#ifndef MG_ENABLE_SNTP
#define MG_ENABLE_SNTP 0
#endif
#endif /* CS_MONGOOSE_SRC_FEATURES_H_ */
#ifdef MG_MODULE_LINES
#line 1 "mongoose/src/net_if.h"
......@@ -5591,3 +5595,59 @@ uint32_t mg_coap_compose(struct mg_coap_message *cm, struct mbuf *io);
#endif /* MG_ENABLE_COAP */
#endif /* CS_MONGOOSE_SRC_COAP_H_ */
#ifdef MG_MODULE_LINES
#line 1 "mongoose/src/sntp.h"
#endif
/*
* Copyright (c) 2016 Cesanta Software Limited
* All rights reserved
*/
#ifndef CS_MONGOOSE_SRC_SNTP_H_
#define CS_MONGOOSE_SRC_SNTP_H_
#if MG_ENABLE_SNTP
#define MG_SNTP_EVENT_BASE 500
/*
* Received reply from time server. Event handler parameter contains
* pointer to mg_sntp_message structure
*/
#define MG_SNTP_REPLY (MG_SNTP_EVENT_BASE + 1)
/* Received malformed SNTP packet */
#define MG_SNTP_MALFORMED_REPLY (MG_SNTP_EVENT_BASE + 2)
/* Failed to get time from server (timeout etc) */
#define MG_SNTP_FAILED (MG_SNTP_EVENT_BASE + 3)
struct mg_sntp_message {
/* if server sends this flags, user should not send requests to it */
int kiss_of_death;
/* usual mg_time */
double time;
};
/* Establishes connection to given sntp server */
struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr,
mg_event_handler_t event_handler,
const char *sntp_server_name);
/* Sends time request to given connection */
void mg_sntp_send_request(struct mg_connection *c);
/*
* Helper function
* Establishes connection to time server, tries to send request
* repeats sending SNTP_ATTEMPTS times every SNTP_TIMEOUT sec
* (if needed)
* See sntp_client example
*/
struct mg_connection *mg_sntp_get_time(struct mg_mgr *mgr,
mg_event_handler_t event_handler,
const char *sntp_server_name);
#endif
#endif /* CS_MONGOOSE_SRC_SNTP_H_ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment