mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-10 16:54:16 +07:00
Implement SNTP client
PUBLISHED_FROM=ac54bcbc81a9ee688e8b90e261172be76a9fbacd
This commit is contained in:
committed by
Cesanta Bot
parent
1ff61837ac
commit
9e517fde00
@@ -0,0 +1,3 @@
|
||||
PROG = sntp_client
|
||||
MODULE_CFLAGS = -DMG_ENABLE_SNTP
|
||||
include ../examples.mk
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
+292
@@ -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
|
||||
/*
|
||||
|
||||
+60
@@ -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_ */
|
||||
|
||||
Reference in New Issue
Block a user