1 /* grecs - Gray's Extensible Configuration System
2    Copyright (C) 2007-2016 Sergey Poznyakoff
3 
4    Grecs is free software; you can redistribute it and/or modify it
5    under the terms of the GNU General Public License as published by the
6    Free Software Foundation; either version 3 of the License, or (at your
7    option) any later version.
8 
9    Grecs is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with Grecs. If not, see <http://www.gnu.org/licenses/>. */
16 
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <sys/un.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25 #include <stdlib.h>
26 #include "grecs.h"
27 
28 static void
uint32_to_bytes(unsigned char * bytes,uint32_t u)29 uint32_to_bytes (unsigned char *bytes, uint32_t u)
30 {
31 	int i;
32 
33 	for (i = 0; i < 4; i++)	{
34 		bytes[i] = u & 0xff;
35 		u >>= 8;
36 	}
37 }
38 
39 int
grecs_inaddr_to_bytes(int af,void * buf,unsigned char * bytes)40 grecs_inaddr_to_bytes(int af, void *buf, unsigned char *bytes)
41 {
42 	uint32_t u;
43 
44 	switch (af) {
45 	case AF_INET:
46 		memcpy(&u, buf, sizeof u);
47 		uint32_to_bytes(bytes, u);
48 		return 4;
49 
50 	case AF_INET6:
51 		memcpy(bytes, buf, 16);
52 		return 16;
53 	}
54 	return 0;
55 }
56 
57 int
grecs_sockaddr_to_bytes(unsigned char * bytes,struct sockaddr const * sa)58 grecs_sockaddr_to_bytes(unsigned char *bytes, struct sockaddr const *sa)
59 {
60 	switch (sa->sa_family) {
61 	case AF_INET:
62 		uint32_to_bytes(bytes,
63 				((struct sockaddr_in*)sa)->sin_addr.s_addr);
64 		return 4;
65 
66 	case AF_INET6:
67 		memcpy(bytes, &((struct sockaddr_in6*)sa)->sin6_addr, 16);
68 		return 16;
69 	}
70 	return 0;
71 }
72 
73 int
grecs_sockaddr_to_cidr(struct grecs_cidr * cidr,const struct sockaddr * sa)74 grecs_sockaddr_to_cidr(struct grecs_cidr *cidr, const struct sockaddr *sa)
75 {
76 	unsigned char address[GRECS_INADDR_BYTES];
77 	int len;
78 	int i;
79 
80 	len = grecs_sockaddr_to_bytes(address, sa);
81 	if (len == 0)
82 		return -1;
83 	cidr->family = sa->sa_family;
84 	cidr->len = len;
85 	memcpy(cidr->address, address, sizeof(cidr->address));
86 	for (i = 0; i < GRECS_INADDR_BYTES; i++)
87 		cidr->netmask[i] = 0xff;
88 	return 0;
89 }
90 
91 static void
masklen_to_netmask(unsigned char * buf,size_t len,size_t masklen)92 masklen_to_netmask(unsigned char *buf, size_t len, size_t masklen)
93 {
94 	int i, cnt;
95 
96 	cnt = masklen / 8;
97 	for (i = 0; i < cnt; i++)
98 		buf[i] = 0xff;
99 	if (i == GRECS_INADDR_BYTES)
100 		return;
101 	cnt = 8 - masklen % 8;
102 	buf[i++] = (0xff >> cnt) << cnt;
103 	for (; i < GRECS_INADDR_BYTES; i++)
104 		buf[i] = 0;
105 }
106 
107 int
grecs_str_to_cidr(struct grecs_cidr * pcidr,const char * str,grecs_locus_t const * locus)108 grecs_str_to_cidr(struct grecs_cidr *pcidr, const char *str,
109 		  grecs_locus_t const *locus)
110 {
111 	int rc;
112 	char ipbuf[41];
113 	struct grecs_cidr cidr;
114 	char *p;
115 	size_t len;
116 	union {
117 		struct in_addr in;
118 		struct in6_addr in6;
119 	} inaddr;
120 
121 	p = strchr(str, '/');
122 	if (p)
123 		len = p - str;
124 	else
125 		len = strlen(str);
126 
127 	if (len > sizeof(ipbuf)) {
128 		grecs_error(locus, 0, _("invalid network mask: %s"),
129 			    str);
130 		return -1;
131 	}
132 
133 	memcpy(ipbuf, str, len);
134 	ipbuf[len] = 0;
135 
136 	if (grecs_str_is_ipv4(ipbuf))
137 		cidr.family = AF_INET;
138 	else if (grecs_str_is_ipv6(ipbuf))
139 		cidr.family = AF_INET6;
140 	else {
141 		grecs_error(locus, 0, _("unrecognized address family: %s"),
142 			    str);
143 		return -1;
144 	}
145 
146 	rc = inet_pton(cidr.family, ipbuf, &inaddr);
147 	if (rc == -1) {
148 		grecs_error(locus, 0, _("unrecognized address family: %s"),
149 			    str);
150 		return -1;
151 	} else if (rc != 1) {
152 		grecs_error(locus, 0, _("invalid network address: %s"),
153 			    str);
154 		return -1;
155 	}
156 
157 	cidr.len = grecs_inaddr_to_bytes(cidr.family, &inaddr, cidr.address);
158 	if (cidr.len == 0) {
159 		grecs_error(locus, 0, _("unrecognized address family: %s"),
160 			    str);
161 		return -1;
162 	}
163 
164 	if (p) {
165 		char *end;
166 		unsigned long masklen;
167 
168 		p++;
169 
170 		masklen = strtoul(p, &end, 10);
171 		if (*end == 0)
172 			masklen_to_netmask(cidr.netmask, cidr.len, masklen);
173 		else if ((cidr.family == AF_INET && grecs_str_is_ipv4(p))
174 			 || (cidr.family == AF_INET6
175 			     && grecs_str_is_ipv6(ipbuf))) {
176 			rc = inet_pton(cidr.family, p, &inaddr);
177 			if (rc != 1) {
178 				grecs_error(locus, 0, _("invalid network mask: %s"),
179 					    str);
180 				return -1;
181 			}
182 			grecs_inaddr_to_bytes(cidr.family, &inaddr,
183 					      cidr.netmask);
184 		} else {
185 			grecs_error(locus, 0, _("invalid network mask: %s"),
186 				    str);
187 			return -1;
188 		}
189 	} else
190 		masklen_to_netmask(cidr.netmask, cidr.len, cidr.len * 8);
191 
192 	memcpy(pcidr, &cidr, sizeof(*pcidr));
193 	return 0;
194 }
195 
196 int
grecs_cidr_match(struct grecs_cidr * a,struct grecs_cidr * b)197 grecs_cidr_match(struct grecs_cidr *a, struct grecs_cidr *b)
198 {
199 	int i;
200 
201 	if (a->family != b->family)
202 		return 1;
203 	for (i = 0; i < a->len; i++) {
204 		if (a->address[i] != (b->address[i] & a->netmask[i]))
205 			return 1;
206 	}
207 	return 0;
208 }
209 
210 int
grecs_sockadd_cidr_match(struct sockaddr * sa,struct grecs_cidr * cidr)211 grecs_sockadd_cidr_match(struct sockaddr *sa, struct grecs_cidr *cidr)
212 {
213 	struct grecs_cidr t;
214 	if (grecs_sockaddr_to_cidr(&t, sa))
215 		return 1;
216 	return grecs_cidr_match(cidr, &t);
217 }
218