3 * $Id: inet.c,v 1.5 2003/11/25 14:08:23 mdw Exp $
5 * Protocol specific definitions for IPv4 sockets
7 * (c) 1999 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of the `fw' port forwarder.
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.
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.
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.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.5 2003/11/25 14:08:23 mdw
33 * Debianization. Socket target options. Internet binding.
35 * Revision 1.4 2002/01/13 14:49:56 mdw
36 * Conditional compilation for @getnetbyname@, since Cygwin doesn't have
39 * Revision 1.3 2000/08/01 17:59:56 mdw
40 * Switch over to using `size_t' for socket address lengths.
42 * Revision 1.2 1999/07/27 18:30:53 mdw
43 * Various minor portability fixes.
45 * Revision 1.1 1999/07/26 23:34:11 mdw
46 * New socket address types.
50 /*----- Header files ------------------------------------------------------*/
60 #include <sys/types.h>
63 #include <sys/socket.h>
64 #include <netinet/in.h>
65 #include <arpa/inet.h>
68 #include <mLib/alloc.h>
69 #include <mLib/dstr.h>
70 #include <mLib/fdflags.h>
71 #include <mLib/report.h>
84 /*----- Data structures ---------------------------------------------------*/
86 typedef struct inet_addrx
{
88 struct sockaddr_in sin
;
91 typedef struct inet_opts
{
96 typedef struct inet_srcopts
{
102 typedef struct inet_targopts
{
106 static inet_srcopts inet_globalsrc
=
107 { { { 0 }, { INADDR_ANY
} }, 0, &inet_globalsrc
.acl
};
108 static inet_targopts inet_globaltarg
=
109 { { { 0 }, { INADDR_ANY
} } };
111 /*----- Protocol operations -----------------------------------------------*/
115 static addr
*inet_read(scanner
*sc
, unsigned type
)
117 inet_addrx
*ia
= xmalloc(sizeof(*ia
));
119 ia
->a
.ops
= &inet_ops
;
120 ia
->a
.sz
= sizeof(struct sockaddr_in
);
121 memset(&ia
->sin
, 0, sizeof(ia
->sin
));
122 ia
->sin
.sin_family
= AF_INET
;
124 /* --- Read the host address part --- */
128 if (sc
->t
== CTOK_WORD
&& strcmp(sc
->d
.buf
, "port") == 0)
130 ia
->sin
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
135 conf_name(sc
, '.', &d
);
136 if ((h
= gethostbyname(d
.buf
)) == 0)
137 error(sc
, "couldn't resolve Internet address `%s'", d
.buf
);
138 memcpy(&ia
->sin
.sin_addr
, h
->h_addr
, sizeof(struct in_addr
));
145 /* --- Read the port number --- */
150 if (sc
->t
!= CTOK_WORD
)
151 error(sc
, "parse error, TCP port expected");
152 if (isdigit((unsigned char)sc
->d
.buf
[0]))
153 ia
->sin
.sin_port
= htons(atoi(sc
->d
.buf
));
154 else if ((s
= getservbyname(sc
->d
.buf
, "tcp")) == 0)
155 error(sc
, "unknown tcp service `%s'", sc
->d
.buf
);
157 ia
->sin
.sin_port
= s
->s_port
;
164 /* --- @destroy@ --- */
166 static void inet_destroy(addr
*a
)
168 inet_addrx
*ia
= (inet_addrx
*)a
;
172 /* --- @print@ --- */
174 static void inet_print(addr
*a
, unsigned type
, dstr
*d
)
176 inet_addrx
*ia
= (inet_addrx
*)a
;
179 dstr_putf(d
, "inet:%u", (unsigned)ntohs(ia
->sin
.sin_port
));
182 dstr_putf(d
, "inet:%s:%u",
183 inet_ntoa(ia
->sin
.sin_addr
),
184 (unsigned)ntohs(ia
->sin
.sin_port
));
189 /* --- @initopts@ --- */
191 static addr_opts
*inet_initsrcopts(void)
193 inet_srcopts
*io
= CREATE(inet_srcopts
);
194 *io
= inet_globalsrc
;
196 io
->acltail
= &io
->acl
;
200 static addr_opts
*inet_inittargopts(void)
202 inet_targopts
*io
= CREATE(inet_targopts
);
203 *io
= inet_globaltarg
;
207 /* --- @option@ --- */
209 static void addropt(scanner
*sc
, inet_opts
*io
)
217 if (sc
->t
== CTOK_WORD
&& strcmp(sc
->d
.buf
, "any") == 0)
218 io
->bind
.s_addr
= INADDR_ANY
;
220 conf_name(sc
, '.', &d
);
221 if ((h
= gethostbyname(d
.buf
)) == 0)
222 error(sc
, "couldn't resolve address `%s'", d
.buf
);
223 memcpy(&io
->bind
, h
->h_addr
, sizeof(struct in_addr
));
227 static int srcopt(scanner
*sc
, addr_opts
*ao
)
229 inet_srcopts
*io
= (inet_srcopts
*)ao
;
232 CONF_BEGIN(sc
, "source", "Internet socket source")
234 /* --- Initialization --- */
237 if (!inet_globalsrc
.acltail
)
238 inet_globalsrc
.acltail
= &inet_globalsrc
.acl
;
239 io
= &inet_globalsrc
;
242 /* --- Source address configuration --- */
244 if (strcmp(sc
->d
.buf
, "addr") == 0) {
245 addropt(sc
, &io
->io
);
249 /* --- Access control limitations --- */
251 if ((strcmp(sc
->d
.buf
, "allow") == 0 && (act
= ACL_ALLOW
, 1)) ||
252 (strcmp(sc
->d
.buf
, "deny") == 0 && (act
= ACL_DENY
, 1))) {
258 /* --- Find out what's going on --- */
261 if (sc
->t
== CTOK_WORD
&& strcmp(sc
->d
.buf
, "from") == 0)
264 if (sc
->t
== CTOK_WORD
&& strcmp(sc
->d
.buf
, "priv-port") == 0) {
265 acl_addpriv(&io
->acltail
, act
);
268 if (sc
->t
== CTOK_WORD
&& strcmp(sc
->d
.buf
, "host") == 0)
271 /* --- Find the host or network address --- */
273 conf_name(sc
, '.', &d
);
274 #ifdef HAVE_GETNETBYNAME
275 if ((n
= getnetbyname(d
.buf
)) != 0)
276 a
.s_addr
= htonl(n
->n_net
);
279 if ((h
= gethostbyname(d
.buf
)) == 0)
280 error(sc
, "couldn't resolve address `%s'", d
.buf
);
282 memcpy(&a
, h
->h_addr
, sizeof(struct in_addr
));
284 /* --- Find the netmask, if any --- */
291 conf_name(sc
, '.', &d
);
292 if (strchr(d
.buf
, '.') == 0) {
297 m
.s_addr
= htonl((~0ul << (32 - n
)) & 0xffffffff);
299 #ifdef HAVE_INET_ATON
300 if (!inet_aton(d
.buf
, &m
))
301 error(sc
, "bad netmask `%s'", d
.buf
);
303 m
.s_addr
= inet_addr(d
.buf
);
309 /* --- Add the access control entry --- */
311 acl_addhost(&io
->acltail
, act
, a
, m
);
316 /* --- Anything unrecognized --- */
321 static int targopt(scanner
*sc
, addr_opts
*ao
)
323 inet_targopts
*io
= (inet_targopts
*)ao
;
325 CONF_BEGIN(sc
, "dest", "Internet socket target");
326 if (strcmp(sc
->d
.buf
, "addr") == 0) {
327 addropt(sc
, &io
->io
);
333 static int inet_option(scanner
*sc
, addr_opts
*ao
, unsigned type
)
335 CONF_BEGIN(sc
, "inet", "Internet socket");
336 if (type
!= ADDR_DEST
&& srcopt(sc
, ao
))
338 if (type
!= ADDR_SRC
&& targopt(sc
, ao
))
343 /* --- @freeopts@ --- */
345 static void inet_freesrcopts(addr_opts
*ao
)
347 inet_srcopts
*io
= (inet_srcopts
*)ao
;
352 static void inet_freetargopts(addr_opts
*ao
)
354 inet_targopts
*io
= (inet_targopts
*)ao
;
360 static int inet_bind(addr
*a
, addr_opts
*ao
)
362 inet_addrx
*ia
= (inet_addrx
*)a
;
363 inet_srcopts
*io
= (inet_srcopts
*)ao
;
364 struct sockaddr_in sin
;
368 if ((fd
= socket(PF_INET
, SOCK_STREAM
, 0)) < 0)
370 setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &opt
, sizeof(opt
));
371 fdflags(fd
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
373 sin
.sin_addr
= io
->io
.bind
;
374 if (bind(fd
, (struct sockaddr
*)&sin
, sizeof(ia
->sin
)))
384 /* --- @accept@ --- */
386 static reffd
*inet_accept(int fd
, addr_opts
*ao
, const char *desc
)
388 inet_srcopts
*io
= (inet_srcopts
*)ao
;
391 size_t lsinsz
= sizeof(q
.lsin
), rsinsz
= sizeof(q
.rsin
);
394 /* --- Accept the new connection --- */
396 if ((nfd
= accept(fd
, (struct sockaddr
*)&q
.rsin
, &rsinsz
)) < 0)
398 if (getsockname(nfd
, (struct sockaddr
*)&q
.lsin
, &lsinsz
)) {
403 q
.r
= reffd_init(nfd
);
405 /* --- Find out whether this connection is allowed --- */
407 if (!acl_check(io
->acl
, q
.rsin
.sin_addr
, ntohs(q
.rsin
.sin_port
), &act
))
408 acl_check(inet_globalsrc
.acl
, q
.rsin
.sin_addr
,
409 ntohs(q
.rsin
.sin_port
), &act
);
410 if (act
!= ACL_ALLOW
) {
412 if (!(io
->io
.ao
.f
& ADDRF_NOLOG
))
418 /* --- Everything seems to be OK --- */
421 if (!(io
->io
.ao
.f
& ADDRF_NOLOG
))
426 /* --- @connect@ --- */
428 static int inet_connect(addr
*a
, addr_opts
*ao
, conn
*c
, endpt
*e
)
430 inet_addrx
*ia
= (inet_addrx
*)a
;
431 inet_targopts
*io
= (inet_targopts
*)ao
;
434 if ((fd
= socket(PF_INET
, SOCK_STREAM
, 0)) < 0)
436 if (io
->io
.bind
.s_addr
!= INADDR_ANY
) {
437 struct sockaddr_in sin
;
438 memset(&sin
, 0, sizeof(sin
));
439 sin
.sin_family
= AF_INET
;
440 sin
.sin_addr
= io
->io
.bind
;
442 if (bind(fd
, (struct sockaddr
*)&sin
, sizeof(sin
)))
445 fdflags(fd
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
446 return (conn_init(c
, sel
, fd
, (struct sockaddr
*)&ia
->sin
, sizeof(ia
->sin
),
447 starget_connected
, e
));
454 /* --- Ops table --- */
456 addr_ops inet_ops
= {
458 inet_read
, inet_destroy
, inet_print
,
459 inet_initsrcopts
, inet_option
, inet_freesrcopts
,
460 inet_bind
, 0, inet_accept
,
461 inet_inittargopts
, inet_freetargopts
,
465 /*----- That's all, folks -------------------------------------------------*/