зеркало из https://github.com/github/putty.git
Unix: turn LocalProxySocket into a general FdSocket.
The new FdSocket just takes an arbitrary pair of file descriptors to
read and write, optionally with an extra input fd providing the
standard error output from a command. uxproxy.c now just does the
forking and pipe setup, and once it's got all its fds, it hands off to
FdSocket to actually do the reading and writing.
This is very like the reorganisation I did on the Windows side in
commit 98a6a3553
(back in 2013, in preparation for named-pipe sockets
and connection sharing). The idea is that it should enable me to make
a thing that the PuTTY code base sees as a Socket, but which actually
connects to the standard I/O handles of the process it lives in.
This commit is contained in:
Родитель
7de8801e73
Коммит
c95b277798
3
Recipe
3
Recipe
|
@ -270,7 +270,8 @@ MISC = misc marshal
|
||||||
MISCNET = timing callback MISC version settings tree234 proxy CONF be_misc
|
MISCNET = timing callback MISC version settings tree234 proxy CONF be_misc
|
||||||
WINMISC = MISCNET winstore winnet winhandl cmdline windefs winmisc winproxy
|
WINMISC = MISCNET winstore winnet winhandl cmdline windefs winmisc winproxy
|
||||||
+ wintime winhsock errsock winsecur winucs miscucs
|
+ wintime winhsock errsock winsecur winucs miscucs
|
||||||
UXMISC = MISCNET uxstore uxsel uxnet uxpeer uxmisc uxproxy errsock time
|
UXMISC = MISCNET uxstore uxsel uxnet uxpeer uxmisc time
|
||||||
|
+ uxproxy uxfdsock errsock
|
||||||
|
|
||||||
# import.c and dependencies, for PuTTYgen-like utilities that have to
|
# import.c and dependencies, for PuTTYgen-like utilities that have to
|
||||||
# load foreign key files.
|
# load foreign key files.
|
||||||
|
|
|
@ -344,6 +344,11 @@ extern const struct BackendVtable serial_backend;
|
||||||
*/
|
*/
|
||||||
int so_peercred(int fd, int *pid, int *uid, int *gid);
|
int so_peercred(int fd, int *pid, int *uid, int *gid);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* uxfdsock.c.
|
||||||
|
*/
|
||||||
|
Socket *make_fd_socket(int infd, int outfd, int inerrfd, Plug *plug);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Default font setting, which can vary depending on NOT_X_WINDOWS.
|
* Default font setting, which can vary depending on NOT_X_WINDOWS.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,364 @@
|
||||||
|
/*
|
||||||
|
* uxfdsick.c: implementation of Socket that just talks to two
|
||||||
|
* existing input and output file descriptors.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "tree234.h"
|
||||||
|
#include "putty.h"
|
||||||
|
#include "network.h"
|
||||||
|
|
||||||
|
typedef struct FdSocket {
|
||||||
|
int outfd, infd, inerrfd;
|
||||||
|
|
||||||
|
bufchain pending_output_data;
|
||||||
|
bufchain pending_input_data;
|
||||||
|
bufchain pending_input_error_data;
|
||||||
|
enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof;
|
||||||
|
|
||||||
|
int pending_error;
|
||||||
|
|
||||||
|
Plug *plug;
|
||||||
|
|
||||||
|
Socket sock;
|
||||||
|
} FdSocket;
|
||||||
|
|
||||||
|
static void fdsocket_select_result_input(int fd, int event);
|
||||||
|
static void fdsocket_select_result_output(int fd, int event);
|
||||||
|
static void fdsocket_select_result_input_error(int fd, int event);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Trees to look up the fds in.
|
||||||
|
*/
|
||||||
|
static tree234 *fdsocket_by_outfd;
|
||||||
|
static tree234 *fdsocket_by_infd;
|
||||||
|
static tree234 *fdsocket_by_inerrfd;
|
||||||
|
|
||||||
|
static int fdsocket_infd_cmp(void *av, void *bv)
|
||||||
|
{
|
||||||
|
FdSocket *a = (FdSocket *)av;
|
||||||
|
FdSocket *b = (FdSocket *)bv;
|
||||||
|
if (a->infd < b->infd)
|
||||||
|
return -1;
|
||||||
|
if (a->infd > b->infd)
|
||||||
|
return +1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static int fdsocket_infd_find(void *av, void *bv)
|
||||||
|
{
|
||||||
|
int a = *(int *)av;
|
||||||
|
FdSocket *b = (FdSocket *)bv;
|
||||||
|
if (a < b->infd)
|
||||||
|
return -1;
|
||||||
|
if (a > b->infd)
|
||||||
|
return +1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static int fdsocket_inerrfd_cmp(void *av, void *bv)
|
||||||
|
{
|
||||||
|
FdSocket *a = (FdSocket *)av;
|
||||||
|
FdSocket *b = (FdSocket *)bv;
|
||||||
|
if (a->inerrfd < b->inerrfd)
|
||||||
|
return -1;
|
||||||
|
if (a->inerrfd > b->inerrfd)
|
||||||
|
return +1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static int fdsocket_inerrfd_find(void *av, void *bv)
|
||||||
|
{
|
||||||
|
int a = *(int *)av;
|
||||||
|
FdSocket *b = (FdSocket *)bv;
|
||||||
|
if (a < b->inerrfd)
|
||||||
|
return -1;
|
||||||
|
if (a > b->inerrfd)
|
||||||
|
return +1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static int fdsocket_outfd_cmp(void *av, void *bv)
|
||||||
|
{
|
||||||
|
FdSocket *a = (FdSocket *)av;
|
||||||
|
FdSocket *b = (FdSocket *)bv;
|
||||||
|
if (a->outfd < b->outfd)
|
||||||
|
return -1;
|
||||||
|
if (a->outfd > b->outfd)
|
||||||
|
return +1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static int fdsocket_outfd_find(void *av, void *bv)
|
||||||
|
{
|
||||||
|
int a = *(int *)av;
|
||||||
|
FdSocket *b = (FdSocket *)bv;
|
||||||
|
if (a < b->outfd)
|
||||||
|
return -1;
|
||||||
|
if (a > b->outfd)
|
||||||
|
return +1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Plug *fdsocket_plug(Socket *s, Plug *p)
|
||||||
|
{
|
||||||
|
FdSocket *fds = container_of(s, FdSocket, sock);
|
||||||
|
Plug *ret = fds->plug;
|
||||||
|
if (p)
|
||||||
|
fds->plug = p;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fdsocket_close(Socket *s)
|
||||||
|
{
|
||||||
|
FdSocket *fds = container_of(s, FdSocket, sock);
|
||||||
|
|
||||||
|
if (fds->outfd >= 0) {
|
||||||
|
del234(fdsocket_by_outfd, fds);
|
||||||
|
uxsel_del(fds->outfd);
|
||||||
|
close(fds->outfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fds->infd >= 0) {
|
||||||
|
del234(fdsocket_by_infd, fds);
|
||||||
|
uxsel_del(fds->infd);
|
||||||
|
close(fds->infd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fds->inerrfd >= 0) {
|
||||||
|
del234(fdsocket_by_inerrfd, fds);
|
||||||
|
uxsel_del(fds->inerrfd);
|
||||||
|
close(fds->inerrfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
bufchain_clear(&fds->pending_input_data);
|
||||||
|
bufchain_clear(&fds->pending_output_data);
|
||||||
|
|
||||||
|
delete_callbacks_for_context(fds);
|
||||||
|
|
||||||
|
sfree(fds);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fdsocket_error_callback(void *vs)
|
||||||
|
{
|
||||||
|
FdSocket *fds = (FdSocket *)vs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Just in case other socket work has caused this socket to vanish
|
||||||
|
* or become somehow non-erroneous before this callback arrived...
|
||||||
|
*/
|
||||||
|
if (!fds->pending_error)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An error has occurred on this socket. Pass it to the plug.
|
||||||
|
*/
|
||||||
|
plug_closing(fds->plug, strerror(fds->pending_error),
|
||||||
|
fds->pending_error, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fdsocket_try_send(FdSocket *fds)
|
||||||
|
{
|
||||||
|
int sent = 0;
|
||||||
|
|
||||||
|
while (bufchain_size(&fds->pending_output_data) > 0) {
|
||||||
|
void *data;
|
||||||
|
int len, ret;
|
||||||
|
|
||||||
|
bufchain_prefix(&fds->pending_output_data, &data, &len);
|
||||||
|
ret = write(fds->outfd, data, len);
|
||||||
|
if (ret < 0 && errno != EWOULDBLOCK) {
|
||||||
|
if (!fds->pending_error) {
|
||||||
|
fds->pending_error = errno;
|
||||||
|
queue_toplevel_callback(fdsocket_error_callback, fds);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} else if (ret <= 0) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
bufchain_consume(&fds->pending_output_data, ret);
|
||||||
|
sent += ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fds->outgoingeof == EOF_PENDING) {
|
||||||
|
del234(fdsocket_by_outfd, fds);
|
||||||
|
close(fds->outfd);
|
||||||
|
uxsel_del(fds->outfd);
|
||||||
|
fds->outfd = -1;
|
||||||
|
fds->outgoingeof = EOF_SENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bufchain_size(&fds->pending_output_data) == 0)
|
||||||
|
uxsel_del(fds->outfd);
|
||||||
|
else
|
||||||
|
uxsel_set(fds->outfd, 2, fdsocket_select_result_output);
|
||||||
|
|
||||||
|
return sent;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fdsocket_write(Socket *s, const void *data, int len)
|
||||||
|
{
|
||||||
|
FdSocket *fds = container_of(s, FdSocket, sock);
|
||||||
|
|
||||||
|
assert(fds->outgoingeof == EOF_NO);
|
||||||
|
|
||||||
|
bufchain_add(&fds->pending_output_data, data, len);
|
||||||
|
|
||||||
|
fdsocket_try_send(fds);
|
||||||
|
|
||||||
|
return bufchain_size(&fds->pending_output_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fdsocket_write_oob(Socket *s, const void *data, int len)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* oob data is treated as inband; nasty, but nothing really
|
||||||
|
* better we can do
|
||||||
|
*/
|
||||||
|
return fdsocket_write(s, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fdsocket_write_eof(Socket *s)
|
||||||
|
{
|
||||||
|
FdSocket *fds = container_of(s, FdSocket, sock);
|
||||||
|
|
||||||
|
assert(fds->outgoingeof == EOF_NO);
|
||||||
|
fds->outgoingeof = EOF_PENDING;
|
||||||
|
|
||||||
|
fdsocket_try_send(fds);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fdsocket_flush(Socket *s)
|
||||||
|
{
|
||||||
|
/* FdSocket *fds = container_of(s, FdSocket, sock); */
|
||||||
|
/* do nothing */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fdsocket_set_frozen(Socket *s, int is_frozen)
|
||||||
|
{
|
||||||
|
FdSocket *fds = container_of(s, FdSocket, sock);
|
||||||
|
|
||||||
|
if (fds->infd < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (is_frozen)
|
||||||
|
uxsel_del(fds->infd);
|
||||||
|
else
|
||||||
|
uxsel_set(fds->infd, 1, fdsocket_select_result_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *fdsocket_socket_error(Socket *s)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fdsocket_select_result_input(int fd, int event)
|
||||||
|
{
|
||||||
|
FdSocket *fds;
|
||||||
|
char buf[20480];
|
||||||
|
int retd;
|
||||||
|
|
||||||
|
if (!(fds = find234(fdsocket_by_infd, &fd, fdsocket_infd_find)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
retd = read(fds->infd, buf, sizeof(buf));
|
||||||
|
if (retd > 0) {
|
||||||
|
plug_receive(fds->plug, 0, buf, retd);
|
||||||
|
} else {
|
||||||
|
if (retd < 0) {
|
||||||
|
plug_closing(fds->plug, strerror(errno), errno, 0);
|
||||||
|
} else {
|
||||||
|
plug_closing(fds->plug, NULL, 0, 0);
|
||||||
|
}
|
||||||
|
del234(fdsocket_by_infd, fds);
|
||||||
|
uxsel_del(fds->infd);
|
||||||
|
close(fds->infd);
|
||||||
|
fds->infd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fdsocket_select_result_output(int fd, int event)
|
||||||
|
{
|
||||||
|
FdSocket *fds;
|
||||||
|
|
||||||
|
if (!(fds = find234(fdsocket_by_outfd, &fd, fdsocket_outfd_find)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (fdsocket_try_send(fds))
|
||||||
|
plug_sent(fds->plug, bufchain_size(&fds->pending_output_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fdsocket_select_result_input_error(int fd, int event)
|
||||||
|
{
|
||||||
|
FdSocket *fds;
|
||||||
|
char buf[20480];
|
||||||
|
int retd;
|
||||||
|
|
||||||
|
if (!(fds = find234(fdsocket_by_inerrfd, &fd, fdsocket_inerrfd_find)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
retd = read(fd, buf, sizeof(buf));
|
||||||
|
if (retd > 0) {
|
||||||
|
log_proxy_stderr(fds->plug, &fds->pending_input_error_data, buf, retd);
|
||||||
|
} else {
|
||||||
|
del234(fdsocket_by_inerrfd, fds);
|
||||||
|
uxsel_del(fds->inerrfd);
|
||||||
|
close(fds->inerrfd);
|
||||||
|
fds->inerrfd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SocketVtable FdSocket_sockvt = {
|
||||||
|
fdsocket_plug,
|
||||||
|
fdsocket_close,
|
||||||
|
fdsocket_write,
|
||||||
|
fdsocket_write_oob,
|
||||||
|
fdsocket_write_eof,
|
||||||
|
fdsocket_flush,
|
||||||
|
fdsocket_set_frozen,
|
||||||
|
fdsocket_socket_error,
|
||||||
|
NULL, /* peer_info */
|
||||||
|
};
|
||||||
|
|
||||||
|
Socket *make_fd_socket(int infd, int outfd, int inerrfd, Plug *plug)
|
||||||
|
{
|
||||||
|
FdSocket *fds;
|
||||||
|
|
||||||
|
fds = snew(FdSocket);
|
||||||
|
fds->sock.vt = &FdSocket_sockvt;
|
||||||
|
fds->plug = plug;
|
||||||
|
fds->outgoingeof = EOF_NO;
|
||||||
|
fds->pending_error = 0;
|
||||||
|
|
||||||
|
fds->infd = infd;
|
||||||
|
fds->outfd = outfd;
|
||||||
|
fds->inerrfd = inerrfd;
|
||||||
|
|
||||||
|
bufchain_init(&fds->pending_input_data);
|
||||||
|
bufchain_init(&fds->pending_output_data);
|
||||||
|
bufchain_init(&fds->pending_input_error_data);
|
||||||
|
|
||||||
|
if (fds->outfd >= 0) {
|
||||||
|
if (!fdsocket_by_outfd)
|
||||||
|
fdsocket_by_outfd = newtree234(fdsocket_outfd_cmp);
|
||||||
|
add234(fdsocket_by_outfd, fds);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fds->infd >= 0) {
|
||||||
|
if (!fdsocket_by_infd)
|
||||||
|
fdsocket_by_infd = newtree234(fdsocket_infd_cmp);
|
||||||
|
add234(fdsocket_by_infd, fds);
|
||||||
|
uxsel_set(fds->infd, 1, fdsocket_select_result_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fds->inerrfd >= 0) {
|
||||||
|
assert(fds->inerrfd != fds->infd);
|
||||||
|
if (!fdsocket_by_inerrfd)
|
||||||
|
fdsocket_by_inerrfd = newtree234(fdsocket_inerrfd_cmp);
|
||||||
|
add234(fdsocket_by_inerrfd, fds);
|
||||||
|
uxsel_set(fds->inerrfd, 1, fdsocket_select_result_input_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return &fds->sock;
|
||||||
|
}
|
367
unix/uxproxy.c
367
unix/uxproxy.c
|
@ -14,306 +14,6 @@
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
#include "proxy.h"
|
#include "proxy.h"
|
||||||
|
|
||||||
typedef struct LocalProxySocket {
|
|
||||||
int to_cmd, from_cmd, cmd_err; /* fds */
|
|
||||||
|
|
||||||
char *error;
|
|
||||||
|
|
||||||
Plug *plug;
|
|
||||||
|
|
||||||
bufchain pending_output_data;
|
|
||||||
bufchain pending_input_data;
|
|
||||||
bufchain pending_error_data;
|
|
||||||
enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof;
|
|
||||||
|
|
||||||
int pending_error;
|
|
||||||
|
|
||||||
Socket sock;
|
|
||||||
} LocalProxySocket;
|
|
||||||
|
|
||||||
static void localproxy_select_result(int fd, int event);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Trees to look up the pipe fds in.
|
|
||||||
*/
|
|
||||||
static tree234 *localproxy_by_fromfd;
|
|
||||||
static tree234 *localproxy_by_tofd;
|
|
||||||
static tree234 *localproxy_by_errfd;
|
|
||||||
static int localproxy_fromfd_cmp(void *av, void *bv)
|
|
||||||
{
|
|
||||||
LocalProxySocket *a = (LocalProxySocket *)av;
|
|
||||||
LocalProxySocket *b = (LocalProxySocket *)bv;
|
|
||||||
if (a->from_cmd < b->from_cmd)
|
|
||||||
return -1;
|
|
||||||
if (a->from_cmd > b->from_cmd)
|
|
||||||
return +1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static int localproxy_fromfd_find(void *av, void *bv)
|
|
||||||
{
|
|
||||||
int a = *(int *)av;
|
|
||||||
LocalProxySocket *b = (LocalProxySocket *)bv;
|
|
||||||
if (a < b->from_cmd)
|
|
||||||
return -1;
|
|
||||||
if (a > b->from_cmd)
|
|
||||||
return +1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static int localproxy_tofd_cmp(void *av, void *bv)
|
|
||||||
{
|
|
||||||
LocalProxySocket *a = (LocalProxySocket *)av;
|
|
||||||
LocalProxySocket *b = (LocalProxySocket *)bv;
|
|
||||||
if (a->to_cmd < b->to_cmd)
|
|
||||||
return -1;
|
|
||||||
if (a->to_cmd > b->to_cmd)
|
|
||||||
return +1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static int localproxy_tofd_find(void *av, void *bv)
|
|
||||||
{
|
|
||||||
int a = *(int *)av;
|
|
||||||
LocalProxySocket *b = (LocalProxySocket *)bv;
|
|
||||||
if (a < b->to_cmd)
|
|
||||||
return -1;
|
|
||||||
if (a > b->to_cmd)
|
|
||||||
return +1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static int localproxy_errfd_cmp(void *av, void *bv)
|
|
||||||
{
|
|
||||||
LocalProxySocket *a = (LocalProxySocket *)av;
|
|
||||||
LocalProxySocket *b = (LocalProxySocket *)bv;
|
|
||||||
if (a->cmd_err < b->cmd_err)
|
|
||||||
return -1;
|
|
||||||
if (a->cmd_err > b->cmd_err)
|
|
||||||
return +1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static int localproxy_errfd_find(void *av, void *bv)
|
|
||||||
{
|
|
||||||
int a = *(int *)av;
|
|
||||||
LocalProxySocket *b = (LocalProxySocket *)bv;
|
|
||||||
if (a < b->cmd_err)
|
|
||||||
return -1;
|
|
||||||
if (a > b->cmd_err)
|
|
||||||
return +1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* basic proxy socket functions */
|
|
||||||
|
|
||||||
static Plug *sk_localproxy_plug (Socket *s, Plug *p)
|
|
||||||
{
|
|
||||||
LocalProxySocket *ps = container_of(s, LocalProxySocket, sock);
|
|
||||||
Plug *ret = ps->plug;
|
|
||||||
if (p)
|
|
||||||
ps->plug = p;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sk_localproxy_close (Socket *s)
|
|
||||||
{
|
|
||||||
LocalProxySocket *ps = container_of(s, LocalProxySocket, sock);
|
|
||||||
|
|
||||||
if (ps->to_cmd >= 0) {
|
|
||||||
del234(localproxy_by_tofd, ps);
|
|
||||||
uxsel_del(ps->to_cmd);
|
|
||||||
close(ps->to_cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ps->from_cmd >= 0) {
|
|
||||||
del234(localproxy_by_fromfd, ps);
|
|
||||||
uxsel_del(ps->from_cmd);
|
|
||||||
close(ps->from_cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ps->cmd_err >= 0) {
|
|
||||||
del234(localproxy_by_errfd, ps);
|
|
||||||
uxsel_del(ps->cmd_err);
|
|
||||||
close(ps->cmd_err);
|
|
||||||
}
|
|
||||||
|
|
||||||
bufchain_clear(&ps->pending_input_data);
|
|
||||||
bufchain_clear(&ps->pending_output_data);
|
|
||||||
bufchain_clear(&ps->pending_error_data);
|
|
||||||
|
|
||||||
delete_callbacks_for_context(ps);
|
|
||||||
|
|
||||||
sfree(ps);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void localproxy_error_callback(void *vs)
|
|
||||||
{
|
|
||||||
LocalProxySocket *ps = (LocalProxySocket *)vs;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Just in case other socket work has caused this socket to vanish
|
|
||||||
* or become somehow non-erroneous before this callback arrived...
|
|
||||||
*/
|
|
||||||
if (!ps->pending_error)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* An error has occurred on this socket. Pass it to the plug.
|
|
||||||
*/
|
|
||||||
plug_closing(ps->plug, strerror(ps->pending_error), ps->pending_error, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int localproxy_try_send(LocalProxySocket *ps)
|
|
||||||
{
|
|
||||||
int sent = 0;
|
|
||||||
|
|
||||||
while (bufchain_size(&ps->pending_output_data) > 0) {
|
|
||||||
void *data;
|
|
||||||
int len, ret;
|
|
||||||
|
|
||||||
bufchain_prefix(&ps->pending_output_data, &data, &len);
|
|
||||||
ret = write(ps->to_cmd, data, len);
|
|
||||||
if (ret < 0 && errno != EWOULDBLOCK) {
|
|
||||||
if (!ps->pending_error) {
|
|
||||||
ps->pending_error = errno;
|
|
||||||
queue_toplevel_callback(localproxy_error_callback, ps);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
} else if (ret <= 0) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
bufchain_consume(&ps->pending_output_data, ret);
|
|
||||||
sent += ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ps->outgoingeof == EOF_PENDING) {
|
|
||||||
del234(localproxy_by_tofd, ps);
|
|
||||||
close(ps->to_cmd);
|
|
||||||
uxsel_del(ps->to_cmd);
|
|
||||||
ps->to_cmd = -1;
|
|
||||||
ps->outgoingeof = EOF_SENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bufchain_size(&ps->pending_output_data) == 0)
|
|
||||||
uxsel_del(ps->to_cmd);
|
|
||||||
else
|
|
||||||
uxsel_set(ps->to_cmd, 2, localproxy_select_result);
|
|
||||||
|
|
||||||
return sent;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sk_localproxy_write (Socket *s, const void *data, int len)
|
|
||||||
{
|
|
||||||
LocalProxySocket *ps = container_of(s, LocalProxySocket, sock);
|
|
||||||
|
|
||||||
assert(ps->outgoingeof == EOF_NO);
|
|
||||||
|
|
||||||
bufchain_add(&ps->pending_output_data, data, len);
|
|
||||||
|
|
||||||
localproxy_try_send(ps);
|
|
||||||
|
|
||||||
return bufchain_size(&ps->pending_output_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sk_localproxy_write_oob (Socket *s, const void *data, int len)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* oob data is treated as inband; nasty, but nothing really
|
|
||||||
* better we can do
|
|
||||||
*/
|
|
||||||
return sk_localproxy_write(s, data, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sk_localproxy_write_eof (Socket *s)
|
|
||||||
{
|
|
||||||
LocalProxySocket *ps = container_of(s, LocalProxySocket, sock);
|
|
||||||
|
|
||||||
assert(ps->outgoingeof == EOF_NO);
|
|
||||||
ps->outgoingeof = EOF_PENDING;
|
|
||||||
|
|
||||||
localproxy_try_send(ps);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sk_localproxy_flush (Socket *s)
|
|
||||||
{
|
|
||||||
/* LocalProxySocket *ps = container_of(s, LocalProxySocket, sock); */
|
|
||||||
/* do nothing */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sk_localproxy_set_frozen (Socket *s, int is_frozen)
|
|
||||||
{
|
|
||||||
LocalProxySocket *ps = container_of(s, LocalProxySocket, sock);
|
|
||||||
|
|
||||||
if (ps->from_cmd < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (is_frozen)
|
|
||||||
uxsel_del(ps->from_cmd);
|
|
||||||
else
|
|
||||||
uxsel_set(ps->from_cmd, 1, localproxy_select_result);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char * sk_localproxy_socket_error (Socket *s)
|
|
||||||
{
|
|
||||||
LocalProxySocket *ps = container_of(s, LocalProxySocket, sock);
|
|
||||||
return ps->error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void localproxy_select_result(int fd, int event)
|
|
||||||
{
|
|
||||||
LocalProxySocket *s;
|
|
||||||
char buf[20480];
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!(s = find234(localproxy_by_fromfd, &fd, localproxy_fromfd_find)) &&
|
|
||||||
!(s = find234(localproxy_by_errfd, &fd, localproxy_errfd_find)) &&
|
|
||||||
!(s = find234(localproxy_by_tofd, &fd, localproxy_tofd_find)) )
|
|
||||||
return; /* boggle */
|
|
||||||
|
|
||||||
if (event == 1) {
|
|
||||||
if (fd == s->cmd_err) {
|
|
||||||
ret = read(fd, buf, sizeof(buf));
|
|
||||||
if (ret > 0) {
|
|
||||||
log_proxy_stderr(s->plug, &s->pending_error_data, buf, ret);
|
|
||||||
} else {
|
|
||||||
del234(localproxy_by_errfd, s);
|
|
||||||
uxsel_del(s->cmd_err);
|
|
||||||
close(s->cmd_err);
|
|
||||||
s->cmd_err = -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert(fd == s->from_cmd);
|
|
||||||
ret = read(fd, buf, sizeof(buf));
|
|
||||||
if (ret > 0) {
|
|
||||||
plug_receive(s->plug, 0, buf, ret);
|
|
||||||
} else {
|
|
||||||
if (ret < 0) {
|
|
||||||
plug_closing(s->plug, strerror(errno), errno, 0);
|
|
||||||
} else {
|
|
||||||
plug_closing(s->plug, NULL, 0, 0);
|
|
||||||
}
|
|
||||||
del234(localproxy_by_fromfd, s);
|
|
||||||
uxsel_del(s->from_cmd);
|
|
||||||
close(s->from_cmd);
|
|
||||||
s->from_cmd = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (event == 2) {
|
|
||||||
assert(fd == s->to_cmd);
|
|
||||||
if (localproxy_try_send(s))
|
|
||||||
plug_sent(s->plug, bufchain_size(&s->pending_output_data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const SocketVtable LocalProxySocket_sockvt = {
|
|
||||||
sk_localproxy_plug,
|
|
||||||
sk_localproxy_close,
|
|
||||||
sk_localproxy_write,
|
|
||||||
sk_localproxy_write_oob,
|
|
||||||
sk_localproxy_write_eof,
|
|
||||||
sk_localproxy_flush,
|
|
||||||
sk_localproxy_set_frozen,
|
|
||||||
sk_localproxy_socket_error,
|
|
||||||
NULL, /* peer_info */
|
|
||||||
};
|
|
||||||
|
|
||||||
Socket *platform_new_connection(SockAddr *addr, const char *hostname,
|
Socket *platform_new_connection(SockAddr *addr, const char *hostname,
|
||||||
int port, int privport,
|
int port, int privport,
|
||||||
int oobinline, int nodelay, int keepalive,
|
int oobinline, int nodelay, int keepalive,
|
||||||
|
@ -321,24 +21,13 @@ Socket *platform_new_connection(SockAddr *addr, const char *hostname,
|
||||||
{
|
{
|
||||||
char *cmd;
|
char *cmd;
|
||||||
|
|
||||||
LocalProxySocket *ret;
|
|
||||||
int to_cmd_pipe[2], from_cmd_pipe[2], cmd_err_pipe[2], pid, proxytype;
|
int to_cmd_pipe[2], from_cmd_pipe[2], cmd_err_pipe[2], pid, proxytype;
|
||||||
|
int infd, outfd, inerrfd;
|
||||||
|
|
||||||
proxytype = conf_get_int(conf, CONF_proxy_type);
|
proxytype = conf_get_int(conf, CONF_proxy_type);
|
||||||
if (proxytype != PROXY_CMD && proxytype != PROXY_FUZZ)
|
if (proxytype != PROXY_CMD && proxytype != PROXY_FUZZ)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ret = snew(LocalProxySocket);
|
|
||||||
ret->sock.vt = &LocalProxySocket_sockvt;
|
|
||||||
ret->plug = plug;
|
|
||||||
ret->error = NULL;
|
|
||||||
ret->outgoingeof = EOF_NO;
|
|
||||||
ret->pending_error = 0;
|
|
||||||
|
|
||||||
bufchain_init(&ret->pending_input_data);
|
|
||||||
bufchain_init(&ret->pending_output_data);
|
|
||||||
bufchain_init(&ret->pending_error_data);
|
|
||||||
|
|
||||||
if (proxytype == PROXY_CMD) {
|
if (proxytype == PROXY_CMD) {
|
||||||
cmd = format_telnet_command(addr, port, conf);
|
cmd = format_telnet_command(addr, port, conf);
|
||||||
|
|
||||||
|
@ -355,21 +44,15 @@ Socket *platform_new_connection(SockAddr *addr, const char *hostname,
|
||||||
if (pipe(to_cmd_pipe) < 0 ||
|
if (pipe(to_cmd_pipe) < 0 ||
|
||||||
pipe(from_cmd_pipe) < 0 ||
|
pipe(from_cmd_pipe) < 0 ||
|
||||||
pipe(cmd_err_pipe) < 0) {
|
pipe(cmd_err_pipe) < 0) {
|
||||||
ret->error = dupprintf("pipe: %s", strerror(errno));
|
|
||||||
sfree(cmd);
|
sfree(cmd);
|
||||||
return &ret->sock;
|
return new_error_socket_fmt(plug, "pipe: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
cloexec(to_cmd_pipe[1]);
|
cloexec(to_cmd_pipe[1]);
|
||||||
cloexec(from_cmd_pipe[0]);
|
cloexec(from_cmd_pipe[0]);
|
||||||
cloexec(cmd_err_pipe[0]);
|
cloexec(cmd_err_pipe[0]);
|
||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
|
if (pid == 0) {
|
||||||
if (pid < 0) {
|
|
||||||
ret->error = dupprintf("fork: %s", strerror(errno));
|
|
||||||
sfree(cmd);
|
|
||||||
return &ret->sock;
|
|
||||||
} else if (pid == 0) {
|
|
||||||
close(0);
|
close(0);
|
||||||
close(1);
|
close(1);
|
||||||
dup2(to_cmd_pipe[0], 0);
|
dup2(to_cmd_pipe[0], 0);
|
||||||
|
@ -385,49 +68,35 @@ Socket *platform_new_connection(SockAddr *addr, const char *hostname,
|
||||||
|
|
||||||
sfree(cmd);
|
sfree(cmd);
|
||||||
|
|
||||||
|
if (pid < 0)
|
||||||
|
return new_error_socket_fmt(plug, "fork: %s", strerror(errno));
|
||||||
|
|
||||||
close(to_cmd_pipe[0]);
|
close(to_cmd_pipe[0]);
|
||||||
close(from_cmd_pipe[1]);
|
close(from_cmd_pipe[1]);
|
||||||
close(cmd_err_pipe[1]);
|
close(cmd_err_pipe[1]);
|
||||||
|
|
||||||
ret->to_cmd = to_cmd_pipe[1];
|
outfd = to_cmd_pipe[1];
|
||||||
ret->from_cmd = from_cmd_pipe[0];
|
infd = from_cmd_pipe[0];
|
||||||
ret->cmd_err = cmd_err_pipe[0];
|
inerrfd = cmd_err_pipe[0];
|
||||||
} else {
|
} else {
|
||||||
cmd = format_telnet_command(addr, port, conf);
|
cmd = format_telnet_command(addr, port, conf);
|
||||||
ret->to_cmd = open("/dev/null", O_WRONLY);
|
outfd = open("/dev/null", O_WRONLY);
|
||||||
if (ret->to_cmd == -1) {
|
if (outfd == -1) {
|
||||||
ret->error = dupprintf("/dev/null: %s", strerror(errno));
|
|
||||||
sfree(cmd);
|
sfree(cmd);
|
||||||
return &ret->sock;
|
return new_error_socket_fmt(
|
||||||
|
plug, "/dev/null: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
ret->from_cmd = open(cmd, O_RDONLY);
|
infd = open(cmd, O_RDONLY);
|
||||||
if (ret->from_cmd == -1) {
|
if (infd == -1) {
|
||||||
ret->error = dupprintf("%s: %s", cmd, strerror(errno));
|
|
||||||
sfree(cmd);
|
sfree(cmd);
|
||||||
return &ret->sock;
|
return new_error_socket_fmt(plug, "%s: %s", cmd, strerror(errno));
|
||||||
}
|
}
|
||||||
sfree(cmd);
|
sfree(cmd);
|
||||||
ret->cmd_err = -1;
|
inerrfd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!localproxy_by_fromfd)
|
|
||||||
localproxy_by_fromfd = newtree234(localproxy_fromfd_cmp);
|
|
||||||
if (!localproxy_by_tofd)
|
|
||||||
localproxy_by_tofd = newtree234(localproxy_tofd_cmp);
|
|
||||||
if (!localproxy_by_errfd)
|
|
||||||
localproxy_by_errfd = newtree234(localproxy_errfd_cmp);
|
|
||||||
|
|
||||||
add234(localproxy_by_fromfd, ret);
|
|
||||||
add234(localproxy_by_tofd, ret);
|
|
||||||
if (ret->cmd_err >= 0)
|
|
||||||
add234(localproxy_by_errfd, ret);
|
|
||||||
|
|
||||||
uxsel_set(ret->from_cmd, 1, localproxy_select_result);
|
|
||||||
if (ret->cmd_err >= 0)
|
|
||||||
uxsel_set(ret->cmd_err, 1, localproxy_select_result);
|
|
||||||
|
|
||||||
/* We are responsible for this and don't need it any more */
|
/* We are responsible for this and don't need it any more */
|
||||||
sk_addr_free(addr);
|
sk_addr_free(addr);
|
||||||
|
|
||||||
return &ret->sock;
|
return make_fd_socket(infd, outfd, inerrfd, plug);
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче