aa1f699e |
1 | /* -*-c-*- |
2 | * |
1a039139 |
3 | * $Id: inet.c,v 1.4 2002/01/13 14:49:56 mdw Exp $ |
aa1f699e |
4 | * |
5 | * Protocol specific definitions for IPv4 sockets |
6 | * |
7 | * (c) 1999 Straylight/Edgeware |
8 | */ |
9 | |
10 | /*----- Licensing notice --------------------------------------------------* |
11 | * |
12 | * This file is part of the `fw' port forwarder. |
13 | * |
14 | * `fw' is free software; you can redistribute it and/or modify |
15 | * it under the terms of the GNU General Public License as published by |
16 | * the Free Software Foundation; either version 2 of the License, or |
17 | * (at your option) any later version. |
18 | * |
19 | * `fw' is distributed in the hope that it will be useful, |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
22 | * GNU General Public License for more details. |
23 | * |
24 | * You should have received a copy of the GNU General Public License |
25 | * along with `fw'; if not, write to the Free Software Foundation, |
26 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
27 | */ |
28 | |
29 | /*----- Revision history --------------------------------------------------* |
30 | * |
31 | * $Log: inet.c,v $ |
1a039139 |
32 | * Revision 1.4 2002/01/13 14:49:56 mdw |
33 | * Conditional compilation for @getnetbyname@, since Cygwin doesn't have |
34 | * it. |
35 | * |
bc241e98 |
36 | * Revision 1.3 2000/08/01 17:59:56 mdw |
37 | * Switch over to using `size_t' for socket address lengths. |
38 | * |
e0ce9d38 |
39 | * Revision 1.2 1999/07/27 18:30:53 mdw |
40 | * Various minor portability fixes. |
41 | * |
aa1f699e |
42 | * Revision 1.1 1999/07/26 23:34:11 mdw |
43 | * New socket address types. |
44 | * |
45 | */ |
46 | |
47 | /*----- Header files ------------------------------------------------------*/ |
48 | |
49 | #include "config.h" |
50 | |
51 | #include <ctype.h> |
52 | #include <errno.h> |
53 | #include <stdio.h> |
54 | #include <stdlib.h> |
55 | #include <string.h> |
56 | |
57 | #include <sys/types.h> |
58 | #include <unistd.h> |
59 | |
60 | #include <sys/socket.h> |
61 | #include <netinet/in.h> |
62 | #include <arpa/inet.h> |
63 | #include <netdb.h> |
64 | |
65 | #include <mLib/alloc.h> |
66 | #include <mLib/dstr.h> |
67 | #include <mLib/report.h> |
68 | #include <mLib/sub.h> |
69 | |
70 | #include "acl.h" |
71 | #include "addr.h" |
72 | #include "conf.h" |
73 | #include "identify.h" |
74 | #include "inet.h" |
75 | #include "reffd.h" |
76 | #include "scan.h" |
77 | |
78 | /*----- Data structures ---------------------------------------------------*/ |
79 | |
80 | typedef struct inet_addrx { |
81 | addr a; |
82 | struct sockaddr_in sin; |
83 | } inet_addrx; |
84 | |
85 | typedef struct inet_opts { |
86 | addr_opts ao; |
87 | acl_entry *acl; |
88 | acl_entry **acltail; |
89 | } inet_opts; |
90 | |
91 | /*----- Protocol operations -----------------------------------------------*/ |
92 | |
93 | /* --- @read@ --- */ |
94 | |
95 | static addr *inet_read(scanner *sc, unsigned type) |
96 | { |
97 | inet_addrx *ia = xmalloc(sizeof(*ia)); |
98 | |
99 | ia->a.ops = &inet_ops; |
100 | ia->a.sz = sizeof(struct sockaddr_in); |
e0ce9d38 |
101 | memset(&ia->sin, 0, sizeof(ia->sin)); |
aa1f699e |
102 | ia->sin.sin_family = AF_INET; |
103 | |
104 | /* --- Read the host address part --- */ |
105 | |
106 | switch (type) { |
107 | case ADDR_SRC: |
108 | if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "port") == 0) |
109 | token(sc); |
110 | ia->sin.sin_addr.s_addr = htonl(INADDR_ANY); |
111 | break; |
112 | case ADDR_DEST: { |
113 | struct hostent *h; |
114 | dstr d = DSTR_INIT; |
115 | conf_name(sc, '.', &d); |
116 | if ((h = gethostbyname(d.buf)) == 0) |
117 | error(sc, "couldn't resolve Internet address `%s'", d.buf); |
118 | memcpy(&ia->sin.sin_addr, h->h_addr, sizeof(struct in_addr)); |
119 | dstr_destroy(&d); |
120 | if (sc->t == ':') |
121 | token(sc); |
122 | } break; |
123 | } |
124 | |
125 | /* --- Read the port number --- */ |
126 | |
127 | { |
128 | struct servent *s; |
129 | |
130 | if (sc->t != CTOK_WORD) |
131 | error(sc, "parse error, TCP port expected"); |
132 | if (isdigit((unsigned char)sc->d.buf[0])) |
133 | ia->sin.sin_port = htons(atoi(sc->d.buf)); |
134 | else if ((s = getservbyname(sc->d.buf, "tcp")) == 0) |
135 | error(sc, "unknown tcp service `%s'", sc->d.buf); |
136 | else |
137 | ia->sin.sin_port = s->s_port; |
138 | token(sc); |
139 | } |
140 | |
141 | return (&ia->a); |
142 | } |
143 | |
144 | /* --- @destroy@ --- */ |
145 | |
146 | static void inet_destroy(addr *a) |
147 | { |
148 | inet_addrx *ia = (inet_addrx *)a; |
149 | DESTROY(ia); |
150 | } |
151 | |
152 | /* --- @print@ --- */ |
153 | |
154 | static void inet_print(addr *a, unsigned type, dstr *d) |
155 | { |
156 | inet_addrx *ia = (inet_addrx *)a; |
157 | switch (type) { |
158 | case ADDR_SRC: |
159 | dstr_putf(d, "inet:%u", (unsigned)ntohs(ia->sin.sin_port)); |
160 | break; |
161 | case ADDR_DEST: |
162 | dstr_putf(d, "inet:%s:%u", |
163 | inet_ntoa(ia->sin.sin_addr), |
164 | (unsigned)ntohs(ia->sin.sin_port)); |
165 | break; |
166 | } |
167 | } |
168 | |
169 | /* --- @initopts@ --- */ |
170 | |
171 | static addr_opts *inet_initopts(void) |
172 | { |
173 | inet_opts *io = CREATE(inet_opts); |
174 | io->acl = 0; |
175 | io->acltail = &io->acl; |
176 | return (&io->ao); |
177 | } |
178 | |
179 | /* --- @option@ --- */ |
180 | |
181 | static int inet_option(scanner *sc, addr_opts *ao) |
182 | { |
183 | inet_opts *io = (inet_opts *)ao; |
184 | |
185 | CONF_BEGIN(sc, "inet", "Internet socket") |
186 | |
187 | unsigned act; |
188 | |
189 | /* --- Access control limitations --- */ |
190 | |
191 | if ((strcmp(sc->d.buf, "allow") == 0 && (act = ACL_ALLOW, 1)) || |
192 | (strcmp(sc->d.buf, "deny") == 0 && (act = ACL_DENY, 1))) { |
193 | struct hostent *h; |
194 | struct netent *n; |
195 | struct in_addr a, m; |
196 | dstr d = DSTR_INIT; |
197 | |
198 | /* --- Find the host or network address --- */ |
199 | |
200 | token(sc); |
201 | if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "from") == 0) |
202 | token(sc); |
203 | conf_name(sc, '.', &d); |
1a039139 |
204 | #ifdef HAVE_GETNETBYNAME |
aa1f699e |
205 | if ((n = getnetbyname(d.buf)) != 0) |
206 | a.s_addr = htonl(n->n_net); |
1a039139 |
207 | else |
208 | #endif |
209 | if ((h = gethostbyname(d.buf)) == 0) |
aa1f699e |
210 | error(sc, "couldn't resolve address `%s'", d.buf); |
211 | else |
212 | memcpy(&a, h->h_addr, sizeof(struct in_addr)); |
213 | |
214 | /* --- Find the netmask, if any --- */ |
215 | |
216 | if (sc->t != '/') |
217 | m.s_addr = ~0ul; |
218 | else { |
219 | token(sc); |
220 | DRESET(&d); |
221 | conf_name(sc, '.', &d); |
222 | if (strchr(d.buf, '.') == 0) { |
223 | int n = atoi(d.buf); |
224 | if (n == 0) |
225 | m.s_addr = 0; |
226 | else |
227 | m.s_addr = htonl((~0ul << (32 - n)) & 0xffffffff); |
228 | } else { |
229 | #ifdef HAVE_INET_ATON |
230 | if (!inet_aton(d.buf, &m)) |
231 | error(sc, "bad netmask `%s'", d.buf); |
232 | #else |
233 | m.s_addr = inet_addr(d.buf); |
234 | #endif |
235 | } |
236 | } |
237 | dstr_destroy(&d); |
238 | |
239 | /* --- Add the access control entry --- */ |
240 | |
241 | acl_add(io ? &io->acltail : 0, act, a, m); |
242 | CONF_ACCEPT; |
243 | } |
244 | |
245 | /* --- Anything unrecognized --- */ |
246 | |
247 | CONF_END; |
248 | } |
249 | |
250 | /* --- @accept@ --- */ |
251 | |
252 | static reffd *inet_accept(int fd, addr_opts *ao, const char *desc) |
253 | { |
254 | inet_opts *io = (inet_opts *)ao; |
255 | int nfd; |
256 | id_req q; |
bc241e98 |
257 | size_t lsinsz = sizeof(q.lsin), rsinsz = sizeof(q.rsin); |
aa1f699e |
258 | |
259 | /* --- Accept the new connection --- */ |
260 | |
261 | if ((nfd = accept(fd, (struct sockaddr *)&q.rsin, &rsinsz)) < 0) |
262 | return (0); |
263 | if (getsockname(nfd, (struct sockaddr *)&q.lsin, &lsinsz)) { |
264 | close(nfd); |
265 | return (0); |
266 | } |
267 | q.desc = desc; |
268 | q.r = reffd_init(nfd); |
269 | |
270 | /* --- Find out whether this connection is allowed --- */ |
271 | |
272 | if (!acl_check(io->acl, q.rsin.sin_addr)) { |
273 | q.act = "refused"; |
274 | if (!(io->ao.f & ADDRF_NOLOG)) |
275 | identify(&q); |
276 | REFFD_DEC(q.r); |
277 | return (0); |
278 | } |
279 | |
280 | /* --- Everything seems to be OK --- */ |
281 | |
282 | q.act = "accepted"; |
283 | if (!(io->ao.f & ADDRF_NOLOG)) |
284 | identify(&q); |
285 | return (q.r); |
286 | } |
287 | |
288 | /* --- @freeopts@ --- */ |
289 | |
290 | static void inet_freeopts(addr_opts *ao) |
291 | { |
292 | inet_opts *io = (inet_opts *)ao; |
293 | acl_free(io->acl); |
294 | DESTROY(ao); |
295 | } |
296 | |
297 | /* --- Ops table --- */ |
298 | |
299 | addr_ops inet_ops = { |
300 | "inet", PF_INET, |
301 | inet_read, inet_destroy, inet_print, |
302 | inet_initopts, inet_option, inet_accept, inet_freeopts, 0, 0 |
303 | }; |
304 | |
305 | /*----- That's all, folks -------------------------------------------------*/ |