1 /*
2   director.cc
3 
4   $Id: director.cc,v 1.10 2005/05/30 02:13:28 evertonm Exp $
5  */
6 
7 
8 #include <sys/types.h>
9 #include <syslog.h>
10 #include <string.h>
11 #include <errno.h>
12 #include <sys/socket.h>
13 #include <netinet/in.h>
14 #include <arpa/inet.h>
15 
16 
17 #include "signal.h"
18 #include "util.h"
19 #include "addr.h"
20 #include "solve.h"
21 #include "vector.hpp"
22 #include "director.hpp"
23 #include "fd_set.h"
24 
25 
close_sockets()26 void director::close_sockets()
27 {
28   close(fd[0]);
29   close(fd[1]);
30   fd[0] = -1;
31   fd[1] = -1;
32 }
33 
kill_child()34 void director::kill_child()
35 {
36   if (child == -1)
37     return;
38 
39   kill(child, SIGTERM);
40   child = -1;
41 }
42 
run(char * argv[])43 void director::run(char *argv[])
44 {
45 
46   ONVERBOSE(syslog(LOG_DEBUG, "Spawning director: %s", argv[0]));
47 
48   /*
49    * Create unix domain socket
50    */
51   if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
52     syslog(LOG_ERR, "Failure creating unix domain socket: %m");
53     return;
54   }
55 
56   pid_t child_pid = fork();
57   if (child_pid) {
58     /*
59      * Parent code
60      */
61 
62     if (child_pid == -1) {
63       syslog(LOG_ERR, "director::run(): fork() failed for temporary child: %m");
64       close_sockets();
65       return;
66     }
67 
68     return;
69   }
70 
71   /*
72    * Temporary child code
73    */
74 
75   child = fork();
76   if (child) {
77     /*
78      * Temporary child
79      */
80 
81     if (child == -1) {
82       syslog(LOG_ERR, "director::run(): fork() failed for actual child: %m");
83       exit(1);
84     }
85 
86     ONVERBOSE(syslog(LOG_INFO, "Delegating child PID %d to init", child));
87     exit(0);
88   }
89 
90   /*
91    * Actual child
92    */
93 
94   /* Attach stdin to fd[1] */
95   if (dup2(fd[1], 0) == -1) {
96     syslog(LOG_ERR, "Director child: fatal: could not attach stdin to unix domain socket: %m");
97     exit(1);
98   }
99 
100   /* Attach stdout to fd[1] */
101   if (dup2(fd[1], 1) == -1) {
102     syslog(LOG_ERR, "Director child: fatal: could not attach stdout to unix domain socket: %m");
103     exit(1);
104   }
105 
106   /*
107    * Restore default SIGPIPE disposition
108    */
109   void (*prev_handler)(int);
110   prev_handler = signal(SIGPIPE, SIG_DFL);
111   if (prev_handler == SIG_ERR)
112     syslog(LOG_ERR, "Director child: signal() failed restoring default SIGPIPE disposition: %m");
113 
114   ONVERBOSE(syslog(LOG_DEBUG, "Invoking director: %s", argv[0]));
115 
116   /*
117    * Invoke external director program
118    */
119 
120   close_fds(2); /* release all file descriptors */
121 
122   execv(argv[0], argv);
123 
124   syslog(LOG_ERR, "Director child PID %d: fatal: execv(%s) failed: %m", getpid(), argv[0]);
125   exit(1);
126 }
127 
spawn()128 int director::spawn()
129 {
130   int result = -1;
131   char **argv = 0;
132   char *dir_str = safe_strdup(args);
133 
134   ONVERBOSE(syslog(LOG_DEBUG, "Parsing director: %s", args));
135 
136   /*
137    * Parse director string into argv
138    */
139 
140   const char *SEP = "\r\n\t ";
141 
142   char *prog_name = strtok(dir_str, SEP);
143   if (!prog_name) {
144     syslog(LOG_ERR, "Invalid null director!");
145     goto clean;
146   }
147 
148   {
149     vector<char*> arg_list;
150     arg_list.push(prog_name);
151 
152     for (;;) {
153       char *arg = strtok(0, SEP);
154       if (!arg)
155 	break;
156       arg_list.push(arg);
157     }
158 
159     argv = (char **) malloc((arg_list.get_size() + 1) * sizeof(char*));
160     if (!argv)
161       goto clean;
162 
163     {
164       int i = 0;
165       iterator<vector<char*>,char*> it(arg_list);
166       for (it.start(); it.cont(); it.next(), ++i)
167 	argv[i] = it.get();
168       argv[i] = 0;
169     }
170   }
171 
172   /*
173    * Run external director
174    */
175   run(argv);
176 
177   result = 0;
178 
179 clean:
180   free(dir_str);
181 
182   if (argv)
183     free(argv);
184 
185   return result;
186 }
187 
director(const char * str)188 director::director(const char *str)
189 {
190   args = safe_strdup(str);
191   fd[0] = -1;
192   fd[1] = -1;
193   child = -1;
194   address_buf_size = 32;
195   address.len = 0;
196   address.addr = (char *) malloc(address_buf_size * sizeof(char *));
197   if (!address.addr) {
198     syslog(LOG_ERR, "director::director(): malloc(%d) failed", address_buf_size);
199     exit(1);
200   }
201 }
202 
show() const203 void director::show() const
204 {
205   syslog(LOG_INFO, "[%s]", args);
206 }
207 
get_addr(const char * protoname,const struct sockaddr_in * cli_sa,const struct sockaddr_in * local_cli_sa,const struct ip_addr ** addr,int * prt)208 int director::get_addr(const char *protoname,
209 	const struct sockaddr_in *cli_sa,
210 	const struct sockaddr_in *local_cli_sa,
211 	const struct ip_addr **addr, int *prt)
212 {
213   int fd0 = fd[0];
214 
215   if (fd0 == -1) {
216     if (spawn())
217       syslog(LOG_ERR, "director::get_addr(): spawn() failed");
218 
219     return -1;
220   }
221 
222   int cli_src_port = htons(cli_sa->sin_port);
223   int cli_loc_port = htons(local_cli_sa->sin_port);
224 
225   const int ADDR_STR_BUF_SZ = 128;
226   char cli_src_addr[ADDR_STR_BUF_SZ];
227   char cli_loc_addr[ADDR_STR_BUF_SZ];
228 
229   safe_strcpy(cli_src_addr, inet_ntoa(cli_sa->sin_addr), ADDR_STR_BUF_SZ);
230   safe_strcpy(cli_loc_addr, inet_ntoa(local_cli_sa->sin_addr), ADDR_STR_BUF_SZ);
231 
232   /*
233    * Write query
234    */
235 
236   const int WR_BUF_SZ = 128;
237   char wr_buf[WR_BUF_SZ];
238 
239   int len = snprintf(wr_buf, WR_BUF_SZ, "%s %s %d %s %d\n", protoname, cli_src_addr, cli_src_port, cli_loc_addr, cli_loc_port);
240   if (len == -1) {
241     syslog(LOG_ERR, "director::get_addr() failed due to snprintf() overflow");
242     return -1;
243   }
244 
245   int wr = write(fd0, wr_buf, len);
246   if (wr != len) {
247     if (wr == -1) {
248       switch (errno) {
249       case EBADF:
250       case EINVAL:
251       case EPIPE:
252 	syslog(LOG_ERR, "director::get_addr(): finishing child: can't write to external director: %m");
253 	close_sockets();
254 	kill_child();
255 	break;
256       default:
257 	syslog(LOG_ERR, "director::get_addr(): skipping: can't write to external director: %m");
258       }
259 
260       return -1;
261     }
262   }
263 
264   /*
265    * Read response
266    */
267 
268   const int RD_BUF_SZ = 128;
269   char rd_buf[RD_BUF_SZ];
270 
271   int rd = read(fd0, rd_buf, RD_BUF_SZ);
272   if (rd == -1) {
273     switch (errno) {
274       case EBADF:
275       case EINVAL:
276 	syslog(LOG_ERR, "director::get_addr(): finishing child: can't read from external director: %m");
277 	close_sockets();
278 	kill_child();
279 	break;
280     default:
281 	syslog(LOG_ERR, "director::get_addr(): skipping: can't read from external director: %m");
282     }
283 
284     return -1;
285   }
286 
287   if (rd == 0) {
288     syslog(LOG_ERR, "director::get_addr(): can't read from external director: external director closed communication");
289     close_sockets();
290     kill_child();
291 
292     return -1;
293   }
294 
295   /*
296    * Parse response
297    */
298 
299   const char *SEP = "\r\n\t ";
300   char *response = strtok(rd_buf, SEP);
301   if (!response) {
302     syslog(LOG_ERR, "External director returned null response");
303     return -1;
304   }
305 
306   if (!strcmp(response, "reject")) {
307     ONVERBOSE(syslog(LOG_INFO, "External director rejected source"));
308     return -1;
309   }
310 
311   if (strcmp(response, "forward")) {
312     syslog(LOG_ERR, "External director returned invalid response: %s", response);
313     return -1;
314   }
315 
316   char *remote_host = strtok(0, SEP);
317   if (!remote_host) {
318     syslog(LOG_ERR, "External director returned null hostname");
319     return -1;
320   }
321 
322   char *remote_port = strtok(0, SEP);
323   if (!remote_port) {
324     syslog(LOG_ERR, "External director returned null port");
325     return -1;
326   }
327 
328   ONVERBOSE2(syslog(LOG_DEBUG, "External director forwarded to %s:%s", remote_host, remote_port));
329 
330   /*
331    * Solve response
332    */
333 
334   int rem_port = solve_portnumber(remote_port, protoname);
335   if (rem_port == -1) {
336     syslog(LOG_ERR, "Can't resolve port pointed by external director: %s", remote_port);
337     return -1;
338   }
339 
340   const int ADDR_BUF_SZ = 32;
341   char addr_buf[ADDR_BUF_SZ];
342   size_t addr_buf_len = ADDR_BUF_SZ;
343 
344   int result = solve_hostname_addr(addr_buf, &addr_buf_len, remote_host);
345   if (result) {
346     syslog(LOG_ERR, "Can't resolve hostname pointed by external director: %s", remote_host);
347     return -1;
348   }
349 
350   /*
351    * Return address/port
352    */
353 
354   if (addr_buf_len > address_buf_size) {
355     syslog(LOG_ERR, "Insufficient space in local buffer for address (local_buffer_size=%d < address_length=%d)", address_buf_size, addr_buf_len);
356     return -1;
357   }
358 
359   /* Copy address */
360   memcpy(address.addr, addr_buf, addr_buf_len);
361   address.len = addr_buf_len;
362 
363   *addr = &address;
364   *prt = rem_port;
365 
366   return 0;
367 }
368 
369 /* Eof: dst_addr.cc */
370