Hi,
On Wed, 2015-09-30 at 22:08 -0700, Naveen Singh wrote:
> From: Naveen Singh <[email protected]>
>
> Current NTP code is written with an assumption that timeserver is
> always an IPv4 address. If there is an IPv6 timeserver then the socket
> operation would fail with error as Permission denied (13). This change in
> ntp.c ensures that code works fine with both IPv4 and IPv6 address.
> ---
> src/ntp.c | 129
> ++++++++++++++++++++++++++++++++++++++++++++++++--------------
> 1 file changed, 101 insertions(+), 28 deletions(-)
This was patch version 2, so the next one is version 3. Add the version
to the Subject: line as [PATCH v3], this can be done with
'git ... --subject-prefix="PATCH v3" ...'.
> diff --git a/src/ntp.c b/src/ntp.c
> index 2c313a4..a55365d 100644
> --- a/src/ntp.c
> +++ b/src/ntp.c
> @@ -18,7 +18,6 @@
> * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
> USA
> *
> */
> -
> #ifdef HAVE_CONFIG_H
> #include <config.h>
> #endif
> @@ -34,6 +33,7 @@
> #include <netinet/in.h>
> #include <netinet/ip.h>
> #include <arpa/inet.h>
> +#include <netdb.h>
>
> #include <glib.h>
>
> @@ -117,12 +117,12 @@ static struct timespec mtx_time;
> static int transmit_fd = 0;
>
> static char *timeserver = NULL;
> -static struct sockaddr_in timeserver_addr;
> +static struct sockaddr_storage timeserver_addr;
> static gint poll_id = 0;
> static gint timeout_id = 0;
> static guint retries = 0;
>
> -static void send_packet(int fd, const char *server, uint32_t timeout);
> +static void send_packet(int fd, const char *server, uint32_t timeout, int
> family);
>
> static void next_server(void)
> {
> @@ -143,17 +143,21 @@ static gboolean send_timeout(gpointer user_data)
> if (retries++ == NTP_SEND_RETRIES)
> next_server();
> else
> - send_packet(transmit_fd, timeserver, timeout << 1);
> + send_packet(transmit_fd, timeserver, timeout << 1,
> timeserver_addr.ss_family);
>
> return FALSE;
> }
>
> -static void send_packet(int fd, const char *server, uint32_t timeout)
> +static void send_packet(int fd, const char *server, uint32_t timeout, int
> family)
Instead of supplying a char *server, why don't we give a struct
sockaddr* instead? What's the purpose of carrying the server
"name" (nah, the IP address as a string), if it is already resolved in
the calling function? BTW, it is available as timeserver_addr, so do we
really need any of this information here?
> {
> struct ntp_msg msg;
> - struct sockaddr_in addr;
> + struct sockaddr_in in4addr;
> + struct sockaddr_in6 in6addr;
> + struct sockaddr * addr;
> struct timeval transmit_timeval;
> ssize_t len;
> + int size;
> + unsigned char * addrptr;
>
> /*
> * At some point, we could specify the actual system precision with:
> @@ -168,10 +172,29 @@ static void send_packet(int fd, const char *server,
> uint32_t timeout)
> msg.poll = 10; // max
> msg.precision = NTP_PRECISION_S;
>
> - memset(&addr, 0, sizeof(addr));
> - addr.sin_family = AF_INET;
> - addr.sin_port = htons(123);
> - addr.sin_addr.s_addr = inet_addr(server);
> + if (family == AF_INET) {
> + memset(&in4addr, 0, sizeof(in4addr));
> + in4addr.sin_family = AF_INET;
> + in4addr.sin_port = htons(123);
> + size = sizeof(in4addr);
> + addrptr = (unsigned char *)&in4addr.sin_addr.s_addr;
> + addr = (struct sockaddr *)&in4addr;
> + } else if (family == AF_INET6){
> + memset(&in6addr, 0, sizeof(in6addr));
> + in6addr.sin6_family = AF_INET6;
> + in6addr.sin6_port = htons(123);
> + size = sizeof(in6addr);
> + addrptr = in6addr.sin6_addr.__in6_u.__u6_addr8;
> + addr = (struct sockaddr *)&in6addr;
> + } else {
> + DBG("wrong family type");
> + return;
> + }
> + if (inet_pton(family, server, addrptr) == 0)
> + {
> + DBG("cannot convert ip address string");
> + return;
> + }
The above is unnecessary. You already have timeserver_addr set or the
function gets a sockaddr * to use.
> gettimeofday(&transmit_timeval, NULL);
> clock_gettime(CLOCK_MONOTONIC, &mtx_time);
> @@ -180,7 +203,7 @@ static void send_packet(int fd, const char *server,
> uint32_t timeout)
> msg.xmttime.fraction = htonl(transmit_timeval.tv_usec * 1000);
>
> len = sendto(fd, &msg, sizeof(msg), MSG_DONTWAIT,
> - &addr, sizeof(addr));
> + addr, size);
Use the stored timeserver_addr or supplied sockaddr * family information
to set the size to either sizeof(struct sockaddr_in) or sizeof(struct
sockaddr_in6).
> if (len < 0) {
> connman_error("Time request for server %s failed (%d/%s)",
> server, errno, strerror(errno));
> @@ -213,7 +236,7 @@ static gboolean next_poll(gpointer user_data)
> if (!timeserver || transmit_fd == 0)
> return FALSE;
>
> - send_packet(transmit_fd, timeserver, NTP_SEND_TIMEOUT);
> + send_packet(transmit_fd, timeserver, NTP_SEND_TIMEOUT,
> timeserver_addr.ss_family);
>
> return FALSE;
> }
> @@ -363,7 +386,7 @@ static gboolean received_data(GIOChannel *channel,
> GIOCondition condition,
> gpointer user_data)
> {
> unsigned char buf[128];
> - struct sockaddr_in sender_addr;
> + struct sockaddr_storage sender_addr;
> struct msghdr msg;
> struct iovec iov;
> struct cmsghdr *cmsg;
> @@ -396,9 +419,18 @@ static gboolean received_data(GIOChannel *channel,
> GIOCondition condition,
> if (len < 0)
> return TRUE;
>
> - if (timeserver_addr.sin_addr.s_addr != sender_addr.sin_addr.s_addr)
> - /* only accept messages from the timeserver */
> + if (sender_addr.ss_family == AF_INET) {
> + if (((struct sockaddr_in *)×erver_addr)->sin_addr.s_addr
> != ((struct sockaddr_in *)&sender_addr)->sin_addr.s_addr)
> + return TRUE;
> + } else if(sender_addr.ss_family == AF_INET6) {
> + if (memcmp(((struct sockaddr_in6
> *)×erver_addr)->sin6_addr.__in6_u.__u6_addr8,
> + ((struct sockaddr_in6
> *)&sender_addr)->sin6_addr.__in6_u.__u6_addr8,
> + 16) != 0)
Use memcmp for both of these. First check address families, if they
match the size can be deduced (for example as above) and have only a
single memcmp to do the matching.
> + return TRUE;
> + } else {
> + connman_error("Not a valid family type");
> return TRUE;
> + }
>
> tv = NULL;
> clock_gettime(CLOCK_MONOTONIC, &mrx_time);
> @@ -422,36 +454,78 @@ static gboolean received_data(GIOChannel *channel,
> GIOCondition condition,
> static void start_ntp(char *server)
> {
> GIOChannel *channel;
> - struct sockaddr_in addr;
> + struct sockaddr * addr;
> + struct sockaddr_in in4addr;
> + struct sockaddr_in6 in6addr;
> + struct addrinfo hint;
> + struct addrinfo *info;
> + int family;
> int tos = IPTOS_LOWDELAY, timestamp = 1;
> + int size;
> + int ret;
>
> if (!server)
> return;
>
> - DBG("server %s", server);
> + memset(&hint, 0, sizeof(hint));
> + hint.ai_family = AF_UNSPEC;
> + hint.ai_flags = AI_NUMERICHOST;
> + ret = getaddrinfo(server, NULL, &hint, &info);
> +
> + if (ret) {
> + connman_error("cannot get server info");
> + return;
> + }
> +
> + family = info->ai_family;
> + freeaddrinfo(info);
Before throwing away info, the address is already available as
->sin6_addr or ->sin_addr. See man getaddrinfo. Store the address and
the address family in timeserver_addr.
> +
> + DBG("server %s family %d", server, family);
>
> if (channel_watch > 0)
> goto send;
>
> - transmit_fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
> - if (transmit_fd < 0) {
> + transmit_fd = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
> + if (transmit_fd <= 0) {
> connman_error("Failed to open time server socket");
> return;
> }
>
> - memset(&addr, 0, sizeof(addr));
> - addr.sin_family = AF_INET;
> + if (family == AF_INET) {
> + memset(&in4addr, 0, sizeof(in4addr));
> + in4addr.sin_family = family;
> + addr = (struct sockaddr *)&in4addr;
> + size = sizeof(in4addr);
> + if (inet_pton(family, server, &(((struct sockaddr_in
> *)×erver_addr)->sin_addr.s_addr)) == 0) {
> + DBG("cannot convert ipv4 address string");
> + }
> + } else if (family == AF_INET6) {
> + memset(&in6addr, 0, sizeof(in6addr));
> + in6addr.sin6_family = family;
> + addr = (struct sockaddr *)&in6addr;
> + size = sizeof(in6addr);
> + if (inet_pton(family, server, ((struct sockaddr_in6
> *)×erver_addr)->sin6_addr.__in6_u.__u6_addr8) == 0) {
> + DBG("cannot convert ipv6 address string");
> + }
> + } else {
> + connman_error("Family not correct");
> + return;
> + }
No need for the above, look into the 'info' supplied to getaddrinfo()
above.
> - if (bind(transmit_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
> + timeserver_addr.ss_family = family;
> +
> + if (bind(transmit_fd, (struct sockaddr *) addr, size) < 0) {
> connman_error("Failed to bind time server socket");
> close(transmit_fd);
> return;
> }
>
> - if (setsockopt(transmit_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0)
> {
> - connman_error("Failed to set type of service option");
> - close(transmit_fd);
> - return;
> + if (family == AF_INET) {
> + if (setsockopt(transmit_fd, IPPROTO_IP, IP_TOS, &tos,
> sizeof(tos)) < 0) {
> + connman_error("Failed to set type of service option");
> + close(transmit_fd);
> + return;
> + }
> }
>
> if (setsockopt(transmit_fd, SOL_SOCKET, SO_TIMESTAMP, ×tamp,
> @@ -479,7 +553,7 @@ static void start_ntp(char *server)
> g_io_channel_unref(channel);
>
> send:
> - send_packet(transmit_fd, server, NTP_SEND_TIMEOUT);
> + send_packet(transmit_fd, server, NTP_SEND_TIMEOUT, family);
> }
>
> int __connman_ntp_start(char *server)
> @@ -493,7 +567,6 @@ int __connman_ntp_start(char *server)
> g_free(timeserver);
>
> timeserver = g_strdup(server);
> - timeserver_addr.sin_addr.s_addr = inet_addr(server);
>
> start_ntp(timeserver);
>
Patrik
_______________________________________________
connman mailing list
[email protected]
https://lists.connman.net/mailman/listinfo/connman