Tidy up UKERNEL includes
[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
36 #include <afs/stds.h>
37
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/ioctl.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include <stdio.h>
44 #include <netdb.h>
45 #include <ctype.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <syslog.h>
49 #include <unistd.h>
50
51 #if     HAVE_GETOPT_H
52 #include <getopt.h>
53 #else
54 int getopt (int, char * const *, const char *);
55 int optind, opterr;
56 char *optarg;
57 #endif
58
59 #define BUFFER_SIZE 2048
60
61
62 char *prog;
63
64 int num_servers, cur_server;
65 struct sockaddr_in *servers;
66
67
68 void
69 perrorexit(char *str)
70 {
71     perror(str);
72     exit(1);
73 }
74
75
76 void
77 setup_servers(int argc, char **argv)
78 {
79     int i;
80     u_int fwdaddr;
81     u_short fwdport;
82
83     num_servers = argc;
84
85     servers = malloc(sizeof(*servers) * num_servers);
86     if (servers == NULL)
87         perrorexit("malloc failed");
88
89     for (i = 0; i < num_servers; i++) {
90         char *host, *port;
91
92         fwdport = htons(7004);
93
94         host = argv[i];
95         port = strchr(host, '/');
96         if (port != NULL) {
97             *port++ = 0;
98
99             if (isdigit(port[0])) {
100                 fwdport = htons(atoi(port));
101             }
102             else {
103                 struct servent *srv = getservbyname(port, "udp");
104                 if (!srv) {
105                     fprintf(stderr, "%s: unknown service %s\n", prog, port);
106                     exit(1);
107                 }
108                 fwdport = srv->s_port;
109             }
110         }
111
112         if (isdigit(host[0])) {
113             fwdaddr = inet_addr(host);
114         }
115         else {
116             struct hostent *h = gethostbyname(host);
117             if (!h) {
118                 fprintf(stderr, "%s: unknown host %s\n", prog, host);
119                 exit(1);
120             }
121             bcopy(h->h_addr, &fwdaddr, 4);
122         }
123
124         servers[i].sin_family = AF_INET;
125         servers[i].sin_addr.s_addr = fwdaddr;
126         servers[i].sin_port = fwdport;
127     }
128 }
129
130
131 int
132 setup_socket(u_short port)
133 {
134     int s, rv;
135     struct sockaddr_in sin;
136
137     s = socket(AF_INET, SOCK_DGRAM, 0);
138     if (s < 0)
139         perrorexit("Couldn't create socket");
140
141     sin.sin_family = AF_INET;
142     sin.sin_addr.s_addr = 0;
143     sin.sin_port = htons(port);
144
145     rv = bind(s, (struct sockaddr *)&sin, sizeof(sin));
146     if (rv < 0)
147         perrorexit("Couldn't bind socket");
148
149     return s;
150 }
151
152
153 int
154 packet_is_reply(struct sockaddr_in *from)
155 {
156     int i;
157
158     for (i = 0; i < num_servers; i++) {
159         struct sockaddr_in *sin = &servers[i];
160
161         if (from->sin_addr.s_addr == sin->sin_addr.s_addr &&
162             from->sin_port == sin->sin_port)
163         {
164             return 1;
165         }
166     }
167
168     return 0;
169 }
170
171
172 int
173 main(int argc, char **argv)
174 {
175     int c, s, rv;
176     u_short port;
177
178     if (argc < 2) {
179         fprintf(stderr,
180                 "usage: %s [-p port] <host>[/port] [host/port ...]\n",
181                 argv[0]);
182         exit(1);
183     }
184
185     prog = argv[0];
186     port = 7004;
187
188     while ((c = getopt(argc, argv, "p:")) != -1) {
189         switch (c) {
190         case 'p':
191             port = atoi(optarg);
192             break;
193         default:
194             fprintf(stderr, "%s: invalid option '%c'\n", prog, c);
195             exit(1);
196         }
197     }
198
199     /*
200      * hmm, different implementations of getopt seem to do different things
201      * when there aren't any options.  linux sets optind = 1, which I would
202      * call correct, but sunos sets optind = 0.  try to do the right thing.
203      */
204     if (optind == 0)
205         optind = 1;
206
207     setup_servers(argc - optind, argv + optind);
208     s = setup_socket(port);
209
210     openlog("ka-forwarder", LOG_PID, LOG_DAEMON);
211
212     for (;;) {
213         char buf[BUFFER_SIZE], *bufp, *sendptr;
214         struct sockaddr_in from, reply, *to;
215         size_t sendlen;
216         socklen_t fromlen;
217
218         bufp = buf + 8;
219         fromlen = sizeof(from);
220
221         rv = recvfrom(s, bufp, sizeof(buf) - 8,
222                       0, (struct sockaddr *)&from, &fromlen);
223         if (rv < 0) {
224             syslog(LOG_ERR, "recvfrom: %m");
225             sleep(1);
226             continue;
227         }
228
229         if (packet_is_reply(&from)) {
230             /* this is a reply, forward back to user */
231
232             to = &reply;
233             reply.sin_family = AF_INET;
234             bcopy(bufp, &reply.sin_addr.s_addr, 4);
235             bcopy(bufp + 4, &reply.sin_port, 2);
236             sendptr = bufp + 8;
237             sendlen = rv - 8;
238         }
239         else {
240             /* this is a request, forward to server */
241
242             cur_server = (cur_server + 1) % num_servers;
243             to = &servers[cur_server];
244
245             bcopy(&from.sin_addr.s_addr, bufp - 8, 4);
246             bcopy(&from.sin_port, bufp - 4, 2);
247
248             sendptr = bufp - 8;
249             sendlen = rv + 8;
250         }
251
252         {
253             char a1[16], a2[16];
254             strcpy(a1, inet_ntoa(from.sin_addr));
255             strcpy(a2, inet_ntoa(to->sin_addr));
256
257             syslog(LOG_INFO, "forwarding %"AFS_SIZET_FMT" bytes from %s/%d to %s/%d\n",
258                    sendlen, a1, htons(from.sin_port), a2, htons(to->sin_port));
259         }
260
261         rv = sendto(s, sendptr, sendlen,
262                     0, (struct sockaddr *)to, sizeof(*to));
263         if (rv < 0) {
264             syslog(LOG_ERR, "sendto: %m");
265         }
266     }
267 }