2007-05-09 11:01:33 +00:00
|
|
|
|
/* sockprox - Proxy for local sockets with logging facilities
|
|
|
|
|
* Copyright (C) 2007 g10 Code GmbH.
|
|
|
|
|
*
|
|
|
|
|
* sockprox is free software; you can redistribute it and/or modify it
|
|
|
|
|
* under the terms of the GNU General Public License as published by
|
2007-07-04 19:49:40 +00:00
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
2007-05-09 11:01:33 +00:00
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* sockprox is distributed in the hope that it will be useful, but
|
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
* General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2007-07-04 19:49:40 +00:00
|
|
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
2007-05-09 11:01:33 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Hacked by Moritz Schulte <moritz@g10code.com>.
|
|
|
|
|
|
|
|
|
|
Usage example:
|
|
|
|
|
|
|
|
|
|
Run a server which binds to a local socket. For example,
|
|
|
|
|
gpg-agent. gpg-agent's local socket is specified with --server.
|
|
|
|
|
sockprox opens a new local socket (here "mysock"); the whole
|
|
|
|
|
traffic between server and client is written to "/tmp/prot" in this
|
|
|
|
|
case.
|
|
|
|
|
|
|
|
|
|
./sockprox --server /tmp/gpg-PKdD8r/S.gpg-agent.ssh \
|
|
|
|
|
--listen mysock --protocol /tmp/prot
|
|
|
|
|
|
|
|
|
|
Then, redirect your ssh-agent client to sockprox by setting
|
|
|
|
|
SSH_AUTH_SOCK to "mysock".
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <getopt.h>
|
|
|
|
|
#include <stddef.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/un.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <pthread.h>
|
|
|
|
|
|
|
|
|
|
struct opt
|
|
|
|
|
{
|
|
|
|
|
char *protocol_file;
|
|
|
|
|
char *server_spec;
|
|
|
|
|
char *listen_spec;
|
|
|
|
|
int verbose;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct opt opt = { NULL, NULL, NULL, 0 };
|
|
|
|
|
|
|
|
|
|
struct thread_data
|
|
|
|
|
{
|
|
|
|
|
int client_sock;
|
|
|
|
|
FILE *protocol_file;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
create_server_socket (const char *filename, int *new_sock)
|
|
|
|
|
{
|
|
|
|
|
struct sockaddr_un name;
|
|
|
|
|
size_t size;
|
|
|
|
|
int sock;
|
|
|
|
|
int ret;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
/* Create the socket. */
|
|
|
|
|
sock = socket (PF_LOCAL, SOCK_STREAM, 0);
|
|
|
|
|
if (sock < 0)
|
|
|
|
|
{
|
|
|
|
|
err = errno;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Bind a name to the socket. */
|
|
|
|
|
name.sun_family = AF_LOCAL;
|
|
|
|
|
strncpy (name.sun_path, filename, sizeof (name.sun_path));
|
|
|
|
|
name.sun_path[sizeof (name.sun_path) - 1] = '\0';
|
|
|
|
|
|
|
|
|
|
size = SUN_LEN (&name);
|
|
|
|
|
|
|
|
|
|
remove (filename);
|
|
|
|
|
|
|
|
|
|
ret = bind (sock, (struct sockaddr *) &name, size);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
err = errno;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = listen (sock, 2);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
err = errno;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*new_sock = sock;
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
connect_to_socket (const char *filename, int *new_sock)
|
|
|
|
|
{
|
|
|
|
|
struct sockaddr_un srvr_addr;
|
|
|
|
|
size_t len;
|
|
|
|
|
int sock;
|
|
|
|
|
int ret;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
sock = socket (PF_LOCAL, SOCK_STREAM, 0);
|
|
|
|
|
if (sock == -1)
|
|
|
|
|
{
|
|
|
|
|
err = errno;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset (&srvr_addr, 0, sizeof srvr_addr);
|
|
|
|
|
srvr_addr.sun_family = AF_LOCAL;
|
|
|
|
|
strncpy (srvr_addr.sun_path, filename, sizeof (srvr_addr.sun_path) - 1);
|
|
|
|
|
srvr_addr.sun_path[sizeof (srvr_addr.sun_path) - 1] = 0;
|
|
|
|
|
len = SUN_LEN (&srvr_addr);
|
|
|
|
|
|
|
|
|
|
ret = connect (sock, (struct sockaddr *) &srvr_addr, len);
|
|
|
|
|
if (ret == -1)
|
|
|
|
|
{
|
|
|
|
|
close (sock);
|
|
|
|
|
err = errno;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*new_sock = sock;
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
log_data (unsigned char *data, size_t length,
|
|
|
|
|
FILE *from, FILE *to, FILE *protocol)
|
|
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
int ret;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
flockfile (protocol);
|
|
|
|
|
fprintf (protocol, "%i -> %i: ", fileno (from), fileno (to));
|
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
|
fprintf (protocol, "%02X", data[i]);
|
|
|
|
|
fprintf (protocol, "\n");
|
|
|
|
|
funlockfile (protocol);
|
|
|
|
|
|
|
|
|
|
ret = fflush (protocol);
|
|
|
|
|
if (ret == EOF)
|
|
|
|
|
err = errno;
|
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
transfer_data (FILE *from, FILE *to, FILE *protocol)
|
|
|
|
|
{
|
|
|
|
|
unsigned char buffer[BUFSIZ];
|
|
|
|
|
size_t len, written;
|
|
|
|
|
int err;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
len = fread (buffer, 1, sizeof (buffer), from);
|
|
|
|
|
if (len == 0)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
err = log_data (buffer, len, from, to, protocol);
|
|
|
|
|
if (err)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
written = fwrite (buffer, 1, len, to);
|
|
|
|
|
if (written != len)
|
|
|
|
|
{
|
|
|
|
|
err = errno;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = fflush (to);
|
|
|
|
|
if (ret == EOF)
|
|
|
|
|
{
|
|
|
|
|
err = errno;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ferror (from))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
io_loop (FILE *client, FILE *server, FILE *protocol)
|
|
|
|
|
{
|
|
|
|
|
fd_set active_fd_set, read_fd_set;
|
|
|
|
|
int ret;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
FD_ZERO (&active_fd_set);
|
|
|
|
|
FD_SET (fileno (client), &active_fd_set);
|
|
|
|
|
FD_SET (fileno (server), &active_fd_set);
|
|
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
read_fd_set = active_fd_set;
|
|
|
|
|
|
|
|
|
|
/* FIXME: eof? */
|
|
|
|
|
|
|
|
|
|
ret = select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
err = errno;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (FD_ISSET (fileno (client), &read_fd_set))
|
|
|
|
|
{
|
|
|
|
|
if (feof (client))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Forward data from client to server. */
|
|
|
|
|
err = transfer_data (client, server, protocol);
|
|
|
|
|
}
|
|
|
|
|
else if (FD_ISSET (fileno (server), &read_fd_set))
|
|
|
|
|
{
|
|
|
|
|
if (feof (server))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Forward data from server to client. */
|
|
|
|
|
err = transfer_data (server, client, protocol);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Set the `O_NONBLOCK' flag of DESC if VALUE is nonzero,
|
|
|
|
|
or clear the flag if VALUE is 0.
|
|
|
|
|
Return 0 on success, or -1 on error with `errno' set. */
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
set_nonblock_flag (int desc, int value)
|
|
|
|
|
{
|
|
|
|
|
int oldflags = fcntl (desc, F_GETFL, 0);
|
|
|
|
|
int err;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/* If reading the flags failed, return error indication now. */
|
|
|
|
|
if (oldflags == -1)
|
|
|
|
|
return -1;
|
|
|
|
|
/* Set just the flag we want to set. */
|
|
|
|
|
if (value != 0)
|
|
|
|
|
oldflags |= O_NONBLOCK;
|
|
|
|
|
else
|
|
|
|
|
oldflags &= ~O_NONBLOCK;
|
|
|
|
|
/* Store modified flag word in the descriptor. */
|
|
|
|
|
|
|
|
|
|
ret = fcntl (desc, F_SETFL, oldflags);
|
|
|
|
|
if (ret == -1)
|
|
|
|
|
err = errno;
|
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
|
serve_client (void *data)
|
|
|
|
|
{
|
|
|
|
|
struct thread_data *thread_data = data;
|
|
|
|
|
int client_sock = thread_data->client_sock;
|
|
|
|
|
int server_sock;
|
|
|
|
|
FILE *protocol = thread_data->protocol_file;
|
|
|
|
|
FILE *client;
|
|
|
|
|
FILE *server;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
client = NULL;
|
|
|
|
|
server = NULL;
|
|
|
|
|
|
|
|
|
|
/* Connect to server. */
|
|
|
|
|
err = connect_to_socket (opt.server_spec, &server_sock);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/* Set IO mode to nonblicking. */
|
|
|
|
|
err = set_nonblock_flag (server_sock, 1);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
client = fdopen (client_sock, "r+");
|
|
|
|
|
if (! client)
|
|
|
|
|
{
|
|
|
|
|
err = errno;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
server = fdopen (server_sock, "r+");
|
|
|
|
|
if (! server)
|
|
|
|
|
{
|
|
|
|
|
err = errno;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = io_loop (client, server, protocol);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
if (client)
|
|
|
|
|
fclose (client);
|
|
|
|
|
else
|
|
|
|
|
close (client_sock);
|
|
|
|
|
|
|
|
|
|
if (server)
|
|
|
|
|
fclose (server);
|
|
|
|
|
else
|
|
|
|
|
close (server_sock);
|
|
|
|
|
|
|
|
|
|
free (data);
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
run_proxy (void)
|
|
|
|
|
{
|
|
|
|
|
int client_sock;
|
|
|
|
|
int my_sock;
|
|
|
|
|
int err;
|
|
|
|
|
struct sockaddr_un clientname;
|
|
|
|
|
size_t size;
|
|
|
|
|
pthread_t mythread;
|
|
|
|
|
struct thread_data *thread_data;
|
|
|
|
|
FILE *protocol_file;
|
|
|
|
|
pthread_attr_t thread_attr;
|
|
|
|
|
|
|
|
|
|
protocol_file = NULL;
|
|
|
|
|
|
|
|
|
|
err = pthread_attr_init (&thread_attr);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
err = pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (opt.protocol_file)
|
|
|
|
|
{
|
|
|
|
|
protocol_file = fopen (opt.protocol_file, "a");
|
|
|
|
|
if (! protocol_file)
|
|
|
|
|
{
|
|
|
|
|
err = errno;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
protocol_file = stdout;
|
|
|
|
|
|
|
|
|
|
err = create_server_socket (opt.listen_spec, &my_sock);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
/* Accept new client. */
|
|
|
|
|
size = sizeof (clientname);
|
|
|
|
|
client_sock = accept (my_sock,
|
|
|
|
|
(struct sockaddr *) &clientname,
|
|
|
|
|
&size);
|
|
|
|
|
if (client_sock < 0)
|
|
|
|
|
{
|
|
|
|
|
err = errno;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set IO mode to nonblicking. */
|
|
|
|
|
err = set_nonblock_flag (client_sock, 1);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
close (client_sock);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Got new client -> handle in new process. */
|
|
|
|
|
|
|
|
|
|
thread_data = malloc (sizeof (*thread_data));
|
|
|
|
|
if (! thread_data)
|
|
|
|
|
{
|
|
|
|
|
err = errno;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
thread_data->client_sock = client_sock;
|
|
|
|
|
thread_data->protocol_file = protocol_file;
|
|
|
|
|
|
|
|
|
|
err = pthread_create (&mythread, &thread_attr, serve_client, thread_data);
|
|
|
|
|
if (err)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/* ? */
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
pthread_attr_destroy (&thread_attr);
|
|
|
|
|
fclose (protocol_file); /* FIXME, err checking. */
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
print_help (int ret)
|
|
|
|
|
{
|
|
|
|
|
printf ("Usage: sockprox [options] "
|
|
|
|
|
"--server SERVER-SOCKET --listen PROXY-SOCKET\n");
|
|
|
|
|
exit (ret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
main (int argc, char **argv)
|
|
|
|
|
{
|
|
|
|
|
struct option long_options[] =
|
|
|
|
|
{
|
|
|
|
|
{ "help", no_argument, 0, 'h' },
|
|
|
|
|
{ "verbose", no_argument, &opt.verbose, 1 },
|
|
|
|
|
{ "protocol", required_argument, 0, 'p' },
|
|
|
|
|
{ "server", required_argument, 0, 's' },
|
|
|
|
|
{ "listen", required_argument, 0, 'l' },
|
|
|
|
|
{ 0, 0, 0, 0 }
|
|
|
|
|
};
|
|
|
|
|
int ret;
|
|
|
|
|
int err;
|
|
|
|
|
int c;
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
int opt_idx = 0;
|
|
|
|
|
c = getopt_long (argc, argv, "hvp:s:l:",
|
|
|
|
|
long_options, &opt_idx);
|
|
|
|
|
|
|
|
|
|
if (c == -1)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
switch (c)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
if (long_options[opt_idx].flag)
|
|
|
|
|
break;
|
|
|
|
|
printf ("option %s", long_options[opt_idx].name);
|
|
|
|
|
if (optarg)
|
|
|
|
|
printf (" with arg %s", optarg);
|
|
|
|
|
printf ("\n");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'p':
|
|
|
|
|
opt.protocol_file = optarg;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 's':
|
|
|
|
|
opt.server_spec = optarg;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'l':
|
|
|
|
|
opt.listen_spec = optarg;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'v':
|
|
|
|
|
opt.verbose = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'h':
|
|
|
|
|
print_help (EXIT_SUCCESS);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
{
|
|
|
|
|
printf ("server: %s\n", opt.server_spec ? opt.server_spec : "");
|
|
|
|
|
printf ("listen: %s\n", opt.listen_spec ? opt.listen_spec : "");
|
|
|
|
|
printf ("protocol: %s\n", opt.protocol_file ? opt.protocol_file : "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! (opt.server_spec && opt.listen_spec))
|
|
|
|
|
print_help (EXIT_FAILURE);
|
|
|
|
|
|
|
|
|
|
err = run_proxy ();
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "run_proxy() failed: %s\n", strerror (err));
|
|
|
|
|
ret = EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/* ? */
|
|
|
|
|
ret = EXIT_SUCCESS;
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Local Variables:
|
|
|
|
|
compile-command: "cc -Wall -g -o sockprox sockprox.c -lpthread"
|
|
|
|
|
End:
|
|
|
|
|
*/
|