tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

control_proto.c (11362B)


      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 connection_write_str_to_buf(const char *s, control_connection_t *conn)
     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. */
     40 void
     41 connection_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 */
     70 size_t
     71 write_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 */
    129 size_t
    130 read_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>. */
    168 void
    169 send_control_done(control_connection_t *conn)
    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 */
    181 MOCK_IMPL(void,
    182 control_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 */
    196 void
    197 control_vprintf_reply(control_connection_t *conn, int code, int c,
    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 */
    213 void
    214 control_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 */
    220 void
    221 control_printf_endreply(control_connection_t *conn, int code,
    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 */
    232 void
    233 control_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 */
    239 void
    240 control_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 */
    251 void
    252 control_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 */
    258 void
    259 control_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 */
    270 void
    271 control_write_data(control_connection_t *conn, const char *data)
    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 */
    287 void
    288 control_write_reply_line(control_connection_t *conn,
    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 */
    316 void
    317 control_write_reply_lines(control_connection_t *conn, smartlist_t *lines)
    318 {
    319  bool lastone = false;
    320 
    321  SMARTLIST_FOREACH_BEGIN(lines, control_reply_line_t *, line) {
    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 */
    337 void
    338 control_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 */
    356 void
    357 control_reply_append_kv(smartlist_t *reply, const char *key, const char *val)
    358 {
    359  int len = smartlist_len(reply);
    360  control_reply_line_t *line;
    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 */
    374 void
    375 control_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 */
    386 void
    387 control_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 */
    400 void
    401 control_reply_add_done(smartlist_t *reply)
    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. */
    408 void
    409 control_reply_line_free_(control_reply_line_t *line)
    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. */
    419 void
    420 control_reply_clear(smartlist_t *reply)
    421 {
    422  SMARTLIST_FOREACH(reply, control_reply_line_t *, line,
    423                    control_reply_line_free(line));
    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. */
    429 void
    430 control_reply_free_(smartlist_t *reply)
    431 {
    432  control_reply_clear(reply);
    433  smartlist_free_(reply);
    434 }