ka-forwarder-20060731
[openafs.git] / src / kauth / ka-forwarder.c
1 /*
2  * COPYRIGHT NOTICE
3  * Copyright (c) 1994 Carnegie Mellon University
4  * All Rights Reserved.
5  * 
6  * See <cmu_copyright.h> for use and distribution information.
7  */
8
9 /*
10  * HISTORY
11  * $Log$
12  * Revision 1.1  2006/07/31 17:34:26  rra
13  * DELTA ka-forwarder-20060731
14  * AUTHOR rra@stanford.edu
15  *
16  * Add ka-forwarder.
17  *
18  * Revision 1.1  1997/06/03 18:23:54  kenh
19  * .
20  *
21  * Revision 1.4  1996/08/09  01:00:21  jhutz
22  *      When initializing the array of fakeka servers, remember to set
23  *      the address family of each server; otherwise SunOS complains.
24  *      [1996/08/09  00:58:46  jhutz]
25  *
26  * Revision 1.3  1996/08/09  00:17:19  jhutz
27  *      Merged in changes from Chuck Silvers:
28  *      - Support for more than one fakeka server
29  *      - Support for specifying ports for each fakeka server separately from the
30  *        others, and from the port we listen on.
31  * 
32  *      Plus a minor bug fix to Chuck's code.
33  *      Basically, this version is designed to provide both reliability and
34  *      load-balancing cheaply.  Basically, we forward packets to all of the
35  *      fakeka servers in round-robin fashion.  So, if a client is losing on
36  *      one server, its retry should go to a different one, if more than one
37  *      is specified.
38  *      [1996/08/03  02:13:36  jhutz]
39  * 
40  * Revision 1.2  1995/02/23  18:26:36  chs
41  *      Created.
42  *      [1995/02/23  18:26:03  chs]
43  * 
44  * $EndLog$
45  */
46
47 /*
48  * This program is intended to run on afs DB servers.
49  * Its function is to forward KA requests to a fakeka server
50  * running on an MIT kerberos server.
51  */
52
53 #include <sys/types.h>
54 #include <sys/socket.h>
55 #include <sys/ioctl.h>
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
58 #include <stdio.h>
59 #include <netdb.h>
60 #include <ctype.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <syslog.h>
64 #include <unistd.h>
65
66 #if     HAVE_GETOPT_H
67 #include <getopt.h>
68 #else
69 int getopt (int, char * const *, const char *);
70 int optind, opterr;
71 char *optarg;
72 #endif
73
74 #define BUFFER_SIZE 2048
75
76
77 char *prog;
78
79 int num_servers, cur_server;
80 struct sockaddr_in *servers;
81
82
83 void
84 perrorexit(str)
85 char *str;
86 {
87     perror(str);
88     exit(1);
89 }
90
91
92 void
93 setup_servers(argc, argv)
94 int argc;
95 char **argv;
96 {
97     int i;
98     u_int fwdaddr;
99     u_short fwdport;
100
101     num_servers = argc;
102
103     servers = malloc(sizeof(*servers) * num_servers);
104     if (servers == NULL)
105         perrorexit("malloc failed");
106
107     for (i = 0; i < num_servers; i++) {
108         char *host, *port;
109
110         fwdport = htons(7004);
111
112         host = argv[i];
113         port = strchr(host, '/');
114         if (port != NULL) {
115             *port++ = 0;
116
117             if (isdigit(port[0])) {
118                 fwdport = htons(atoi(port));
119             }
120             else {
121                 struct servent *srv = getservbyname(port, "udp");
122                 if (!srv) {
123                     fprintf(stderr, "%s: unknown service %s\n", prog, port);
124                     exit(1);
125                 }
126                 fwdport = srv->s_port;
127             }
128         }
129
130         if (isdigit(host[0])) {
131             fwdaddr = inet_addr(host);
132         }
133         else {
134             struct hostent *h = gethostbyname(host);
135             if (!h) {
136                 fprintf(stderr, "%s: unknown host %s\n", prog, host);
137                 exit(1);
138             }
139             bcopy(h->h_addr, &fwdaddr, 4);
140         }
141
142         servers[i].sin_family = AF_INET;
143         servers[i].sin_addr.s_addr = fwdaddr;
144         servers[i].sin_port = fwdport;
145     }
146 }
147
148
149 int
150 setup_socket(port)
151 u_short port;
152 {
153     int s, rv;
154     struct sockaddr_in sin;
155
156     s = socket(AF_INET, SOCK_DGRAM, 0);
157     if (s < 0)
158         perrorexit("Couldn't create socket");
159
160     sin.sin_family = AF_INET;
161     sin.sin_addr.s_addr = 0;
162     sin.sin_port = htons(port);
163
164     rv = bind(s, (struct sockaddr *)&sin, sizeof(sin));
165     if (rv < 0)
166         perrorexit("Couldn't bind socket");
167
168     return s;
169 }
170
171
172 int
173 packet_is_reply(from)
174 struct sockaddr_in *from;
175 {
176     int i;
177
178     for (i = 0; i < num_servers; i++) {
179         struct sockaddr_in *sin = &servers[i];
180
181         if (from->sin_addr.s_addr == sin->sin_addr.s_addr &&
182             from->sin_port == sin->sin_port)
183         {
184             return 1;
185         }
186     }
187
188     return 0;
189 }
190
191
192 int
193 main(argc, argv)
194 int argc;
195 char **argv;
196 {
197     int c, s, rv;
198     u_short port;
199
200     if (argc < 2) {
201         fprintf(stderr,
202                 "usage: %s [-p port] <host>[/port] [host/port ...]\n",
203                 argv[0]);
204         exit(1);
205     }
206
207     prog = argv[0];
208     port = 7004;
209
210     while ((c = getopt(argc, argv, "p:")) != -1) {
211         switch (c) {
212         case 'p':
213             port = atoi(optarg);
214             break;
215         default:
216             fprintf(stderr, "%s: invalid option '%c'\n", prog, c);
217             exit(1);
218         }
219     }
220
221     /*
222      * hmm, different implementations of getopt seem to do different things
223      * when there aren't any options.  linux sets optind = 1, which I would
224      * call correct, but sunos sets optind = 0.  try to do the right thing.
225      */
226     if (optind == 0)
227         optind = 1;
228
229     setup_servers(argc - optind, argv + optind);
230     s = setup_socket(port);
231
232     openlog("ka-forwarder", LOG_PID, LOG_DAEMON);
233
234     for (;;) {
235         char buf[BUFFER_SIZE], *bufp, *sendptr;
236         struct sockaddr_in from, reply, *to;
237         int fromlen, sendlen;
238
239         bufp = buf + 8;
240         fromlen = sizeof(from);
241
242         rv = recvfrom(s, bufp, sizeof(buf) - 8,
243                       0, (struct sockaddr *)&from, &fromlen);
244         if (rv < 0) {
245             syslog(LOG_ERR, "recvfrom: %m");
246             sleep(1);
247             continue;
248         }
249
250         if (packet_is_reply(&from)) {
251             /* this is a reply, forward back to user */
252
253             to = &reply;
254             reply.sin_family = AF_INET;
255             bcopy(bufp, &reply.sin_addr.s_addr, 4);
256             bcopy(bufp + 4, &reply.sin_port, 2);
257             sendptr = bufp + 8;
258             sendlen = rv - 8;
259         }
260         else {
261             /* this is a request, forward to server */
262
263             cur_server = (cur_server + 1) % num_servers;
264             to = &servers[cur_server];
265
266             bcopy(&from.sin_addr.s_addr, bufp - 8, 4);
267             bcopy(&from.sin_port, bufp - 4, 2);
268
269             sendptr = bufp - 8;
270             sendlen = rv + 8;
271         }
272
273         {
274             char a1[16], a2[16];
275             strcpy(a1, inet_ntoa(from.sin_addr));
276             strcpy(a2, inet_ntoa(to->sin_addr));
277
278             syslog(LOG_INFO, "forwarding %d bytes from %s/%d to %s/%d\n",
279                    sendlen, a1, htons(from.sin_port), a2, htons(to->sin_port));
280         }
281
282         rv = sendto(s, sendptr, sendlen,
283                     0, (struct sockaddr *)to, sizeof(*to));
284         if (rv < 0) {
285             syslog(LOG_ERR, "sendto: %m");
286         }
287     }
288 }