void end_session(struct session *s) { int err; logmsg(LOG_INFO, "#%d ending session", s->id); /* Flush output buffers. */ if (s->client_bufev && s->client_fd != -1) evbuffer_write(s->client_bufev->output, s->client_fd); if (s->server_bufev && s->server_fd != -1) evbuffer_write(s->server_bufev->output, s->server_fd); if (s->client_fd != -1) close(s->client_fd); if (s->server_fd != -1) close(s->server_fd); if (s->client_bufev) bufferevent_free(s->client_bufev); if (s->server_bufev) bufferevent_free(s->server_bufev); /* Remove rulesets by commiting empty ones. */ err = 0; if (prepare_commit(s->id) == -1) err = errno; else if (do_commit() == -1) { err = errno; do_rollback(); } if (err) logmsg(LOG_ERR, "#%d pf rule removal failed: %s", s->id, strerror(err)); LIST_REMOVE(s, entry); free(s); session_count--; }
int allow_data_connection(struct session *s) { struct sockaddr *client_sa, *orig_sa, *proxy_sa, *server_sa; int prepared = 0; /* * The pf rules below do quite some NAT rewriting, to keep up * appearances. Points to keep in mind: * 1) The client must think it's talking to the real server, * for both control and data connections. Transparently. * 2) The server must think that the proxy is the client. * 3) Source and destination ports are rewritten to minimize * port collisions, to aid security (some systems pick weak * ports) or to satisfy RFC requirements (source port 20). */ /* Cast this once, to make code below it more readable. */ client_sa = sstosa(&s->client_ss); server_sa = sstosa(&s->server_ss); proxy_sa = sstosa(&s->proxy_ss); if (fixed_server) /* Fixed server: data connections must appear to come from / go to the original server, not the fixed one. */ orig_sa = sstosa(&s->orig_server_ss); else /* Server not fixed: orig_server == server. */ orig_sa = sstosa(&s->server_ss); /* Passive modes. */ if (s->cmd == CMD_PASV || s->cmd == CMD_EPSV) { s->port = parse_port(s->cmd); if (s->port < MIN_PORT) { logmsg(LOG_CRIT, "#%d bad port in '%s'", s->id, linebuf); return (0); } s->proxy_port = pick_proxy_port(); logmsg(LOG_INFO, "#%d passive: client to server port %d" " via port %d", s->id, s->port, s->proxy_port); if (prepare_commit(s->id) == -1) goto fail; prepared = 1; proxy_reply(s->cmd, orig_sa, s->proxy_port); logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, linebuf); /* pass in from $client to $orig_server port $proxy_port rdr-to $server port $port */ if (add_rdr(s->id, client_sa, s->client_rd, orig_sa, s->proxy_port, server_sa, s->port, getrtable()) == -1) goto fail; /* pass out from $client to $server port $port nat-to $proxy */ if (add_nat(s->id, client_sa, getrtable(), server_sa, s->port, proxy_sa, PF_NAT_PROXY_PORT_LOW, PF_NAT_PROXY_PORT_HIGH) == -1) goto fail; } /* Active modes. */ if (s->cmd == CMD_PORT || s->cmd == CMD_EPRT) { logmsg(LOG_INFO, "#%d active: server to client port %d" " via port %d", s->id, s->port, s->proxy_port); if (prepare_commit(s->id) == -1) goto fail; prepared = 1; /* pass in from $server to $proxy port $proxy_port rdr-to $client port $port */ if (add_rdr(s->id, server_sa, getrtable(), proxy_sa, s->proxy_port, client_sa, s->port, s->client_rd) == -1) goto fail; /* pass out from $server to $client port $port nat-to $orig_server port $natport */ if (rfc_mode && s->cmd == CMD_PORT) { /* Rewrite sourceport to RFC mandated 20. */ if (add_nat(s->id, server_sa, s->client_rd, client_sa, s->port, orig_sa, 20, 20) == -1) goto fail; } else { /* Let pf pick a source port from the standard range. */ if (add_nat(s->id, server_sa, s->client_rd, client_sa, s->port, orig_sa, PF_NAT_PROXY_PORT_LOW, PF_NAT_PROXY_PORT_HIGH) == -1) goto fail; } } /* Commit rules if they were prepared. */ if (prepared && (do_commit() == -1)) { if (errno != EBUSY) goto fail; /* One more try if busy. */ usleep(5000); if (do_commit() == -1) goto fail; } s->cmd = CMD_NONE; s->port = 0; return (1); fail: logmsg(LOG_CRIT, "#%d pf operation failed: %s", s->id, strerror(errno)); if (prepared) do_rollback(); return (0); }
int main(int argc, const char** argv) { ndb_init(); const char* usage = "Usage: ndbsql [-h] [-d dsn] [-f file] [stmt]\n-h help\n-d <database name or connect string>\n-f <file name> batch mode\nstmt single SQL statement\n"; const char* dsn = "TEST_DB"; bool helpFlg = false, batchMode = false; const char* fileName = 0; FILE* inputFile = stdin; const char* singleStmt = 0; s_readBuf = (char*)malloc(s_bufSize); while (++argv, --argc > 0) { const char* arg = argv[0]; if (arg[0] != '-') break; if (strcmp(arg, "-d") == 0) { if (++argv, --argc > 0) { dsn = argv[0]; continue; } } if (strcmp(arg, "-h") == 0) { helpFlg = true; continue; } if (strcmp(arg, "-f") == 0) { if (++argv, --argc > 0) { fileName = argv[0]; continue; } } ndbout << usage; return 1; } if (helpFlg) { ndbout << usage << "\n"; print_help(); return 0; } if (fileName != 0) { if (argc > 0) { ndbout << usage; return 1; } if ((inputFile = fopen(fileName, "r")) == 0) { ndbout << "Could not read file " << fileName << ": " << strerror(errno) << endl; return 1; } batchMode = true; } if (argc > 0) { singleStmt = argv[0]; batchMode = true; } if (! batchMode) ndbout << "NDB Cluster NDB SQL -- A simple SQL Command-line Interface\n\n"; Con con(dsn); if (do_connect(con) < 0) return 1; if (! batchMode) ndbout << "Terminate SQL statements with a semi-colon ';'\n"; char* line = 0; char* line2 = 0; char* line3 = 0; unsigned lineno = 0; bool has_semi; bool exit_on_error = false; int exit_code = 0; while (1) { free(line); line = 0; lineno = 0; more_lines: free(line2); free(line3); line2 = line3 = 0; lineno++; has_semi = false; char prompt[20]; if (lineno == 1) strcpy(prompt, "SQL> "); else sprintf(prompt, "%4d ", lineno); if (singleStmt != 0) { line = strdup(singleStmt); int n = strlen(line); while (n > 0 && isspace(line[n - 1])) { line[--n] = 0; } if (n > 0 && line[n - 1] == ';') line[n - 1] = 0; has_semi = true; // regardless } else { const char *line1 = readline_gets(prompt, batchMode, inputFile); if (line1 != 0) { if (line == 0) line = strdup(line1); else { line = (char*)realloc(line, strlen(line) + 1 + strlen(line1) + 1); strcat(line, "\n"); strcat(line, line1); } if (batchMode) ndbout << prompt << line1 << endl; } else { if (! batchMode) ndbout << endl; if (line != 0) ndbout << "Ignored unterminated SQL statement" << endl; break; } } line2 = (char*)malloc(strlen(line) + 1); { char* p = line2; char* q = line; bool str = false; while (*q != 0) { if (*q == '\'') { str = !str; *p++ = *q++; } else if (!str && *q == '-' && *(q + 1) == '-') { while (*q != 0 && *q != '\n') q++; } else *p++ = *q++; } *p = 0; int n = strlen(line2); while (n > 0 && isspace(line2[n - 1])) line2[--n] = 0; if (n > 0 && line2[n - 1] == ';') { line2[--n] = 0; has_semi = true; } } line3 = strdup(line2); char* tok[10]; int ntok = 0; tok[ntok] = strtok(line3, " "); while (tok[ntok] != 0) { ntok++; if (ntok == 10) break; tok[ntok] = strtok(0, " "); } if (ntok == 0) continue; if (!strcasecmp(tok[0], "help") || !strcmp(tok[0], "?")) { if (ntok != 2) print_help(); else if (!strcasecmp(tok[1], "create")) print_help_create(); else if (!strcasecmp(tok[1], "insert")) print_help_insert(); else if (strcasecmp(tok[1], "select")) print_help_select(); else if (!strcasecmp(tok[1], "delete")) print_help_update(); else if (!strcasecmp(tok[1], "update")) print_help_update(); else if (!strcasecmp(tok[1], "virtual")) print_help_virtual(); else print_help(); continue; } if (!strcasecmp(tok[0], "list")) { if (ntok == 2 && !strcasecmp(tok[1], "tables")) { free(line2); line2 = strdup("SELECT TABLE_NAME FROM ODBC$TABLES"); has_semi = true; } else { ndbout << "Invalid list option - try help" << endl; continue; } } if (ntok == 1 && !strcasecmp(tok[0], "quit")) break; if (ntok == 1 && !strcasecmp(tok[0], "exit")) break; if (ntok == 1 && !strcasecmp(tok[0], "bye")) break; if (!strcasecmp(tok[0], "set")) { if (ntok == 1) { char* p; p = getenv("NDB_ODBC_TRACE"); ndbout << "Trace level is " << (p ? atoi(p) : 0) << endl; int ret = get_autocommit(con); if (ret != -1) ndbout << "Autocommit is " << (ret == SQL_AUTOCOMMIT_ON ? "on" : "off") << endl; } else if (ntok == 3 && !strcasecmp(tok[1], "trace")) { static char env[40]; int n = tok[2] ? atoi(tok[2]) : 0; sprintf(env, "NDB_ODBC_TRACE=%d", n); putenv(env); ndbout << "Trace level set to " << n << endl; } else if (ntok == 3 && !strcasecmp(tok[1], "autocommit")) { if (tok[2] && !strcasecmp(tok[2], "on")) { int ret = set_autocommit(con, SQL_AUTOCOMMIT_ON); if (ret != -1) ndbout << "Autocommit set to ON" << endl; } else if (tok[2] && !strcasecmp(tok[2], "off")) { int ret = set_autocommit(con, SQL_AUTOCOMMIT_OFF); if (ret != -1) ndbout << "Autocommit set to OFF - transaction may time out" << endl; } else { ndbout << "Invalid autocommit option - try help" << endl; } } else { ndbout << "Invalid set command - try help" << endl; } continue; } if (ntok >= 2 && !strcasecmp(tok[0], "whenever") && !strcasecmp(tok[1], "sqlerror")) { if (ntok == 3 && !strcasecmp(tok[2], "exit")) exit_on_error = true; else if (ntok == 3 && !strcasecmp(tok[2], "continue")) exit_on_error = false; else { ndbout << "Invalid whenever clause - try help" << endl; } continue; } if (!strcasecmp(tok[0], "commit")) { if (ntok == 1) { if (do_commit(con) != -1) ndbout << "Commit done" << endl; else { exit_code = 1; if (exit_on_error) { ndbout << "Exit on error" << endl; break; } } } else { ndbout << "Invalid commit command - try help" << endl; } continue; } if (!strcasecmp(tok[0], "rollback")) { if (ntok == 1) { if (do_rollback(con) != -1) ndbout << "Rollback done" << endl; else { exit_code = 1; if (exit_on_error) { ndbout << "Exit on error" << endl; break; } } } else { ndbout << "Invalid commit command - try help" << endl; } continue; } if (! has_semi) goto more_lines; if (do_stmt(con, line2) != 0) { exit_code = 1; if (exit_on_error) { ndbout << "Exit on error" << endl; break; } } if (singleStmt) break; } do_disconnect(con); return exit_code; }