From: Philipp Zabel Date: Fri, 26 Feb 2010 17:19:01 +0000 (+0100) Subject: Add TorControl X-Git-Tag: v0.0.4-1~3 X-Git-Url: http://vcs.maemo.org/git/?a=commitdiff_plain;ds=sidebyside;h=a8e2af276fd78044a27fdd0c2293dabd664ad0fc;p=tor-status Add TorControl --- diff --git a/Makefile.am b/Makefile.am index a0aaec9..e7d7778 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,10 +22,13 @@ icon18_DATA = \ data/statusarea_tor_connecting.png status_area_applet_tor_la_SOURCES = \ - src/status-area-applet-tor.c + src/status-area-applet-tor.c \ + src/torcontrol.c \ + src/torcontrol-socket.c status_area_applet_tor_la_VALASOURCES = \ - src/status-area-applet-tor.vala + src/status-area-applet-tor.vala \ + src/torcontrol.vala src/status-area-applet-tor.c: ${status_area_applet_tor_la_VALASOURCES} ${VALAC} -C ${status_area_applet_tor_la_VALAFLAGS} $^ diff --git a/configure.ac b/configure.ac index d934f83..3441f00 100644 --- a/configure.ac +++ b/configure.ac @@ -14,7 +14,7 @@ AC_STDC_HEADERS AC_PROG_INSTALL AC_PROG_LIBTOOL -CFLAGS="$CFLAGS -Wall -ansi -Wmissing-prototypes -Wmissing-declarations" +CFLAGS="$CFLAGS -Wall -Wmissing-prototypes -Wmissing-declarations" PKG_CHECK_MODULES(CONIC, conic >= 0.22) AC_SUBST(CONIC_LIBS) diff --git a/src/torcontrol-socket.c b/src/torcontrol-socket.c new file mode 100644 index 0000000..a8002d0 --- /dev/null +++ b/src/torcontrol-socket.c @@ -0,0 +1,80 @@ +/* This file is part of libtorcontrol. + * + * Copyright (C) 2010 Philipp Zabel + * + * libtorcontrol is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libtorcontrol is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libtorcontrol. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +int tor_control_open_socket (int port, GError **error); + +int tor_control_open_socket (int port, GError **error) { + int status; + char *service; + struct addrinfo hints; + struct addrinfo *result, *rp; + int sockfd; + int fd; + + memset (&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + service = g_strdup_printf ("%d", port); + + status = getaddrinfo (NULL, service, &hints, &result); + + g_free (service); + + if (status != 0) { + g_set_error (error, + /* domain: */ g_quark_from_string ("TORCTLERR"), /* code: */ 1, + "getaddrinfo error: %s", gai_strerror (status)); + return -1; + } + + for (rp = result; rp != NULL; rp = rp->ai_next) { + sockfd = socket (result->ai_family, result->ai_socktype, + result->ai_protocol); + if (sockfd == -1) + continue; + + status = connect (sockfd, result->ai_addr, result->ai_addrlen); + if (status != -1) + break; + + close (sockfd); + } + + freeaddrinfo (result); + + if (rp == NULL) { + g_set_error (error, + /* domain: */ g_quark_from_string ("TORCTLERR"), /* code: */ 2, + "socket/connect error: %s", strerror (errno)); + return -1; + } + + return sockfd; +} + diff --git a/src/torcontrol.vala b/src/torcontrol.vala new file mode 100644 index 0000000..817255b --- /dev/null +++ b/src/torcontrol.vala @@ -0,0 +1,248 @@ +/* This file is part of libtorcontrol. + * + * Copyright (C) 2010 Philipp Zabel + * + * libtorcontrol is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libtorcontrol is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libtorcontrol. If not, see . + */ + +namespace TorControl { + // There are no Vala bindings for getaddrinfo, open socket in C code. + [CCode (cname = "tor_control_open_socket")] + extern int open_socket (int port) throws GLib.Error; + + errordomain TorError { + UNRECOGNIZED_COMMAND_ARGUMENT = 513, + AUTHENTICATION_REQUIRED = 514, + BAD_AUTHENTICATION = 515, + UNSPECIFIED = 550, + UNRECOGNIZED_EVENT = 552 + } + + public class Connection : GLib.Object { + int control_port; + int socket_fd; + GLib.IOChannel channel; + bool async_lock = false; + + public Connection () throws GLib.Error { + control_port = 9051; + socket_fd = TorControl.open_socket (control_port); + channel = new GLib.IOChannel.unix_new (socket_fd); + channel.set_encoding (null); + channel.set_buffered (false); + } + + public Connection.with_port (int port) throws GLib.Error { + control_port = port; + socket_fd = TorControl.open_socket (control_port); + channel = new GLib.IOChannel.unix_new (socket_fd); + channel.set_encoding (null); + channel.set_buffered (false); + } + + private void send_command (string command) throws GLib.ConvertError, GLib.IOChannelError { + size_t bytes_written; + unowned char[] buf = (char[]) command; + + buf.length = (int) command.length; + + channel.write_chars (buf, out bytes_written); + } + + private string receive_result () throws GLib.ConvertError, GLib.IOChannelError, TorError { + size_t bytes_read; + char[] buf = new char[4096]; + + channel.read_chars (buf, out bytes_read); + + buf[bytes_read] = 0; + + // FIXME + unowned string p = (string) buf; + while (p.has_prefix ("6")) { + char *crlf = p.str ("\r\n"); + if (crlf == null) { + print ("ERROR - missing newline\n"); + return ""; + } + crlf[0] = '\0'; + print ("ASYNC: \"%s\"\n", p); + p = (string) (crlf + 2); + } + + if (p.has_prefix ("2")) { + return "%s".printf (p); + } if (p.has_prefix ("5")) { + int code = p.to_int (); + string message = "%s".printf (p.offset (4)); + + switch (code) { + case 513: + throw new TorError.UNRECOGNIZED_COMMAND_ARGUMENT (message); + case 514: + throw new TorError.AUTHENTICATION_REQUIRED (message); + case 515: + throw new TorError.BAD_AUTHENTICATION (message); + case 550: + throw new TorError.UNSPECIFIED (message); + case 552: + throw new TorError.UNRECOGNIZED_EVENT (message); + default: + print ("Unknown error %d: \"%s\"\n", code, message); + return ""; + } + } else { + print ("Error: \"%s\"\n", p); + return ""; + } + } + + private SourceFunc continuation; + private async string receive_result_async () throws GLib.ConvertError, GLib.IOChannelError, TorError { + channel.add_watch (IOCondition.IN | IOCondition.PRI, tor_io_func); + continuation = receive_result_async.callback; + yield; + string result = receive_result (); + async_lock = false; + return result; + } + + private bool tor_io_func (IOChannel source, IOCondition condition) { + if ((condition & (IOCondition.IN | IOCondition.PRI)) != 0) { + if (async_lock) + continuation (); + } + return false; + } + + public void authenticate (string password = "") throws GLib.ConvertError, GLib.IOChannelError, TorError { + send_command ("AUTHENTICATE \"%s\"\r\n".printf (password)); + + receive_result (); + } + + public async void authenticate_async (string password = "") throws GLib.ConvertError, GLib.IOChannelError, TorError { + if (async_lock) { + throw new TorError.UNSPECIFIED ("only one async command at a time!"); + } + async_lock = true; + send_command ("AUTHENTICATE \"%s\"\r\n".printf (password)); + + yield receive_result_async (); + } + + public string get_conf (string conf) throws GLib.ConvertError, GLib.IOChannelError, TorError { + send_command ("GETCONF " + conf + "\r\n"); + + string result = receive_result (); + + if (result[3] == '-') { + return result; + } if (result.offset (4).has_prefix (conf + "=")) { + return "%s".printf (result.offset (4 + conf.length + 1)); + } else { + print ("get_conf error: \"%s\"\n", result); + return ""; + } + } + + public async string get_conf_async (string conf) throws GLib.ConvertError, GLib.IOChannelError, TorError { + if (async_lock) { + throw new TorError.UNSPECIFIED ("only one async command at a time!"); + } + async_lock = true; + send_command ("GETCONF " + conf + "\r\n"); + + string result = yield receive_result_async (); + + if (result[3] == '-') { + return result; + } if (result.offset (4).has_prefix (conf + "=")) { + return "%s".printf (result.offset (4 + conf.length + 1)); + } else { + print ("get_conf error: \"%s\"\n", result); + return ""; + } + } + + + public bool get_conf_bool (string conf) throws GLib.ConvertError, GLib.IOChannelError, TorError { + return (bool) get_conf (conf).to_int (); + } + + public async bool get_conf_bool_async (string conf) throws GLib.ConvertError, GLib.IOChannelError, TorError { + var result = yield get_conf_async (conf).to_int (); + return (bool) result; + } + + public SList get_conf_list (string conf) throws GLib.ConvertError, GLib.IOChannelError, TorError { + string result = get_conf (conf); + return evaluate_list (conf, result); + } + + public async SList get_conf_list_async (string conf) throws GLib.ConvertError, GLib.IOChannelError, TorError { + string result = yield get_conf_async (conf); + return evaluate_list (conf, result); + } + + private SList evaluate_list (string conf, string result) { + string prefix = conf + "="; + SList list = new SList (); + unowned string p = result; + for (char *crlf = p.str ("\r\n"); crlf != null; crlf = p.str ("\r\n")) { + crlf[0] = '\0'; + if (p.offset (4).has_prefix (prefix)) { + list.append (p.offset (4 + prefix.length)); + } + p = (string) (crlf + 2); + } + return list; + } + + public void set_conf (string conf, string val) throws GLib.ConvertError, GLib.IOChannelError, TorError { + send_command ("SETCONF " + conf + "=" + val + "\r\n"); + + string result = receive_result (); + + if (!result.offset (4).has_prefix ("OK")) { + print ("set_conf error: \"%s\"\n", result); + } + } + + public void set_conf_bool (string conf, bool val) throws GLib.ConvertError, GLib.IOChannelError, TorError { + set_conf (conf, val ? "1" : "0"); + } + + public void set_conf_list (string conf, SList values) throws GLib.ConvertError, GLib.IOChannelError, TorError { + string command = "SETCONF"; + foreach (string val in values) { + command += " %s=%s".printf (conf, val); + } + send_command (command + "\r\n"); + + string result = receive_result (); + + if (!result.offset (4).has_prefix ("OK")) { + print ("set_conf_list error: \"%s\"\n", result); + } + } + + public void set_events (string events) throws GLib.ConvertError, GLib.IOChannelError, TorError { + send_command ("SETEVENTS " + events + "\r\n"); + + receive_result (); + } + + } +}