* (c) 2007 Martin Mares <mj@ucw.cz>
*/
-/*
- * FIXME:
- * - competition timeout & per-contestant exceptions
- */
-
#undef LOCAL_DEBUG
-#include "lib/lib.h"
-#include "lib/conf.h"
-#include "lib/getopt.h"
-#include "lib/ipaccess.h"
-#include "lib/fastbuf.h"
+#include "ucw/lib.h"
+#include "ucw/conf.h"
+#include "ucw/getopt.h"
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <netinet/in.h>
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
+
+#include "submitd.h"
/*** CONFIGURATION ***/
+static char *log_name;
static uns port = 8888;
static uns dh_bits = 1024;
static uns max_conn = 10;
static uns session_timeout;
-static byte *ca_cert_name = "?";
-static byte *server_cert_name = "?";
-static byte *server_key_name = "?";
+uns max_versions;
+static char *ca_cert_name = "?";
+static char *server_cert_name = "?";
+static char *server_key_name = "?";
+char *history_format;
static clist access_rules;
+static uns trace_tls;
+uns max_request_size;
+uns max_attachment_size;
+uns trace_commands;
-struct access_rule {
- cnode n;
- struct ip_addrmask addrmask;
- uns allow_admin;
- uns plain_text;
- uns max_conn;
+static struct cf_section ip_node_conf = {
+ CF_TYPE(struct ip_node),
+ CF_ITEMS {
+ CF_USER("IP", PTR_TO(struct ip_node, addrmask), &ip_addrmask_type),
+ CF_END
+ }
};
static struct cf_section access_conf = {
CF_TYPE(struct access_rule),
CF_ITEMS {
- CF_USER("IP", PTR_TO(struct access_rule, addrmask), &ip_addrmask_type),
+ CF_LIST("IP", PTR_TO(struct access_rule, ip_list), &ip_node_conf),
CF_UNS("Admin", PTR_TO(struct access_rule, allow_admin)),
CF_UNS("PlainText", PTR_TO(struct access_rule, plain_text)),
CF_UNS("MaxConn", PTR_TO(struct access_rule, max_conn)),
}
};
-static byte *
-config_init(void)
-{
- clist_init(&access_rules);
- return NULL;
-}
-
static struct cf_section submitd_conf = {
- CF_INIT(config_init),
CF_ITEMS {
+ CF_STRING("LogFile", &log_name),
CF_UNS("Port", &port),
CF_UNS("DHBits", &dh_bits),
CF_UNS("MaxConn", &max_conn),
CF_UNS("SessionTimeout", &session_timeout),
+ CF_UNS("MaxRequestSize", &max_request_size),
+ CF_UNS("MaxAttachSize", &max_attachment_size),
+ CF_UNS("MaxVersions", &max_versions),
CF_STRING("CACert", &ca_cert_name),
CF_STRING("ServerCert", &server_cert_name),
CF_STRING("ServerKey", &server_key_name),
+ CF_STRING("History", &history_format),
CF_LIST("Access", &access_rules, &access_conf),
+ CF_UNS("TraceTLS", &trace_tls),
+ CF_UNS("TraceCommands", &trace_commands),
CF_END
}
};
/*** CONNECTIONS ***/
-struct conn {
- cnode n;
- u32 ip; // Used by the main loop connection logic
- pid_t pid;
- uns id;
- struct access_rule *rule; // Rule matched by this connection
- int sk; // Client socket
- gnutls_session_t tls; // TLS session
- struct fastbuf rx_fb, tx_fb; // Fastbufs for communication with the client
-};
-
static clist connections;
static uns last_conn_id;
static uns num_conn;
static struct conn *
conn_new(void)
{
- struct conn *c = xmalloc(sizeof(*c));
+ struct conn *c = xmalloc_zero(sizeof(*c));
c->id = ++last_conn_id;
clist_add_tail(&connections, &c->n);
num_conn++;
static void
conn_free(struct conn *c)
{
+ xfree(c->ip_string);
+ xfree(c->cert_name);
clist_remove(&c->n);
num_conn--;
xfree(c);
lookup_rule(u32 ip)
{
CLIST_FOR_EACH(struct access_rule *, r, access_rules)
- if (ip_addrmask_match(&r->addrmask, ip))
- return r;
+ CLIST_FOR_EACH(struct ip_node *, n, r->ip_list)
+ if (ip_addrmask_match(&n->addrmask, ip))
+ return r;
return NULL;
}
int err;
err = gnutls_init(&s, GNUTLS_SERVER); TLS_CHECK(gnutls_init);
- err = gnutls_set_default_priority(s); TLS_CHECK(gnutls_set_default_priority); // FIXME
+ err = gnutls_set_default_priority(s); TLS_CHECK(gnutls_set_default_priority);
gnutls_credentials_set(s, GNUTLS_CRD_CERTIFICATE, cert_cred);
gnutls_certificate_server_set_request(s, GNUTLS_CERT_REQUEST);
gnutls_dh_set_prime_bits(s, dh_bits);
}
static const char *
-tls_verify_cert(gnutls_session_t s)
+tls_verify_cert(struct conn *c)
{
+ gnutls_session_t s = c->tls;
uns status, num_certs;
int err;
gnutls_x509_crt_t cert;
return "Cannot import certificate";
/* XXX: We do not check expiration and activation since the keys are generated for a single contest only anyway. */
- byte dn[256];
+ char dn[256];
size_t dn_len = sizeof(dn);
err = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn, &dn_len);
if (err < 0)
return "Cannot retrieve common name";
- log(L_INFO, "Cert CN: %s", dn);
+ if (trace_tls)
+ msg(L_INFO, "Cert CN: %s", dn);
+ c->cert_name = xstrdup(dn);
/* Check certificate purpose */
- byte purp[256];
+ char purp[256];
int purpi = 0;
do
{
}
static void
-tls_log_params(gnutls_session_t s)
+tls_log_params(struct conn *c)
{
+ if (!trace_tls)
+ return;
+ gnutls_session_t s = c->tls;
const char *proto = gnutls_protocol_get_name(gnutls_protocol_get_version(s));
const char *kx = gnutls_kx_get_name(gnutls_kx_get(s));
const char *cert = gnutls_certificate_type_get_name(gnutls_certificate_type_get(s));
const char *comp = gnutls_compression_get_name(gnutls_compression_get(s));
const char *cipher = gnutls_cipher_get_name(gnutls_cipher_get(s));
const char *mac = gnutls_mac_get_name(gnutls_mac_get(s));
- log(L_DEBUG, "TLS params: proto=%s kx=%s cert=%s comp=%s cipher=%s mac=%s",
+ msg(L_DEBUG, "TLS params: proto=%s kx=%s cert=%s comp=%s cipher=%s mac=%s",
proto, kx, cert, comp, cipher, mac);
}
-/*** SOCKET FASTBUFS ***/
+/*** FASTBUFS OVER SOCKETS AND TLS ***/
-static void NONRET
+void NONRET // Fatal protocol violation
client_error(char *msg, ...)
{
va_list args;
va_start(args, msg);
- vlog_msg(L_ERROR_R, msg, args);
+ vmsg(L_ERROR_R, msg, args);
exit(0);
}
static void
client_loop(struct conn *c)
{
+ setproctitle("submitd: client %s", c->ip_string);
log_pid = c->id;
init_sk_fastbufs(c);
int err = gnutls_handshake(c->tls);
if (err < 0)
client_error("TLS handshake failed: %s", gnutls_strerror(err));
- tls_log_params(c->tls);
- const char *cert_err = tls_verify_cert(c->tls);
+ tls_log_params(c);
+ const char *cert_err = tls_verify_cert(c);
if (cert_err)
client_error("TLS certificate failure: %s", cert_err);
init_tls_fastbufs(c);
}
- for (;;)
+ alarm(session_timeout);
+ if (!process_init(c))
+ msg(L_ERROR, "Protocol handshake failed");
+ else
{
- alarm(session_timeout);
- byte buf[1024];
- if (!bgets(&c->rx_fb, buf, sizeof(buf)))
- break;
- bputsn(&c->tx_fb, buf);
- bflush(&c->tx_fb);
+ setproctitle("submitd: client %s (%s)", c->ip_string, c->user);
+ for (;;)
+ {
+ alarm(session_timeout);
+ if (!process_command(c))
+ break;
+ }
}
if (c->tls)
static void
reap_child(pid_t pid, int status)
{
- byte msg[EXIT_STATUS_MSG_SIZE];
- if (format_exit_status(msg, status))
- log(L_ERROR, "Child %d %s", (int)pid, msg);
+ char buf[EXIT_STATUS_MSG_SIZE];
+ if (format_exit_status(buf, status))
+ msg(L_ERROR, "Child %d %s", (int)pid, buf);
CLIST_FOR_EACH(struct conn *, c, connections)
if (c->pid == pid)
{
- log(L_INFO, "Connection %d closed", c->id);
+ msg(L_INFO, "Connection %d closed", c->id);
conn_free(c);
return;
}
- log(L_ERROR, "Cannot find connection for child process %d", (int)pid);
+ msg(L_ERROR, "Cannot find connection for child process %d", (int)pid);
}
static int listen_sk;
die("accept: %m");
}
- byte ipbuf[INET_ADDRSTRLEN];
+ char ipbuf[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &sa.sin_addr, ipbuf, sizeof(ipbuf));
u32 addr = ntohl(sa.sin_addr.s_addr);
uns port = ntohs(sa.sin_port);
}
struct conn *c = conn_new();
- log(L_INFO, "Connection from %s:%d (id %d, %s, %s)",
+ msg(L_INFO, "Connection from %s:%d (id %d, %s, %s)",
ipbuf, port, c->id,
(rule->plain_text ? "plain-text" : "TLS"),
(rule->allow_admin ? "admin" : "user"));
c->ip = addr;
+ c->ip_string = xstrdup(ipbuf);
c->sk = sk;
c->rule = rule;
{
conn_free(c);
err = "Server overloaded";
- log(L_ERROR, "Fork failed: %m");
+ msg(L_ERROR, "Fork failed: %m");
goto reject2;
}
if (!c->pid)
return;
reject:
- log(L_ERROR_R, "Connection from %s:%d rejected (%s)", ipbuf, port, err);
+ msg(L_ERROR_R, "Connection from %s:%d rejected (%s)", ipbuf, port, err);
reject2: ;
// Write an error message to the socket, but do not allow it to slow us down
struct linger ling = { .l_onoff=0 };
if (setsockopt(sk, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)) < 0)
- log(L_ERROR, "Cannot set SO_LINGER: %m");
+ msg(L_ERROR, "Cannot set SO_LINGER: %m");
write(sk, "-", 1);
write(sk, err, strlen(err));
write(sk, "\n", 1);
int main(int argc, char **argv)
{
setproctitle_init(argc, argv);
- cf_def_file = "config";
+ cf_def_file = "cf/submitd";
cf_declare_section("SubmitD", &submitd_conf, 0);
+ cf_declare_section("Tasks", &tasks_conf, 0);
int opt;
if ((opt = cf_getopt(argc, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL)) >= 0)
die("This program has no options");
- log(L_INFO, "Initializing TLS");
+ log_file(log_name);
+
+ msg(L_INFO, "Initializing TLS");
tls_init();
conn_init();
sk_init();
- log(L_INFO, "Listening on port %d", port);
+ msg(L_INFO, "Listening on port %d", port);
struct sigaction sa = {
.sa_handler = sigchld_handler
for (;;)
{
+ setproctitle("submitd: %d connections", num_conn);
int status;
pid_t pid = waitpid(-1, &status, WNOHANG);
if (pid > 0)