LCOV - code coverage report
Current view: top level - feature/control - control_proto.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 148 188 78.7 %
Date: 2021-11-24 03:28:48 Functions: 20 24 83.3 %

          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 : }

Generated by: LCOV version 1.14