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 <string.h>
27 #include "grecs.h"
28
29 static void
uint32_to_bytes(unsigned char * bytes,uint32_t u)30 uint32_to_bytes (unsigned char *bytes, uint32_t u)
31 {
32 int i;
33
34 for (i = 0; i < 4; i++) {
35 bytes[i] = u & 0xff;
36 u >>= 8;
37 }
38 }
39
40 int
grecs_inaddr_to_bytes(int af,void * buf,unsigned char * bytes)41 grecs_inaddr_to_bytes(int af, void *buf, unsigned char *bytes)
42 {
43 uint32_t u;
44
45 switch (af) {
46 case AF_INET:
47 memcpy(&u, buf, sizeof u);
48 uint32_to_bytes(bytes, u);
49 return 4;
50
51 case AF_INET6:
52 memcpy(bytes, buf, 16);
53 return 16;
54 }
55 return 0;
56 }
57
58 int
grecs_sockaddr_to_bytes(unsigned char * bytes,struct sockaddr const * sa)59 grecs_sockaddr_to_bytes(unsigned char *bytes, struct sockaddr const *sa)
60 {
61 switch (sa->sa_family) {
62 case AF_INET:
63 uint32_to_bytes(bytes,
64 ((struct sockaddr_in*)sa)->sin_addr.s_addr);
65 return 4;
66
67 case AF_INET6:
68 memcpy(bytes, &((struct sockaddr_in6*)sa)->sin6_addr, 16);
69 return 16;
70 }
71 return 0;
72 }
73
74 int
grecs_sockaddr_to_cidr(struct grecs_cidr * cidr,const struct sockaddr * sa)75 grecs_sockaddr_to_cidr(struct grecs_cidr *cidr, const struct sockaddr *sa)
76 {
77 unsigned char address[GRECS_INADDR_BYTES];
78 int len;
79 int i;
80
81 len = grecs_sockaddr_to_bytes(address, sa);
82 if (len == 0)
83 return -1;
84 cidr->family = sa->sa_family;
85 cidr->len = len;
86 memcpy(cidr->address, address, sizeof(cidr->address));
87 for (i = 0; i < GRECS_INADDR_BYTES; i++)
88 cidr->netmask[i] = 0xff;
89 return 0;
90 }
91
92 static void
masklen_to_netmask(unsigned char * buf,size_t len,size_t masklen)93 masklen_to_netmask(unsigned char *buf, size_t len, size_t masklen)
94 {
95 int i, cnt;
96
97 cnt = masklen / 8;
98 for (i = 0; i < cnt; i++)
99 buf[i] = 0xff;
100 if (i == GRECS_INADDR_BYTES)
101 return;
102 cnt = 8 - masklen % 8;
103 buf[i++] = (0xff >> cnt) << cnt;
104 for (; i < GRECS_INADDR_BYTES; i++)
105 buf[i] = 0;
106 }
107
108 int
grecs_str_to_cidr(struct grecs_cidr * pcidr,const char * str,grecs_locus_t const * locus)109 grecs_str_to_cidr(struct grecs_cidr *pcidr, const char *str,
110 grecs_locus_t const *locus)
111 {
112 int rc;
113 char ipbuf[41];
114 struct grecs_cidr cidr;
115 char *p;
116 size_t len;
117 union {
118 struct in_addr in;
119 struct in6_addr in6;
120 } inaddr;
121
122 p = strchr(str, '/');
123 if (p)
124 len = p - str;
125 else
126 len = strlen(str);
127
128 if (len > sizeof(ipbuf)) {
129 grecs_error(locus, 0, _("invalid network mask: %s"),
130 str);
131 return -1;
132 }
133
134 memcpy(ipbuf, str, len);
135 ipbuf[len] = 0;
136
137 if (grecs_str_is_ipv4(ipbuf))
138 cidr.family = AF_INET;
139 else if (grecs_str_is_ipv6(ipbuf))
140 cidr.family = AF_INET6;
141 else {
142 grecs_error(locus, 0, _("unrecognized address family: %s"),
143 str);
144 return -1;
145 }
146
147 rc = inet_pton(cidr.family, ipbuf, &inaddr);
148 if (rc == -1) {
149 grecs_error(locus, 0, _("unrecognized address family: %s"),
150 str);
151 return -1;
152 } else if (rc != 1) {
153 grecs_error(locus, 0, _("invalid network address: %s"),
154 str);
155 return -1;
156 }
157
158 cidr.len = grecs_inaddr_to_bytes(cidr.family, &inaddr, cidr.address);
159 if (cidr.len == 0) {
160 grecs_error(locus, 0, _("unrecognized address family: %s"),
161 str);
162 return -1;
163 }
164
165 if (p) {
166 char *end;
167 unsigned long masklen;
168
169 p++;
170
171 masklen = strtoul(p, &end, 10);
172 if (*end == 0)
173 masklen_to_netmask(cidr.netmask, cidr.len, masklen);
174 else if ((cidr.family == AF_INET && grecs_str_is_ipv4(p))
175 || (cidr.family == AF_INET6
176 && grecs_str_is_ipv6(ipbuf))) {
177 rc = inet_pton(cidr.family, p, &inaddr);
178 if (rc != 1) {
179 grecs_error(locus, 0, _("invalid network mask: %s"),
180 str);
181 return -1;
182 }
183 grecs_inaddr_to_bytes(cidr.family, &inaddr,
184 cidr.netmask);
185 } else {
186 grecs_error(locus, 0, _("invalid network mask: %s"),
187 str);
188 return -1;
189 }
190 } else
191 masklen_to_netmask(cidr.netmask, cidr.len, cidr.len * 8);
192
193 memcpy(pcidr, &cidr, sizeof(*pcidr));
194 return 0;
195 }
196
197 int
grecs_cidr_match(struct grecs_cidr * a,struct grecs_cidr * b)198 grecs_cidr_match(struct grecs_cidr *a, struct grecs_cidr *b)
199 {
200 int i;
201
202 if (a->family != b->family)
203 return 1;
204 for (i = 0; i < a->len; i++) {
205 if (a->address[i] != (b->address[i] & a->netmask[i]))
206 return 1;
207 }
208 return 0;
209 }
210
211 int
grecs_sockadd_cidr_match(struct sockaddr * sa,struct grecs_cidr * cidr)212 grecs_sockadd_cidr_match(struct sockaddr *sa, struct grecs_cidr *cidr)
213 {
214 struct grecs_cidr t;
215 if (grecs_sockaddr_to_cidr(&t, sa))
216 return 1;
217 return grecs_cidr_match(cidr, &t);
218 }
219