/** * call the python function to intercept the handshake packet * * @return PROXY_SEND_QUERY to send the packet from the client * PROXY_NO_DECISION to pass the server packet unmodified */ static network_mysqld_python_stmt_ret proxy_python_read_handshake(network_mysqld_con *con) { network_mysqld_con_python_t *st = con->plugin_con_state; network_mysqld_python_stmt_ret ret = PROXY_NO_DECISION; if(!CHECK_FUNC(read_handshake)) return ret; PyObject *result = PyObject_CallFunctionObjArgs( GET_FUNC(read_handshake), st->proxy, NULL); GET_PYTHON_RESULT(read_handshake) switch(ret) { case PROXY_NO_DECISION: break; case PROXY_SEND_QUERY: g_warning("%s.%d: (read_handshake) return proxy.PROXY_SEND_QUERY is " "deprecated, use PROXY_SEND_RESULT instead", __FILE__, __LINE__); ret = PROXY_SEND_RESULT; case PROXY_SEND_RESULT: if(network_mysqld_con_python_handle_proxy_response(con, st->proxy)) network_mysqld_con_send_error(con->client, C("(python) handling " "proxy.response failed, check error-log")); break; default: ret = PROXY_NO_DECISION; break; } return ret; }
static network_mysqld_python_stmt_ret proxy_python_read_auth(network_mysqld_con *con) { network_mysqld_python_stmt_ret ret = PROXY_NO_DECISION; network_mysqld_con_python_t *st = con->plugin_con_state; if(!CHECK_FUNC(read_auth)) return ret; PyObject *result = PyObject_CallFunctionObjArgs( GET_FUNC(read_auth), st->proxy, NULL); GET_PYTHON_RESULT(read_auth) switch(ret) { case PROXY_NO_DECISION: break; case PROXY_SEND_RESULT: /* answer directly */ if (network_mysqld_con_python_handle_proxy_response(con, st->proxy)) network_mysqld_con_send_error(con->client, C("(python) handling " "proxy.response failed, check error-log")); break; case PROXY_SEND_QUERY: /* something is in the injection queue, pull it from there and replace * the content of original packet */ if (st->injected.queries->length) ret = PROXY_SEND_INJECTION; else ret = PROXY_NO_DECISION; break; default: ret = PROXY_NO_DECISION; break; } return ret; }
int plugin_debug_con_handle_stmt(chassis *chas, network_mysqld_con *con, GString *s) { gsize i, j; GPtrArray *fields; GPtrArray *rows; GPtrArray *row; switch(s->str[NET_HEADER_SIZE]) { case COM_QUERY: fields = NULL; rows = NULL; row = NULL; /* support the basic commands sent by the mysql shell */ if (0 == g_ascii_strncasecmp(s->str + NET_HEADER_SIZE + 1, C("select @@version_comment limit 1"))) { MYSQL_FIELD *field; fields = network_mysqld_proto_fielddefs_new(); field = network_mysqld_proto_fielddef_new(); field->name = g_strdup("@@version_comment"); field->type = FIELD_TYPE_VAR_STRING; g_ptr_array_add(fields, field); rows = g_ptr_array_new(); row = g_ptr_array_new(); g_ptr_array_add(row, g_strdup("MySQL Enterprise Agent")); g_ptr_array_add(rows, row); network_mysqld_con_send_resultset(con->client, fields, rows); } else if (0 == g_ascii_strncasecmp(s->str + NET_HEADER_SIZE + 1, C("select USER()"))) { MYSQL_FIELD *field; fields = network_mysqld_proto_fielddefs_new(); field = network_mysqld_proto_fielddef_new(); field->name = g_strdup("USER()"); field->type = FIELD_TYPE_VAR_STRING; g_ptr_array_add(fields, field); rows = g_ptr_array_new(); row = g_ptr_array_new(); g_ptr_array_add(row, g_strdup("root")); g_ptr_array_add(rows, row); network_mysqld_con_send_resultset(con->client, fields, rows); } else { #if 0 MYSQL_FIELD *field = NULL; lua_State *L = chas->priv->sc->L; if (0 == luaL_loadstring(L, s->str + NET_HEADER_SIZE + 1) && 0 == lua_pcall(L, 0, 1, 0)) { /* let's see what is on the stack * - scalars are turned into strings * return "foo" * - 1-dim tables are turned into a single-row result-set * return { foo = "bar", baz = "foz" } * - 2-dim tables are turned into a multi-row result-set * return { { foo = "bar" }, { "foz" } } */ switch (lua_type(L, -1)) { case LUA_TTABLE: /* take the names from the fields */ fields = network_mysqld_proto_fielddefs_new(); lua_pushnil(L); while (lua_next(L, -2) != 0) { if (lua_istable(L, -1)) { /* 2-dim table * * we only only take the keys from the first row * afterwards we ignore them */ lua_pushnil(L); while (lua_next(L, -2) != 0) { if (!rows) { /* this is the 1st round, add the keys */ lua_table_key_to_mysql_field(L, fields); } if (!row) row = g_ptr_array_new(); if (lua_isboolean(L, -1)) { g_ptr_array_add(row, g_strdup(lua_toboolean(L, -1) ? "TRUE" : "FALSE")); } else if (lua_isnumber(L, -1)) { g_ptr_array_add(row, g_strdup_printf("%.0f", lua_tonumber(L, -1))); } else { g_ptr_array_add(row, g_strdup(lua_tostring(L, -1))); } lua_pop(L, 1); /* pop the value, but keep the key on the stack */ } if (!rows) rows = g_ptr_array_new(); g_ptr_array_add(rows, row); row = NULL; } else { /* 1-dim table */ lua_table_key_to_mysql_field(L, fields); if (!row) row = g_ptr_array_new(); if (lua_isboolean(L, -1)) { g_ptr_array_add(row, g_strdup(lua_toboolean(L, -1) ? "TRUE" : "FALSE")); } else if (lua_isnumber(L, -1)) { g_ptr_array_add(row, g_strdup_printf("%.0f", lua_tonumber(L, -1))); } else { g_ptr_array_add(row, g_strdup(lua_tostring(L, -1))); } } lua_pop(L, 1); /* pop the value, but keep the key on the stack */ } if (row) { /* in 1-dim we have to append the row to the result-set, * in 2-dim this is already done and row is NULL */ if (!rows) rows = g_ptr_array_new(); g_ptr_array_add(rows, row); } break; default: /* a scalar value */ fields = network_mysqld_proto_fielddefs_new(); field = network_mysqld_proto_fielddef_new(); field->name = g_strdup("lua"); field->type = FIELD_TYPE_VAR_STRING; g_ptr_array_add(fields, field); rows = g_ptr_array_new(); row = g_ptr_array_new(); g_ptr_array_add(row, g_strdup(lua_tostring(L, -1))); g_ptr_array_add(rows, row); break; } lua_pop(L, 1); /* get rid of the result */ network_mysqld_con_send_resultset(con->client, fields, rows); } /* if we don't have fields for the resultset, we should have a * error-msg on the stack */ if (!fields) { size_t err_len = 0; const char *err; err = lua_tolstring(L, -1, &err_len); network_mysqld_con_send_error(con->client, err, err_len); lua_pop(L, 1); } #endif } /* clean up */ if (fields) { network_mysqld_proto_fielddefs_free(fields); fields = NULL; } if (rows) { for (i = 0; i < rows->len; i++) { row = rows->pdata[i]; for (j = 0; j < row->len; j++) { g_free(row->pdata[j]); } g_ptr_array_free(row, TRUE); } g_ptr_array_free(rows, TRUE); rows = NULL; } break; case COM_QUIT: break; case COM_INIT_DB: network_mysqld_con_send_ok(con->client); break; default: network_mysqld_con_send_error(con->client, C("unknown COM_*")); break; } return 0; }
/** * Handle the proxy.response to con. * proxy.response * .type can be either ERR, OK or RAW * .resultset (in case of OK) * .fields * .rows * .errmsg (in case of ERR) * .packet (in case of nil) * Note: if error occurred, should set the error string. */ int network_mysqld_con_python_handle_proxy_response(network_mysqld_con *con, PyObject *proxy){ assert(proxy); PyObject *response = PyObject_GetAttrString(proxy, "response"); //Note: the response is fetched through the tp_getset, and is a new reference. assert(response); Py_DECREF(response); PyObject *res_type = PyObject_GetAttrString(response, "type"); if(!res_type){ network_mysqld_con_send_error(con->client, C("Cannot get proxy.response.type")); return -1; } int res_type_int = PyInt_AsLong(res_type); Py_DECREF(res_type); switch(res_type_int){ case MYSQLD_PACKET_OK:{ PyObject *resultset = PyObject_GetAttrString(response, "resultset"); if(!resultset){ PyErr_Clear(); guint64 affected_rows = 0; guint64 insert_id = 0; PyObject *ar = PyObject_GetAttrString(response, "affected_rows"); if(!ar) PyErr_Clear(); else if(PyLong_Check(ar)) affected_rows = PyLong_AsLong(ar); else if(PyInt_Check(ar)) affected_rows = PyInt_AsLong(ar); Py_XDECREF(ar); PyObject *ii = PyObject_GetAttrString(response, "insert_id"); if(!ii) PyErr_Clear(); else if(PyLong_Check(ii)) insert_id = PyLong_AsLong(ii); else if(PyInt_Check(ii)) insert_id = PyInt_AsLong(ii); Py_XDECREF(ii); network_mysqld_con_send_ok_full(con->client, affected_rows, insert_id, 0x0002, 0); } else{ Py_DECREF(resultset); GPtrArray *fields = create_fields(resultset); if(!fields){ network_mysqld_con_send_error(con->client, C("Cannot get proxy.response.resultset.fields!")); PyErr_Print(); PyErr_Clear(); return -1; } if(fields->len <= 0){ network_mysqld_con_send_error(con->client, C("Size of proxy.response.resultset.fields is 0")); network_mysqld_proto_fielddefs_free(fields); return -1; } GPtrArray *rows = create_rows(resultset); if(!rows){ network_mysqld_con_send_error(con->client, C("Cannot get proxy.response.resultset.rows")); PyErr_Print(); network_mysqld_proto_fielddefs_free(fields); return -1; } network_mysqld_con_send_resultset(con->client, fields, rows); if (fields) { network_mysqld_proto_fielddefs_free(fields); fields = NULL; } if (rows) { guint i; for (i = 0; i < rows->len; i++) { GPtrArray *row = rows->pdata[i]; guint j; for (j = 0; j < row->len; j++) if (row->pdata[j]) g_free(row->pdata[j]); g_ptr_array_free(row, TRUE); } g_ptr_array_free(rows, TRUE); rows = NULL; } } break;} case MYSQLD_PACKET_ERR:{ gint errcode = ER_UNKNOWN_ERROR; /** let's call ourself Dynamic SQL ... 07000 is "dynamic SQL error" */ const gchar *sqlstate = "07000"; gchar *errmsg = NULL; PyObject *err_code = PyObject_GetAttrString(response, "errcode"); if(!err_code) //Here use the default error code: ER_UNKNOWN_ERROR PyErr_Clear(); else{ errcode = PyInt_AsLong(err_code); Py_DECREF(err_code); } PyObject *sql_state = PyObject_GetAttrString(response, "sqlstate"); if(!sql_state) //Here use the default sql state: 07000 PyErr_Clear(); else{ sqlstate = PyString_AsString(sql_state); Py_DECREF(sql_state); } PyObject *err_msg = PyObject_GetAttrString(response, "errmsg"); if(!err_msg){ PyErr_Clear(); network_mysqld_con_send_error(con->client, C("(python) proxy.response.errmsg is nil")); } else{ errmsg = PyString_AsString(err_msg); Py_DECREF(err_msg); network_mysqld_con_send_error_full(con->client, errmsg, strlen(errmsg), errcode, sqlstate); } break;} case MYSQLD_PACKET_RAW:{ PyObject *packets = PyObject_GetAttrString(response, "packets"); if(!packets) goto queue_reset; int i; for(i = 0; i < PySequence_Size(packets); i++){ PyObject *item = PySequence_GetItem(packets, i); //If invalid items doesn't influces valid ones before them. if(!item) goto queue_reset; if(!PyString_Check(item)){ PyErr_SetString(PyExc_ValueError, "proxy.response.packets' " "items should be strings."); Py_DECREF(item); Py_DECREF(packets); goto queue_reset; } network_mysqld_queue_append(con->client, con->client->send_queue, PyString_AsString(item), PyString_Size(item)); Py_DECREF(item); } Py_DECREF(packets); queue_reset: /* reset the packet-id checks */ network_mysqld_queue_reset(con->client); break;} default: g_critical("Now the response type is unknown: %d", res_type_int); return -1; } return 0; }
static int network_mysqld_con_handle_stmt(chassis G_GNUC_UNUSED *chas, network_mysqld_con *con, GString *s) { gsize i, j; GPtrArray *fields; GPtrArray *rows; GPtrArray *row; switch(s->str[NET_HEADER_SIZE]) { case COM_QUERY: fields = NULL; rows = NULL; row = NULL; if (0 == g_ascii_strncasecmp(s->str + NET_HEADER_SIZE + 1, C("select @@version_comment limit 1"))) { MYSQL_FIELD *field; fields = network_mysqld_proto_fielddefs_new(); field = network_mysqld_proto_fielddef_new(); field->name = g_strdup("@@version_comment"); field->type = FIELD_TYPE_VAR_STRING; g_ptr_array_add(fields, field); rows = g_ptr_array_new(); row = g_ptr_array_new(); g_ptr_array_add(row, g_strdup("MySQL Enterprise Agent")); g_ptr_array_add(rows, row); network_mysqld_con_send_resultset(con->client, fields, rows); } else if (0 == g_ascii_strncasecmp(s->str + NET_HEADER_SIZE + 1, C("select USER()"))) { MYSQL_FIELD *field; fields = network_mysqld_proto_fielddefs_new(); field = network_mysqld_proto_fielddef_new(); field->name = g_strdup("USER()"); field->type = FIELD_TYPE_VAR_STRING; g_ptr_array_add(fields, field); rows = g_ptr_array_new(); row = g_ptr_array_new(); g_ptr_array_add(row, g_strdup("root")); g_ptr_array_add(rows, row); network_mysqld_con_send_resultset(con->client, fields, rows); } else { network_mysqld_con_send_error(con->client, C("(master-server) query not known")); } /* clean up */ if (fields) { network_mysqld_proto_fielddefs_free(fields); fields = NULL; } if (rows) { for (i = 0; i < rows->len; i++) { row = rows->pdata[i]; for (j = 0; j < row->len; j++) { g_free(row->pdata[j]); } g_ptr_array_free(row, TRUE); } g_ptr_array_free(rows, TRUE); rows = NULL; } break; case COM_QUIT: break; case COM_INIT_DB: network_mysqld_con_send_ok(con->client); break; default: network_mysqld_con_send_error(con->client, C("unknown COM_*")); break; } return 0; }
static network_mysqld_lua_stmt_ret master_lua_read_query(network_mysqld_con *con) { network_mysqld_con_lua_t *st = con->plugin_con_state; char command = -1; network_socket *recv_sock = con->client; GList *chunk = recv_sock->recv_queue->chunks->head; GString *packet = chunk->data; if (packet->len < NET_HEADER_SIZE) return PROXY_SEND_QUERY; /* packet too short */ command = packet->str[NET_HEADER_SIZE + 0]; if (COM_QUERY == command) { /* we need some more data after the COM_QUERY */ if (packet->len < NET_HEADER_SIZE + 2) return PROXY_SEND_QUERY; /* LOAD DATA INFILE is nasty */ if (packet->len - NET_HEADER_SIZE - 1 >= sizeof("LOAD ") - 1 && 0 == g_ascii_strncasecmp(packet->str + NET_HEADER_SIZE + 1, C("LOAD "))) return PROXY_SEND_QUERY; } /* reset the query status */ network_injection_queue_reset(st->injected.queries); /* ok, here we go */ #ifdef HAVE_LUA_H switch(network_mysqld_con_lua_register_callback(con, con->config->lua_script)) { case REGISTER_CALLBACK_SUCCESS: break; case REGISTER_CALLBACK_LOAD_FAILED: network_mysqld_con_send_error(con->client, C("MySQL Proxy Lua script failed to load. Check the error log.")); con->state = CON_STATE_SEND_ERROR; return PROXY_SEND_RESULT; case REGISTER_CALLBACK_EXECUTE_FAILED: network_mysqld_con_send_error(con->client, C("MySQL Proxy Lua script failed to execute. Check the error log.")); con->state = CON_STATE_SEND_ERROR; return PROXY_SEND_RESULT; } if (st->L) { lua_State *L = st->L; network_mysqld_lua_stmt_ret ret = PROXY_NO_DECISION; g_assert(lua_isfunction(L, -1)); lua_getfenv(L, -1); g_assert(lua_istable(L, -1)); /** * reset proxy.response to a empty table */ lua_getfield(L, -1, "proxy"); g_assert(lua_istable(L, -1)); lua_newtable(L); lua_setfield(L, -2, "response"); lua_pop(L, 1); /** * get the call back */ lua_getfield_literal(L, -1, C("read_query")); if (lua_isfunction(L, -1)) { /* pass the packet as parameter */ lua_pushlstring(L, packet->str + NET_HEADER_SIZE, packet->len - NET_HEADER_SIZE); if (lua_pcall(L, 1, 1, 0) != 0) { /* hmm, the query failed */ g_critical("(read_query) %s", lua_tostring(L, -1)); lua_pop(L, 2); /* fenv + errmsg */ /* perhaps we should clean up ?*/ return PROXY_SEND_QUERY; } else { if (lua_isnumber(L, -1)) { ret = lua_tonumber(L, -1); } lua_pop(L, 1); } switch (ret) { case PROXY_SEND_RESULT: /* check the proxy.response table for content, * */ if (network_mysqld_con_lua_handle_proxy_response(con, con->config->lua_script)) { /** * handling proxy.response failed * * send a ERR packet */ network_mysqld_con_send_error(con->client, C("(lua) handling proxy.response failed, check error-log")); } break; case PROXY_NO_DECISION: /** * PROXY_NO_DECISION and PROXY_SEND_QUERY may pick another backend */ break; case PROXY_SEND_QUERY: /* send the injected queries * * injection_new(..., query); * * */ if (st->injected.queries->length) { ret = PROXY_SEND_INJECTION; } break; default: break; } lua_pop(L, 1); /* fenv */ } else { lua_pop(L, 2); /* fenv + nil */ } g_assert(lua_isfunction(L, -1)); if (ret != PROXY_NO_DECISION) { return ret; } } else { network_mysqld_con_handle_stmt(NULL, con, packet); return PROXY_SEND_RESULT; } #endif return PROXY_NO_DECISION; }
static network_mysqld_python_stmt_ret proxy_python_read_query_result(network_mysqld_con *con) { network_socket *send_sock = con->client; network_socket *recv_sock = con->server; injection *inj = NULL; network_mysqld_con_python_t *st = con->plugin_con_state; network_mysqld_python_stmt_ret ret = PROXY_NO_DECISION; GString *packet; /** * check if we want to forward the statement to the client * if not, clean the send-queue */ if (0 == st->injected.queries->length) return PROXY_NO_DECISION; if(!CHECK_FUNC(read_query_result)) return ret; inj = g_queue_pop_head(st->injected.queries); inj->result_queue = con->server->recv_queue->chunks; PyObject * injection_obj = Injection_New(inj); if(!injection_obj){ PyErr_Print(); network_mysqld_con_send_error(con->client, C("PyProxy: Failed to create injection object.")); con->state = CON_STATE_SEND_ERROR; return PROXY_SEND_RESULT; } PyObject *result = PyObject_CallFunctionObjArgs( GET_FUNC(read_query_result), st->proxy, injection_obj, NULL); Py_DECREF(injection_obj); GET_PYTHON_RESULT(read_query_result) if (!con->resultset_is_needed && (PROXY_NO_DECISION != ret)) { g_critical("%s: read_query_result() in %s tries to modify the resultset, " "but hasn't asked to buffer it in proxy.query.append(..., True). " "We ignore the change to the result-set.", G_STRLOC, con->config->python_script); ret = PROXY_NO_DECISION; } switch (ret) { case PROXY_SEND_RESULT: /* we can only replace the result, if we buffer it */ g_assert_cmpint(con->resultset_is_needed, ==, TRUE); while ((packet = g_queue_pop_head(recv_sock->recv_queue->chunks))) g_string_free(packet, TRUE); if (network_mysqld_con_python_handle_proxy_response(con, st->proxy)) if (!st->injected.sent_resultset) network_mysqld_con_send_error(con->client, C("(python) handling" " proxy.response failed, check error-log")); /* fall through */ case PROXY_NO_DECISION: if (!st->injected.sent_resultset) { // make sure we send only one result-set per client-query while ((packet = g_queue_pop_head(recv_sock->recv_queue->chunks))) network_mysqld_queue_append_raw(send_sock, send_sock->send_queue, packet); st->injected.sent_resultset++; break; } g_critical("%s.%d: got asked to send a resultset, but ignoring it as " "we already have sent %d resultset(s). injection-id: %d", __FILE__, __LINE__, st->injected.sent_resultset, inj->id); st->injected.sent_resultset++; /* fall through */ case PROXY_IGNORE_RESULT: /* trash the packets for the injection query */ if (!con->resultset_is_needed) { g_critical("%s: we tried to send more than one resultset to the " "client, but didn't had them buffered. Now the client " "is out of sync may have closed the connection on us. " "Please use proxy.queries.append(..., True) to fix this.", G_STRLOC); break; } while ((packet = g_queue_pop_head(recv_sock->recv_queue->chunks))) g_string_free(packet, TRUE); break; default: /* invalid return code */ g_message("%s.%d: return-code for read_query_result() was neither " "PROXY_SEND_RESULT or PROXY_IGNORE_RESULT, will ignore the result", __FILE__, __LINE__); while ((packet = g_queue_pop_head(send_sock->send_queue->chunks))) g_string_free(packet, TRUE); break; } injection_free(inj); return ret; }