]> git.neil.brown.name Git - metad.git/blob - control.c
dbf114f084fc87d3c7f0864c77e85499b6f91931
[metad.git] / control.c
1
2 #define IN_CONTROL
3 #include        <stdio.h>
4 #include        "metad.h"
5 #include        <sys/socket.h>
6 #include        <netinet/in.h>
7 #include        <fcntl.h>
8 #ifdef SOLARIS
9 #include        <sys/fcntl.h>
10 #else
11 #include        <sys/file.h>
12 #endif
13
14 #include        <netdb.h>
15 #include        <ctype.h>
16
17 /* control.c - handle the control ports
18  * listen on udp port for packets with command to obey
19  * listen on tcp port for connections.
20  *   when get connection, listen for command, and possibly return data
21  *
22  */
23
24 /* only allow one active tcp connection for now,
25  * but make sure it times out
26  */
27
28 static int udp_sock;
29 static int tcp_listen;
30
31 static struct tcpcon {
32         int     sock;
33         char buf[1024]; /*for incoming command */
34         char host[1024];        /* host connection is from */
35         int buflen;             /* how much has been read */
36         char *outbuf;   /* outgoing data */
37         int  outlen;    /* size of outgoing data */
38         int  outpos;    /* how much sent so far */
39         time_t connect_time;/* when the connection was established */
40 } tcpcon;
41
42
43
44
45 void return_error(struct tcpcon *con, char *fmt, char *a, char *b, char *c)
46 {
47         char buf[1024];
48         char *rv;
49         extern char version[];
50         if (con)
51         {
52                 sprintf(buf, fmt, a, b, c);
53                 sprintf(buf+strlen(buf), " (metad version %s)", version);
54                 rv = (char*)malloc(strlen(buf)+2);
55                 strcpy(rv+1, buf);
56                 rv[0]= 1;
57                 con->outbuf = rv;
58                 con->outlen = strlen(buf)+2;
59                 con->outpos = 0;
60         }
61 }
62
63 static int address_ok(struct sockaddr_in *sa, char *host)
64 {
65         struct hostent *he;
66         int len;
67         int a;
68         static char *tail = ".cse.unsw.edu.au";
69
70         if (ntohs(sa->sin_port) >= 1024 && geteuid() == 0)
71                 return 0;
72         if (sa->sin_addr.s_addr == htonl(0x7f000001))
73         {
74                 strcpy(host, "localhost");
75                 return 1; /* localhost */
76         }
77         he = gethostbyaddr((char*)&sa->sin_addr, 4, AF_INET);
78         if (he == NULL)
79                 return 0;
80         strcpy(host, he->h_name);
81         he = gethostbyname(host);
82         if (he == NULL)
83                 return 0;
84         for (a=0; he->h_addr_list[a] ; a++)
85                 if (memcmp(&sa->sin_addr, he->h_addr_list[a], 4)==0)
86                 {
87                         /* well, we have a believeable name */
88
89                         len = strlen(host);
90                         if (len > strlen(tail) && strcasecmp(tail, host+len - strlen(tail))== 0)
91                                 return 1;
92                         return 0;
93                 }
94         return 0;
95 }
96
97 static void run_command(char *buf, char *host, struct tcpcon *con)
98 {
99         char *cp;
100         char **words, **wp;
101
102         for (cp= buf; *cp ; cp++)
103         {
104                 if (*cp == '\r' || *cp == '\n') *cp = 0;
105         }
106         wp = words = strsplit(buf, " ");
107         if (isdigit(wp[0][0]))
108                 wp++; /* old gossip put a port number at the start for return info */
109         if (!do_command(wp, host, con))
110         {
111                 /* possibly return error */
112                 if (con)
113                         return_error(con, "unknown command %s", wp[0], NULL, NULL);
114         }
115 }
116
117 void nodelay(int socket)
118 {
119         int f;
120         f = fcntl(socket, F_GETFL, 0);
121         fcntl(socket, F_SETFL, f|O_NDELAY);
122         fcntl(socket, F_SETFD, 1); /* set close-on-exec */
123 }
124
125 int control_init()
126 {
127         udp_sock = socket(AF_INET, SOCK_DGRAM, 0);
128         if (udp_sock >= 0)
129         {
130                 struct sockaddr_in sa;
131                 memset(&sa, 0, sizeof(sa));
132                 sa.sin_family = AF_INET;
133                 sa.sin_port = udp_port();
134                 nodelay(udp_sock);
135                 if (bind(udp_sock, (struct sockaddr *)&sa, sizeof(sa)) != 0)
136                 {
137                         error("cannot bind udp port");
138                         return -1;
139                 }
140         }
141         else
142         {
143                 error("cannot create udp socket");
144                 return -1;
145         }
146         tcp_listen = socket(AF_INET, SOCK_STREAM, 0);
147         if (tcp_listen >= 0)
148         {
149                 struct sockaddr_in sa;
150                 int i = 1;
151                 nodelay(tcp_listen);
152                 memset(&sa, 0, sizeof(sa));
153                 sa.sin_family = AF_INET;
154                 sa.sin_port = tcp_port();
155                 setsockopt(tcp_listen, SOL_SOCKET, SO_REUSEADDR, (char*)&i, 4);
156                 if (bind(tcp_listen, (struct sockaddr *)&sa, sizeof(sa)) != 0)
157                 {
158                         error("cannot bind tcp port");
159                         return -1;
160                 }
161                 listen(tcp_listen, 5);
162         }
163         else
164         {
165                 error("Cannot create tcp socket");
166                 return -1;
167         }
168         tcpcon.sock = -1;
169         return 0;
170 }
171
172 void control_close(void)
173 {
174         close(tcp_listen);
175         close(udp_sock);
176         close(tcpcon.sock);
177 }
178
179 void check_control(void)
180 {
181         /* first check udp */
182         if (readyon(udp_sock))
183         {
184                 char buf[1024];
185                 char host[1024];
186                 int n;
187                 struct sockaddr_in sa;
188                 unsigned int salen = sizeof(sa);
189                 n = recvfrom(udp_sock, buf, sizeof(buf)-1, 0, (struct sockaddr *)&sa, &salen );
190                 if (n>0 && address_ok(&sa, host))
191                 {
192                         buf[n] = 0;
193                         run_command(buf, host, NULL);
194                 }
195         }
196         listenon(udp_sock);
197
198         /* then check tcpcon or tcp_listen */
199         if (tcpcon.sock != -1)
200         {
201                 time_t now;
202                 time(&now);
203                 if (tcpcon.connect_time + 120 < now)
204                 {
205                         /* just give up */
206                         close(tcpcon.sock);
207                         tcpcon.sock = -1;
208                         if (tcpcon.outbuf) free(tcpcon.outbuf);
209                         tcpcon.outbuf = NULL;
210                         listenon(tcp_listen);
211                 }
212                 else if (tcpcon.outbuf)
213                 {
214                         if (canwrite(tcpcon.sock) && tcpcon.outpos < tcpcon.outlen)
215                         {
216                                 int l = tcpcon.outlen - tcpcon.outpos;
217                                 if (l>1024) l = 1024;
218                                 l = write(tcpcon.sock, tcpcon.outbuf+tcpcon.outpos, l);
219                                 if (l< 0)
220                                 {
221                                         close(tcpcon.sock); tcpcon.sock = -1; free(tcpcon.outbuf);
222                                         tcpcon.outbuf = NULL;
223                                 }
224                                 else
225                                         tcpcon.outpos += l;
226                                 if (tcpcon.outpos >= tcpcon.outlen)
227                                 {
228                                         close(tcpcon.sock); tcpcon.sock = -1; free(tcpcon.outbuf);
229                                         tcpcon.outbuf = NULL;
230                                 }
231                         }
232                         if (tcpcon.sock == -1)
233                                 listenon(tcp_listen);
234                         else
235                                 writeon(tcpcon.sock);
236                 }
237                 else /* we are still reading a command */
238                 {
239                         if (readyon(tcpcon.sock))
240                         {
241                                 int l = sizeof(tcpcon.buf) - tcpcon.buflen;
242                                 l = read(tcpcon.sock, tcpcon.buf+tcpcon.buflen, l-1);
243                                 if (l<0)
244                                 {
245                                         close(tcpcon.sock); tcpcon.sock = -1;
246                                 }
247                                 else
248                                 {
249                                         tcpcon.buf[l] = 0;
250                                         if (l == 0 || strchr(tcpcon.buf, '\n') || strchr(tcpcon.buf, '\r') || strlen(tcpcon.buf) < l)
251                                         {
252                                                 run_command(tcpcon.buf, tcpcon.host, &tcpcon);
253                                                 if (tcpcon.outbuf == NULL)
254                                                 {
255                                                         tcpcon.outbuf = malloc(1);
256                                                         tcpcon.outbuf[0] = 0;
257                                                         tcpcon.outlen = 1;
258                                                         tcpcon.outpos = 0;
259                                                 }
260                                         }
261                                 }
262                         }
263                         if (tcpcon.sock == -1)
264                                 listenon(tcp_listen);
265                         else if (tcpcon.outbuf)
266                                 writeon(tcpcon.sock);
267                         else
268                                 listenon(tcpcon.sock);
269                 }
270         }
271         else
272         {
273                 if (readyon(tcp_listen))
274                 {
275                         struct sockaddr_in sa;
276                         unsigned int salen = sizeof(sa);
277                         tcpcon.buflen = 0;
278                         tcpcon.outbuf = NULL;
279                         tcpcon.sock = accept(tcp_listen, (struct sockaddr *)&sa, &salen);
280                         if (tcpcon.sock >= 0)
281                         {
282                                 nodelay(tcpcon.sock);
283                                 if (address_ok(&sa, tcpcon.host))
284                                 {
285                                         time(&tcpcon.connect_time);
286                                         listenon(tcpcon.sock);
287                                         waituntil(tcpcon.connect_time+122);
288                                 }
289                                 else
290                                 {
291                                         close(tcpcon.sock);
292                                         tcpcon.sock = -1;
293                                 }
294                         }
295                 }
296                 if (tcpcon.sock < 0)
297                         listenon(tcp_listen);
298         }
299
300 }
301
302 void set_reply(struct tcpcon *con, char *reply, int len)
303 {
304         if (con)
305         {
306                 con->outbuf = reply;
307                 con->outlen = len;
308                 con->outpos = 0;
309         }
310         else free(reply);
311 }