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