1/*
2 * socktoa.c	socktoa(), sockporttoa(), and sock_hash()
3 */
4
5#ifdef HAVE_CONFIG_H
6#include <config.h>
7#endif
8
9#include <sys/types.h>
10#ifdef HAVE_SYS_SOCKET_H
11#include <sys/socket.h>
12#endif
13#ifdef HAVE_NETINET_IN_H
14#include <netinet/in.h>
15#endif
16
17#include <stdio.h>
18#include <arpa/inet.h>
19#include <isc/result.h>
20#include <isc/netaddr.h>
21#include <isc/sockaddr.h>
22
23#include "ntp_fp.h"
24#include "lib_strbuf.h"
25#include "ntp_stdlib.h"
26#include "ntp.h"
27
28/*
29 * socktoa - return a numeric host name from a sockaddr_storage structure
30 */
31const char *
32socktoa(
33	const sockaddr_u *sock
34	)
35{
36	int		saved_errno;
37	char *		res;
38	char *		addr;
39	u_long		scope;
40
41	saved_errno = socket_errno();
42	LIB_GETBUF(res);
43
44	if (NULL == sock) {
45		strlcpy(res, "(null)", LIB_BUFLENGTH);
46	} else {
47		switch(AF(sock)) {
48
49		case AF_INET:
50		case AF_UNSPEC:
51			inet_ntop(AF_INET, PSOCK_ADDR4(sock), res,
52				  LIB_BUFLENGTH);
53			break;
54
55		case AF_INET6:
56			inet_ntop(AF_INET6, PSOCK_ADDR6(sock), res,
57				  LIB_BUFLENGTH);
58			scope = SCOPE_VAR(sock);
59			if (0 != scope && !strchr(res, '%')) {
60				addr = res;
61				LIB_GETBUF(res);
62				snprintf(res, LIB_BUFLENGTH, "%s%%%lu",
63					 addr, scope);
64				res[LIB_BUFLENGTH - 1] = '\0';
65			}
66			break;
67
68		default:
69			snprintf(res, LIB_BUFLENGTH,
70				 "(socktoa unknown family %d)",
71				 AF(sock));
72		}
73	}
74	errno = saved_errno;
75
76	return res;
77}
78
79
80const char *
81sockporttoa(
82	const sockaddr_u *sock
83	)
84{
85	int		saved_errno;
86	const char *	atext;
87	char *		buf;
88
89	saved_errno = socket_errno();
90	atext = socktoa(sock);
91	LIB_GETBUF(buf);
92	snprintf(buf, LIB_BUFLENGTH,
93		 (IS_IPV6(sock))
94		     ? "[%s]:%hu"
95		     : "%s:%hu",
96		 atext, SRCPORT(sock));
97	errno = saved_errno;
98
99	return buf;
100}
101
102
103/*
104 * sock_hash - hash a sockaddr_u structure
105 */
106u_short
107sock_hash(
108	const sockaddr_u *addr
109	)
110{
111	u_int hashVal;
112	u_int j;
113	size_t len;
114	const u_char *pch;
115
116	hashVal = 0;
117	len = 0;
118
119	/*
120	 * We can't just hash the whole thing because there are hidden
121	 * fields in sockaddr_in6 that might be filled in by recvfrom(),
122	 * so just use the family, port and address.
123	 */
124	pch = (const void *)&AF(addr);
125	hashVal = 37 * hashVal + *pch;
126	if (sizeof(AF(addr)) > 1) {
127		pch++;
128		hashVal = 37 * hashVal + *pch;
129	}
130	switch(AF(addr)) {
131	case AF_INET:
132		pch = (const void *)&SOCK_ADDR4(addr);
133		len = sizeof(SOCK_ADDR4(addr));
134		break;
135
136	case AF_INET6:
137		pch = (const void *)&SOCK_ADDR6(addr);
138		len = sizeof(SOCK_ADDR6(addr));
139		break;
140	}
141
142	for (j = 0; j < len ; j++)
143		hashVal = 37 * hashVal + pch[j];
144
145	return (u_short)(hashVal & USHRT_MAX);
146}
147
148
149int
150sockaddr_masktoprefixlen(
151	const sockaddr_u *	psa
152	)
153{
154	isc_netaddr_t	isc_na;
155	isc_sockaddr_t	isc_sa;
156	u_int		pfxlen;
157	isc_result_t	result;
158	int		rc;
159
160	ZERO(isc_sa);
161	memcpy(&isc_sa.type, psa,
162	       min(sizeof(isc_sa.type), sizeof(*psa)));
163	isc_netaddr_fromsockaddr(&isc_na, &isc_sa);
164	result = isc_netaddr_masktoprefixlen(&isc_na, &pfxlen);
165	rc = (ISC_R_SUCCESS == result)
166		 ? (int)pfxlen
167		 : -1;
168
169	return rc;
170}
171