1 /* Copyright 2008 Bernhard R. Fischer, Daniel Haslinger.
2 *
3 * This file is part of OnionCat.
4 *
5 * OnionCat is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License.
8 *
9 * OnionCat is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with OnionCat. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /*! @file
19 * Contains functions for local controller interface.
20 *
21 * @author Bernhard Fischer <rahra _at_ cypherpunk at>
22 * @version 2008/02/03-01
23 */
24
25
26 #include "ocat.h"
27 #include "ocat_netdesc.h"
28 #include "ocathosts.h"
29
30
31 /*! ctrl_handler handles connections to local control port.
32 * @param p void* typcasted to int contains fd of connected socket.
33 * @return Currently always returns NULL.
34 *
35 * FIXME: ctrl_handler probably is not thread-safe.
36 */
ctrl_handler(void * p)37 void *ctrl_handler(void *p)
38 {
39 int fd, c;
40 FILE *ff, *fo;
41 char buf[FRAME_SIZE], addrstr[INET6_ADDRSTRLEN], onionstr[NDESC(name_size)], timestr[32], *s, *tokbuf, *bufp;
42 int rlen, cfd;
43 struct tm *tm;
44 OcatPeer_t *peer;
45 struct in6_addr in6;
46 int pfd[2];
47
48 detach_thread();
49
50 if (pipe(pfd) == -1)
51 log_msg(LOG_EMERG, "couldn't create pipe: \"%s\"", strerror(errno)), exit(1);
52
53 fd = (long) p;
54 if (CNF(config_read))
55 {
56 if (!(ff = fdopen(fd, "r+")))
57 {
58 log_msg(LOG_ERR, "could not open %d for writing: %s", fd, strerror(errno));
59 oe_close(pfd[0]);
60 oe_close(pfd[1]);
61 return NULL;
62 }
63 log_debug("fd %d fdopen'ed \"r+\"", fd);
64 fo = ff;
65 if (setvbuf(ff, NULL, _IONBF, 0))
66 log_msg(LOG_ERR, "could not setup line buffering: %s", strerror(errno));
67 }
68 else
69 {
70 if (!(ff = fdopen(fd, "r")))
71 {
72 log_msg(LOG_ERR, "could not open %d for reading: %s", fd, strerror(errno));
73 CNF(config_read) = 1;
74 oe_close(pfd[0]);
75 oe_close(pfd[1]);
76 return NULL;
77 }
78 log_debug("fd %d fdopen'ed \"r\"", fd);
79 fo = CNF(logf) ? CNF(logf) : stderr;
80 //CNF(config_read) = 1;
81 }
82
83 lock_setup();
84 CNF(ctrl_active)++;
85 unlock_setup();
86
87 fprintf(fo, "%s\n", CNF(version));
88 fprintf(fo, "*** ATTENTION! Controller interface not thread-safe yet! Usage could cause deadlocks. ***\n");
89
90 for (;;)
91 {
92 if (CNF(config_read))
93 fprintf(fo, "%s> ", CNF(onion_url));
94
95 c = getc(ff);
96 if (c == EOF)
97 {
98 log_debug("EOF received.");
99 break;
100 }
101 else if (c == 4)
102 {
103 log_debug("^D received.");
104 break;
105 }
106 else if (c == 0x1b)
107 {
108 log_debug("ESC received");
109 if (ungetc(c, ff) == EOF)
110 {
111 log_debug("received EOF on ungetc");
112 break;
113 }
114 }
115 else
116 {
117 if (ungetc(c, ff) == EOF)
118 {
119 log_debug("received EOF on ungetc");
120 break;
121 }
122 }
123
124 if (!fgets(buf, FRAME_SIZE, ff))
125 {
126 if (!feof(ff))
127 log_msg(LOG_ERR, "error reading from %d");
128 break;
129 }
130
131 #ifdef DEBUG
132 for (c = 0; c < strlen(buf); c++)
133 snprintf(&buf[strlen(buf) + 2 + c * 3], FRAME_SIZE - strlen(buf) - 2 - c * 3, "%02x ", buf[c]);
134 log_debug("xenc input buf: %s", &buf[strlen(buf) + 2]);
135 #endif
136
137 if (!(rlen = oe_remtr(buf)))
138 continue;
139
140 if (!(bufp = strtok_r(buf, " \t\r\n", &tokbuf)))
141 continue;
142
143 // "exit"/"quit" => terminate thread
144 if (!strncmp(bufp, "exit", 4) || !strncmp(bufp, "quit", 4))
145 break;
146 // "status"
147 else if (!strcmp(bufp, "status"))
148 {
149 lock_peers();
150 for (peer = get_first_peer(); peer; peer = peer->next)
151 // FIXME: should peer be locked?
152 if (peer->state == PEER_ACTIVE)
153 {
154 tm = localtime(&peer->otime);
155 strftime(timestr, 32, "%c", tm);
156 fprintf(fo, "[%s]\n fd = %d\n addr = %s\n dir = \"%s\" (%d)\n idle = %lds\n bytes_in = %ld\n bytes_out = %ld\n setup_delay = %lds\n opening_time = \"%s\"\n conn type = \"%s\" (%d)\n rand = 0x%08x\n",
157 IN6_IS_ADDR_UNSPECIFIED(&peer->addr) ? "--unidentified--" : ipv6tonion(&peer->addr, onionstr), peer->tcpfd,
158 inet_ntop(AF_INET6, &peer->addr, addrstr, INET6_ADDRSTRLEN),
159 peer->dir == PEER_INCOMING ? "IN" : "OUT", peer->dir,
160 (long) (time(NULL) - peer->time), peer->in, peer->out, (long) peer->sdelay, timestr,
161 peer->perm ? "PERMANENT" : "TEMPORARY", peer->perm, peer->rand
162 );
163 }
164 unlock_peers();
165 }
166 else if (!strcmp(bufp, "close"))
167 {
168 cfd = atoi(bufp +6);
169 lock_peers();
170 for (peer = get_first_peer(); peer; peer = peer->next)
171 if (peer->tcpfd == cfd)
172 {
173 oe_close(cfd);
174 delete_peer(peer);
175 log_msg(LOG_INFO | LOG_FCONN, "%d was successfully closed up on user request", cfd);
176 break;
177 }
178 if (!peer)
179 {
180 log_msg(LOG_INFO, "no peer with fd %d exists\n", cfd);
181 fprintf(fo, "no peer with fd %d exists\n", cfd);
182 }
183 unlock_peers();
184 }
185 else if (!strcmp(bufp, "threads"))
186 {
187 print_threads(ff);
188 }
189 else if (!strcmp(bufp, "terminate"))
190 {
191 log_msg(LOG_INFO, "terminate request from control port");
192 kill(getpid(), SIGINT);
193 }
194 else if (!strcmp(bufp, "route"))
195 {
196 if (rlen > 6)
197 {
198 if ((c = parse_route(bufp + 6)) == E_RT_SYNTAX)
199 if ((c = ipv6_parse_route(bufp + 6)) > 0)
200 c = 0;
201 switch (c)
202 {
203 case E_RT_NOTORGW:
204 s = "gateway has not TOR prefix";
205 break;
206
207 case E_RT_ILLNM:
208 s = "illegal netmask or prefix length";
209 break;
210
211 case E_RT_DUP:
212 s = "route already exists";
213 break;
214
215 case E_RT_GWSELF:
216 s = "gateway points to me";
217 break;
218
219 default:
220 s = "";
221 }
222 if (c)
223 fprintf(ff, "ERR %d %s\n", c, s);
224 }
225 else
226 {
227 print_routes(fo);
228 ipv6_print_routes(fo);
229 }
230 }
231 else if (!strcmp(bufp, "connect"))
232 {
233 if ((s = strtok_r(NULL, " \t\r\n", &tokbuf)))
234 {
235 if ((strlen(s) != 16) || (oniontipv6(s, &in6) == -1))
236 fprintf(ff, "ERR \"%s\" not valid .onion-URL\n", bufp + 8);
237 else
238 {
239 if (!(s = strtok_r(NULL, " \t\r\n", &tokbuf)))
240 socks_queue(in6, 0);
241 else if (!strcmp(s, "perm"))
242 socks_queue(in6, 1);
243 else
244 fprintf(ff, "ERR unknown param \"%s\"\n", s);
245 }
246 }
247 else
248 fprintf(ff, "ERR missing args\n");
249 }
250 else if (!strcmp(bufp, "macs"))
251 {
252 print_mac_tbl(ff);
253 }
254 else if (!strcmp(bufp, "queue"))
255 {
256 print_socks_queue((FILE*) (long) pfd[1]);
257 for (;;)
258 {
259 read(pfd[0], buf, 1);
260 if (!buf[0])
261 break;
262 fprintf(ff, "%c", buf[0]);
263 }
264 }
265 else if (!strcmp(bufp, "setup"))
266 {
267 print_setup_struct(ff);
268 }
269 else if (!strcmp(bufp, "version"))
270 {
271 fprintf(ff, "%s\n", CNF(version));
272 }
273 else if (!strcmp(bufp, "hosts"))
274 {
275 hosts_list(ff);
276 }
277 else if (!strcmp(bufp, "hreload"))
278 {
279 hosts_check();
280 }
281 else if (!strcmp(bufp, "help") || !strcmp(bufp, "?"))
282 {
283 fprintf(fo,
284 "commands:\n"
285 "exit | quit .... exit from control interface\n"
286 "terminate ...... terminate OnionCat\n"
287 "close <n> ...... close file descriptor <n> of a peer\n"
288 "hosts .......... list hosts database\n"
289 "hreload ........ reload hosts database\n"
290 "status ......... list peer status\n"
291 "threads ........ show active threads\n"
292 "route .......... show routing table\n"
293 "route <dst IP> <netmask> <IPv6 gw>\n"
294 " ............. add route to routing table\n"
295 "connect <.onion-URL> [\"perm\"]\n"
296 " ............. connect to a hidden service. if \"perm\" is set,\n"
297 " ............. connection will stay open forever\n"
298 "macs ........... show MAC address table\n"
299 "queue .......... list pending SOCKS connections\n"
300 "setup .......... show internal setup struct\n"
301 "version ........ show version\n"
302 );
303 }
304 else
305 {
306 fprintf(fo, "ERR unknown command: \"%s\"\n", buf);
307 }
308 }
309
310 if (CNF(config_read))
311 fprintf(fo, "Good bye!\n");
312 log_msg(LOG_INFO | LOG_FCONN, "closing session %d", fd);
313 if (fclose(ff) == EOF)
314 log_msg(LOG_ERR, "error closing control stream: \"%s\"", strerror(errno));
315 // fclose also closes the fd according to the man page
316
317 if (!CNF(config_read))
318 CNF(config_read) = 1;
319
320 // close pipe
321 oe_close(pfd[0]);
322 oe_close(pfd[1]);
323
324 lock_setup();
325 CNF(ctrl_active)--;
326 unlock_setup();
327
328 return NULL;
329 }
330
331
run_ctrl_handler(int fd)332 int run_ctrl_handler(int fd)
333 {
334 // check number of controller sessions
335 // FIXME: listener should be closed or acceptor delayed instead of
336 // counting after session acceptance.
337 lock_setup();
338 if (CNF(ctrl_active) >= CNF(max_ctrl))
339 {
340 log_msg(LOG_WARNING, "maximum number of controller sessions reached");
341 oe_close(fd);
342 fd = -1;
343 }
344 unlock_setup();
345 if (fd == -1)
346 return -1;
347
348 return (int) run_ocat_thread("ctrl_handler", ctrl_handler, (void*) (long) fd);
349 }
350
351
ocat_controller(void * p)352 void *ocat_controller(void *p)
353 {
354 run_listeners(CNF(ctrl_listen), CNF(ctrl_listen_fd), CNF(ctrl_listen_cnt), run_ctrl_handler);
355 return NULL;
356 }
357
358