Merge from vendor branch NTPD:
[dragonfly.git] / contrib / ipfilter / relay.c
1 /*
2  * Sample program to be used as a transparent proxy.
3  *
4  * Must be executed with permission enough to do an ioctl on /dev/ipl
5  * or equivalent.  This is just a sample and is only alpha quality.
6  * - Darren Reed (8 April 1996)
7  */
8 #include <unistd.h>
9 #include <stdio.h>
10 #include <fcntl.h>
11 #include <sys/types.h>
12 #include <sys/time.h>
13 #include <sys/errno.h>
14 #include <sys/syslog.h>
15 #include <sys/ioctl.h>
16 #include <netinet/in.h>
17 #include <net/if.h>
18 #include <sys/socket.h>
19 #if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 105000000)
20 # include <poll.h>
21 # define USE_POLL
22 #endif
23 #include "ip_nat.h"
24
25 #define RELAY_BUFSZ     8192
26
27 char    ibuff[RELAY_BUFSZ];
28 char    obuff[RELAY_BUFSZ];
29
30 int relay(ifd, ofd, rfd)
31 int ifd, ofd, rfd;
32 {
33 #ifdef  USE_POLL
34         struct pollfd set[3];
35 #else
36         fd_set  rfds, wfds;
37 #endif
38         char    *irh, *irt, *rrh, *rrt;
39         char    *iwh, *iwt, *rwh, *rwt;
40         int     nfd, n, rw;
41
42         irh = irt = ibuff;
43         iwh = iwt = obuff;
44         nfd = ifd;
45         if (nfd < ofd)
46                 nfd = ofd;
47         if (nfd < rfd)
48                 nfd = rfd;
49
50 #ifdef  USE_POLL
51         set[0].fd = rfd;
52         set[1].fd = ifd;
53         set[2].fd = ofd;
54 #endif
55
56         while (1) {
57 #ifdef  USE_POLL
58                 set[0].events = (iwh < (obuff + RELAY_BUFSZ) ? POLLIN : 0) |
59                                 (irh > irt ? POLLOUT : 0);
60                 set[1].events = (irh < (ibuff + RELAY_BUFSZ) ? POLLIN : 0);
61                 set[2].events = (iwh > iwt ? POLLOUT : 0);
62
63                 switch ((n = poll(set, 3, INFTIM)))
64 #else
65                 FD_ZERO(&rfds);
66                 FD_ZERO(&wfds);
67                 if (irh > irt)
68                         FD_SET(rfd, &wfds);
69                 if (irh < (ibuff + RELAY_BUFSZ))
70                         FD_SET(ifd, &rfds);
71                 if (iwh > iwt)
72                         FD_SET(ofd, &wfds);
73                 if (iwh < (obuff + RELAY_BUFSZ))
74                         FD_SET(rfd, &rfds);
75
76                 switch ((n = select(nfd + 1, &rfds, &wfds, NULL, NULL)))
77 #endif
78                 {
79                 case -1 :
80                 case 0 :
81                         return -1;
82                 default :
83 #ifdef  USE_POLL
84                         if (set[1].revents & POLLIN)
85 #else
86                         if (FD_ISSET(ifd, &rfds))
87 #endif
88                         {
89                                 rw = read(ifd, irh, ibuff + RELAY_BUFSZ - irh);
90                                 if (rw == -1)
91                                         return -1;
92                                 if (rw == 0)
93                                         return 0;
94                                 irh += rw;
95                                 n--;
96                         }
97 #ifdef  USE_POLL
98                         if (set[2].revents & POLLOUT)
99 #else
100                         if (n && FD_ISSET(ofd, &wfds))
101 #endif
102                         {
103                                 rw = write(ofd, iwt, iwh  - iwt);
104                                 if (rw == -1)
105                                         return -1;
106                                 iwt += rw;
107                                 n--;
108                         }
109 #ifdef  USE_POLL
110                         if (set[0].revents & POLLIN)
111 #else
112                         if (n && FD_ISSET(rfd, &rfds))
113 #endif
114                         {
115                                 rw = read(rfd, iwh, obuff + RELAY_BUFSZ - iwh);
116                                 if (rw == -1)
117                                         return -1;
118                                 if (rw == 0)
119                                         return 0;
120                                 iwh += rw;
121                                 n--;
122                         }
123 #ifdef  USE_POLL
124                         if (set[0].revents & POLLOUT)
125 #else
126                         if (n && FD_ISSET(rfd, &wfds))
127 #endif
128                         {
129                                 rw = write(rfd, irt, irh  - irt);
130                                 if (rw == -1)
131                                         return -1;
132                                 irt += rw;
133                                 n--;
134                         }
135                         if (irh == irt)
136                                 irh = irt = ibuff;
137                         if (iwh == iwt)
138                                 iwh = iwt = obuff;
139                 }
140         }
141 }
142
143 main(argc, argv)
144 int argc;
145 char *argv[];
146 {
147         struct  sockaddr_in     sin;
148         natlookup_t     nl;
149         natlookup_t     *nlp = &nl;
150         int     fd, sl = sizeof(sl), se;
151
152         openlog(argv[0], LOG_PID|LOG_NDELAY, LOG_DAEMON);
153         if ((fd = open("/dev/ipnat", O_RDONLY)) == -1) {
154                 se = errno;
155                 perror("open");
156                 errno = se;
157                 syslog(LOG_ERR, "open: %m\n");
158                 exit(-1);
159         }
160
161         bzero(&nl, sizeof(nl));
162         nl.nl_flags = IPN_TCP;
163
164         bzero(&sin, sizeof(sin));
165         sin.sin_family = AF_INET;
166         sl = sizeof(sin);
167         if (getsockname(0, (struct sockaddr *)&sin, &sl) == -1) {
168                 se = errno;
169                 perror("getsockname");
170                 errno = se;
171                 syslog(LOG_ERR, "getsockname: %m\n");
172                 exit(-1);
173         } else {
174                 nl.nl_inip.s_addr = sin.sin_addr.s_addr;
175                 nl.nl_inport = sin.sin_port;
176         }
177
178         bzero(&sin, sizeof(sin));
179         sin.sin_family = AF_INET;
180         sl = sizeof(sin);
181         if (getpeername(0, (struct sockaddr *)&sin, &sl) == -1) {
182                 se = errno;
183                 perror("getpeername");
184                 errno = se;
185                 syslog(LOG_ERR, "getpeername: %m\n");
186                 exit(-1);
187         } else {
188                 nl.nl_outip.s_addr = sin.sin_addr.s_addr;
189                 nl.nl_outport = sin.sin_port;
190         }
191
192         if (ioctl(fd, SIOCGNATL, &nlp) == -1) {
193                 se = errno;
194                 perror("ioctl");
195                 errno = se;
196                 syslog(LOG_ERR, "ioctl: %m\n");
197                 exit(-1);
198         }
199
200         sin.sin_port = nl.nl_realport;
201         sin.sin_addr = nl.nl_realip;
202         sl = sizeof(sin);
203
204         fd = socket(AF_INET, SOCK_STREAM, 0);
205         if (connect(fd, (struct sockaddr *)&sin, sl) == -1) {
206                 se = errno;
207                 perror("connect");
208                 errno = se;
209                 syslog(LOG_ERR, "connect: %m\n");
210                 exit(-1);
211         }
212
213         (void) ioctl(fd, F_SETFL, ioctl(fd, F_GETFL, 0)|O_NONBLOCK);
214         (void) ioctl(0, F_SETFL, ioctl(fd, F_GETFL, 0)|O_NONBLOCK);
215         (void) ioctl(1, F_SETFL, ioctl(fd, F_GETFL, 0)|O_NONBLOCK);
216
217         syslog(LOG_NOTICE, "connected to %s,%d\n", inet_ntoa(sin.sin_addr),
218                 ntohs(sin.sin_port));
219         if (relay(0, 1, fd) == -1) {
220                 se = errno;
221                 perror("relay");
222                 errno = se;
223                 syslog(LOG_ERR, "relay: %m\n");
224                 exit(-1);
225         }
226         exit(0);
227 }