Tor  0.4.7.0-alpha-dev
metrics.c
Go to the documentation of this file.
1 /* Copyright (c) 2007-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
3 
4 /**
5  * @file metrics.c
6  * @brief Metrics subsystem.
7  **/
8 
9 #include "orconfig.h"
10 
11 #include "core/or/or.h"
12 
13 #include "lib/encoding/confline.h"
14 #include "lib/log/util_bug.h"
15 #include "lib/malloc/malloc.h"
17 #include "lib/net/resolve.h"
18 #include "lib/string/printf.h"
19 #include "lib/net/nettypes.h"
20 #include "lib/net/address.h"
21 
23 #include "core/or/connection_or.h"
24 #include "core/or/connection_st.h"
25 #include "core/or/policies.h"
26 #include "core/or/port_cfg_st.h"
27 #include "core/proto/proto_http.h"
28 
31 
32 #include "app/config/config.h"
33 #include "app/main/subsysmgr.h"
34 
35 /** Metrics format driver set by the MetricsPort option. */
37 
38 /** Return true iff the given peer address is allowed by our MetricsPortPolicy
39  * option that is is in that list. */
40 static bool
42 {
43  tor_assert(peer_addr);
44 
45  return metrics_policy_permits_address(peer_addr);
46 }
47 
48 /** Helper: For a metrics port connection, write the HTTP response header
49  * using the data length passed. */
50 static void
51 write_metrics_http_response(const size_t data_len, connection_t *conn)
52 {
53  char date[RFC1123_TIME_LEN+1];
54  buf_t *buf = buf_new_with_capacity(128 + data_len);
55 
57  buf_add_printf(buf, "HTTP/1.0 200 OK\r\nDate: %s\r\n", date);
58  buf_add_printf(buf, "Content-Type: text/plain; charset=utf-8\r\n");
59  buf_add_printf(buf, "Content-Length: %" TOR_PRIuSZ "\r\n", data_len);
60  buf_add_string(buf, "\r\n");
61 
62  connection_buf_add_buf(conn, buf);
63  buf_free(buf);
64 }
65 
66 /** Return newly allocated buffer containing the output of all subsystems
67  * having metrics.
68  *
69  * This is used to output the content on the MetricsPort. */
70 buf_t *
72 {
73  buf_t *data = buf_new();
74 
75  /* Go over all subsystems that exposes a metrics store. */
76  for (unsigned i = 0; i < n_tor_subsystems; ++i) {
77  const smartlist_t *stores;
78  const subsys_fns_t *sys = tor_subsystems[i];
79 
80  /* Skip unsupported subsystems. */
81  if (!sys->supported) {
82  continue;
83  }
84 
85  if (sys->get_metrics && (stores = sys->get_metrics())) {
86  SMARTLIST_FOREACH_BEGIN(stores, const metrics_store_t *, store) {
87  metrics_store_get_output(fmt, store, data);
88  } SMARTLIST_FOREACH_END(store);
89  }
90  }
91 
92  return data;
93 }
94 
95 /** Process what is in the inbuf of this connection of type metrics.
96  *
97  * Return 0 on success else -1 on error for which the connection is marked for
98  * close. */
99 int
101 {
102  int ret = -1;
103  char *headers = NULL, *command = NULL, *url = NULL;
104  const char *errmsg = NULL;
105 
106  tor_assert(conn);
108 
109  if (!metrics_request_allowed(&conn->addr)) {
110  /* Close connection. Don't bother returning anything if you are not
111  * allowed by being on the policy list. */
112  errmsg = NULL;
113  goto err;
114  }
115 
116  const int http_status =
117  connection_fetch_from_buf_http(conn, &headers, 1024, NULL, NULL, 1024, 0);
118  if (http_status < 0) {
119  errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
120  goto err;
121  } else if (http_status == 0) {
122  /* no HTTP request yet. */
123  ret = 0;
124  goto done;
125  }
126 
127  const int cmd_status = parse_http_command(headers, &command, &url);
128  if (cmd_status < 0) {
129  errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
130  goto err;
131  } else if (strcmpstart(command, "GET")) {
132  errmsg = "HTTP/1.0 405 Method Not Allowed\r\n\r\n";
133  goto err;
134  }
135  tor_assert(url);
136 
137  /* Where we expect the query to come for. */
138 #define EXPECTED_URL_PATH "/metrics"
139 #define EXPECTED_URL_PATH_LEN (sizeof(EXPECTED_URL_PATH) - 1) /* No NUL */
140 
141  if (!strcmpstart(url, EXPECTED_URL_PATH) &&
142  strlen(url) == EXPECTED_URL_PATH_LEN) {
143  buf_t *data = metrics_get_output(the_format);
144 
146  connection_buf_add_buf(conn, data);
147  buf_free(data);
148  } else {
149  errmsg = "HTTP/1.0 404 Not Found\r\n\r\n";
150  goto err;
151  }
152 
153  ret = 0;
154  goto done;
155 
156  err:
157  if (errmsg) {
158  log_info(LD_EDGE, "HTTP metrics error: saying %s", escaped(errmsg));
159  connection_buf_add(errmsg, strlen(errmsg), conn);
160  }
161  connection_mark_and_flush(conn);
162 
163  done:
164  tor_free(headers);
165  tor_free(command);
166  tor_free(url);
167 
168  return ret;
169 }
170 
171 /** Parse metrics ports from options. On success, add the port to the ports
172  * list and return 0. On failure, set err_msg_out to a newly allocated string
173  * describing the problem and return -1. */
174 int
176  char **err_msg_out)
177 {
178  int num_elems, ok = 0, ret = -1;
179  const char *addrport_str = NULL, *fmt_str = NULL;
180  smartlist_t *elems = NULL;
181  port_cfg_t *cfg = NULL;
182 
183  tor_assert(options);
184  tor_assert(ports);
185 
186  /* No metrics port to configure, just move on . */
187  if (!options->MetricsPort_lines) {
188  return 0;
189  }
190 
191  elems = smartlist_new();
192 
193  /* Split between the protocol and the address/port. */
194  num_elems = smartlist_split_string(elems,
195  options->MetricsPort_lines->value, " ",
196  SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK, 2);
197  if (num_elems < 1) {
198  *err_msg_out = tor_strdup("MetricsPort is missing port.");
199  goto end;
200  }
201 
202  addrport_str = smartlist_get(elems, 0);
203  if (num_elems >= 2) {
204  /* Parse the format if any. */
205  fmt_str = smartlist_get(elems, 1);
206  if (!strcasecmp(fmt_str, "prometheus")) {
208  } else {
209  tor_asprintf(err_msg_out, "MetricsPort unknown format: %s", fmt_str);
210  goto end;
211  }
212  }
213 
214  /* Port configuration with default address. */
215  cfg = port_cfg_new(0);
217 
218  /* Parse the port first. Then an address if any can be found. */
219  cfg->port = (int) tor_parse_long(addrport_str, 10, 0, 65535, &ok, NULL);
220  if (ok) {
221  tor_addr_parse(&cfg->addr, "127.0.0.1");
222  } else {
223  /* We probably have a host:port situation */
224  if (tor_addr_port_lookup(addrport_str, &cfg->addr,
225  (uint16_t *) &cfg->port) < 0) {
226  *err_msg_out = tor_strdup("MetricsPort address/port failed to parse or "
227  "resolve.");
228  goto end;
229  }
230  }
231  /* Add it to the ports list. */
232  smartlist_add(ports, cfg);
233 
234  /* It is set. MetricsPort doesn't support the NoListen options or such that
235  * would prevent from being a real listener port. */
236  options->MetricsPort_set = 1;
237 
238  /* Success. */
239  ret = 0;
240 
241  end:
242  if (ret != 0) {
243  port_cfg_free(cfg);
244  }
245  SMARTLIST_FOREACH(elems, char *, e, tor_free(e));
246  smartlist_free(elems);
247  return ret;
248 }
249 
250 /** Called when conn has gotten its socket closed. */
251 int
253 {
254  tor_assert(conn);
255 
256  log_info(LD_EDGE, "Metrics connection reached EOF. Closing.");
257  connection_mark_for_close(conn);
258  return 0;
259 }
260 
261 /** Called when conn has no more bytes left on its outbuf. Return 0 indicating
262  * success. */
263 int
265 {
266  tor_assert(conn);
267  return 0;
268 }
269 
270 /** Initialize the subsystem. */
271 void
273 {
274 }
275 
276 /** Cleanup and free any global memory of this subsystem. */
277 void
279 {
280 }
int tor_addr_parse(tor_addr_t *addr, const char *src)
Definition: address.c:1349
Headers for address.h.
time_t approx_time(void)
Definition: approx_time.c:32
void buf_add_printf(buf_t *buf, const char *format,...)
Definition: buffers.c:568
buf_t * buf_new_with_capacity(size_t size)
Definition: buffers.c:356
buf_t * buf_new(void)
Definition: buffers.c:365
size_t buf_datalen(const buf_t *buf)
Definition: buffers.c:394
void buf_add_string(buf_t *buf, const char *string)
Definition: buffers.c:561
port_cfg_t * port_cfg_new(size_t namelen)
Definition: config.c:5837
tor_cmdline_mode_t command
Definition: config.c:2440
Header file for config.c.
Header for confline.c.
int connection_fetch_from_buf_http(connection_t *conn, char **headers_out, size_t max_headerlen, char **body_out, size_t *body_used, size_t max_bodylen, int force_complete)
Definition: connection.c:4248
void connection_buf_add_buf(connection_t *conn, buf_t *buf)
Definition: connection.c:4731
Header file for connection.c.
#define CONN_TYPE_METRICS
Definition: connection.h:79
#define CONN_TYPE_METRICS_LISTENER
Definition: connection.h:77
Header file for connection_or.c.
Base connection structure.
int parse_http_command(const char *headers, char **command_out, char **url_out)
Definition: directory.c:271
Header file for directory.c.
const char * escaped(const char *s)
Definition: escape.c:126
#define LD_EDGE
Definition: log.h:94
Headers for util_malloc.c.
#define tor_free(p)
Definition: malloc.h:52
int metrics_connection_process_inbuf(connection_t *conn)
Definition: metrics.c:100
static void write_metrics_http_response(const size_t data_len, connection_t *conn)
Definition: metrics.c:51
int metrics_connection_reached_eof(connection_t *conn)
Definition: metrics.c:252
void metrics_init(void)
Definition: metrics.c:272
int metrics_connection_finished_flushing(connection_t *conn)
Definition: metrics.c:264
static bool metrics_request_allowed(const tor_addr_t *peer_addr)
Definition: metrics.c:41
static metrics_format_t the_format
Definition: metrics.c:36
void metrics_cleanup(void)
Definition: metrics.c:278
int metrics_parse_ports(or_options_t *options, smartlist_t *ports, char **err_msg_out)
Definition: metrics.c:175
buf_t * metrics_get_output(const metrics_format_t fmt)
Definition: metrics.c:71
Header for feature/metrics/metrics.c.
metrics_format_t
@ METRICS_FORMAT_PROMETHEUS
void metrics_store_get_output(const metrics_format_t fmt, const metrics_store_t *store, buf_t *data)
Header for lib/metrics/metrics_store.c.
Declarations for types used throughout the Tor networking system.
Master header file for Tor-specific functionality.
long tor_parse_long(const char *s, int base, long min, long max, int *ok, char **next)
Definition: parse_int.c:59
int metrics_policy_permits_address(const tor_addr_t *addr)
Definition: policies.c:1069
Header file for policies.c.
Listener port configuration structure.
int tor_asprintf(char **strp, const char *fmt,...)
Definition: printf.c:75
Header for printf.c.
Header for proto_http.c.
int tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out)
Definition: resolve.c:252
Header for resolve.c.
smartlist_t * smartlist_new(void)
void smartlist_add(smartlist_t *sl, void *element)
#define SMARTLIST_FOREACH_BEGIN(sl, type, var)
#define SMARTLIST_FOREACH(sl, type, var, cmd)
int smartlist_split_string(smartlist_t *sl, const char *str, const char *sep, int flags, int max)
unsigned int type
Definition: connection_st.h:50
tor_addr_t addr
struct config_line_t * MetricsPort_lines
uint8_t type
Definition: port_cfg_st.h:23
tor_addr_t addr
Definition: port_cfg_st.h:20
bool supported
Definition: subsys.h:54
const struct smartlist_t *(* get_metrics)(void)
Definition: subsys.h:204
Header for subsysmgr.c.
const struct subsys_fns_t * tor_subsystems[]
void format_rfc1123_time(char *buf, time_t t)
Definition: time_fmt.c:182
Macros to manage assertions, fatal and non-fatal.
#define tor_assert(expr)
Definition: util_bug.h:102
int strcmpstart(const char *s1, const char *s2)
Definition: util_string.c:215