Line data Source code
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 :
12 : #include "core/mainloop/connection.h"
13 : #include "core/or/circuitbuild.h"
14 : #include "core/or/circuitlist.h"
15 : #include "core/or/connection_edge.h"
16 : #include "feature/control/control_proto.h"
17 : #include "feature/nodelist/nodelist.h"
18 :
19 : #include "core/or/cpath_build_state_st.h"
20 : #include "core/or/entry_connection_st.h"
21 : #include "core/or/or_connection_st.h"
22 : #include "core/or/origin_circuit_st.h"
23 : #include "core/or/socks_request_st.h"
24 : #include "feature/control/control_connection_st.h"
25 : #include "lib/container/smartlist.h"
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 : */
31 : void
32 0 : connection_write_str_to_buf(const char *s, control_connection_t *conn)
33 : {
34 0 : size_t len = strlen(s);
35 0 : connection_buf_add(s, len, TO_CONN(conn));
36 0 : }
37 :
38 : /** Acts like sprintf, but writes its formatted string to the end of
39 : * <b>conn</b>-\>outbuf. */
40 : void
41 32 : connection_printf_to_buf(control_connection_t *conn, const char *format, ...)
42 : {
43 32 : va_list ap;
44 32 : char *buf = NULL;
45 32 : int len;
46 :
47 32 : va_start(ap,format);
48 32 : len = tor_vasprintf(&buf, format, ap);
49 32 : va_end(ap);
50 :
51 32 : if (len < 0) {
52 0 : log_err(LD_BUG, "Unable to format string for controller.");
53 0 : tor_assert(0);
54 : }
55 :
56 32 : connection_buf_add(buf, (size_t)len, TO_CONN(conn));
57 :
58 32 : tor_free(buf);
59 32 : }
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 : */
70 : size_t
71 6 : write_escaped_data(const char *data, size_t len, char **out)
72 : {
73 6 : tor_assert(len < SIZE_MAX - 9);
74 6 : size_t sz_out = len+8+1;
75 6 : char *outp;
76 6 : const char *start = data, *end;
77 6 : size_t i;
78 6 : int start_of_line;
79 84624 : for (i=0; i < len; ++i) {
80 84618 : if (data[i] == '\n') {
81 1338 : sz_out += 2; /* Maybe add a CR; maybe add a dot. */
82 1338 : if (sz_out >= SIZE_T_CEILING) {
83 0 : log_warn(LD_BUG, "Input to write_escaped_data was too long");
84 0 : *out = tor_strdup(".\r\n");
85 0 : return 3;
86 : }
87 : }
88 : }
89 6 : *out = outp = tor_malloc(sz_out);
90 6 : end = data+len;
91 6 : start_of_line = 1;
92 84624 : while (data < end) {
93 84618 : if (*data == '\n') {
94 1338 : if (data > start && data[-1] != '\r')
95 1338 : *outp++ = '\r';
96 : start_of_line = 1;
97 83280 : } else if (*data == '.') {
98 0 : if (start_of_line) {
99 0 : start_of_line = 0;
100 0 : *outp++ = '.';
101 : }
102 : } else {
103 : start_of_line = 0;
104 : }
105 84618 : *outp++ = *data++;
106 : }
107 6 : if (outp < *out+2 || fast_memcmp(outp-2, "\r\n", 2)) {
108 0 : *outp++ = '\r';
109 0 : *outp++ = '\n';
110 : }
111 6 : *outp++ = '.';
112 6 : *outp++ = '\r';
113 6 : *outp++ = '\n';
114 6 : *outp = '\0'; /* NUL-terminate just in case. */
115 6 : tor_assert(outp >= *out);
116 6 : 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 : */
129 : size_t
130 6 : read_escaped_data(const char *data, size_t len, char **out)
131 : {
132 6 : char *outp;
133 6 : const char *next;
134 6 : const char *end;
135 :
136 6 : *out = outp = tor_malloc(len+1);
137 :
138 6 : end = data+len;
139 :
140 21 : while (data < end) {
141 : /* we're at the start of a line. */
142 15 : if (*data == '.')
143 5 : ++data;
144 15 : next = memchr(data, '\n', end-data);
145 15 : if (next) {
146 15 : size_t n_to_copy = next-data;
147 : /* Don't copy a CR that precedes this LF. */
148 15 : if (n_to_copy && *(next-1) == '\r')
149 14 : --n_to_copy;
150 15 : memcpy(outp, data, n_to_copy);
151 15 : outp += n_to_copy;
152 15 : 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 0 : memcpy(outp, data, end-data);
156 0 : outp += (end-data);
157 0 : *outp = '\0';
158 0 : return outp - *out;
159 : }
160 15 : *outp++ = '\n';
161 : }
162 :
163 6 : *outp = '\0';
164 6 : return outp - *out;
165 : }
166 :
167 : /** Send a "DONE" message down the control connection <b>conn</b>. */
168 : void
169 5 : send_control_done(control_connection_t *conn)
170 : {
171 5 : control_write_endreply(conn, 250, "OK");
172 5 : }
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 : */
181 32 : MOCK_IMPL(void,
182 : control_write_reply, (control_connection_t *conn, int code, int c,
183 : const char *s))
184 : {
185 32 : connection_printf_to_buf(conn, "%03d%c%s\r\n", code, c, s);
186 32 : }
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 : */
196 : void
197 26 : control_vprintf_reply(control_connection_t *conn, int code, int c,
198 : const char *fmt, va_list ap)
199 : {
200 26 : char *buf = NULL;
201 26 : int len;
202 :
203 26 : len = tor_vasprintf(&buf, fmt, ap);
204 26 : if (len < 0) {
205 0 : log_err(LD_BUG, "Unable to format string for controller.");
206 0 : tor_assert(0);
207 : }
208 26 : control_write_reply(conn, code, c, buf);
209 26 : tor_free(buf);
210 26 : }
211 :
212 : /** Write an EndReplyLine */
213 : void
214 16 : control_write_endreply(control_connection_t *conn, int code, const char *s)
215 : {
216 16 : control_write_reply(conn, code, ' ', s);
217 16 : }
218 :
219 : /** Write a formatted EndReplyLine */
220 : void
221 17 : control_printf_endreply(control_connection_t *conn, int code,
222 : const char *fmt, ...)
223 : {
224 17 : va_list ap;
225 :
226 17 : va_start(ap, fmt);
227 17 : control_vprintf_reply(conn, code, ' ', fmt, ap);
228 17 : va_end(ap);
229 17 : }
230 :
231 : /** Write a MidReplyLine */
232 : void
233 5 : control_write_midreply(control_connection_t *conn, int code, const char *s)
234 : {
235 5 : control_write_reply(conn, code, '-', s);
236 5 : }
237 :
238 : /** Write a formatted MidReplyLine */
239 : void
240 9 : control_printf_midreply(control_connection_t *conn, int code, const char *fmt,
241 : ...)
242 : {
243 9 : va_list ap;
244 :
245 9 : va_start(ap, fmt);
246 9 : control_vprintf_reply(conn, code, '-', fmt, ap);
247 9 : va_end(ap);
248 9 : }
249 :
250 : /** Write a DataReplyLine */
251 : void
252 0 : control_write_datareply(control_connection_t *conn, int code, const char *s)
253 : {
254 0 : control_write_reply(conn, code, '+', s);
255 0 : }
256 :
257 : /** Write a formatted DataReplyLine */
258 : void
259 0 : control_printf_datareply(control_connection_t *conn, int code, const char *fmt,
260 : ...)
261 : {
262 0 : va_list ap;
263 :
264 0 : va_start(ap, fmt);
265 0 : control_vprintf_reply(conn, code, '+', fmt, ap);
266 0 : va_end(ap);
267 0 : }
268 :
269 : /** Write a CmdData */
270 : void
271 0 : control_write_data(control_connection_t *conn, const char *data)
272 : {
273 0 : char *esc = NULL;
274 0 : size_t esc_len;
275 :
276 0 : esc_len = write_escaped_data(data, strlen(data), &esc);
277 0 : connection_buf_add(esc, esc_len, TO_CONN(conn));
278 0 : tor_free(esc);
279 0 : }
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 : */
287 : void
288 14 : control_write_reply_line(control_connection_t *conn,
289 : const control_reply_line_t *line, bool lastone)
290 : {
291 14 : const config_line_t *kvline = line->kvline;
292 14 : char *s = NULL;
293 :
294 14 : 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 0 : tor_assert(kvline->next == NULL);
298 0 : control_printf_datareply(conn, line->code, "%s=", kvline->key);
299 0 : control_write_data(conn, kvline->value);
300 0 : return;
301 : }
302 14 : s = kvline_encode(kvline, line->flags);
303 14 : if (lastone) {
304 9 : control_write_endreply(conn, line->code, s);
305 : } else {
306 5 : control_write_midreply(conn, line->code, s);
307 : }
308 14 : 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 : */
316 : void
317 9 : control_write_reply_lines(control_connection_t *conn, smartlist_t *lines)
318 : {
319 9 : bool lastone = false;
320 :
321 23 : SMARTLIST_FOREACH_BEGIN(lines, control_reply_line_t *, line) {
322 14 : if (line_sl_idx >= line_sl_len - 1)
323 9 : lastone = true;
324 14 : control_write_reply_line(conn, line, lastone);
325 14 : } SMARTLIST_FOREACH_END(line);
326 9 : }
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 : */
337 : void
338 15 : control_reply_add_one_kv(smartlist_t *reply, int code, int flags,
339 : const char *key, const char *val)
340 : {
341 15 : control_reply_line_t *line = tor_malloc_zero(sizeof(*line));
342 :
343 15 : line->code = code;
344 15 : line->flags = flags;
345 15 : config_line_append(&line->kvline, key, val);
346 15 : smartlist_add(reply, line);
347 15 : }
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 : */
356 : void
357 2 : control_reply_append_kv(smartlist_t *reply, const char *key, const char *val)
358 : {
359 2 : int len = smartlist_len(reply);
360 2 : control_reply_line_t *line;
361 :
362 2 : tor_assert(len > 0);
363 :
364 2 : line = smartlist_get(reply, len - 1);
365 2 : config_line_append(&line->kvline, key, val);
366 2 : }
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 : */
374 : void
375 8 : control_reply_add_str(smartlist_t *reply, int code, const char *s)
376 : {
377 8 : control_reply_add_one_kv(reply, code, KV_OMIT_KEYS|KV_RAW, "", s);
378 8 : }
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 : */
386 : void
387 5 : control_reply_add_printf(smartlist_t *reply, int code, const char *fmt, ...)
388 : {
389 5 : va_list ap;
390 5 : char *buf = NULL;
391 :
392 5 : va_start(ap, fmt);
393 5 : (void)tor_vasprintf(&buf, fmt, ap);
394 5 : va_end(ap);
395 5 : control_reply_add_str(reply, code, buf);
396 5 : tor_free(buf);
397 5 : }
398 :
399 : /** Add a "250 OK" line to a set of control reply lines */
400 : void
401 2 : control_reply_add_done(smartlist_t *reply)
402 : {
403 2 : control_reply_add_str(reply, 250, "OK");
404 2 : }
405 :
406 : /** Free a control_reply_line_t. Don't call this directly; use the
407 : * control_reply_line_free() macro instead. */
408 : void
409 15 : control_reply_line_free_(control_reply_line_t *line)
410 : {
411 15 : if (!line)
412 : return;
413 15 : config_free_lines(line->kvline);
414 15 : 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. */
419 : void
420 17 : control_reply_clear(smartlist_t *reply)
421 : {
422 32 : SMARTLIST_FOREACH(reply, control_reply_line_t *, line,
423 : control_reply_line_free(line));
424 17 : smartlist_clear(reply);
425 17 : }
426 :
427 : /** Free a smartlist of control_reply_line_t. Don't call this
428 : * directly; use the control_reply_free() macro instead. */
429 : void
430 13 : control_reply_free_(smartlist_t *reply)
431 : {
432 13 : control_reply_clear(reply);
433 13 : smartlist_free_(reply);
434 13 : }
|