just checkpointing

--HG--
extra : convert_revision : fbbf404290f3fea3dfa9f6f53eba9389057e9044
This commit is contained in:
Matt Johnston 2004-08-10 17:09:52 +00:00
parent 254e8e3452
commit a712baa8e5
24 changed files with 675 additions and 653 deletions

View File

@ -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)

View File

@ -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 {

View File

@ -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;

View File

@ -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
View 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;
}

View File

@ -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
View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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

View File

@ -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;

View File

@ -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");
}

View File

@ -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;
}

View File

@ -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
View 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

View File

@ -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
View 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
View 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
View 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 */

View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -1,6 +0,0 @@
#ifndef _REMOTETCPFWD_H
#define _REMOTETCPFWD_H
void recv_msg_global_request_remotetcp();
#endif /* _REMOTETCPFWD_H */