1 /* This file is part of libtorcontrol.
3 * Copyright (C) 2010 Philipp Zabel
5 * libtorcontrol 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, either version 3 of the License, or
8 * (at your option) any later version.
10 * libtorcontrol is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with libtorcontrol. If not, see <http://www.gnu.org/licenses/>.
19 namespace TorControl {
20 // There are no Vala bindings for getaddrinfo, open socket in C code.
21 [CCode (cname = "tor_control_open_socket")]
22 extern int open_socket (int port) throws GLib.Error;
24 errordomain TorError {
25 UNRECOGNIZED_COMMAND_ARGUMENT = 513,
26 AUTHENTICATION_REQUIRED = 514,
27 BAD_AUTHENTICATION = 515,
29 UNRECOGNIZED_EVENT = 552
32 public class Connection : GLib.Object {
35 GLib.IOChannel channel;
36 bool async_lock = false;
38 public Connection () throws GLib.Error {
40 socket_fd = TorControl.open_socket (control_port);
41 channel = new GLib.IOChannel.unix_new (socket_fd);
42 channel.set_encoding (null);
43 channel.set_buffered (false);
46 public Connection.with_port (int port) throws GLib.Error {
48 socket_fd = TorControl.open_socket (control_port);
49 channel = new GLib.IOChannel.unix_new (socket_fd);
50 channel.set_encoding (null);
51 channel.set_buffered (false);
54 private void send_command (string command) throws GLib.ConvertError, GLib.IOChannelError {
56 unowned char[] buf = (char[]) command;
58 buf.length = (int) command.length;
60 channel.write_chars (buf, out bytes_written);
63 private string receive_result () throws GLib.ConvertError, GLib.IOChannelError, TorError {
65 char[] buf = new char[4096];
67 channel.read_chars (buf, out bytes_read);
72 unowned string p = (string) buf;
73 while (p.has_prefix ("6")) {
74 char *crlf = p.str ("\r\n");
76 print ("ERROR - missing newline\n");
80 print ("ASYNC: \"%s\"\n", p);
81 p = (string) (crlf + 2);
84 if (p.has_prefix ("2")) {
85 return "%s".printf (p);
86 } if (p.has_prefix ("5")) {
87 int code = p.to_int ();
88 string message = "%s".printf (p.offset (4));
92 throw new TorError.UNRECOGNIZED_COMMAND_ARGUMENT (message);
94 throw new TorError.AUTHENTICATION_REQUIRED (message);
96 throw new TorError.BAD_AUTHENTICATION (message);
98 throw new TorError.UNSPECIFIED (message);
100 throw new TorError.UNRECOGNIZED_EVENT (message);
102 print ("Unknown error %d: \"%s\"\n", code, message);
106 print ("Error: \"%s\"\n", p);
111 private SourceFunc continuation;
112 private async string receive_result_async () throws GLib.ConvertError, GLib.IOChannelError, TorError {
113 channel.add_watch (IOCondition.IN | IOCondition.PRI, tor_io_func);
114 continuation = receive_result_async.callback;
116 string result = receive_result ();
121 private bool tor_io_func (IOChannel source, IOCondition condition) {
122 if ((condition & (IOCondition.IN | IOCondition.PRI)) != 0) {
129 public void authenticate (string password = "") throws GLib.ConvertError, GLib.IOChannelError, TorError {
130 send_command ("AUTHENTICATE \"%s\"\r\n".printf (password));
135 public async void authenticate_async (string password = "") throws GLib.ConvertError, GLib.IOChannelError, TorError {
137 throw new TorError.UNSPECIFIED ("only one async command at a time!");
140 send_command ("AUTHENTICATE \"%s\"\r\n".printf (password));
142 yield receive_result_async ();
145 public string get_conf (string conf) throws GLib.ConvertError, GLib.IOChannelError, TorError {
146 send_command ("GETCONF " + conf + "\r\n");
148 string result = receive_result ();
150 if (result[3] == '-') {
152 } if (result.offset (4).has_prefix (conf + "=")) {
153 return "%s".printf (result.offset (4 + conf.length + 1));
155 print ("get_conf error: \"%s\"\n", result);
160 public async string get_conf_async (string conf) throws GLib.ConvertError, GLib.IOChannelError, TorError {
162 throw new TorError.UNSPECIFIED ("only one async command at a time!");
165 send_command ("GETCONF " + conf + "\r\n");
167 string result = yield receive_result_async ();
169 if (result[3] == '-') {
171 } if (result.offset (4).has_prefix (conf + "=")) {
172 return "%s".printf (result.offset (4 + conf.length + 1));
174 print ("get_conf error: \"%s\"\n", result);
180 public bool get_conf_bool (string conf) throws GLib.ConvertError, GLib.IOChannelError, TorError {
181 return (bool) get_conf (conf).to_int ();
184 public async bool get_conf_bool_async (string conf) throws GLib.ConvertError, GLib.IOChannelError, TorError {
185 var result = yield get_conf_async (conf).to_int ();
186 return (bool) result;
189 public SList<string> get_conf_list (string conf) throws GLib.ConvertError, GLib.IOChannelError, TorError {
190 string result = get_conf (conf);
191 return evaluate_list (conf, result);
194 public async SList<string> get_conf_list_async (string conf) throws GLib.ConvertError, GLib.IOChannelError, TorError {
195 string result = yield get_conf_async (conf);
196 return evaluate_list (conf, result);
199 private SList<string> evaluate_list (string conf, string result) {
200 string prefix = conf + "=";
201 SList<string> list = new SList<string> ();
202 unowned string p = result;
203 for (char *crlf = p.str ("\r\n"); crlf != null; crlf = p.str ("\r\n")) {
205 if (p.offset (4).has_prefix (prefix)) {
206 list.append (p.offset (4 + prefix.length));
208 p = (string) (crlf + 2);
213 public void set_conf (string conf, string val) throws GLib.ConvertError, GLib.IOChannelError, TorError {
214 send_command ("SETCONF " + conf + "=" + val + "\r\n");
216 string result = receive_result ();
218 if (!result.offset (4).has_prefix ("OK")) {
219 print ("set_conf error: \"%s\"\n", result);
223 public void set_conf_bool (string conf, bool val) throws GLib.ConvertError, GLib.IOChannelError, TorError {
224 set_conf (conf, val ? "1" : "0");
227 public void set_conf_list (string conf, SList<string> values) throws GLib.ConvertError, GLib.IOChannelError, TorError {
228 string command = "SETCONF";
229 foreach (string val in values) {
230 command += " %s=%s".printf (conf, val);
232 send_command (command + "\r\n");
234 string result = receive_result ();
236 if (!result.offset (4).has_prefix ("OK")) {
237 print ("set_conf_list error: \"%s\"\n", result);
241 public void set_events (string events) throws GLib.ConvertError, GLib.IOChannelError, TorError {
242 send_command ("SETEVENTS " + events + "\r\n");