X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=popproxy.c;h=d2ccbf76f709b0a16c3231c46387e926f36b0f25;hb=cd4f92d6945a7611e1221958450c734d7b939784;hp=d43c9d6f06a38eb89023932c56a0b8382eda080e;hpb=e15c63276b77ae8fe7308409da31804383b4dba2;p=cascardo%2Frnetproxy.git diff --git a/popproxy.c b/popproxy.c index d43c9d6..d2ccbf7 100644 --- a/popproxy.c +++ b/popproxy.c @@ -19,75 +19,320 @@ */ #include -#include #include +#include #include +#include +#include +#include +#include #include "log.h" -#include "nethook.h" -#include "proto_detect.h" +#include "pop.h" + +#include "hcconn.h" +#include "hcconn_ssl.h" +#include "tcp_connect.h" + +#include "usermap.h" #define CONFFILE SYSCONFDIR "/popproxy.conf" -void new_client (GServer* server, GConn* conn, gpointer data) +struct pop_address { - net_hook_t* hook; - if (conn == NULL) + char *server; + char *port; + int ssl; +}; + +static HCConn * +server_conn_new (char *server, char *port, int ssl) +{ + int fd; + HCConn *conn; + HCConn *ssl_conn; + int r; + fd = hc_tcp_connect (server, port); + if (fd < 0) + { + g_warning ("Could not connect to server at %s:%s.", server, port); + return NULL; + } + conn = hc_conn_new (NULL, NULL); + r = hc_conn_set_driver_channel (conn, fd); + if (r != 0) + { + hc_conn_close (conn); + close (fd); + return NULL; + } + if (!ssl) + return conn; + ssl_conn = hc_conn_new (NULL, NULL); + r = hc_conn_set_driver_ssl_client (ssl_conn, conn); + if (r != 0) + { + hc_conn_close (ssl_conn); + hc_conn_close (conn); + return NULL; + } + return ssl_conn; +} + +static HCConn * +client_conn_new (int fd) +{ + HCConn *conn; + HCConn *ssl_conn; + HCConn *pop_conn; + int r; + conn = hc_conn_new (NULL, NULL); + r = hc_conn_set_driver_channel (conn, fd); + if (r != 0) + { + hc_conn_close (conn); + close (fd); + return NULL; + } + ssl_conn = hc_conn_new (NULL, NULL); + hc_conn_set_driver_ssl_server (ssl_conn, conn); + if (r != 0) + { + hc_conn_close (ssl_conn); + hc_conn_close (conn); + return NULL; + } + pop_conn = hc_conn_new (NULL, NULL); + r = hc_conn_set_driver_pop (pop_conn, ssl_conn); + if (r != 0) + { + hc_conn_close (pop_conn); + hc_conn_close (ssl_conn); + return NULL; + } + return pop_conn; +} + +static void +push_other (HCConn *conn, HCEvent event, gpointer data) +{ + char buffer[4096]; + int r; + switch (event) + { + case HC_EVENT_READ: + while ((r = hc_conn_read (conn, buffer, sizeof (buffer))) > 0) + hc_conn_write (data, buffer, r); + break; + case HC_EVENT_CLOSE: + hc_conn_close (conn); + hc_conn_close (data); + break; + } +} + +static void +new_client (int fd, struct sockaddr *addr, socklen_t saddr, gpointer data) +{ + HCConn *client_conn; + HCConn *server_conn; + struct pop_address *address = data; + if (fd < 0) { g_critical ("Server has received an error event."); return; } - g_message ("Received connection from %s.", conn->hostname); - hook = proto_detect_new (conn); - gnet_conn_read (conn); + + /* FIXME: Should be independent of address type. */ + g_message ("Received connection from %s.", + inet_ntoa (((struct sockaddr_in *) addr)->sin_addr)); + + server_conn = server_conn_new (address->server, address->port, + address->ssl); + if (server_conn == NULL) + { + return; + } + client_conn = client_conn_new (fd); + if (client_conn == NULL) + { + hc_conn_close (server_conn); + return; + } + + hc_conn_set_callback (client_conn, push_other, server_conn); + hc_conn_set_callback (server_conn, push_other, client_conn); + } -static gchar* configfile; +static gchar *configfile; +static gboolean foreground; static GOptionEntry opt_entries[] = { { "config-file", 'c', 0, G_OPTION_ARG_FILENAME, &configfile, "Configuration file location", "file" }, + { "foreground", 'f', 0, G_OPTION_ARG_NONE, &foreground, + "Run in foreground", 0 }, { NULL } }; -int main (int argc, char** argv) +int main (int argc, char **argv) { - GOptionContext* opt_ctx; + GOptionContext *opt_ctx; GKeyFile *keyfile; - GInetAddr* inetaddr; - gchar* conf_address; - gint port; + GError *error; + int server_fd; + gchar *conf_address; + gchar *port; + gchar *server_address; + gchar *server_port; + int server_ssl; + gchar *certfile; + gchar *ssl_keyfile; + gchar *policy; + struct pop_address pop_address; - gnet_init (); - pop_log_init (); + signal (SIGPIPE, SIG_IGN); + + gnutls_global_init (); configfile = CONFFILE; opt_ctx = g_option_context_new (""); g_option_context_add_main_entries (opt_ctx, opt_entries, NULL); - if (!g_option_context_parse (opt_ctx, &argc, &argv, NULL)) + + error = NULL; + if (!g_option_context_parse (opt_ctx, &argc, &argv, &error)) { - g_log (NULL, G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL, - "Could not parse command line options."); + g_critical ("Could not parse command line options: %s.", + error->message); + g_error_free (error); + exit (1); } g_option_context_free (opt_ctx); - keyfile = g_key_file_new (); + keyfile = g_key_file_new (); + + error = NULL; + if (g_key_file_load_from_file (keyfile, configfile, + G_KEY_FILE_NONE, &error) == FALSE) + { + g_critical ("Could not load configuration file %s: %s.", + configfile, error->message); + g_error_free (error); + exit (1); + } + + error = NULL; + certfile = g_key_file_get_string (keyfile, "global", "certfile", + &error); + if (certfile == NULL && error != NULL) + { + g_critical ("No certification file specified: %s.", + error->message); + g_error_free (error); + exit (1); + } + error = NULL; + ssl_keyfile = g_key_file_get_string (keyfile, "global", "keyfile", + &error); + if (ssl_keyfile == NULL && error != NULL) + { + ssl_keyfile = g_strdup (certfile); + g_error_free (error); + } + + + error = NULL; + conf_address = g_key_file_get_string (keyfile, "global", "address", + &error); + if (conf_address == NULL && error != NULL) + { + conf_address = g_strdup ("0.0.0.0"); + g_error_free (error); + } + error = NULL; + port = g_key_file_get_string (keyfile, "global", "port", &error); + if (port == NULL && error != NULL) + { + port = g_strdup ("110"); + g_error_free (error); + } + error = NULL; + server_address = g_key_file_get_string (keyfile, "global", "server", + &error); + if (server_address == NULL && error != NULL) + { + server_address = g_strdup ("127.0.0.1"); + g_error_free (error); + } + error = NULL; + server_port = g_key_file_get_string (keyfile, "global", "server_port", + &error); + if (server_port == NULL && error != NULL) + { + server_port = g_strdup ("995"); + g_error_free (error); + } + error = NULL; + server_ssl = g_key_file_get_boolean (keyfile, "global", "server_ssl", + &error); + if (server_ssl == 0 && error != NULL) + { + server_ssl = 0; + g_error_free (error); + } + + error = NULL; + policy = g_key_file_get_string (keyfile, "global", "policy", + &error); + if (policy == NULL && error != NULL) + { + policy = g_strdup ("deny"); + g_error_free (error); + } + + if (!strcmp (policy, "allow")) + ACCESS_DEFAULT = ACCESS_ALLOW; + g_free (policy); - g_key_file_load_from_file (keyfile, configfile, G_KEY_FILE_NONE, NULL); - conf_address = g_key_file_get_string (keyfile, "global", "address", NULL); - port = g_key_file_get_integer (keyfile, "global", "port", NULL); + pop_address.server = server_address; + pop_address.port = server_port; + pop_address.ssl = server_ssl; - g_message ("Listen address is %s:%d.", conf_address, port); + server_fd = hc_tcp_server (port); + if (server_fd < 0) + { + g_critical ("Could not create server."); + exit (1); + } + hc_server_add_watch (server_fd, new_client, &pop_address); + + pop_log_init (); + + g_message ("Listening at %s:%s.", conf_address, port); + if (ACCESS_DEFAULT == ACCESS_ALLOW) + g_message ("Authorizing users by default."); - inetaddr = gnet_inetaddr_new_nonblock (conf_address, port); - gnet_server_new (inetaddr, port, new_client, NULL); + if (!foreground) + daemon (0, 0); - daemon (0, 0); + g_free (conf_address); + g_free (port); + + hc_conn_ssl_server_init_credentials (certfile, ssl_keyfile); + + g_free (certfile); + g_free (ssl_keyfile); g_main_loop_run (g_main_loop_new (g_main_context_default (), TRUE)); + gnutls_global_deinit (); + + g_free (server_address); + g_free (server_port); + return 0; }