#include "core/or/or.h"
#include "core/mainloop/connection.h"
#include "core/or/circuitbuild.h"
#include "core/or/circuitlist.h"
#include "core/or/connection_edge.h"
#include "feature/control/control_proto.h"
#include "feature/nodelist/nodelist.h"
#include "core/or/cpath_build_state_st.h"
#include "core/or/entry_connection_st.h"
#include "core/or/or_connection_st.h"
#include "core/or/origin_circuit_st.h"
#include "core/or/socks_request_st.h"
#include "feature/control/control_connection_st.h"
#include "lib/container/smartlist.h"
#include "lib/encoding/kvline.h"
void
connection_write_str_to_buf(const char *s, control_connection_t *conn)
{
size_t len = strlen(s);
connection_buf_add(s, len, TO_CONN(conn));
}
void
connection_printf_to_buf(control_connection_t *conn, const char *format, ...)
{
va_list ap;
char *buf = NULL;
int len;
va_start(ap,format);
len = tor_vasprintf(&buf, format, ap);
va_end(ap);
if (len < 0) {
log_err(LD_BUG, "Unable to format string for controller.");
tor_assert(0);
}
connection_buf_add(buf, (size_t)len, TO_CONN(conn));
tor_free(buf);
}
size_t
write_escaped_data(const char *data, size_t len, char **out)
{
tor_assert(len < SIZE_MAX - 9);
size_t sz_out = len+8+1;
char *outp;
const char *start = data, *end;
size_t i;
int start_of_line;
for (i=0; i < len; ++i) {
if (data[i] == '\n') {
sz_out += 2;
if (sz_out >= SIZE_T_CEILING) {
log_warn(LD_BUG, "Input to write_escaped_data was too long");
*out = tor_strdup(".\r\n");
return 3;
}
}
}
*out = outp = tor_malloc(sz_out);
end = data+len;
start_of_line = 1;
while (data < end) {
if (*data == '\n') {
if (data > start && data[-1] != '\r')
*outp++ = '\r';
start_of_line = 1;
} else if (*data == '.') {
if (start_of_line) {
start_of_line = 0;
*outp++ = '.';
}
} else {
start_of_line = 0;
}
*outp++ = *data++;
}
if (outp < *out+2 || fast_memcmp(outp-2, "\r\n", 2)) {
*outp++ = '\r';
*outp++ = '\n';
}
*outp++ = '.';
*outp++ = '\r';
*outp++ = '\n';
*outp = '\0';
tor_assert(outp >= *out);
tor_assert((size_t)(outp - *out) <= sz_out);
return outp - *out;
}
size_t
read_escaped_data(const char *data, size_t len, char **out)
{
char *outp;
const char *next;
const char *end;
*out = outp = tor_malloc(len+1);
end = data+len;
while (data < end) {
if (*data == '.')
++data;
next = memchr(data, '\n', end-data);
if (next) {
size_t n_to_copy = next-data;
if (n_to_copy && *(next-1) == '\r')
--n_to_copy;
memcpy(outp, data, n_to_copy);
outp += n_to_copy;
data = next+1;
} else {
memcpy(outp, data, end-data);
outp += (end-data);
*outp = '\0';
return outp - *out;
}
*outp++ = '\n';
}
*outp = '\0';
return outp - *out;
}
void
send_control_done(control_connection_t *conn)
{
control_write_endreply(conn, 250, "OK");
}
MOCK_IMPL(void,
control_write_reply, (control_connection_t *conn, int code, int c,
const char *s))
{
connection_printf_to_buf(conn, "%03d%c%s\r\n", code, c, s);
}
void
control_vprintf_reply(control_connection_t *conn, int code, int c,
const char *fmt, va_list ap)
{
char *buf = NULL;
int len;
len = tor_vasprintf(&buf, fmt, ap);
if (len < 0) {
log_err(LD_BUG, "Unable to format string for controller.");
tor_assert(0);
}
control_write_reply(conn, code, c, buf);
tor_free(buf);
}
void
control_write_endreply(control_connection_t *conn, int code, const char *s)
{
control_write_reply(conn, code, ' ', s);
}
void
control_printf_endreply(control_connection_t *conn, int code,
const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
control_vprintf_reply(conn, code, ' ', fmt, ap);
va_end(ap);
}
void
control_write_midreply(control_connection_t *conn, int code, const char *s)
{
control_write_reply(conn, code, '-', s);
}
void
control_printf_midreply(control_connection_t *conn, int code, const char *fmt,
...)
{
va_list ap;
va_start(ap, fmt);
control_vprintf_reply(conn, code, '-', fmt, ap);
va_end(ap);
}
void
control_write_datareply(control_connection_t *conn, int code, const char *s)
{
control_write_reply(conn, code, '+', s);
}
void
control_printf_datareply(control_connection_t *conn, int code, const char *fmt,
...)
{
va_list ap;
va_start(ap, fmt);
control_vprintf_reply(conn, code, '+', fmt, ap);
va_end(ap);
}
void
control_write_data(control_connection_t *conn, const char *data)
{
char *esc = NULL;
size_t esc_len;
esc_len = write_escaped_data(data, strlen(data), &esc);
connection_buf_add(esc, esc_len, TO_CONN(conn));
tor_free(esc);
}
void
control_write_reply_line(control_connection_t *conn,
const control_reply_line_t *line, bool lastone)
{
const config_line_t *kvline = line->kvline;
char *s = NULL;
if (strpbrk(kvline->value, "\r\n") != NULL) {
tor_assert(kvline->next == NULL);
control_printf_datareply(conn, line->code, "%s=", kvline->key);
control_write_data(conn, kvline->value);
return;
}
s = kvline_encode(kvline, line->flags);
if (lastone) {
control_write_endreply(conn, line->code, s);
} else {
control_write_midreply(conn, line->code, s);
}
tor_free(s);
}
void
control_write_reply_lines(control_connection_t *conn, smartlist_t *lines)
{
bool lastone = false;
SMARTLIST_FOREACH_BEGIN(lines, control_reply_line_t *, line) {
if (line_sl_idx >= line_sl_len - 1)
lastone = true;
control_write_reply_line(conn, line, lastone);
} SMARTLIST_FOREACH_END(line);
}
void
control_reply_add_one_kv(smartlist_t *reply, int code, int flags,
const char *key, const char *val)
{
control_reply_line_t *line = tor_malloc_zero(sizeof(*line));
line->code = code;
line->flags = flags;
config_line_append(&line->kvline, key, val);
smartlist_add(reply, line);
}
void
control_reply_append_kv(smartlist_t *reply, const char *key, const char *val)
{
int len = smartlist_len(reply);
control_reply_line_t *line;
tor_assert(len > 0);
line = smartlist_get(reply, len - 1);
config_line_append(&line->kvline, key, val);
}
void
control_reply_add_str(smartlist_t *reply, int code, const char *s)
{
control_reply_add_one_kv(reply, code, KV_OMIT_KEYS|KV_RAW, "", s);
}
void
control_reply_add_printf(smartlist_t *reply, int code, const char *fmt, ...)
{
va_list ap;
char *buf = NULL;
va_start(ap, fmt);
(void)tor_vasprintf(&buf, fmt, ap);
va_end(ap);
control_reply_add_str(reply, code, buf);
tor_free(buf);
}
void
control_reply_add_done(smartlist_t *reply)
{
control_reply_add_str(reply, 250, "OK");
}
void
control_reply_line_free_(control_reply_line_t *line)
{
if (!line)
return;
config_free_lines(line->kvline);
tor_free_(line);
}
void
control_reply_clear(smartlist_t *reply)
{
SMARTLIST_FOREACH(reply, control_reply_line_t *, line,
control_reply_line_free(line));
smartlist_clear(reply);
}
void
control_reply_free_(smartlist_t *reply)
{
control_reply_clear(reply);
smartlist_free_(reply);
}