mirror of
https://github.com/clearml/dropbear
synced 2025-03-12 06:41:20 +00:00
just checkpointing
--HG-- extra : convert_revision : fbbf404290f3fea3dfa9f6f53eba9389057e9044
This commit is contained in:
parent
254e8e3452
commit
a712baa8e5
11
Makefile.in
11
Makefile.in
@ -24,15 +24,16 @@ COMMONOBJS=dbutil.o buffer.o \
|
||||
|
||||
SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \
|
||||
svr-authpasswd.o svr-authpubkey.o svr-session.o svr-service.o \
|
||||
svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o
|
||||
svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\
|
||||
svr-tcpfwd.o
|
||||
|
||||
CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \
|
||||
cli-session.o cli-service.o cli-runopts.o cli-chansession.o \
|
||||
cli-authpubkey.o
|
||||
cli-authpubkey.o cli-tcpfwd.o
|
||||
|
||||
CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \
|
||||
common-channel.o common-chansession.o termcodes.o loginrec.o \
|
||||
tcpfwd-direct.o tcpfwd-remote.o listener.o process-packet.o \
|
||||
tcp-accept.o tcp-connect.o listener.o process-packet.o \
|
||||
common-runopts.o
|
||||
|
||||
KEYOBJS=dropbearkey.o gendss.o genrsa.o
|
||||
@ -45,8 +46,8 @@ HEADERS=options.h dbutil.h session.h packet.h algo.h ssh.h buffer.h kex.h \
|
||||
dss.h bignum.h signkey.h rsa.h random.h service.h auth.h authpasswd.h \
|
||||
debug.h channel.h chansession.h config.h queue.h sshpty.h \
|
||||
termcodes.h gendss.h genrsa.h authpubkey.h runopts.h includes.h \
|
||||
loginrec.h atomicio.h x11fwd.h agentfwd.h tcpfwd-direct.h compat.h \
|
||||
tcpfwd-remote.h listener.h
|
||||
loginrec.h atomicio.h x11fwd.h agentfwd.h tcp-accept.h compat.h \
|
||||
tcp-connect.h listener.h
|
||||
|
||||
dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS)
|
||||
dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS)
|
||||
|
@ -13,17 +13,22 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign);
|
||||
void cli_pubkeyfail() {
|
||||
|
||||
struct PubkeyList *keyitem;
|
||||
struct PubkeyList **previtem;
|
||||
|
||||
TRACE(("enter cli_pubkeyfail"));
|
||||
previtem = &cli_opts.pubkeys;
|
||||
|
||||
/* Find the key we failed with, and remove it */
|
||||
for (keyitem = cli_opts.pubkeys; keyitem != NULL; keyitem = keyitem->next) {
|
||||
if (keyitem->next == cli_ses.lastpubkey) {
|
||||
keyitem->next = cli_ses.lastpubkey->next;
|
||||
if (keyitem == cli_ses.lastpubkey) {
|
||||
*previtem = keyitem->next;
|
||||
}
|
||||
previtem = &keyitem;
|
||||
}
|
||||
|
||||
sign_key_free(cli_ses.lastpubkey->key); /* It won't be used again */
|
||||
m_free(cli_ses.lastpubkey);
|
||||
|
||||
TRACE(("leave cli_pubkeyfail"));
|
||||
}
|
||||
|
||||
@ -145,6 +150,7 @@ int cli_auth_pubkey() {
|
||||
/* Send a trial request */
|
||||
send_msg_userauth_pubkey(cli_opts.pubkeys->key,
|
||||
cli_opts.pubkeys->type, 0);
|
||||
cli_ses.lastpubkey = cli_opts.pubkeys;
|
||||
TRACE(("leave cli_auth_pubkey-success"));
|
||||
return 1;
|
||||
} else {
|
||||
|
@ -47,6 +47,12 @@ static void printhelp() {
|
||||
"-T Don't allocate a pty\n"
|
||||
#ifdef DROPBEAR_PUBKEY_AUTH
|
||||
"-i <identityfile> (multiple allowed)\n"
|
||||
#endif
|
||||
#ifndef DISABLE_REMOTETCPFWD
|
||||
"-L <listenport:remotehsot:reportport> Local port forwarding\n"
|
||||
#endif
|
||||
#ifndef DISABLE_TCPFWD_DIRECT
|
||||
"-R <listenport:remotehost:remoteport> Remote port forwarding\n"
|
||||
#endif
|
||||
,DROPBEAR_VERSION, cli_opts.progname);
|
||||
}
|
||||
@ -59,6 +65,12 @@ void cli_getopts(int argc, char ** argv) {
|
||||
#ifdef DROPBEAR_PUBKEY_AUTH
|
||||
int nextiskey = 0; /* A flag if the next argument is a keyfile */
|
||||
#endif
|
||||
#ifdef DROPBEAR_CLI_LOCALTCP
|
||||
int nextislocal = 0;
|
||||
#endif
|
||||
#ifdef DROPBEAR_CLI_REMOTETCP
|
||||
int nextisremote = 0;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@ -71,6 +83,12 @@ void cli_getopts(int argc, char ** argv) {
|
||||
cli_opts.wantpty = 9; /* 9 means "it hasn't been touched", gets set later */
|
||||
#ifdef DROPBEAR_PUBKEY_AUTH
|
||||
cli_opts.pubkeys = NULL;
|
||||
#endif
|
||||
#ifdef DROPBEAR_CLI_LOCALTCP
|
||||
cli_opts.localports = NULL;
|
||||
#endif
|
||||
#ifdef DROPBEAR_CLI_REMOTETCP
|
||||
cli_opts.remoteports = NULL;
|
||||
#endif
|
||||
opts.nolocaltcp = 0;
|
||||
opts.noremotetcp = 0;
|
||||
|
@ -4,8 +4,8 @@
|
||||
#include "kex.h"
|
||||
#include "ssh.h"
|
||||
#include "packet.h"
|
||||
#include "tcpfwd-direct.h"
|
||||
#include "tcpfwd-remote.h"
|
||||
#include "tcp-accept.h"
|
||||
#include "tcp-connect.h"
|
||||
#include "channel.h"
|
||||
#include "random.h"
|
||||
#include "service.h"
|
||||
@ -31,7 +31,6 @@ static const packettype cli_packettypes[] = {
|
||||
{SSH_MSG_KEXDH_REPLY, recv_msg_kexdh_reply}, /* client */
|
||||
{SSH_MSG_NEWKEYS, recv_msg_newkeys},
|
||||
{SSH_MSG_SERVICE_ACCEPT, recv_msg_service_accept}, /* client */
|
||||
{SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_remotetcp},
|
||||
{SSH_MSG_CHANNEL_REQUEST, recv_msg_channel_request},
|
||||
{SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open},
|
||||
{SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof},
|
||||
|
34
cli-tcpfwd.c
Normal file
34
cli-tcpfwd.c
Normal file
@ -0,0 +1,34 @@
|
||||
#include "includes.h"
|
||||
#include "options.h"
|
||||
#include "tcp-accept.h"
|
||||
#include "tcp-connect.h"
|
||||
#include "channel.h"
|
||||
|
||||
static const struct ChanType cli_chan_tcplocal = {
|
||||
1, /* sepfds */
|
||||
"direct-tcpip",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
static int cli_localtcp(char* port) {
|
||||
|
||||
struct TCPListener* tcpinfo = NULL;
|
||||
|
||||
tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener*));
|
||||
tcpinfo->addr = NULL;
|
||||
tcpinfo->port = port;
|
||||
tcpinfo->chantype = &cli_chan_tcplocal;
|
||||
|
||||
ret = listen_tcpfwd(tcpinfo);
|
||||
|
||||
if (ret == DROPBEAR_FAILURE) {
|
||||
DROPBEAR_LOG(LOG_WARNING, "Failed to listen on port %s", port);
|
||||
m_free(tcpinfo);
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -32,8 +32,6 @@
|
||||
#include "dbutil.h"
|
||||
#include "channel.h"
|
||||
#include "ssh.h"
|
||||
#include "tcpfwd-direct.h"
|
||||
#include "tcpfwd-remote.h"
|
||||
#include "listener.h"
|
||||
|
||||
static void send_msg_channel_open_failure(unsigned int remotechan, int reason,
|
||||
@ -307,13 +305,6 @@ static void send_msg_channel_close(struct Channel *channel) {
|
||||
if (channel->type->closehandler) {
|
||||
channel->type->closehandler(channel);
|
||||
}
|
||||
#if 0
|
||||
if (channel->type == CHANNEL_ID_SESSION) {
|
||||
send_exitsignalstatus(channel);
|
||||
|
||||
closechansess(channel);
|
||||
}
|
||||
#endif
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
@ -545,23 +536,6 @@ void recv_msg_channel_request() {
|
||||
send_msg_channel_failure(channel);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* handle according to channel type */
|
||||
switch (channel->type) {
|
||||
|
||||
case CHANNEL_ID_SESSION:
|
||||
TRACE(("continue recv_msg_channel_request: session request"));
|
||||
/* XXX server */
|
||||
/* Here we need to do things channel-specific style. Function
|
||||
* pointer callbacks perhaps */
|
||||
chansessionrequest(channel);
|
||||
break;
|
||||
|
||||
default:
|
||||
send_msg_channel_failure(channel);
|
||||
}
|
||||
#endif
|
||||
|
||||
TRACE(("leave recv_msg_channel_request"));
|
||||
|
||||
}
|
||||
@ -797,23 +771,6 @@ void recv_msg_channel_open() {
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* type specific initialisation */
|
||||
if (typeval == CHANNEL_ID_SESSION) {
|
||||
newchansess(channel);
|
||||
#ifndef DISABLE_LOCALTCPFWD
|
||||
} else if (typeval == CHANNEL_ID_TCPDIRECT) {
|
||||
if (ses.opts->nolocaltcp) {
|
||||
errtype = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
|
||||
} else if (newtcpdirect(channel) == DROPBEAR_FAILURE) {
|
||||
errtype = SSH_OPEN_CONNECT_FAILED;
|
||||
deletechannel(channel);
|
||||
goto failure;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/* success */
|
||||
send_msg_channel_open_confirmation(channel, channel->recvwindow,
|
||||
channel->recvmaxpacket);
|
||||
|
175
dbutil.c
175
dbutil.c
@ -113,8 +113,109 @@ void dropbear_trace(const char* format, ...) {
|
||||
}
|
||||
#endif /* DEBUG_TRACE */
|
||||
|
||||
/* Listen on address:port. Unless address is NULL, in which case listen on
|
||||
* everything (ie 0.0.0.0, or ::1 - note that this is IPv? agnostic. Linux is
|
||||
* broken with respect to listening to v6 or v4, so the addresses you get when
|
||||
* people connect will be wrong. It doesn't break things, just looks quite
|
||||
* ugly. Returns the number of sockets bound on success, or -1 on failure. On
|
||||
* failure, if errstring wasn't NULL, it'll be a newly malloced error
|
||||
* string.*/
|
||||
int dropbear_listen(const char* address, const char* port,
|
||||
int *socks, unsigned int sockcount, char **errstring, int *maxfd) {
|
||||
|
||||
struct addrinfo hints, *res, *res0;
|
||||
int err;
|
||||
unsigned int nsock;
|
||||
struct linger linger;
|
||||
int val;
|
||||
int sock;
|
||||
|
||||
TRACE(("enter dropbear_listen"));
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC; /* TODO: let them flag v4 only etc */
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
err = getaddrinfo(address, port, &hints, &res0);
|
||||
|
||||
if (err) {
|
||||
if (errstring != NULL && *errstring == NULL) {
|
||||
int len;
|
||||
len = 20 + strlen(gai_strerror(err));
|
||||
*errstring = (char*)m_malloc(len);
|
||||
snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err));
|
||||
}
|
||||
TRACE(("leave dropbear_listen: failed resolving"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
nsock = 0;
|
||||
for (res = res0; res != NULL && nsock < sockcount;
|
||||
res = res->ai_next) {
|
||||
|
||||
/* Get a socket */
|
||||
socks[nsock] = socket(res->ai_family, res->ai_socktype,
|
||||
res->ai_protocol);
|
||||
|
||||
sock = socks[nsock]; /* For clarity */
|
||||
|
||||
if (sock < 0) {
|
||||
err = errno;
|
||||
TRACE(("socket() failed"));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Various useful socket options */
|
||||
val = 1;
|
||||
/* set to reuse, quick timeout */
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &val, sizeof(val));
|
||||
linger.l_onoff = 1;
|
||||
linger.l_linger = 5;
|
||||
setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&linger, sizeof(linger));
|
||||
|
||||
/* disable nagle */
|
||||
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val));
|
||||
|
||||
if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
|
||||
err = errno;
|
||||
close(sock);
|
||||
TRACE(("bind() failed"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (listen(sock, 20) < 0) {
|
||||
err = errno;
|
||||
close(sock);
|
||||
TRACE(("listen() failed"));
|
||||
continue;
|
||||
}
|
||||
|
||||
*maxfd = MAX(*maxfd, sock);
|
||||
|
||||
nsock++;
|
||||
}
|
||||
|
||||
if (nsock == 0) {
|
||||
if (errstring != NULL && *errstring == NULL) {
|
||||
int len;
|
||||
len = 20 + strlen(strerror(err));
|
||||
*errstring = (char*)m_malloc(len);
|
||||
snprintf(*errstring, len, "Error connecting: %s", strerror(err));
|
||||
TRACE(("leave dropbear_listen: failure, %s", strerror(err)));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE(("leave dropbear_listen: success, %d socks bound", nsock));
|
||||
return nsock;
|
||||
}
|
||||
|
||||
/* Connect via TCP to a host. Connection will try ipv4 or ipv6, will
|
||||
* return immediately if nonblocking is set */
|
||||
* return immediately if nonblocking is set. On failure, if errstring
|
||||
* wasn't null, it will be a newly malloced error message */
|
||||
|
||||
/* TODO: maxfd */
|
||||
int connect_remote(const char* remotehost, const char* remoteport,
|
||||
int nonblocking, char ** errstring) {
|
||||
|
||||
@ -197,58 +298,70 @@ int connect_remote(const char* remotehost, const char* remoteport,
|
||||
}
|
||||
|
||||
freeaddrinfo(res0);
|
||||
if (sock > 0 && errstring != NULL && *errstring != NULL) {
|
||||
m_free(*errstring);
|
||||
}
|
||||
|
||||
TRACE(("leave connect_remote: sock %d", sock));
|
||||
TRACE(("leave connect_remote: sock %d\n", sock));
|
||||
return sock;
|
||||
}
|
||||
|
||||
/* Return a string representation of the socket address passed. The return
|
||||
* value is allocated with malloc() */
|
||||
unsigned char * getaddrstring(struct sockaddr * addr) {
|
||||
unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) {
|
||||
|
||||
char *retstring;
|
||||
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
|
||||
char *retstring = NULL;
|
||||
int ret;
|
||||
unsigned int len;
|
||||
|
||||
/* space for "255.255.255.255:65535\0" = 22 */
|
||||
retstring = m_malloc(22);
|
||||
len = sizeof(struct sockaddr_storage);
|
||||
|
||||
switch (addr->sa_family) {
|
||||
case PF_INET:
|
||||
snprintf(retstring, 22, "%s:%hu",
|
||||
inet_ntoa(((struct sockaddr_in *)addr)->sin_addr),
|
||||
((struct sockaddr_in *)addr)->sin_port);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* XXX ipv6 */
|
||||
strcpy(retstring, "Bad protocol");
|
||||
ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf),
|
||||
sbuf, sizeof(sbuf), NI_NUMERICSERV | NI_NUMERICHOST);
|
||||
|
||||
if (ret != 0) {
|
||||
/* This is a fairly bad failure - it'll fallback to IP if it
|
||||
* just can't resolve */
|
||||
dropbear_exit("failed lookup (%d, %d)", ret, errno);
|
||||
}
|
||||
|
||||
if (withport) {
|
||||
len = strlen(hbuf) + 2 + strlen(sbuf);
|
||||
retstring = (char*)m_malloc(len);
|
||||
snprintf(retstring, len, "%s:%s", hbuf, sbuf);
|
||||
} else {
|
||||
retstring = m_strdup(hbuf);
|
||||
}
|
||||
|
||||
return retstring;
|
||||
|
||||
}
|
||||
|
||||
/* Get the hostname corresponding to the address addr. On failure, the IP
|
||||
* address is returned. The return value is allocated with strdup() */
|
||||
char* getaddrhostname(struct sockaddr * addr) {
|
||||
char* getaddrhostname(struct sockaddr_storage * addr) {
|
||||
|
||||
struct hostent *host = NULL;
|
||||
char * retstring;
|
||||
char hbuf[NI_MAXHOST];
|
||||
char sbuf[NI_MAXSERV];
|
||||
int ret;
|
||||
unsigned int len;
|
||||
|
||||
#ifdef DO_HOST_LOOKUP
|
||||
host = gethostbyaddr((char*)&((struct sockaddr_in*)addr)->sin_addr,
|
||||
sizeof(struct in_addr), AF_INET);
|
||||
#endif
|
||||
|
||||
if (host == NULL) {
|
||||
/* return the address */
|
||||
retstring = inet_ntoa(((struct sockaddr_in *)addr)->sin_addr);
|
||||
} else {
|
||||
/* return the hostname */
|
||||
retstring = host->h_name;
|
||||
len = sizeof(struct sockaddr_storage);
|
||||
|
||||
ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf),
|
||||
sbuf, sizeof(sbuf), NI_NUMERICSERV);
|
||||
|
||||
if (ret != 0) {
|
||||
/* On some systems (Darwin does it) we get EINTR from getnameinfo
|
||||
* somehow. Eew. So we'll just return the IP, since that doesn't seem
|
||||
* to exhibit that behaviour. */
|
||||
return getaddrstring(addr, 0);
|
||||
}
|
||||
|
||||
return m_strdup(retstring);
|
||||
return m_strdup(hbuf);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TRACE
|
||||
void printhex(unsigned char* buf, int len) {
|
||||
|
||||
|
6
dbutil.h
6
dbutil.h
@ -44,10 +44,12 @@ void dropbear_trace(const char* format, ...);
|
||||
void printhex(unsigned char* buf, int len);
|
||||
#endif
|
||||
char * stripcontrol(const char * text);
|
||||
unsigned char * getaddrstring(struct sockaddr * addr);
|
||||
unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport);
|
||||
int dropbear_listen(const char* address, const char* port,
|
||||
int *socks, unsigned int sockcount, char **errstring, int *maxfd);
|
||||
int connect_remote(const char* remotehost, const char* remoteport,
|
||||
int nonblocking, char ** errstring);
|
||||
char* getaddrhostname(struct sockaddr * addr);
|
||||
char* getaddrhostname(struct sockaddr_storage * addr);
|
||||
int buf_readfile(buffer* buf, const char* filename);
|
||||
int buf_getline(buffer * line, FILE * authfile);
|
||||
|
||||
|
42
listener.c
42
listener.c
@ -14,15 +14,16 @@ void listeners_initialise() {
|
||||
|
||||
void set_listener_fds(fd_set * readfds) {
|
||||
|
||||
unsigned int i;
|
||||
unsigned int i, j;
|
||||
struct Listener *listener;
|
||||
|
||||
/* check each in turn */
|
||||
for (i = 0; i < ses.listensize; i++) {
|
||||
listener = ses.listeners[i];
|
||||
if (listener != NULL) {
|
||||
TRACE(("set listener fd %d", listener->sock));
|
||||
FD_SET(listener->sock, readfds);
|
||||
for (j = 0; j < listener->nsocks; j++) {
|
||||
FD_SET(listener->socks[j], readfds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,16 +31,19 @@ void set_listener_fds(fd_set * readfds) {
|
||||
|
||||
void handle_listeners(fd_set * readfds) {
|
||||
|
||||
unsigned int i;
|
||||
unsigned int i, j;
|
||||
struct Listener *listener;
|
||||
int sock;
|
||||
|
||||
/* check each in turn */
|
||||
for (i = 0; i < ses.listensize; i++) {
|
||||
listener = ses.listeners[i];
|
||||
if (listener != NULL) {
|
||||
TRACE(("handle listener num %d fd %d", i, listener->sock));
|
||||
if (FD_ISSET(listener->sock, readfds)) {
|
||||
listener->accepter(listener);
|
||||
for (j = 0; j < listener->nsocks; j++) {
|
||||
sock = listener->socks[j];
|
||||
if (FD_ISSET(sock, readfds)) {
|
||||
listener->accepter(listener, sock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -48,8 +52,9 @@ void handle_listeners(fd_set * readfds) {
|
||||
|
||||
/* accepter(int fd, void* typedata) is a function to accept connections,
|
||||
* cleanup(void* typedata) happens when cleaning up */
|
||||
struct Listener* new_listener(int sock, int type, void* typedata,
|
||||
void (*accepter)(struct Listener*),
|
||||
struct Listener* new_listener(int socks[], unsigned int nsocks,
|
||||
int type, void* typedata,
|
||||
void (*accepter)(struct Listener*, int sock),
|
||||
void (*cleanup)(struct Listener*)) {
|
||||
|
||||
unsigned int i, j;
|
||||
@ -65,7 +70,9 @@ struct Listener* new_listener(int sock, int type, void* typedata,
|
||||
if (i == ses.listensize) {
|
||||
if (ses.listensize > MAX_LISTENERS) {
|
||||
TRACE(("leave newlistener: too many already"));
|
||||
close(sock);
|
||||
for (j = 0; j < nsocks; j++) {
|
||||
close(socks[i]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -80,15 +87,18 @@ struct Listener* new_listener(int sock, int type, void* typedata,
|
||||
}
|
||||
}
|
||||
|
||||
ses.maxfd = MAX(ses.maxfd, sock);
|
||||
for (j = 0; j < nsocks; j++) {
|
||||
ses.maxfd = MAX(ses.maxfd, socks[j]);
|
||||
}
|
||||
|
||||
TRACE(("new listener num %d fd %d", i, sock));
|
||||
TRACE(("new listener num %d ", i));
|
||||
|
||||
newlisten = (struct Listener*)m_malloc(sizeof(struct Listener));
|
||||
newlisten->index = i;
|
||||
newlisten->type = type;
|
||||
newlisten->typedata = typedata;
|
||||
newlisten->sock = sock;
|
||||
newlisten->nsocks = nsocks;
|
||||
memcpy(newlisten->socks, socks, nsocks * sizeof(int));
|
||||
newlisten->accepter = accepter;
|
||||
newlisten->cleanup = cleanup;
|
||||
|
||||
@ -116,11 +126,15 @@ struct Listener * get_listener(int type, void* typedata,
|
||||
|
||||
void remove_listener(struct Listener* listener) {
|
||||
|
||||
unsigned int j;
|
||||
|
||||
if (listener->cleanup) {
|
||||
listener->cleanup(listener);
|
||||
}
|
||||
|
||||
close(listener->sock);
|
||||
for (j = 0; j < listener->nsocks; j++) {
|
||||
close(listener->socks[j]);
|
||||
}
|
||||
ses.listeners[listener->index] = NULL;
|
||||
m_free(listener);
|
||||
|
||||
|
10
listener.h
10
listener.h
@ -6,11 +6,12 @@
|
||||
|
||||
struct Listener {
|
||||
|
||||
int sock;
|
||||
int socks[DROPBEAR_MAX_SOCKS];
|
||||
unsigned int nsocks;
|
||||
|
||||
int index; /* index in the array of listeners */
|
||||
|
||||
void (*accepter)(struct Listener*);
|
||||
void (*accepter)(struct Listener*, int sock);
|
||||
void (*cleanup)(struct Listener*);
|
||||
|
||||
int type; /* CHANNEL_ID_X11, CHANNEL_ID_AGENT,
|
||||
@ -25,8 +26,9 @@ void listeners_initialise();
|
||||
void handle_listeners(fd_set * readfds);
|
||||
void set_listener_fds(fd_set * readfds);
|
||||
|
||||
struct Listener* new_listener(int sock, int type, void* typedata,
|
||||
void (*accepter)(struct Listener*),
|
||||
struct Listener* new_listener(int socks[], unsigned int nsocks,
|
||||
int type, void* typedata,
|
||||
void (*accepter)(struct Listener*, int sock),
|
||||
void (*cleanup)(struct Listener*));
|
||||
|
||||
struct Listener * get_listener(int type, void* typedata,
|
||||
|
@ -280,6 +280,9 @@
|
||||
/* For a 4096 bit DSS key, empirically determined to be 1590 bytes */
|
||||
#define MAX_PRIVKEY_SIZE 1600
|
||||
|
||||
#define DROPBEAR_MAX_SOCKS 2 /* IPv4, IPv6 are all we'll get for now. Revisit
|
||||
in a few years time.... */
|
||||
|
||||
#ifndef ENABLE_X11FWD
|
||||
#define DISABLE_X11FWD
|
||||
#endif
|
||||
|
@ -44,7 +44,7 @@
|
||||
|
||||
static int send_msg_channel_open_agent(int fd);
|
||||
static int bindagent(int fd, struct ChanSess * chansess);
|
||||
static void agentaccept(struct Listener * listener);
|
||||
static void agentaccept(struct Listener * listener, int sock);
|
||||
|
||||
/* Handles client requests to start agent forwarding, sets up listening socket.
|
||||
* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
|
||||
@ -78,7 +78,7 @@ int agentreq(struct ChanSess * chansess) {
|
||||
}
|
||||
|
||||
/* pass if off to listener */
|
||||
chansess->agentlistener = new_listener( fd, 0, chansess,
|
||||
chansess->agentlistener = new_listener( &fd, 1, 0, chansess,
|
||||
agentaccept, NULL);
|
||||
|
||||
if (chansess->agentlistener == NULL) {
|
||||
@ -97,11 +97,11 @@ fail:
|
||||
/* accepts a connection on the forwarded socket and opens a new channel for it
|
||||
* back to the client */
|
||||
/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
|
||||
static void agentaccept(struct Listener * listener) {
|
||||
static void agentaccept(struct Listener * listener, int sock) {
|
||||
|
||||
int fd;
|
||||
|
||||
fd = accept(listener->sock, NULL, NULL);
|
||||
fd = accept(sock, NULL, NULL);
|
||||
if (fd < 0) {
|
||||
TRACE(("accept failed"));
|
||||
return;
|
||||
|
@ -408,6 +408,8 @@ static void get_termmodes(struct ChanSess *chansess) {
|
||||
}
|
||||
|
||||
len = buf_getint(ses.payload);
|
||||
TRACE(("term mode str %d p->l %d p->p %d",
|
||||
len, ses.payload->len , ses.payload->pos));
|
||||
if (len != ses.payload->len - ses.payload->pos) {
|
||||
dropbear_exit("bad term mode string");
|
||||
}
|
||||
|
84
svr-main.c
84
svr-main.c
@ -29,7 +29,7 @@
|
||||
#include "signkey.h"
|
||||
#include "runopts.h"
|
||||
|
||||
static int listensockets(int *sock, int *maxfd);
|
||||
static int listensockets(int *sock, int sockcount, int *maxfd);
|
||||
static void sigchld_handler(int dummy);
|
||||
static void sigsegv_handler(int);
|
||||
static void sigintterm_handler(int fish);
|
||||
@ -49,10 +49,10 @@ int main(int argc, char ** argv)
|
||||
unsigned int i, j;
|
||||
int val;
|
||||
int maxsock = -1;
|
||||
struct sockaddr remoteaddr;
|
||||
struct sockaddr_storage remoteaddr;
|
||||
int remoteaddrlen;
|
||||
int listensocks[MAX_LISTEN_ADDR];
|
||||
unsigned int listensockcount = 0;
|
||||
int listensockcount = 0;
|
||||
FILE * pidfile;
|
||||
|
||||
int childsock;
|
||||
@ -127,7 +127,10 @@ int main(int argc, char ** argv)
|
||||
|
||||
/* Set up the listening sockets */
|
||||
/* XXX XXX ports */
|
||||
listensockcount = listensockets(listensocks, &maxsock);
|
||||
listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock);
|
||||
if (listensockcount < 0) {
|
||||
dropbear_exit("No listening ports available.");
|
||||
}
|
||||
|
||||
/* incoming connection select loop */
|
||||
for(;;) {
|
||||
@ -138,7 +141,7 @@ int main(int argc, char ** argv)
|
||||
seltimeout.tv_usec = 0;
|
||||
|
||||
/* listening sockets */
|
||||
for (i = 0; i < listensockcount; i++) {
|
||||
for (i = 0; i < (unsigned int)listensockcount; i++) {
|
||||
FD_SET(listensocks[i], &fds);
|
||||
}
|
||||
|
||||
@ -179,12 +182,12 @@ int main(int argc, char ** argv)
|
||||
}
|
||||
|
||||
/* handle each socket which has something to say */
|
||||
for (i = 0; i < listensockcount; i++) {
|
||||
for (i = 0; i < (unsigned int)listensockcount; i++) {
|
||||
if (!FD_ISSET(listensocks[i], &fds))
|
||||
continue;
|
||||
|
||||
/* child connection XXX - ip6 stuff here */
|
||||
remoteaddrlen = sizeof(struct sockaddr_in);
|
||||
remoteaddrlen = sizeof(remoteaddr);
|
||||
childsock = accept(listensocks[i], &remoteaddr, &remoteaddrlen);
|
||||
|
||||
if (childsock < 0) {
|
||||
@ -222,7 +225,7 @@ int main(int argc, char ** argv)
|
||||
monstartup((u_long)&_start, (u_long)&etext);
|
||||
#endif /* DEBUG_FORKGPROF */
|
||||
|
||||
addrstring = getaddrstring(&remoteaddr);
|
||||
addrstring = getaddrstring(&remoteaddr, 1);
|
||||
dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
|
||||
m_free(addrstring);
|
||||
|
||||
@ -231,7 +234,7 @@ int main(int argc, char ** argv)
|
||||
}
|
||||
|
||||
/* make sure we close sockets */
|
||||
for (i = 0; i < listensockcount; i++) {
|
||||
for (i = 0; i < (unsigned int)listensockcount; i++) {
|
||||
if (m_close(listensocks[i]) == DROPBEAR_FAILURE) {
|
||||
dropbear_exit("Couldn't close socket");
|
||||
}
|
||||
@ -289,59 +292,30 @@ static void sigintterm_handler(int fish) {
|
||||
}
|
||||
|
||||
/* Set up listening sockets for all the requested ports */
|
||||
static int listensockets(int *sock, int *maxfd) {
|
||||
static int listensockets(int *sock, int sockcount, int *maxfd) {
|
||||
|
||||
int listensock; /* listening fd */
|
||||
struct sockaddr_in listen_addr;
|
||||
struct linger linger;
|
||||
unsigned int i;
|
||||
int val;
|
||||
char portstring[6];
|
||||
char* errstring = NULL;
|
||||
unsigned int sockpos = 0;
|
||||
int nsock;
|
||||
|
||||
for (i = 0; i < svr_opts.portcount; i++) {
|
||||
|
||||
/* iterate through all the sockets to listen on */
|
||||
listensock = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (listensock < 0) {
|
||||
dropbear_exit("Failed to create socket");
|
||||
snprintf(portstring, sizeof(portstring), "%d", svr_opts.ports[i]);
|
||||
nsock = dropbear_listen(NULL, portstring, &sock[sockpos],
|
||||
sockcount - sockpos,
|
||||
&errstring, maxfd);
|
||||
|
||||
if (nsock < 0) {
|
||||
dropbear_log(LOG_WARNING, "Failed listening on port %s: %s",
|
||||
portstring, errstring);
|
||||
m_free(errstring);
|
||||
continue;
|
||||
}
|
||||
|
||||
val = 1;
|
||||
/* set to reuse, quick timeout */
|
||||
setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR,
|
||||
(void*) &val, sizeof(val));
|
||||
linger.l_onoff = 1;
|
||||
linger.l_linger = 5;
|
||||
setsockopt(listensock, SOL_SOCKET, SO_LINGER,
|
||||
(void*)&linger, sizeof(linger));
|
||||
sockpos += nsock;
|
||||
|
||||
/* disable nagle */
|
||||
setsockopt(listensock, IPPROTO_TCP, TCP_NODELAY,
|
||||
(void*)&val, sizeof(val));
|
||||
|
||||
memset((void*)&listen_addr, 0x0, sizeof(listen_addr));
|
||||
listen_addr.sin_family = AF_INET;
|
||||
listen_addr.sin_port = htons(svr_opts.ports[i]);
|
||||
listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
memset(&(listen_addr.sin_zero), '\0', 8);
|
||||
|
||||
if (bind(listensock, (struct sockaddr *)&listen_addr,
|
||||
sizeof(listen_addr)) < 0) {
|
||||
dropbear_exit("Bind failed port %d", svr_opts.ports[i]);
|
||||
}
|
||||
|
||||
/* listen */
|
||||
if (listen(listensock, 20) < 0) { /* TODO set listen count */
|
||||
dropbear_exit("Listen failed");
|
||||
}
|
||||
|
||||
/* nonblock */
|
||||
if (fcntl(listensock, F_SETFL, O_NONBLOCK) < 0) {
|
||||
dropbear_exit("Failed to set non-blocking");
|
||||
}
|
||||
|
||||
sock[i] = listensock;
|
||||
*maxfd = MAX(listensock, *maxfd);
|
||||
}
|
||||
|
||||
return svr_opts.portcount;
|
||||
return sockpos;
|
||||
}
|
||||
|
@ -35,10 +35,10 @@
|
||||
#include "channel.h"
|
||||
#include "chansession.h"
|
||||
#include "atomicio.h"
|
||||
#include "tcpfwd-direct.h"
|
||||
#include "tcp-accept.h"
|
||||
#include "tcp-connect.h"
|
||||
#include "service.h"
|
||||
#include "auth.h"
|
||||
#include "tcpfwd-remote.h"
|
||||
#include "runopts.h"
|
||||
|
||||
static void svr_remoteclosed();
|
||||
@ -65,7 +65,7 @@ static const packettype svr_packettypes[] = {
|
||||
|
||||
static const struct ChanType *svr_chantypes[] = {
|
||||
&svrchansess,
|
||||
&chan_tcpdirect,
|
||||
&svr_chan_tcpdirect,
|
||||
NULL /* Null termination is mandatory. */
|
||||
};
|
||||
|
||||
|
196
svr-tcpfwd.c
Normal file
196
svr-tcpfwd.c
Normal file
@ -0,0 +1,196 @@
|
||||
#include "includes.h"
|
||||
#include "ssh.h"
|
||||
#include "tcp-accept.h"
|
||||
#include "tcp-connect.h"
|
||||
#include "dbutil.h"
|
||||
#include "session.h"
|
||||
#include "buffer.h"
|
||||
#include "packet.h"
|
||||
#include "listener.h"
|
||||
#include "runopts.h"
|
||||
|
||||
#ifndef DISABLE_SVR_REMOTETCPFWD
|
||||
|
||||
static void send_msg_request_success();
|
||||
static void send_msg_request_failure();
|
||||
static int svr_cancelremotetcp();
|
||||
static int svr_remotetcpreq();
|
||||
|
||||
|
||||
const struct ChanType svr_chan_tcpdirect = {
|
||||
1, /* sepfds */
|
||||
"direct-tcpip",
|
||||
newtcpdirect, /* init */
|
||||
NULL, /* checkclose */
|
||||
NULL, /* reqhandler */
|
||||
NULL /* closehandler */
|
||||
};
|
||||
|
||||
static const struct ChanType svr_chan_tcpremote = {
|
||||
1, /* sepfds */
|
||||
"forwarded-tcpip",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
/* At the moment this is completely used for tcp code (with the name reflecting
|
||||
* that). If new request types are added, this should be replaced with code
|
||||
* similar to the request-switching in chansession.c */
|
||||
void recv_msg_global_request_remotetcp() {
|
||||
|
||||
unsigned char* reqname = NULL;
|
||||
unsigned int namelen;
|
||||
unsigned int wantreply = 0;
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
|
||||
TRACE(("enter recv_msg_global_request_remotetcp"));
|
||||
|
||||
if (opts.noremotetcp) {
|
||||
TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
reqname = buf_getstring(ses.payload, &namelen);
|
||||
wantreply = buf_getbyte(ses.payload);
|
||||
|
||||
if (namelen > MAXNAMLEN) {
|
||||
TRACE(("name len is wrong: %d", namelen));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strcmp("tcpip-forward", reqname) == 0) {
|
||||
ret = svr_remotetcpreq();
|
||||
} else if (strcmp("cancel-tcpip-forward", reqname) == 0) {
|
||||
ret = svr_cancelremotetcp();
|
||||
} else {
|
||||
TRACE(("reqname isn't tcpip-forward: '%s'", reqname));
|
||||
}
|
||||
|
||||
out:
|
||||
if (wantreply) {
|
||||
if (ret == DROPBEAR_SUCCESS) {
|
||||
send_msg_request_success();
|
||||
} else {
|
||||
send_msg_request_failure();
|
||||
}
|
||||
}
|
||||
|
||||
m_free(reqname);
|
||||
|
||||
TRACE(("leave recv_msg_global_request"));
|
||||
}
|
||||
|
||||
|
||||
static void send_msg_request_success() {
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS);
|
||||
encrypt_packet();
|
||||
|
||||
}
|
||||
|
||||
static void send_msg_request_failure() {
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE);
|
||||
encrypt_packet();
|
||||
|
||||
}
|
||||
|
||||
static int matchtcp(void* typedata1, void* typedata2) {
|
||||
|
||||
const struct TCPListener *info1 = (struct TCPListener*)typedata1;
|
||||
const struct TCPListener *info2 = (struct TCPListener*)typedata2;
|
||||
|
||||
return (info1->port == info2->port)
|
||||
&& (info1->chantype == info2->chantype)
|
||||
&& (strcmp(info1->addr, info2->addr) == 0);
|
||||
}
|
||||
|
||||
static int svr_cancelremotetcp() {
|
||||
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
unsigned char * bindaddr = NULL;
|
||||
unsigned int addrlen;
|
||||
unsigned int port;
|
||||
struct Listener * listener = NULL;
|
||||
struct TCPListener tcpinfo;
|
||||
|
||||
TRACE(("enter cancelremotetcp"));
|
||||
|
||||
bindaddr = buf_getstring(ses.payload, &addrlen);
|
||||
if (addrlen > MAX_IP_LEN) {
|
||||
TRACE(("addr len too long: %d", addrlen));
|
||||
goto out;
|
||||
}
|
||||
|
||||
port = buf_getint(ses.payload);
|
||||
|
||||
tcpinfo.addr = bindaddr;
|
||||
tcpinfo.port = port;
|
||||
listener = get_listener(CHANNEL_ID_TCPFORWARDED, &tcpinfo, matchtcp);
|
||||
if (listener) {
|
||||
remove_listener( listener );
|
||||
ret = DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
out:
|
||||
m_free(bindaddr);
|
||||
TRACE(("leave cancelremotetcp"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int svr_remotetcpreq() {
|
||||
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
unsigned char * bindaddr = NULL;
|
||||
unsigned int addrlen;
|
||||
struct TCPListener *tcpinfo = NULL;
|
||||
unsigned int port;
|
||||
|
||||
TRACE(("enter remotetcpreq"));
|
||||
|
||||
bindaddr = buf_getstring(ses.payload, &addrlen);
|
||||
if (addrlen > MAX_IP_LEN) {
|
||||
TRACE(("addr len too long: %d", addrlen));
|
||||
goto out;
|
||||
}
|
||||
|
||||
port = buf_getint(ses.payload);
|
||||
|
||||
if (port == 0) {
|
||||
dropbear_log(LOG_INFO, "Server chosen tcpfwd ports are unsupported");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (port < 1 || port > 65535) {
|
||||
TRACE(("invalid port: %d", port));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ses.allowprivport && port < IPPORT_RESERVED) {
|
||||
TRACE(("can't assign port < 1024 for non-root"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
|
||||
tcpinfo->addr = bindaddr;
|
||||
tcpinfo->port = port;
|
||||
tcpinfo->localport = -1;
|
||||
tcpinfo->chantype = &svr_chan_tcpremote;
|
||||
|
||||
ret = listen_tcpfwd(tcpinfo);
|
||||
|
||||
out:
|
||||
if (ret == DROPBEAR_FAILURE) {
|
||||
/* we only free it if a listener wasn't created, since the listener
|
||||
* has to remember it if it's to be cancelled */
|
||||
m_free(tcpinfo->addr);
|
||||
m_free(tcpinfo);
|
||||
}
|
||||
TRACE(("leave remotetcpreq"));
|
||||
return ret;
|
||||
}
|
||||
#endif
|
@ -37,7 +37,7 @@
|
||||
#define X11BASEPORT 6000
|
||||
#define X11BINDBASE 6010
|
||||
|
||||
static void x11accept(struct Listener* listener);
|
||||
static void x11accept(struct Listener* listener, int sock);
|
||||
static int bindport(int fd);
|
||||
static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr);
|
||||
|
||||
@ -82,7 +82,7 @@ int x11req(struct ChanSess * chansess) {
|
||||
/* listener code will handle the socket now.
|
||||
* No cleanup handler needed, since listener_remove only happens
|
||||
* from our cleanup anyway */
|
||||
chansess->x11listener = new_listener( fd, 0, chansess, x11accept, NULL);
|
||||
chansess->x11listener = new_listener( &fd, 1, 0, chansess, x11accept, NULL);
|
||||
if (chansess->x11listener == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
@ -100,7 +100,7 @@ fail:
|
||||
|
||||
/* accepts a new X11 socket */
|
||||
/* returns DROPBEAR_FAILURE or DROPBEAR_SUCCESS */
|
||||
static void x11accept(struct Listener* listener) {
|
||||
static void x11accept(struct Listener* listener, int sock) {
|
||||
|
||||
int fd;
|
||||
struct sockaddr_in addr;
|
||||
@ -110,7 +110,7 @@ static void x11accept(struct Listener* listener) {
|
||||
|
||||
len = sizeof(addr);
|
||||
|
||||
fd = accept(listener->sock, (struct sockaddr*)&addr, &len);
|
||||
fd = accept(sock, (struct sockaddr*)&addr, &len);
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
88
tcp-accept.c
Normal file
88
tcp-accept.c
Normal file
@ -0,0 +1,88 @@
|
||||
#include "includes.h"
|
||||
#include "ssh.h"
|
||||
#include "tcp-accept.h"
|
||||
#include "dbutil.h"
|
||||
#include "session.h"
|
||||
#include "buffer.h"
|
||||
#include "packet.h"
|
||||
#include "listener.h"
|
||||
#include "runopts.h"
|
||||
|
||||
#ifndef DISABLE_TCP_ACCEPT
|
||||
|
||||
static void accept_tcp(struct Listener *listener, int sock) {
|
||||
|
||||
int fd;
|
||||
struct sockaddr_storage addr;
|
||||
int len;
|
||||
char ipstring[NI_MAXHOST], portstring[NI_MAXSERV];
|
||||
struct TCPListener *tcpinfo = (struct TCPListener*)(listener->typedata);
|
||||
|
||||
len = sizeof(addr);
|
||||
|
||||
fd = accept(sock, (struct sockaddr*)&addr, &len);
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (getnameinfo((struct sockaddr*)&addr, len, ipstring, sizeof(ipstring),
|
||||
portstring, sizeof(portstring),
|
||||
NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (send_msg_channel_open_init(fd, tcpinfo->chantype) == DROPBEAR_SUCCESS) {
|
||||
|
||||
buf_putstring(ses.writepayload, tcpinfo->addr, strlen(tcpinfo->addr));
|
||||
buf_putint(ses.writepayload, tcpinfo->port);
|
||||
buf_putstring(ses.writepayload, ipstring, strlen(ipstring));
|
||||
buf_putint(ses.writepayload, atol(portstring));
|
||||
encrypt_packet();
|
||||
|
||||
} else {
|
||||
/* XXX debug? */
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static void cleanup_tcp(struct Listener *listener) {
|
||||
|
||||
struct TCPListener *tcpinfo = (struct TCPListener*)(listener->typedata);
|
||||
|
||||
m_free(tcpinfo->addr);
|
||||
m_free(tcpinfo);
|
||||
}
|
||||
|
||||
|
||||
int listen_tcpfwd(struct TCPListener* tcpinfo) {
|
||||
|
||||
char portstring[6]; /* "65535\0" */
|
||||
int socks[DROPBEAR_MAX_SOCKS];
|
||||
struct Listener *listener = NULL;
|
||||
int nsocks;
|
||||
|
||||
TRACE(("enter listen_tcpfwd"));
|
||||
|
||||
/* first we try to bind, so don't need to do so much cleanup on failure */
|
||||
snprintf(portstring, sizeof(portstring), "%d", tcpinfo->port);
|
||||
nsocks = dropbear_listen(tcpinfo->addr, portstring, socks,
|
||||
DROPBEAR_MAX_SOCKS, NULL, &ses.maxfd);
|
||||
if (nsocks < 0) {
|
||||
TRACE(("leave listen_tcpfwd: dropbear_listen failed"));
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
listener = new_listener(socks, nsocks, CHANNEL_ID_TCPFORWARDED, tcpinfo,
|
||||
accept_tcp, cleanup_tcp);
|
||||
|
||||
if (listener == NULL) {
|
||||
m_free(tcpinfo);
|
||||
TRACE(("leave listen_tcpfwd: listener failed"));
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
TRACE(("leave listen_tcpfwd: success"));
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
#endif /* DISABLE_REMOTETCPFWD */
|
19
tcp-accept.h
Normal file
19
tcp-accept.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef _REMOTETCPFWD_H
|
||||
#define _REMOTETCPFWD_H
|
||||
|
||||
struct TCPListener {
|
||||
|
||||
/* Local ones */
|
||||
unsigned char *localaddr; /* Can be NULL */
|
||||
unsigned int localport;
|
||||
/* Remote ones: */
|
||||
unsigned char *remoteaddr;
|
||||
unsigned int remoteport;
|
||||
const struct ChanType *chantype;
|
||||
|
||||
};
|
||||
|
||||
void recv_msg_global_request_remotetcp();
|
||||
int listen_tcpfwd(struct TCPListener* tcpinfo);
|
||||
|
||||
#endif /* _REMOTETCPFWD_H */
|
75
tcp-connect.c
Normal file
75
tcp-connect.c
Normal file
@ -0,0 +1,75 @@
|
||||
#include "includes.h"
|
||||
#include "session.h"
|
||||
#include "dbutil.h"
|
||||
#include "channel.h"
|
||||
#include "tcp-connect.h"
|
||||
#include "runopts.h"
|
||||
|
||||
#ifndef DISABLE_TCP_CONNECT
|
||||
|
||||
/* Called upon creating a new direct tcp channel (ie we connect out to an
|
||||
* address */
|
||||
int newtcpdirect(struct Channel * channel) {
|
||||
|
||||
unsigned char* desthost = NULL;
|
||||
unsigned int destport;
|
||||
unsigned char* orighost = NULL;
|
||||
unsigned int origport;
|
||||
char portstring[6];
|
||||
int sock;
|
||||
int len;
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
|
||||
if (opts.nolocaltcp) {
|
||||
TRACE(("leave newtcpdirect: local tcp forwarding disabled"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
desthost = buf_getstring(ses.payload, &len);
|
||||
if (len > MAX_HOST_LEN) {
|
||||
TRACE(("leave newtcpdirect: desthost too long"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
destport = buf_getint(ses.payload);
|
||||
|
||||
orighost = buf_getstring(ses.payload, &len);
|
||||
if (len > MAX_HOST_LEN) {
|
||||
TRACE(("leave newtcpdirect: orighost too long"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
origport = buf_getint(ses.payload);
|
||||
|
||||
/* best be sure */
|
||||
if (origport > 65535 || destport > 65535) {
|
||||
TRACE(("leave newtcpdirect: port > 65535"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
snprintf(portstring, sizeof(portstring), "%d", destport);
|
||||
sock = connect_remote(desthost, portstring, 1, NULL);
|
||||
if (sock < 0) {
|
||||
TRACE(("leave newtcpdirect: sock failed"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
ses.maxfd = MAX(ses.maxfd, sock);
|
||||
|
||||
/* Note that infd is actually the "outgoing" direction on the
|
||||
* tcp connection, vice versa for outfd.
|
||||
* We don't set outfd, that will get set after the connection's
|
||||
* progress succeeds */
|
||||
channel->infd = sock;
|
||||
channel->initconn = 1;
|
||||
|
||||
ret = DROPBEAR_SUCCESS;
|
||||
|
||||
out:
|
||||
m_free(desthost);
|
||||
m_free(orighost);
|
||||
TRACE(("leave newtcpdirect: ret %d", ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* DISABLE_TCPFWD_DIRECT */
|
@ -28,7 +28,8 @@
|
||||
#include "includes.h"
|
||||
#include "channel.h"
|
||||
|
||||
extern const struct ChanType chan_tcpdirect;
|
||||
extern const struct ChanType svr_chan_tcpdirect;
|
||||
int newtcpdirect(struct Channel * channel);
|
||||
|
||||
#endif
|
||||
#endif
|
159
tcpfwd-direct.c
159
tcpfwd-direct.c
@ -1,159 +0,0 @@
|
||||
#include "includes.h"
|
||||
#include "session.h"
|
||||
#include "dbutil.h"
|
||||
#include "channel.h"
|
||||
#include "tcpfwd-direct.h"
|
||||
#include "runopts.h"
|
||||
|
||||
#ifndef DISABLE_TCPFWD_DIRECT
|
||||
static int newtcpdirect(struct Channel * channel);
|
||||
static int newtcp(const char * host, int port);
|
||||
|
||||
const struct ChanType chan_tcpdirect = {
|
||||
1, /* sepfds */
|
||||
"direct-tcpip",
|
||||
newtcpdirect, /* init */
|
||||
NULL, /* checkclose */
|
||||
NULL, /* reqhandler */
|
||||
NULL /* closehandler */
|
||||
};
|
||||
|
||||
|
||||
/* Called upon creating a new direct tcp channel (ie we connect out to an
|
||||
* address */
|
||||
static int newtcpdirect(struct Channel * channel) {
|
||||
|
||||
unsigned char* desthost = NULL;
|
||||
unsigned int destport;
|
||||
unsigned char* orighost = NULL;
|
||||
unsigned int origport;
|
||||
char portstring[6];
|
||||
int sock;
|
||||
int len;
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
|
||||
if (opts.nolocaltcp) {
|
||||
TRACE(("leave newtcpdirect: local tcp forwarding disabled"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
desthost = buf_getstring(ses.payload, &len);
|
||||
if (len > MAX_HOST_LEN) {
|
||||
TRACE(("leave newtcpdirect: desthost too long"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
destport = buf_getint(ses.payload);
|
||||
|
||||
orighost = buf_getstring(ses.payload, &len);
|
||||
if (len > MAX_HOST_LEN) {
|
||||
TRACE(("leave newtcpdirect: orighost too long"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
origport = buf_getint(ses.payload);
|
||||
|
||||
/* best be sure */
|
||||
if (origport > 65535 || destport > 65535) {
|
||||
TRACE(("leave newtcpdirect: port > 65535"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
snprintf(portstring, sizeof(portstring), "%d", destport);
|
||||
sock = connect_remote(desthost, portstring, 1, NULL);
|
||||
if (sock < 0) {
|
||||
TRACE(("leave newtcpdirect: sock failed"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
ses.maxfd = MAX(ses.maxfd, sock);
|
||||
|
||||
/* Note that infd is actually the "outgoing" direction on the
|
||||
* tcp connection, vice versa for outfd.
|
||||
* We don't set outfd, that will get set after the connection's
|
||||
* progress succeeds */
|
||||
channel->infd = sock;
|
||||
channel->initconn = 1;
|
||||
|
||||
ret = DROPBEAR_SUCCESS;
|
||||
|
||||
out:
|
||||
m_free(desthost);
|
||||
m_free(orighost);
|
||||
TRACE(("leave newtcpdirect: ret %d", ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initiate a new TCP connection - this is non-blocking, so the socket
|
||||
* returned will need to be checked for success when it is first written.
|
||||
* Similarities with OpenSSH's connect_to() are not coincidental.
|
||||
* Returns -1 on failure */
|
||||
#if 0
|
||||
static int newtcp(const char * host, int port) {
|
||||
|
||||
int sock = -1;
|
||||
char portstring[6];
|
||||
struct addrinfo *res = NULL, *ai;
|
||||
int val;
|
||||
|
||||
struct addrinfo hints;
|
||||
|
||||
TRACE(("enter newtcp"));
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
/* TCP, either ip4 or ip6 */
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
|
||||
snprintf(portstring, sizeof(portstring), "%d", port);
|
||||
if (getaddrinfo(host, portstring, &hints, &res) != 0) {
|
||||
if (res) {
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
TRACE(("leave newtcp: failed getaddrinfo"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Use the first socket that works */
|
||||
for (ai = res; ai != NULL; ai = ai->ai_next) {
|
||||
|
||||
if (ai->ai_family != PF_INET && ai->ai_family != PF_INET6) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sock = socket(ai->ai_family, SOCK_STREAM, 0);
|
||||
if (sock < 0) {
|
||||
TRACE(("TCP socket() failed"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
|
||||
close(sock);
|
||||
TRACE(("TCP non-blocking failed"));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* non-blocking, so it might return without success (EINPROGRESS) */
|
||||
if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
|
||||
if (errno == EINPROGRESS) {
|
||||
TRACE(("connect in progress"));
|
||||
} else {
|
||||
close(sock);
|
||||
TRACE(("TCP connect failed"));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
|
||||
if (ai == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val));
|
||||
return sock;
|
||||
}
|
||||
#endif
|
||||
#endif /* DISABLE_TCPFWD_DIRECT */
|
317
tcpfwd-remote.c
317
tcpfwd-remote.c
@ -1,317 +0,0 @@
|
||||
#include "includes.h"
|
||||
#include "ssh.h"
|
||||
#include "tcpfwd-remote.h"
|
||||
#include "dbutil.h"
|
||||
#include "session.h"
|
||||
#include "buffer.h"
|
||||
#include "packet.h"
|
||||
#include "listener.h"
|
||||
#include "runopts.h"
|
||||
|
||||
#ifndef DISABLE_REMOTETCPFWD
|
||||
|
||||
struct RemoteTCP {
|
||||
|
||||
unsigned char* addr;
|
||||
unsigned int port;
|
||||
|
||||
};
|
||||
|
||||
static void send_msg_request_success();
|
||||
static void send_msg_request_failure();
|
||||
static int cancelremotetcp();
|
||||
static int remotetcpreq();
|
||||
static int listen_tcpfwd(unsigned char* bindaddr, unsigned int port);
|
||||
static void acceptremote(struct Listener *listener);
|
||||
|
||||
/* At the moment this is completely used for tcp code (with the name reflecting
|
||||
* that). If new request types are added, this should be replaced with code
|
||||
* similar to the request-switching in chansession.c */
|
||||
void recv_msg_global_request_remotetcp() {
|
||||
|
||||
unsigned char* reqname = NULL;
|
||||
unsigned int namelen;
|
||||
unsigned int wantreply = 0;
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
|
||||
TRACE(("enter recv_msg_global_request_remotetcp"));
|
||||
|
||||
if (opts.noremotetcp) {
|
||||
TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
reqname = buf_getstring(ses.payload, &namelen);
|
||||
wantreply = buf_getbyte(ses.payload);
|
||||
|
||||
if (namelen > MAXNAMLEN) {
|
||||
TRACE(("name len is wrong: %d", namelen));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strcmp("tcpip-forward", reqname) == 0) {
|
||||
ret = remotetcpreq();
|
||||
} else if (strcmp("cancel-tcpip-forward", reqname) == 0) {
|
||||
ret = cancelremotetcp();
|
||||
} else {
|
||||
TRACE(("reqname isn't tcpip-forward: '%s'", reqname));
|
||||
}
|
||||
|
||||
out:
|
||||
if (wantreply) {
|
||||
if (ret == DROPBEAR_SUCCESS) {
|
||||
send_msg_request_success();
|
||||
} else {
|
||||
send_msg_request_failure();
|
||||
}
|
||||
}
|
||||
|
||||
m_free(reqname);
|
||||
|
||||
TRACE(("leave recv_msg_global_request"));
|
||||
}
|
||||
|
||||
static const struct ChanType chan_tcpremote = {
|
||||
1, /* sepfds */
|
||||
"forwarded-tcpip",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
static void acceptremote(struct Listener *listener) {
|
||||
|
||||
int fd;
|
||||
struct sockaddr addr;
|
||||
int len;
|
||||
char ipstring[NI_MAXHOST], portstring[NI_MAXSERV];
|
||||
struct RemoteTCP *tcpinfo = (struct RemoteTCP*)(listener->typedata);
|
||||
|
||||
len = sizeof(addr);
|
||||
|
||||
fd = accept(listener->sock, &addr, &len);
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (getnameinfo(&addr, len, ipstring, sizeof(ipstring), portstring,
|
||||
sizeof(portstring), NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (send_msg_channel_open_init(fd, &chan_tcpremote) == DROPBEAR_SUCCESS) {
|
||||
|
||||
buf_putstring(ses.writepayload, tcpinfo->addr,
|
||||
strlen(tcpinfo->addr));
|
||||
buf_putint(ses.writepayload, tcpinfo->port);
|
||||
buf_putstring(ses.writepayload, ipstring, strlen(ipstring));
|
||||
buf_putint(ses.writepayload, atol(portstring));
|
||||
encrypt_packet();
|
||||
|
||||
} else {
|
||||
/* XXX debug? */
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static void cleanupremote(struct Listener *listener) {
|
||||
|
||||
struct RemoteTCP *tcpinfo = (struct RemoteTCP*)(listener->typedata);
|
||||
|
||||
m_free(tcpinfo->addr);
|
||||
m_free(tcpinfo);
|
||||
}
|
||||
|
||||
static void send_msg_request_success() {
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS);
|
||||
encrypt_packet();
|
||||
|
||||
}
|
||||
|
||||
static void send_msg_request_failure() {
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE);
|
||||
encrypt_packet();
|
||||
|
||||
}
|
||||
|
||||
static int matchtcp(void* typedata1, void* typedata2) {
|
||||
|
||||
const struct RemoteTCP *info1 = (struct RemoteTCP*)typedata1;
|
||||
const struct RemoteTCP *info2 = (struct RemoteTCP*)typedata2;
|
||||
|
||||
return info1->port == info2->port
|
||||
&& (strcmp(info1->addr, info2->addr) == 0);
|
||||
}
|
||||
|
||||
static int cancelremotetcp() {
|
||||
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
unsigned char * bindaddr = NULL;
|
||||
unsigned int addrlen;
|
||||
unsigned int port;
|
||||
struct Listener * listener = NULL;
|
||||
struct RemoteTCP tcpinfo;
|
||||
|
||||
TRACE(("enter cancelremotetcp"));
|
||||
|
||||
bindaddr = buf_getstring(ses.payload, &addrlen);
|
||||
if (addrlen > MAX_IP_LEN) {
|
||||
TRACE(("addr len too long: %d", addrlen));
|
||||
goto out;
|
||||
}
|
||||
|
||||
port = buf_getint(ses.payload);
|
||||
|
||||
tcpinfo.addr = bindaddr;
|
||||
tcpinfo.port = port;
|
||||
listener = get_listener(CHANNEL_ID_TCPFORWARDED, &tcpinfo, matchtcp);
|
||||
if (listener) {
|
||||
remove_listener( listener );
|
||||
ret = DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
out:
|
||||
m_free(bindaddr);
|
||||
TRACE(("leave cancelremotetcp"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int remotetcpreq() {
|
||||
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
unsigned char * bindaddr = NULL;
|
||||
unsigned int addrlen;
|
||||
unsigned int port;
|
||||
|
||||
TRACE(("enter remotetcpreq"));
|
||||
|
||||
bindaddr = buf_getstring(ses.payload, &addrlen);
|
||||
if (addrlen > MAX_IP_LEN) {
|
||||
TRACE(("addr len too long: %d", addrlen));
|
||||
goto out;
|
||||
}
|
||||
|
||||
port = buf_getint(ses.payload);
|
||||
|
||||
if (port == 0) {
|
||||
dropbear_log(LOG_INFO, "Server chosen tcpfwd ports are unsupported");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (port < 1 || port > 65535) {
|
||||
TRACE(("invalid port: %d", port));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ses.allowprivport && port < IPPORT_RESERVED) {
|
||||
TRACE(("can't assign port < 1024 for non-root"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = listen_tcpfwd(bindaddr, port);
|
||||
|
||||
out:
|
||||
if (ret == DROPBEAR_FAILURE) {
|
||||
/* we only free it if a listener wasn't created, since the listener
|
||||
* has to remember it if it's to be cancelled */
|
||||
m_free(bindaddr);
|
||||
}
|
||||
TRACE(("leave remotetcpreq"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int listen_tcpfwd(unsigned char* bindaddr, unsigned int port) {
|
||||
|
||||
struct RemoteTCP * tcpinfo = NULL;
|
||||
char portstring[6]; /* "65535\0" */
|
||||
struct addrinfo *res = NULL, *ai = NULL;
|
||||
struct addrinfo hints;
|
||||
int sock = -1;
|
||||
struct Listener *listener = NULL;
|
||||
|
||||
TRACE(("enter listen_tcpfwd"));
|
||||
|
||||
/* first we try to bind, so don't need to do so much cleanup on failure */
|
||||
snprintf(portstring, sizeof(portstring), "%d", port);
|
||||
memset(&hints, 0x0, sizeof(hints));
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_family = PF_INET;
|
||||
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
|
||||
|
||||
if (getaddrinfo(bindaddr, portstring, &hints, &res) < 0) {
|
||||
TRACE(("leave listen_tcpfwd: getaddrinfo failed: %s",
|
||||
strerror(errno)));
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* find the first one which works */
|
||||
for (ai = res; ai != NULL; ai = ai->ai_next) {
|
||||
if (ai->ai_family != PF_INET && ai->ai_family != PF_INET6) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sock = socket(ai->ai_family, SOCK_STREAM, 0);
|
||||
if (sock < 0) {
|
||||
TRACE(("socket failed: %s", strerror(errno)));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
|
||||
TRACE(("bind failed: %s", strerror(errno)));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (listen(sock, 20) < 0) {
|
||||
TRACE(("listen failed: %s", strerror(errno)));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
|
||||
TRACE(("fcntl nonblocking failed: %s", strerror(errno)));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* success */
|
||||
break;
|
||||
|
||||
fail:
|
||||
close(sock);
|
||||
}
|
||||
|
||||
|
||||
if (ai == NULL) {
|
||||
TRACE(("no successful sockets"));
|
||||
goto done;
|
||||
}
|
||||
|
||||
tcpinfo = (struct RemoteTCP*)m_malloc(sizeof(struct RemoteTCP));
|
||||
tcpinfo->addr = bindaddr;
|
||||
tcpinfo->port = port;
|
||||
|
||||
listener = new_listener(sock, CHANNEL_ID_TCPFORWARDED, tcpinfo,
|
||||
acceptremote, cleanupremote);
|
||||
|
||||
if (listener == NULL) {
|
||||
m_free(tcpinfo);
|
||||
}
|
||||
|
||||
done:
|
||||
if (res) {
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
|
||||
TRACE(("leave listen_tcpfwd"));
|
||||
if (listener == NULL) {
|
||||
return DROPBEAR_FAILURE;
|
||||
} else {
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* DISABLE_REMOTETCPFWD */
|
@ -1,6 +0,0 @@
|
||||
#ifndef _REMOTETCPFWD_H
|
||||
#define _REMOTETCPFWD_H
|
||||
|
||||
void recv_msg_global_request_remotetcp();
|
||||
|
||||
#endif /* _REMOTETCPFWD_H */
|
Loading…
Reference in New Issue
Block a user