"Fossies" - the Fresh Open Source Software Archive 
Member "portfwd-0.29/src/director.cc" (30 May 2005, 7678 Bytes) of package /linux/privat/old/portfwd-0.29.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
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
26 void director::close_sockets()
27 {
28 close(fd[0]);
29 close(fd[1]);
30 fd[0] = -1;
31 fd[1] = -1;
32 }
33
34 void director::kill_child()
35 {
36 if (child == -1)
37 return;
38
39 kill(child, SIGTERM);
40 child = -1;
41 }
42
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
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
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
203 void director::show() const
204 {
205 syslog(LOG_INFO, "[%s]", args);
206 }
207
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 */