File: | home/avsej/code/libcouchbase/tools/cbc-proxy.cc |
Warning: | line 298, column 62 Use of memory after it is freed |
1 | /* | |||||
2 | * Copyright 2017 Couchbase, Inc. | |||||
3 | * | |||||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |||||
5 | * you may not use this file except in compliance with the License. | |||||
6 | * You may obtain a copy of the License at | |||||
7 | * | |||||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |||||
9 | * | |||||
10 | * Unless required by applicable law or agreed to in writing, software | |||||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |||||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
13 | * See the License for the specific language governing permissions and | |||||
14 | * limitations under the License. | |||||
15 | */ | |||||
16 | ||||||
17 | #define LCB_NO_DEPR_CXX_CTORS | |||||
18 | ||||||
19 | #include "common/my_inttypes.h" | |||||
20 | #include "config.h" | |||||
21 | #include <sys/types.h> | |||||
22 | #include <libcouchbase/couchbase.h> | |||||
23 | #include <libcouchbase/vbucket.h> | |||||
24 | #include <libcouchbase/api3.h> | |||||
25 | #include <libcouchbase/pktfwd.h> | |||||
26 | #include <memcached/protocol_binary.h> | |||||
27 | #include <iostream> | |||||
28 | #include <iomanip> | |||||
29 | #include <cstdio> | |||||
30 | #include <cerrno> | |||||
31 | #include <sstream> | |||||
32 | #include <signal.h> | |||||
33 | #include "common/options.h" | |||||
34 | #include "common/histogram.h" | |||||
35 | ||||||
36 | #include "internal.h" | |||||
37 | ||||||
38 | #include <event2/event.h> | |||||
39 | #include <event2/listener.h> | |||||
40 | #include <event2/bufferevent.h> | |||||
41 | #include <event2/buffer.h> | |||||
42 | ||||||
43 | using namespace cbc; | |||||
44 | using namespace cliopts; | |||||
45 | ||||||
46 | static void die(const char *msg) | |||||
47 | { | |||||
48 | fprintf(stderrstderr, "%s\n", msg); | |||||
49 | exit(EXIT_FAILURE1); | |||||
50 | } | |||||
51 | ||||||
52 | static void good_or_die(lcb_error_t rc, const char *msg = "") | |||||
53 | { | |||||
54 | if (rc != LCB_SUCCESS) { | |||||
55 | fprintf(stderrstderr, "%s\n0x%02x: %s\n", msg, rc, lcb_strerror(NULL__null, rc)); | |||||
56 | exit(EXIT_FAILURE1); | |||||
57 | } | |||||
58 | } | |||||
59 | ||||||
60 | static lcb_t instance = NULL__null; | |||||
61 | static struct event_base *evbase = NULL__null; | |||||
62 | static Histogram hg; | |||||
63 | ||||||
64 | #define LOGARGS(lvl)(instance)->settings, "proxy", LCB_LOG_lvl, "/home/avsej/code/libcouchbase/tools/cbc-proxy.cc" , 64 (instance)->settings, "proxy", LCB_LOG_##lvl, __FILE__"/home/avsej/code/libcouchbase/tools/cbc-proxy.cc", __LINE__64 | |||||
65 | #define CL_LOGFMT"<%s:%s> (cl=%p,fd=%d) " "<%s:%s> (cl=%p,fd=%d) " | |||||
66 | #define CL_LOGID(cl)cl->host, cl->port, (void *)cl, cl->fd cl->host, cl->port, (void *)cl, cl->fd | |||||
67 | ||||||
68 | class Configuration | |||||
69 | { | |||||
70 | public: | |||||
71 | Configuration() : o_trace("trace"), o_port("port") | |||||
72 | { | |||||
73 | o_trace.abbrev('t').description("Show packet trace on INFO log level"); | |||||
74 | o_port.abbrev('p').description("Port for proxy").setDefault(11211); | |||||
75 | } | |||||
76 | ||||||
77 | ~Configuration() | |||||
78 | { | |||||
79 | } | |||||
80 | ||||||
81 | void addToParser(Parser &parser) | |||||
82 | { | |||||
83 | m_params.addToParser(parser); | |||||
84 | parser.addOption(o_trace); | |||||
85 | parser.addOption(o_port); | |||||
86 | } | |||||
87 | ||||||
88 | void processOptions() | |||||
89 | { | |||||
90 | } | |||||
91 | ||||||
92 | void fillCropts(lcb_create_st &opts) | |||||
93 | { | |||||
94 | m_params.fillCropts(opts); | |||||
95 | } | |||||
96 | lcb_error_t doCtls() | |||||
97 | { | |||||
98 | return m_params.doCtls(instance); | |||||
99 | } | |||||
100 | bool useTimings() | |||||
101 | { | |||||
102 | return m_params.useTimings(); | |||||
103 | } | |||||
104 | bool shouldDump() | |||||
105 | { | |||||
106 | return m_params.shouldDump(); | |||||
107 | } | |||||
108 | ||||||
109 | bool isTrace() | |||||
110 | { | |||||
111 | return o_trace.result(); | |||||
112 | } | |||||
113 | ||||||
114 | unsigned port() | |||||
115 | { | |||||
116 | return o_port.result(); | |||||
117 | } | |||||
118 | ||||||
119 | private: | |||||
120 | ConnParams m_params; | |||||
121 | BoolOption o_trace; | |||||
122 | UIntOption o_port; | |||||
123 | }; | |||||
124 | ||||||
125 | static Configuration config; | |||||
126 | ||||||
127 | static struct evconnlistener *listener = NULL__null; | |||||
128 | ||||||
129 | static void cleanup() | |||||
130 | { | |||||
131 | if (instance) { | |||||
132 | if (config.shouldDump()) { | |||||
133 | lcb_dump(instance, stderrstderr, LCB_DUMP_ALL); | |||||
134 | } | |||||
135 | if (config.useTimings()) { | |||||
136 | hg.write(); | |||||
137 | } | |||||
138 | if (instance) { | |||||
139 | lcb_destroy(instance); | |||||
140 | } | |||||
141 | } | |||||
142 | if (listener) { | |||||
143 | evconnlistener_free(listener); | |||||
144 | } | |||||
145 | if (evbase) { | |||||
146 | event_base_free(evbase); | |||||
147 | } | |||||
148 | } | |||||
149 | ||||||
150 | struct client { | |||||
151 | int fd; | |||||
152 | struct bufferevent *bev; | |||||
153 | char host[NI_MAXHOST1025 + 1]; | |||||
154 | char port[NI_MAXSERV32 + 1]; | |||||
155 | }; | |||||
156 | ||||||
157 | static void dump_bytes(const struct client *cl, const char *msg, void *ptr, size_t len) | |||||
158 | { | |||||
159 | if (!config.isTrace()) { | |||||
160 | return; | |||||
161 | } | |||||
162 | ||||||
163 | int width = 16; | |||||
164 | const unsigned char *buf = (const unsigned char *)ptr; | |||||
165 | size_t full_rows = len / width; | |||||
166 | size_t remainder = len % width; | |||||
167 | std::stringstream ss; | |||||
168 | ||||||
169 | ss << msg << ", " << len << " bytes\n" | |||||
170 | " +-------------------------------------------------+\n" | |||||
171 | " | 0 1 2 3 4 5 6 7 8 9 a b c d e f |\n" | |||||
172 | " +--------+-------------------------------------------------+----------------+"; | |||||
173 | ||||||
174 | unsigned int row = 0; | |||||
175 | while (row < full_rows) { | |||||
176 | int row_start_index = row * width; | |||||
177 | // prefix | |||||
178 | ss << "\n |" << std::setw(8) << std::setfill('0') << std::hex << row_start_index << "|"; | |||||
179 | int row_end_index = row_start_index + width; | |||||
180 | // hex | |||||
181 | int i = row_start_index; | |||||
182 | while (i < row_end_index) { | |||||
183 | ss << " " << std::setw(2) << std::setfill('0') << std::hex << (unsigned int)buf[i++]; | |||||
184 | } | |||||
185 | ss << " |"; | |||||
186 | // ascii | |||||
187 | i = row_start_index; | |||||
188 | while (i < row_end_index) { | |||||
189 | char b = buf[i++]; | |||||
190 | if ((b <= 0x1f) || (b >= 0x7f)) { | |||||
191 | ss << '.'; | |||||
192 | } else { | |||||
193 | ss << b; | |||||
194 | } | |||||
195 | } | |||||
196 | ss << "|"; | |||||
197 | row++; | |||||
198 | } | |||||
199 | if (remainder != 0) { | |||||
200 | int row_start_index = full_rows * width; | |||||
201 | // prefix | |||||
202 | ss << "\n |" << std::setw(8) << std::setfill('0') << std::hex << row_start_index << "|"; | |||||
203 | int row_end_index = row_start_index + remainder; | |||||
204 | // hex | |||||
205 | int i = row_start_index; | |||||
206 | while (i < row_end_index) { | |||||
207 | ss << " " << std::setw(2) << std::setfill('0') << std::hex << (unsigned int)buf[i++]; | |||||
208 | } | |||||
209 | i = width - remainder; | |||||
210 | while (i > 0) { | |||||
211 | ss << " "; | |||||
212 | i--; | |||||
213 | } | |||||
214 | ss << " |"; | |||||
215 | // ascii | |||||
216 | i = row_start_index; | |||||
217 | while (i < row_end_index) { | |||||
218 | char b = buf[i++]; | |||||
219 | if ((b <= 0x1f) || (b >= 0x7f)) { | |||||
220 | ss << '.'; | |||||
221 | } else { | |||||
222 | ss << b; | |||||
223 | } | |||||
224 | } | |||||
225 | i = width - remainder; | |||||
226 | while (i > 0) { | |||||
227 | ss << " "; | |||||
228 | i--; | |||||
229 | } | |||||
230 | ss << "|"; | |||||
231 | } | |||||
232 | ss << "\n +--------+-------------------------------------------------+----------------+"; | |||||
233 | lcb_log(LOGARGS(INFO)(instance)->settings, "proxy", LCB_LOG_INFO, "/home/avsej/code/libcouchbase/tools/cbc-proxy.cc" , 233, CL_LOGFMT"<%s:%s> (cl=%p,fd=%d) " "%s", CL_LOGID(cl)cl->host, cl->port, (void *)cl, cl->fd, ss.str().c_str()); | |||||
234 | } | |||||
235 | ||||||
236 | static void pktfwd_callback(lcb_t, const void *cookie, lcb_error_t err, lcb_PKTFWDRESP *resp) | |||||
237 | { | |||||
238 | good_or_die(err, "Failed to forward a packet"); | |||||
239 | ||||||
240 | struct client *cl = (struct client *)cookie; | |||||
241 | struct evbuffer *output = bufferevent_get_output(cl->bev); | |||||
242 | for (unsigned ii = 0; ii < resp->nitems; ii++) { | |||||
243 | dump_bytes(cl, "response", resp->iovs[ii].iov_base, resp->iovs[ii].iov_len); | |||||
244 | evbuffer_expand(output, resp->iovs[ii].iov_len); | |||||
245 | evbuffer_add(output, resp->iovs[ii].iov_base, resp->iovs[ii].iov_len); | |||||
246 | } | |||||
247 | } | |||||
248 | ||||||
249 | static void conn_readcb(struct bufferevent *bev, void *cookie) | |||||
250 | { | |||||
251 | struct client *cl = (struct client *)cookie; | |||||
252 | struct evbuffer *input; | |||||
253 | size_t len; | |||||
254 | ||||||
255 | input = bufferevent_get_input(bev); | |||||
256 | len = evbuffer_get_length(input); | |||||
257 | if (len < 24) { | |||||
258 | lcb_log(LOGARGS(DEBUG)(instance)->settings, "proxy", LCB_LOG_DEBUG, "/home/avsej/code/libcouchbase/tools/cbc-proxy.cc" , 258, CL_LOGFMT"<%s:%s> (cl=%p,fd=%d) " "not enough data for header", CL_LOGID(cl)cl->host, cl->port, (void *)cl, cl->fd); | |||||
259 | return; | |||||
260 | } | |||||
261 | ||||||
262 | protocol_binary_request_header header; | |||||
263 | evbuffer_copyout(input, &header, sizeof(header)); | |||||
264 | lcb_U32 bodylen = ntohl(header.request.bodylen); | |||||
265 | ||||||
266 | size_t pktlen = sizeof(header) + bodylen; | |||||
267 | len = evbuffer_get_length(input); | |||||
268 | if (len < pktlen) { | |||||
269 | lcb_log(LOGARGS(DEBUG)(instance)->settings, "proxy", LCB_LOG_DEBUG, "/home/avsej/code/libcouchbase/tools/cbc-proxy.cc" , 269, CL_LOGFMT"<%s:%s> (cl=%p,fd=%d) " "not enough data for packet", CL_LOGID(cl)cl->host, cl->port, (void *)cl, cl->fd); | |||||
270 | return; | |||||
271 | } | |||||
272 | void *pkt = malloc(pktlen); | |||||
273 | evbuffer_remove(input, pkt, pktlen); | |||||
274 | ||||||
275 | lcb_sched_enter(instance); | |||||
276 | lcb_CMDPKTFWD cmd = {0}; | |||||
277 | cmd.vb.vtype = LCB_KV_COPY; | |||||
278 | cmd.vb.u_buf.contig.bytes = pkt; | |||||
279 | cmd.vb.u_buf.contig.nbytes = pktlen; | |||||
280 | dump_bytes(cl, "request", pkt, pktlen); | |||||
281 | good_or_die(lcb_pktfwd3(instance, cl, &cmd), "Failed to forward packet"); | |||||
282 | lcb_sched_leave(instance); | |||||
283 | } | |||||
284 | ||||||
285 | static void conn_eventcb(struct bufferevent *bev, short events, void *cookie) | |||||
286 | { | |||||
287 | struct client *cl = (struct client *)cookie; | |||||
288 | ||||||
289 | if (events & BEV_EVENT_EOF0x10) { | |||||
| ||||||
290 | lcb_log(LOGARGS(INFO)(instance)->settings, "proxy", LCB_LOG_INFO, "/home/avsej/code/libcouchbase/tools/cbc-proxy.cc" , 290, CL_LOGFMT"<%s:%s> (cl=%p,fd=%d) " "connection closed", CL_LOGID(cl)cl->host, cl->port, (void *)cl, cl->fd); | |||||
291 | bufferevent_free(bev); | |||||
292 | delete cl; | |||||
293 | } else if (events & BEV_EVENT_ERROR0x20) { | |||||
294 | lcb_log(LOGARGS(ERROR)(instance)->settings, "proxy", LCB_LOG_ERROR, "/home/avsej/code/libcouchbase/tools/cbc-proxy.cc" , 294, CL_LOGFMT"<%s:%s> (cl=%p,fd=%d) " "got an error on the connection: %s\n", CL_LOGID(cl)cl->host, cl->port, (void *)cl, cl->fd, strerror(errno(*__errno_location ()))); | |||||
295 | bufferevent_free(bev); | |||||
296 | delete cl; | |||||
297 | } | |||||
298 | lcb_log(LOGARGS(DEBUG)(instance)->settings, "proxy", LCB_LOG_DEBUG, "/home/avsej/code/libcouchbase/tools/cbc-proxy.cc" , 298, CL_LOGFMT"<%s:%s> (cl=%p,fd=%d) " "ignore event 0x%02x", CL_LOGID(cl)cl->host, cl->port, (void *)cl, cl->fd, events); | |||||
| ||||||
299 | } | |||||
300 | ||||||
301 | static void listener_cb(struct evconnlistener *, evutil_socket_tint fd, struct sockaddr *addr, int naddr, void *) | |||||
302 | { | |||||
303 | struct bufferevent *bev; | |||||
304 | bev = bufferevent_socket_new(evbase, fd, BEV_OPT_CLOSE_ON_FREE); | |||||
305 | ||||||
306 | if (!bev) { | |||||
307 | die("Error constructing bufferevent"); | |||||
308 | } | |||||
309 | ||||||
310 | struct client *cl = new client(); | |||||
311 | cl->fd = fd; | |||||
312 | cl->bev = bev; | |||||
313 | getnameinfo(addr, naddr, cl->host, sizeof(cl->host), cl->port, sizeof(cl->port), NI_NUMERICHOST1 | NI_NUMERICSERV2); | |||||
314 | bufferevent_setcb(bev, conn_readcb, NULL__null, conn_eventcb, cl); | |||||
315 | bufferevent_enable(bev, EV_READ0x02 | EV_WRITE0x04); | |||||
316 | lcb_log(LOGARGS(INFO)(instance)->settings, "proxy", LCB_LOG_INFO, "/home/avsej/code/libcouchbase/tools/cbc-proxy.cc" , 316, CL_LOGFMT"<%s:%s> (cl=%p,fd=%d) " "new client connection", CL_LOGID(cl)cl->host, cl->port, (void *)cl, cl->fd); | |||||
317 | } | |||||
318 | ||||||
319 | static void setup_listener() | |||||
320 | { | |||||
321 | struct sockaddr_in sin; | |||||
322 | ||||||
323 | memset(&sin, 0, sizeof(sin)); | |||||
324 | sin.sin_family = AF_INET2; | |||||
325 | sin.sin_port = htons(config.port()); | |||||
326 | ||||||
327 | listener = evconnlistener_new_bind(evbase, listener_cb, NULL__null, LEV_OPT_REUSEABLE(1u<<3) | LEV_OPT_CLOSE_ON_FREE(1u<<1), -1, | |||||
328 | (struct sockaddr *)&sin, sizeof(sin)); | |||||
329 | if (!listener) { | |||||
330 | die("Failed to create proxy listener"); | |||||
331 | } | |||||
332 | lcb_log(LOGARGS(INFO)(instance)->settings, "proxy", LCB_LOG_INFO, "/home/avsej/code/libcouchbase/tools/cbc-proxy.cc" , 332, "Listening incoming proxy connections on port %d", config.port()); | |||||
333 | } | |||||
334 | ||||||
335 | static void bootstrap_callback(lcb_t, lcb_error_t err) | |||||
336 | { | |||||
337 | good_or_die(err, "Failed to bootstrap"); | |||||
338 | lcb_log(LOGARGS(INFO)(instance)->settings, "proxy", LCB_LOG_INFO, "/home/avsej/code/libcouchbase/tools/cbc-proxy.cc" , 338, "connected to Couchbase Server"); | |||||
339 | setup_listener(); | |||||
340 | } | |||||
341 | ||||||
342 | static int terminating = 0; | |||||
343 | static void sigint_handler(int) | |||||
344 | { | |||||
345 | lcb_log(LOGARGS(INFO)(instance)->settings, "proxy", LCB_LOG_INFO, "/home/avsej/code/libcouchbase/tools/cbc-proxy.cc" , 345, "terminating the server"); | |||||
346 | if (!terminating) { | |||||
347 | event_base_loopbreak(evbase); | |||||
348 | terminating = 1; | |||||
349 | } | |||||
350 | } | |||||
351 | ||||||
352 | static void real_main(int argc, char **argv) | |||||
353 | { | |||||
354 | Parser parser; | |||||
355 | ||||||
356 | config.addToParser(parser); | |||||
357 | parser.parse(argc, argv); | |||||
358 | config.processOptions(); | |||||
359 | ||||||
360 | lcb_create_st cropts; | |||||
361 | memset(&cropts, 0, sizeof cropts); | |||||
362 | config.fillCropts(cropts); | |||||
363 | ||||||
364 | /* bind to external libevent loop */ | |||||
365 | evbase = event_base_new(); | |||||
366 | struct lcb_create_io_ops_st ciops; | |||||
367 | memset(&ciops, 0, sizeof(ciops)); | |||||
368 | ciops.v.v0.type = LCB_IO_OPS_LIBEVENT; | |||||
369 | ciops.v.v0.cookie = evbase; | |||||
370 | good_or_die(lcb_create_io_ops(&cropts.v.v3.io, &ciops), "Failed to create and IO ops strucutre for libevent"); | |||||
371 | ||||||
372 | good_or_die(lcb_create(&instance, &cropts), "Failed to create connection"); | |||||
373 | config.doCtls(); | |||||
374 | lcb_set_bootstrap_callback(instance, bootstrap_callback); | |||||
375 | lcb_set_pktfwd_callback(instance, pktfwd_callback); | |||||
376 | ||||||
377 | good_or_die(lcb_connect(instance), "Failed to connect to cluster"); | |||||
378 | if (config.useTimings()) { | |||||
379 | hg.install(instance, stdoutstdout); | |||||
380 | } | |||||
381 | std::atexit(cleanup); | |||||
382 | ||||||
383 | /* setup CTRL-C handler */ | |||||
384 | struct sigaction action; | |||||
385 | sigemptyset(&action.sa_mask); | |||||
386 | action.sa_handler__sigaction_handler.sa_handler = sigint_handler; | |||||
387 | action.sa_flags = 0; | |||||
388 | sigaction(SIGINT2, &action, NULL__null); | |||||
389 | ||||||
390 | event_base_dispatch(evbase); | |||||
391 | } | |||||
392 | ||||||
393 | int main(int argc, char **argv) | |||||
394 | { | |||||
395 | try { | |||||
396 | real_main(argc, argv); | |||||
397 | return 0; | |||||
398 | } catch (std::exception &exc) { | |||||
399 | std::cerr << exc.what() << std::endl; | |||||
400 | exit(EXIT_FAILURE1); | |||||
401 | } | |||||
402 | } |