Tor 0.4.9.0-alpha-dev
control_proto.c
Go to the documentation of this file.
1/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
2 * Copyright (c) 2007-2021, The Tor Project, Inc. */
3/* See LICENSE for licensing information */
4
5/**
6 * \file control_proto.c
7 * \brief Formatting functions for controller data.
8 */
9
10#include "core/or/or.h"
11
14#include "core/or/circuitlist.h"
18
26#include "lib/encoding/kvline.h"
27
28/** Append a NUL-terminated string <b>s</b> to the end of
29 * <b>conn</b>->outbuf.
30 */
31void
33{
34 size_t len = strlen(s);
35 connection_buf_add(s, len, TO_CONN(conn));
36}
37
38/** Acts like sprintf, but writes its formatted string to the end of
39 * <b>conn</b>->outbuf. */
40void
41connection_printf_to_buf(control_connection_t *conn, const char *format, ...)
42{
43 va_list ap;
44 char *buf = NULL;
45 int len;
46
47 va_start(ap,format);
48 len = tor_vasprintf(&buf, format, ap);
49 va_end(ap);
50
51 if (len < 0) {
52 log_err(LD_BUG, "Unable to format string for controller.");
53 tor_assert(0);
54 }
55
56 connection_buf_add(buf, (size_t)len, TO_CONN(conn));
57
58 tor_free(buf);
59}
60
61/** Given a <b>len</b>-character string in <b>data</b>, made of lines
62 * terminated by CRLF, allocate a new string in *<b>out</b>, and copy the
63 * contents of <b>data</b> into *<b>out</b>, adding a period before any period
64 * that appears at the start of a line, and adding a period-CRLF line at
65 * the end. Replace all LF characters sequences with CRLF. Return the number
66 * of bytes in *<b>out</b>.
67 *
68 * This corresponds to CmdData in control-spec.txt.
69 */
70size_t
71write_escaped_data(const char *data, size_t len, char **out)
72{
73 tor_assert(len < SIZE_MAX - 9);
74 size_t sz_out = len+8+1;
75 char *outp;
76 const char *start = data, *end;
77 size_t i;
78 int start_of_line;
79 for (i=0; i < len; ++i) {
80 if (data[i] == '\n') {
81 sz_out += 2; /* Maybe add a CR; maybe add a dot. */
82 if (sz_out >= SIZE_T_CEILING) {
83 log_warn(LD_BUG, "Input to write_escaped_data was too long");
84 *out = tor_strdup(".\r\n");
85 return 3;
86 }
87 }
88 }
89 *out = outp = tor_malloc(sz_out);
90 end = data+len;
91 start_of_line = 1;
92 while (data < end) {
93 if (*data == '\n') {
94 if (data > start && data[-1] != '\r')
95 *outp++ = '\r';
96 start_of_line = 1;
97 } else if (*data == '.') {
98 if (start_of_line) {
99 start_of_line = 0;
100 *outp++ = '.';
101 }
102 } else {
103 start_of_line = 0;
104 }
105 *outp++ = *data++;
106 }
107 if (outp < *out+2 || fast_memcmp(outp-2, "\r\n", 2)) {
108 *outp++ = '\r';
109 *outp++ = '\n';
110 }
111 *outp++ = '.';
112 *outp++ = '\r';
113 *outp++ = '\n';
114 *outp = '\0'; /* NUL-terminate just in case. */
115 tor_assert(outp >= *out);
116 tor_assert((size_t)(outp - *out) <= sz_out);
117 return outp - *out;
118}
119
120/** Given a <b>len</b>-character string in <b>data</b>, made of lines
121 * terminated by CRLF, allocate a new string in *<b>out</b>, and copy
122 * the contents of <b>data</b> into *<b>out</b>, removing any period
123 * that appears at the start of a line, and replacing all CRLF sequences
124 * with LF. Return the number of
125 * bytes in *<b>out</b>.
126 *
127 * This corresponds to CmdData in control-spec.txt.
128 */
129size_t
130read_escaped_data(const char *data, size_t len, char **out)
131{
132 char *outp;
133 const char *next;
134 const char *end;
135
136 *out = outp = tor_malloc(len+1);
137
138 end = data+len;
139
140 while (data < end) {
141 /* we're at the start of a line. */
142 if (*data == '.')
143 ++data;
144 next = memchr(data, '\n', end-data);
145 if (next) {
146 size_t n_to_copy = next-data;
147 /* Don't copy a CR that precedes this LF. */
148 if (n_to_copy && *(next-1) == '\r')
149 --n_to_copy;
150 memcpy(outp, data, n_to_copy);
151 outp += n_to_copy;
152 data = next+1; /* This will point at the start of the next line,
153 * or the end of the string, or a period. */
154 } else {
155 memcpy(outp, data, end-data);
156 outp += (end-data);
157 *outp = '\0';
158 return outp - *out;
159 }
160 *outp++ = '\n';
161 }
162
163 *outp = '\0';
164 return outp - *out;
165}
166
167/** Send a "DONE" message down the control connection <b>conn</b>. */
168void
170{
171 control_write_endreply(conn, 250, "OK");
172}
173
174/** Write a reply to the control channel.
175 *
176 * @param conn control connection
177 * @param code numeric result code
178 * @param c separator character, usually ' ', '-', or '+'
179 * @param s string reply content
180 */
181MOCK_IMPL(void,
182control_write_reply, (control_connection_t *conn, int code, int c,
183 const char *s))
184{
185 connection_printf_to_buf(conn, "%03d%c%s\r\n", code, c, s);
186}
187
188/** Write a formatted reply to the control channel.
189 *
190 * @param conn control connection
191 * @param code numeric result code
192 * @param c separator character, usually ' ', '-', or '+'
193 * @param fmt format string
194 * @param ap va_list from caller
195 */
196void
198 const char *fmt, va_list ap)
199{
200 char *buf = NULL;
201 int len;
202
203 len = tor_vasprintf(&buf, fmt, ap);
204 if (len < 0) {
205 log_err(LD_BUG, "Unable to format string for controller.");
206 tor_assert(0);
207 }
208 control_write_reply(conn, code, c, buf);
209 tor_free(buf);
210}
211
212/** Write an EndReplyLine */
213void
214control_write_endreply(control_connection_t *conn, int code, const char *s)
215{
216 control_write_reply(conn, code, ' ', s);
217}
218
219/** Write a formatted EndReplyLine */
220void
222 const char *fmt, ...)
223{
224 va_list ap;
225
226 va_start(ap, fmt);
227 control_vprintf_reply(conn, code, ' ', fmt, ap);
228 va_end(ap);
229}
230
231/** Write a MidReplyLine */
232void
233control_write_midreply(control_connection_t *conn, int code, const char *s)
234{
235 control_write_reply(conn, code, '-', s);
236}
237
238/** Write a formatted MidReplyLine */
239void
240control_printf_midreply(control_connection_t *conn, int code, const char *fmt,
241 ...)
242{
243 va_list ap;
244
245 va_start(ap, fmt);
246 control_vprintf_reply(conn, code, '-', fmt, ap);
247 va_end(ap);
248}
249
250/** Write a DataReplyLine */
251void
252control_write_datareply(control_connection_t *conn, int code, const char *s)
253{
254 control_write_reply(conn, code, '+', s);
255}
256
257/** Write a formatted DataReplyLine */
258void
259control_printf_datareply(control_connection_t *conn, int code, const char *fmt,
260 ...)
261{
262 va_list ap;
263
264 va_start(ap, fmt);
265 control_vprintf_reply(conn, code, '+', fmt, ap);
266 va_end(ap);
267}
268
269/** Write a CmdData */
270void
272{
273 char *esc = NULL;
274 size_t esc_len;
275
276 esc_len = write_escaped_data(data, strlen(data), &esc);
277 connection_buf_add(esc, esc_len, TO_CONN(conn));
278 tor_free(esc);
279}
280
281/** Write a single reply line to @a conn.
282 *
283 * @param conn control connection
284 * @param line control reply line to write
285 * @param lastone true if this is the last reply line of a multi-line reply
286 */
287void
289 const control_reply_line_t *line, bool lastone)
290{
291 const config_line_t *kvline = line->kvline;
292 char *s = NULL;
293
294 if (strpbrk(kvline->value, "\r\n") != NULL) {
295 /* If a key-value pair needs to be encoded as CmdData, it can be
296 the only key-value pair in that reply line */
297 tor_assert(kvline->next == NULL);
298 control_printf_datareply(conn, line->code, "%s=", kvline->key);
299 control_write_data(conn, kvline->value);
300 return;
301 }
302 s = kvline_encode(kvline, line->flags);
303 if (lastone) {
304 control_write_endreply(conn, line->code, s);
305 } else {
306 control_write_midreply(conn, line->code, s);
307 }
308 tor_free(s);
309}
310
311/** Write a set of reply lines to @a conn.
312 *
313 * @param conn control connection
314 * @param lines smartlist of pointers to control_reply_line_t to write
315 */
316void
318{
319 bool lastone = false;
320
322 if (line_sl_idx >= line_sl_len - 1)
323 lastone = true;
324 control_write_reply_line(conn, line, lastone);
325 } SMARTLIST_FOREACH_END(line);
326}
327
328/** Add a single key-value pair as a new reply line to a control reply
329 * line list.
330 *
331 * @param reply smartlist of pointers to control_reply_line_t
332 * @param code numeric control reply code
333 * @param flags kvline encoding flags
334 * @param key key
335 * @param val value
336 */
337void
338control_reply_add_one_kv(smartlist_t *reply, int code, int flags,
339 const char *key, const char *val)
340{
341 control_reply_line_t *line = tor_malloc_zero(sizeof(*line));
342
343 line->code = code;
344 line->flags = flags;
345 config_line_append(&line->kvline, key, val);
346 smartlist_add(reply, line);
347}
348
349/** Append a single key-value pair to last reply line in a control
350 * reply line list.
351 *
352 * @param reply smartlist of pointers to control_reply_line_t
353 * @param key key
354 * @param val value
355 */
356void
357control_reply_append_kv(smartlist_t *reply, const char *key, const char *val)
358{
359 int len = smartlist_len(reply);
361
362 tor_assert(len > 0);
363
364 line = smartlist_get(reply, len - 1);
365 config_line_append(&line->kvline, key, val);
366}
367
368/** Add new reply line consisting of the string @a s
369 *
370 * @param reply smartlist of pointers to control_reply_line_t
371 * @param code numeric control reply code
372 * @param s string containing the rest of the reply line
373 */
374void
375control_reply_add_str(smartlist_t *reply, int code, const char *s)
376{
377 control_reply_add_one_kv(reply, code, KV_OMIT_KEYS|KV_RAW, "", s);
378}
379
380/** Format a new reply line
381 *
382 * @param reply smartlist of pointers to control_reply_line_t
383 * @param code numeric control reply code
384 * @param fmt format string
385 */
386void
387control_reply_add_printf(smartlist_t *reply, int code, const char *fmt, ...)
388{
389 va_list ap;
390 char *buf = NULL;
391
392 va_start(ap, fmt);
393 (void)tor_vasprintf(&buf, fmt, ap);
394 va_end(ap);
395 control_reply_add_str(reply, code, buf);
396 tor_free(buf);
397}
398
399/** Add a "250 OK" line to a set of control reply lines */
400void
402{
403 control_reply_add_str(reply, 250, "OK");
404}
405
406/** Free a control_reply_line_t. Don't call this directly; use the
407 * control_reply_line_free() macro instead. */
408void
410{
411 if (!line)
412 return;
413 config_free_lines(line->kvline);
414 tor_free_(line);
415}
416
417/** Clear a smartlist of control_reply_line_t. Doesn't free the
418 * smartlist, but does free each individual line. */
419void
421{
424 smartlist_clear(reply);
425}
426
427/** Free a smartlist of control_reply_line_t. Don't call this
428 * directly; use the control_reply_free() macro instead. */
429void
431{
432 control_reply_clear(reply);
433 smartlist_free_(reply);
434}
Header file for circuitbuild.c.
Header file for circuitlist.c.
void config_line_append(config_line_t **lst, const char *key, const char *val)
Definition: confline.c:32
Header file for connection.c.
Header file for connection_edge.c.
Controller connection structure.
void control_write_data(control_connection_t *conn, const char *data)
void control_printf_datareply(control_connection_t *conn, int code, const char *fmt,...)
void control_write_endreply(control_connection_t *conn, int code, const char *s)
void control_printf_midreply(control_connection_t *conn, int code, const char *fmt,...)
void control_write_midreply(control_connection_t *conn, int code, const char *s)
void send_control_done(control_connection_t *conn)
void control_vprintf_reply(control_connection_t *conn, int code, int c, const char *fmt, va_list ap)
void control_write_reply(control_connection_t *conn, int code, int c, const char *s)
void control_printf_endreply(control_connection_t *conn, int code, const char *fmt,...)
size_t read_escaped_data(const char *data, size_t len, char **out)
void connection_write_str_to_buf(const char *s, control_connection_t *conn)
Definition: control_proto.c:32
void control_write_datareply(control_connection_t *conn, int code, const char *s)
size_t write_escaped_data(const char *data, size_t len, char **out)
Definition: control_proto.c:71
void connection_printf_to_buf(control_connection_t *conn, const char *format,...)
Definition: control_proto.c:41
Header file for control_proto.c.
Circuit-build-stse structure.
#define fast_memcmp(a, b, c)
Definition: di_ops.h:28
Entry connection structure.
void control_reply_append_kv(smartlist_t *reply, const char *key, const char *val)
void control_write_reply_lines(control_connection_t *conn, smartlist_t *lines)
void control_reply_clear(smartlist_t *reply)
void control_reply_add_printf(smartlist_t *reply, int code, const char *fmt,...)
void control_reply_add_one_kv(smartlist_t *reply, int code, int flags, const char *key, const char *val)
void control_reply_line_free_(control_reply_line_t *line)
void control_reply_add_str(smartlist_t *reply, int code, const char *s)
void control_reply_add_done(smartlist_t *reply)
void control_write_reply_line(control_connection_t *conn, const control_reply_line_t *line, bool lastone)
void control_reply_free_(smartlist_t *reply)
#define control_reply_line_free(line)
Free and null a control_reply_line_t.
Definition: control_proto.h:55
char * kvline_encode(const config_line_t *line, unsigned flags)
Definition: kvline.c:126
Header for kvline.c.
#define LD_BUG
Definition: log.h:86
void tor_free_(void *mem)
Definition: malloc.c:227
#define tor_free(p)
Definition: malloc.h:56
Header file for nodelist.c.
Master header file for Tor-specific functionality.
#define TO_CONN(c)
Definition: or.h:612
OR connection structure.
Origin circuit structure.
int tor_vasprintf(char **strp, const char *fmt, va_list args)
Definition: printf.c:96
Header for smartlist.c.
void smartlist_add(smartlist_t *sl, void *element)
void smartlist_clear(smartlist_t *sl)
void smartlist_free_(smartlist_t *sl)
#define SMARTLIST_FOREACH_BEGIN(sl, type, var)
#define SMARTLIST_FOREACH(sl, type, var, cmd)
Client request structure.
A reply line for the control protocol.
Definition: control_proto.h:43
config_line_t * kvline
Definition: control_proto.h:46
#define MOCK_IMPL(rv, funcname, arglist)
Definition: testsupport.h:133
#define SIZE_T_CEILING
Definition: torint.h:126
#define tor_assert(expr)
Definition: util_bug.h:103