/* netcat-like program
* For testing, and for exposition.
*/
#include "../gskstreamclient.h"
#include "../ssl/gskstreamssl.h"
#include "../ssl/gskstreamlistenerssl.h"
#include "../gskstreamlistenersocket.h"
#include "../gskstreamfd.h"
#include "../gskmain.h"
#include "../gskinit.h"
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "../gsknameresolver.h"
static void
usage (void)
{
g_printerr ("gsk-netcat --client HOST PORT\n"
"gsk-netcat --server PORT\n"
"\n"
"Options allowed by both client and server:\n"
" --ssl[=CERT:KEY:PASSWORD] Use SSL\n"
"\n"
"Options allowed by server:\n"
" --proxy=HOST:PORT Proxy to given host/port pair.\n"
);
exit(1);
}
typedef enum
{
MODE_NONE,
MODE_SERVER,
MODE_CLIENT
} Mode;
typedef struct
{
char *host;
guint port;
GskSocketAddress *address;
GHookList traps;
gboolean doing_name_lookup;
} HostPort;
/* --- Options --- */
static Mode mode = MODE_NONE;
/* general options: SSL */
static gboolean use_ssl = FALSE;
static char *ssl_cert = NULL;
static char *ssl_key = NULL;
static char *ssl_password = NULL;
/* server options */
static int server_port;
static HostPort proxy_hostport;
/* client options */
static HostPort client_hostport;
/* --- Name Resolution Handling (With Fatal Error Handling) --- */
static void
host_port_init (HostPort *hp)
{
hp->host = NULL;
hp->port = 0;
hp->address = NULL;
hp->doing_name_lookup = FALSE;
g_hook_list_init (&hp->traps, sizeof (GHook));
}
static void
connect_to_stdio (GskStream *str, gboolean terminate_on_input_death)
{
GskStream *in, *out;
GError *error =NULL;
in = gsk_stream_fd_new_auto (STDIN_FILENO);
out = gsk_stream_fd_new_auto (STDOUT_FILENO);
g_assert (in && out);
if (!gsk_stream_attach (in, str, &error))
g_error ("error connecting input: %s", error->message);
if (!gsk_stream_attach (str, out, &error))
g_error ("error connecting output: %s", error->message);
if (terminate_on_input_death)
g_object_weak_ref (G_OBJECT (in), (GWeakNotify) gsk_main_quit, NULL);
g_object_unref (in);
g_object_unref (out);
}
/* --- Client --- */
void
setup_client (void)
{
GError *error = NULL;
GskStream *str;
if (client_hostport.port == 0 || client_hostport.host == NULL)
g_error ("client usage error: you must specify a host and port");
client_hostport.address = gsk_socket_address_symbolic_ipv4_new (client_hostport.host,
client_hostport.port);
str = gsk_stream_new_connecting (client_hostport.address, &error);
if (str == NULL)
g_error ("client: error connecting to %s, port %u: %s",
client_hostport.host, client_hostport.port, error->message);
if (use_ssl)
{
GskStream *ssl_client = gsk_stream_ssl_new_client (ssl_cert,
ssl_key,
ssl_password,
str, &error);
if (ssl_client == NULL)
g_error ("error creating SSL stream: %s", error->message);
g_object_unref (str);
str = ssl_client;
}
connect_to_stdio (str, TRUE);
g_object_weak_ref (G_OBJECT (str), (GWeakNotify) gsk_main_quit, NULL);
g_object_unref (str);
}
/* --- Server --- */
static gboolean
handle_server_listener_accept (GskStream *new_connection,
gpointer data,
GError **error)
{
connect_to_stdio (new_connection, TRUE);
g_object_unref (new_connection);
return TRUE;
}
static void
handle_server_listener_error (GError *error, gpointer data)
{
g_error ("server listener error: %s" ,error->message);
}
void
setup_server (void)
{
/* Create the listener, either regular, or with SSL. */
GskSocketAddress *address;
GskStreamListener *listener;
GError *error = NULL;
address = gsk_socket_address_ipv4_new (gsk_ipv4_ip_address_any, server_port);
listener = gsk_stream_listener_socket_new_bind (address, &error);
if (listener == NULL)
g_error ("error binding to %u: %s", server_port, error->message);
g_object_unref (address);
if (use_ssl)
{
GskStreamListener *ssl_listener = gsk_stream_listener_ssl_new (listener, ssl_cert, ssl_key);
/* XXX: don't we need to handle ssl_password???? */
g_object_unref (listener);
listener = ssl_listener;
}
gsk_stream_listener_handle_accept (listener,
handle_server_listener_accept,
handle_server_listener_error,
NULL, NULL);
}
/* --- Main --- */
int main(int argc, char **argv)
{
int i;
gsk_init_without_threads (&argc, &argv);
host_port_init (&proxy_hostport);
host_port_init (&client_hostport);
/* parse arguments */
for (i = 1; i < argc; i++)
{
const char *arg = argv[i];
if (strcmp (arg, "--client") == 0)
mode = MODE_CLIENT;
else if (strcmp (arg, "--server") == 0)
mode = MODE_SERVER;
else if (strcmp (arg, "--ssl") == 0)
use_ssl = TRUE;
else if (g_str_has_prefix (arg, "--ssl="))
{
char **pieces;
use_ssl = TRUE;
pieces = g_strsplit (strchr(arg,'=') + 1, ":", 3);
if (pieces[0] && pieces[0][0])
ssl_cert = pieces[0];
if (pieces[0] && pieces[1] && pieces[1][0])
ssl_key = pieces[1];
if (pieces[0] && pieces[1] && pieces[2] && pieces[2][0])
ssl_password = pieces[2];
g_free (pieces);
}
else if (arg[0] == '-' || mode == MODE_NONE)
usage ();
else if (mode == MODE_SERVER)
{
/* a port number is allowed */
if (server_port != 0)
g_error ("too many arguments at '%s' (already got last argument, the server port)", arg);
server_port = atoi (arg);
if (server_port == 0)
g_error ("error parsing port from %s", arg);
}
else if (mode == MODE_CLIENT)
{
/* a host, then a port is allowed */
if (client_hostport.host == NULL)
client_hostport.host = g_strdup (arg);
else if (client_hostport.port != 0)
g_error ("too many arguments at '%s' (already got last argument, the client port)", arg);
else
{
client_hostport.port = atoi (arg);
if (client_hostport.port == 0)
g_error ("error parsing port from %s", arg);
}
}
else
g_assert_not_reached (); /* invalid 'mode' */
}
switch (mode)
{
case MODE_CLIENT:
setup_client ();
break;
case MODE_SERVER:
setup_server ();
break;
default:
usage ();
}
return gsk_main_run();
}
syntax highlighted by Code2HTML, v. 0.9.1