1 /* Copyright (c) 2007-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
6 * @brief Metrics subsystem.
11 #include "core/or/or.h"
13 #include "lib/encoding/confline.h"
14 #include "lib/log/util_bug.h"
15 #include "lib/malloc/malloc.h"
16 #include "lib/metrics/metrics_store.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"
22 #include "core/mainloop/connection.h"
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"
29 #include "feature/dircommon/directory.h"
30 #include "feature/metrics/metrics.h"
32 #include "app/config/config.h"
33 #include "app/main/subsysmgr.h"
35 /** Metrics format driver set by the MetricsPort option. */
36 static metrics_format_t the_format
= METRICS_FORMAT_PROMETHEUS
;
38 /** Return true iff the given peer address is allowed by our MetricsPortPolicy
39 * option that is is in that list. */
41 metrics_request_allowed(const tor_addr_t
*peer_addr
)
43 tor_assert(peer_addr
);
45 return metrics_policy_permits_address(peer_addr
);
48 /** Helper: For a metrics port connection, write the HTTP response header
49 * using the data length passed. */
51 write_metrics_http_response(const size_t data_len
, connection_t
*conn
)
53 char date
[RFC1123_TIME_LEN
+1];
54 buf_t
*buf
= buf_new_with_capacity(128 + data_len
);
56 format_rfc1123_time(date
, approx_time());
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");
62 connection_buf_add_buf(conn
, buf
);
66 /** Return newly allocated buffer containing the output of all subsystems
69 * This is used to output the content on the MetricsPort. */
71 metrics_get_output(const metrics_format_t fmt
)
73 buf_t
*data
= buf_new();
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
];
80 /* Skip unsupported subsystems. */
81 if (!sys
->supported
) {
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
);
95 /** Process what is in the inbuf of this connection of type metrics.
97 * Return 0 on success else -1 on error for which the connection is marked for
100 metrics_connection_process_inbuf(connection_t
*conn
)
103 char *headers
= NULL
, *command
= NULL
, *url
= NULL
;
104 const char *errmsg
= NULL
;
107 tor_assert(conn
->type
== CONN_TYPE_METRICS
);
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. */
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";
121 } else if (http_status
== 0) {
122 /* no HTTP request yet. */
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";
131 } else if (strcmpstart(command
, "GET")) {
132 errmsg
= "HTTP/1.0 405 Method Not Allowed\r\n\r\n";
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 */
141 if (!strcmpstart(url
, EXPECTED_URL_PATH
) &&
142 strlen(url
) == EXPECTED_URL_PATH_LEN
) {
143 buf_t
*data
= metrics_get_output(the_format
);
145 write_metrics_http_response(buf_datalen(data
), conn
);
146 connection_buf_add_buf(conn
, data
);
149 errmsg
= "HTTP/1.0 404 Not Found\r\n\r\n";
158 log_info(LD_EDGE
, "HTTP metrics error: saying %s", escaped(errmsg
));
159 connection_buf_add(errmsg
, strlen(errmsg
), conn
);
161 connection_mark_and_flush(conn
);
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. */
175 metrics_parse_ports(or_options_t
*options
, smartlist_t
*ports
,
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
;
186 /* No metrics port to configure, just move on . */
187 if (!options
->MetricsPort_lines
) {
191 elems
= smartlist_new();
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);
198 *err_msg_out
= tor_strdup("MetricsPort is missing port.");
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")) {
207 the_format
= METRICS_FORMAT_PROMETHEUS
;
209 tor_asprintf(err_msg_out
, "MetricsPort unknown format: %s", fmt_str
);
214 /* Port configuration with default address. */
215 cfg
= port_cfg_new(0);
216 cfg
->type
= CONN_TYPE_METRICS_LISTENER
;
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
);
221 tor_addr_parse(&cfg
->addr
, "127.0.0.1");
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 "
231 /* Add it to the ports list. */
232 smartlist_add(ports
, cfg
);
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;
245 SMARTLIST_FOREACH(elems
, char *, e
, tor_free(e
));
246 smartlist_free(elems
);
250 /** Called when conn has gotten its socket closed. */
252 metrics_connection_reached_eof(connection_t
*conn
)
256 log_info(LD_EDGE
, "Metrics connection reached EOF. Closing.");
257 connection_mark_for_close(conn
);
261 /** Called when conn has no more bytes left on its outbuf. Return 0 indicating
264 metrics_connection_finished_flushing(connection_t
*conn
)
270 /** Initialize the subsystem. */
276 /** Cleanup and free any global memory of this subsystem. */
278 metrics_cleanup(void)