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