Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / kauth / ka-forwarder.c
1 /*
2 * COPYRIGHT NOTICE
3 * Copyright (c) 1994 Carnegie Mellon University
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
11 *
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15 *
16 * Carnegie Mellon requests users of this software to return to
17 *
18 * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU
19 * School of Computer Science
20 * Carnegie Mellon University
21 * Pittsburgh PA 15213-3890
22 *
23 * any improvements or extensions that they make and grant Carnegie Mellon
24 * the rights to redistribute these changes.
25 */
26
27 /*
28 * This program is intended to run on afs DB servers.
29 * Its function is to forward KA requests to a fakeka server
30 * running on an MIT kerberos server.
31 */
32
33 #include <afsconfig.h>
34 #include <afs/param.h>
35 #include <afs/stds.h>
36
37 #include <roken.h>
38
39 #include <ctype.h>
40
41 #if HAVE_GETOPT_H
42 #include <getopt.h>
43 #else
44 int getopt (int, char * const *, const char *);
45 int optind, opterr;
46 char *optarg;
47 #endif
48
49 #define BUFFER_SIZE 2048
50
51
52 char *prog;
53
54 int num_servers, cur_server;
55 struct sockaddr_in *servers;
56
57
58 void
59 perrorexit(char *str)
60 {
61 perror(str);
62 exit(1);
63 }
64
65
66 void
67 setup_servers(int argc, char **argv)
68 {
69 int i;
70 u_int fwdaddr;
71 u_short fwdport;
72
73 num_servers = argc;
74
75 servers = malloc(sizeof(*servers) * num_servers);
76 if (servers == NULL)
77 perrorexit("malloc failed");
78
79 for (i = 0; i < num_servers; i++) {
80 char *host, *port;
81
82 fwdport = htons(7004);
83
84 host = argv[i];
85 port = strchr(host, '/');
86 if (port != NULL) {
87 *port++ = 0;
88
89 if (isdigit(port[0])) {
90 fwdport = htons(atoi(port));
91 }
92 else {
93 struct servent *srv = getservbyname(port, "udp");
94 if (!srv) {
95 fprintf(stderr, "%s: unknown service %s\n", prog, port);
96 exit(1);
97 }
98 fwdport = srv->s_port;
99 }
100 }
101
102 if (isdigit(host[0])) {
103 fwdaddr = inet_addr(host);
104 }
105 else {
106 struct hostent *h = gethostbyname(host);
107 if (!h) {
108 fprintf(stderr, "%s: unknown host %s\n", prog, host);
109 exit(1);
110 }
111 bcopy(h->h_addr, &fwdaddr, 4);
112 }
113
114 servers[i].sin_family = AF_INET;
115 servers[i].sin_addr.s_addr = fwdaddr;
116 servers[i].sin_port = fwdport;
117 }
118 }
119
120
121 int
122 setup_socket(u_short port)
123 {
124 int s, rv;
125 struct sockaddr_in sin;
126
127 s = socket(AF_INET, SOCK_DGRAM, 0);
128 if (s < 0)
129 perrorexit("Couldn't create socket");
130
131 sin.sin_family = AF_INET;
132 sin.sin_addr.s_addr = 0;
133 sin.sin_port = htons(port);
134
135 rv = bind(s, (struct sockaddr *)&sin, sizeof(sin));
136 if (rv < 0)
137 perrorexit("Couldn't bind socket");
138
139 return s;
140 }
141
142
143 int
144 packet_is_reply(struct sockaddr_in *from)
145 {
146 int i;
147
148 for (i = 0; i < num_servers; i++) {
149 struct sockaddr_in *sin = &servers[i];
150
151 if (from->sin_addr.s_addr == sin->sin_addr.s_addr &&
152 from->sin_port == sin->sin_port)
153 {
154 return 1;
155 }
156 }
157
158 return 0;
159 }
160
161
162 int
163 main(int argc, char **argv)
164 {
165 int c, s, rv;
166 u_short port;
167
168 if (argc < 2) {
169 fprintf(stderr,
170 "usage: %s [-p port] <host>[/port] [host/port ...]\n",
171 argv[0]);
172 exit(1);
173 }
174
175 prog = argv[0];
176 port = 7004;
177
178 while ((c = getopt(argc, argv, "p:")) != -1) {
179 switch (c) {
180 case 'p':
181 port = atoi(optarg);
182 break;
183 default:
184 fprintf(stderr, "%s: invalid option '%c'\n", prog, c);
185 exit(1);
186 }
187 }
188
189 /*
190 * hmm, different implementations of getopt seem to do different things
191 * when there aren't any options. linux sets optind = 1, which I would
192 * call correct, but sunos sets optind = 0. try to do the right thing.
193 */
194 if (optind == 0)
195 optind = 1;
196
197 setup_servers(argc - optind, argv + optind);
198 s = setup_socket(port);
199
200 openlog("ka-forwarder", LOG_PID, LOG_DAEMON);
201
202 for (;;) {
203 char buf[BUFFER_SIZE], *bufp, *sendptr;
204 struct sockaddr_in from, reply, *to;
205 size_t sendlen;
206 socklen_t fromlen;
207
208 bufp = buf + 8;
209 fromlen = sizeof(from);
210
211 rv = recvfrom(s, bufp, sizeof(buf) - 8,
212 0, (struct sockaddr *)&from, &fromlen);
213 if (rv < 0) {
214 syslog(LOG_ERR, "recvfrom: %m");
215 sleep(1);
216 continue;
217 }
218
219 if (packet_is_reply(&from)) {
220 /* this is a reply, forward back to user */
221
222 to = &reply;
223 reply.sin_family = AF_INET;
224 bcopy(bufp, &reply.sin_addr.s_addr, 4);
225 bcopy(bufp + 4, &reply.sin_port, 2);
226 sendptr = bufp + 8;
227 sendlen = rv - 8;
228 }
229 else {
230 /* this is a request, forward to server */
231
232 cur_server = (cur_server + 1) % num_servers;
233 to = &servers[cur_server];
234
235 bcopy(&from.sin_addr.s_addr, bufp - 8, 4);
236 bcopy(&from.sin_port, bufp - 4, 2);
237
238 sendptr = bufp - 8;
239 sendlen = rv + 8;
240 }
241
242 {
243 char a1[16], a2[16];
244 strcpy(a1, inet_ntoa(from.sin_addr));
245 strcpy(a2, inet_ntoa(to->sin_addr));
246
247 syslog(LOG_INFO, "forwarding %"AFS_SIZET_FMT" bytes from %s/%d to %s/%d\n",
248 sendlen, a1, htons(from.sin_port), a2, htons(to->sin_port));
249 }
250
251 rv = sendto(s, sendptr, sendlen,
252 0, (struct sockaddr *)to, sizeof(*to));
253 if (rv < 0) {
254 syslog(LOG_ERR, "sendto: %m");
255 }
256 }
257 }