Add remote tcp/ip forwarding.
Этот коммит содержится в:
родитель
596b62c027
Коммит
2e02ad041a
2
README
2
README
@ -8,6 +8,8 @@ Version 0.5
|
||||
|
||||
Added DESTDIR support to makefiles (Adam Go│ъbiowski -- I hope that character set translates right)
|
||||
|
||||
Added libssh2_channel_forward_listen_ex(), libssh2_channel_forward_cancel(), and libssh2_channel_forward_accept().
|
||||
|
||||
Version 0.4
|
||||
-----------
|
||||
|
||||
|
@ -159,6 +159,7 @@
|
||||
|
||||
typedef struct _LIBSSH2_SESSION LIBSSH2_SESSION;
|
||||
typedef struct _LIBSSH2_CHANNEL LIBSSH2_CHANNEL;
|
||||
typedef struct _LIBSSH2_LISTENER LIBSSH2_LISTENER;
|
||||
|
||||
#ifdef WIN_32
|
||||
#define LIBSSH2_API __declspec(dllexport)
|
||||
@ -218,6 +219,7 @@ typedef struct _LIBSSH2_CHANNEL LIBSSH2_CHANNEL;
|
||||
#define LIBSSH2_ERROR_ZLIB -29
|
||||
#define LIBSSH2_ERROR_SOCKET_TIMEOUT -30
|
||||
#define LIBSSH2_ERROR_SFTP_PROTOCOL -31
|
||||
#define LIBSSH2_ERROR_REQUEST_DENIED -32
|
||||
|
||||
/* Session API */
|
||||
LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)), LIBSSH2_FREE_FUNC((*my_free)), LIBSSH2_REALLOC_FUNC((*my_realloc)), void *abstract);
|
||||
@ -268,9 +270,17 @@ LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session,
|
||||
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, char *channel_type, int channel_type_len, int window_size, int packet_size, char *message, int message_len);
|
||||
#define libssh2_channel_open_session(session) libssh2_channel_open_ex((session), "session", sizeof("session") - 1, LIBSSH2_CHANNEL_WINDOW_DEFAULT, LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0)
|
||||
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *session, char *host, int port, char *shost, int sport);
|
||||
#define libssh2_channel_direct_tcpip(session, host, port) libssh2_channel_direct_tcpip_ex((session), (host), (port), "127.0.0.1", 22)
|
||||
|
||||
LIBSSH2_API LIBSSH2_LISTENER *libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, char *host, int port, int *bound_port, int queue_maxsize);
|
||||
#define libssh2_channel_forward_listen(session, port) libssh2_channel_forward_listen_ex((session), NULL, (port), NULL, 16)
|
||||
|
||||
LIBSSH2_API int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener);
|
||||
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_forward_accept(LIBSSH2_LISTENER *listener);
|
||||
|
||||
LIBSSH2_API int libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, char *varname, int varname_len, char *value, int value_len);
|
||||
#define libssh2_channel_setenv(channel, varname, value) libssh2_channel_setenv_ex((channel), (varname), strlen(varname), (value), strlen(value))
|
||||
|
||||
|
@ -123,6 +123,19 @@ struct _LIBSSH2_CHANNEL_BRIGADE {
|
||||
LIBSSH2_CHANNEL *head, *tail;
|
||||
};
|
||||
|
||||
struct _LIBSSH2_LISTENER {
|
||||
LIBSSH2_SESSION *session;
|
||||
|
||||
char *host;
|
||||
int port;
|
||||
|
||||
LIBSSH2_CHANNEL *queue;
|
||||
int queue_size;
|
||||
int queue_maxsize;
|
||||
|
||||
LIBSSH2_LISTENER *prev, *next;
|
||||
};
|
||||
|
||||
typedef struct _libssh2_endpoint_data {
|
||||
unsigned char *banner;
|
||||
|
||||
@ -202,6 +215,8 @@ struct _LIBSSH2_SESSION {
|
||||
LIBSSH2_CHANNEL_BRIGADE channels;
|
||||
unsigned long next_channel;
|
||||
|
||||
LIBSSH2_LISTENER *listeners;
|
||||
|
||||
/* Actual I/O socket */
|
||||
int socket_fd;
|
||||
int socket_block;
|
||||
@ -373,6 +388,7 @@ int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_typ
|
||||
libssh2_packet_require_ex((session), (packet_type), (data), (data_len), 0, NULL, 0)
|
||||
int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned long data_len);
|
||||
int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange);
|
||||
unsigned long libssh2_channel_nextid(LIBSSH2_SESSION *session);
|
||||
LIBSSH2_CHANNEL *libssh2_channel_locate(LIBSSH2_SESSION *session, unsigned long channel_id);
|
||||
|
||||
/* Let crypt.c/hostkey.c/comp.c/mac.c expose their method structs */
|
||||
|
187
src/channel.c
187
src/channel.c
@ -41,7 +41,7 @@
|
||||
/* {{{ libssh2_channel_nextid
|
||||
* Determine the next channel ID we can use at our end
|
||||
*/
|
||||
static unsigned long libssh2_channel_nextid(LIBSSH2_SESSION *session)
|
||||
unsigned long libssh2_channel_nextid(LIBSSH2_SESSION *session)
|
||||
{
|
||||
unsigned long id = session->next_channel;
|
||||
LIBSSH2_CHANNEL *channel;
|
||||
@ -257,6 +257,191 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *se
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_channel_forward_listen_ex
|
||||
* Bind a port on the remote host and listen for connections
|
||||
*/
|
||||
LIBSSH2_API LIBSSH2_LISTENER *libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, char *host, int port, int *bound_port, int queue_maxsize)
|
||||
{
|
||||
unsigned char *packet, *s;
|
||||
unsigned long host_len = (host ? strlen(host) : (sizeof("0.0.0.0") - 1));
|
||||
unsigned long packet_len = host_len + (sizeof("tcpip-forward") - 1) + 14;
|
||||
/* packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */
|
||||
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*(s++) = SSH_MSG_GLOBAL_REQUEST;
|
||||
libssh2_htonu32(s, sizeof("tcpip-forward") - 1); s += 4;
|
||||
memcpy(s, "tcpip-forward", sizeof("tcpip-forward") - 1); s += sizeof("tcpip-forward") - 1;
|
||||
*(s++) = 0xFF; /* want_reply */
|
||||
|
||||
libssh2_htonu32(s, host_len); s += 4;
|
||||
memcpy(s, host ? host : "0.0.0.0", host_len); s += host_len;
|
||||
libssh2_htonu32(s, port); s += 4;
|
||||
|
||||
if (libssh2_packet_write(session, packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send global-request packet for forward listen request", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return NULL;
|
||||
}
|
||||
LIBSSH2_FREE(session, packet);
|
||||
|
||||
while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
|
||||
unsigned char *data;
|
||||
unsigned long data_len;
|
||||
|
||||
if (libssh2_packet_ask(session, SSH_MSG_REQUEST_SUCCESS, &data, &data_len, 1) == 0) {
|
||||
LIBSSH2_LISTENER *listener;
|
||||
|
||||
listener = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_LISTENER));
|
||||
if (!listener) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for listener queue", 0);
|
||||
LIBSSH2_FREE(session, data);
|
||||
return NULL;
|
||||
}
|
||||
memset(listener, 0, sizeof(LIBSSH2_LISTENER));
|
||||
listener->session = session;
|
||||
listener->host = LIBSSH2_ALLOC(session, host_len + 1);
|
||||
if (!listener->host) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for listener queue", 0);
|
||||
LIBSSH2_FREE(session, listener);
|
||||
LIBSSH2_FREE(session, data);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(listener->host, host ? host : "0.0.0.0", host_len);
|
||||
listener->host[host_len] = 0;
|
||||
if (data_len >= 5 && !port) {
|
||||
listener->port = libssh2_ntohu32(data + 1);
|
||||
} else {
|
||||
listener->port = port;
|
||||
}
|
||||
|
||||
listener->queue_size = 0;
|
||||
listener->queue_maxsize = queue_maxsize;
|
||||
|
||||
listener->next = session->listeners;
|
||||
listener->prev = NULL;
|
||||
if (session->listeners) {
|
||||
session->listeners->prev = listener;
|
||||
}
|
||||
session->listeners = listener;
|
||||
|
||||
if (bound_port) {
|
||||
*bound_port = listener->port;
|
||||
}
|
||||
|
||||
LIBSSH2_FREE(session, data);
|
||||
return listener;
|
||||
}
|
||||
|
||||
if (libssh2_packet_ask(session, SSH_MSG_REQUEST_FAILURE, &data, &data_len, 0) == 0) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
libssh2_error(session, LIBSSH2_ERROR_REQUEST_DENIED, "Unable to complete request for forward-listen", 0);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_channel_forward_cancel
|
||||
* Stop listening on a remote port and free the listener
|
||||
* Toss out any pending (un-accept()ed) connections
|
||||
*/
|
||||
LIBSSH2_API int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener)
|
||||
{
|
||||
LIBSSH2_SESSION *session = listener->session;
|
||||
LIBSSH2_CHANNEL *queued = listener->queue;
|
||||
unsigned char *packet, *s;
|
||||
unsigned long host_len = strlen(listener->host);
|
||||
unsigned long packet_len = host_len + 14 + sizeof("cancel-tcpip-forward") - 1;
|
||||
/* packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */
|
||||
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*(s++) = SSH_MSG_GLOBAL_REQUEST;
|
||||
libssh2_htonu32(s, sizeof("cancel-tcpip-forward") - 1); s += 4;
|
||||
memcpy(s, "cancel-tcpip-forward", sizeof("cancel-tcpip-forward") - 1); s += sizeof("cancel-tcpip-forward") - 1;
|
||||
*(s++) = 0x00; /* want_reply */
|
||||
|
||||
libssh2_htonu32(s, host_len); s += 4;
|
||||
memcpy(s, listener->host, host_len); s += host_len;
|
||||
libssh2_htonu32(s, listener->port); s += 4;
|
||||
|
||||
if (libssh2_packet_write(session, packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send global-request packet for forward listen request", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
}
|
||||
LIBSSH2_FREE(session, packet);
|
||||
|
||||
while (queued) {
|
||||
LIBSSH2_CHANNEL *next = queued->next;
|
||||
|
||||
libssh2_channel_free(queued);
|
||||
queued = next;
|
||||
}
|
||||
LIBSSH2_FREE(session, listener->host);
|
||||
|
||||
if (listener->next) {
|
||||
listener->next->prev = listener->prev;
|
||||
}
|
||||
if (listener->prev) {
|
||||
listener->prev->next = listener->next;
|
||||
} else {
|
||||
session->listeners = listener->next;
|
||||
}
|
||||
|
||||
LIBSSH2_FREE(session, listener);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_channel_forward_accept
|
||||
* Accept a connection
|
||||
*/
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_forward_accept(LIBSSH2_LISTENER *listener)
|
||||
{
|
||||
while (libssh2_packet_read(listener->session, 0) > 0);
|
||||
|
||||
if (listener->queue) {
|
||||
LIBSSH2_SESSION *session = listener->session;
|
||||
LIBSSH2_CHANNEL *channel;
|
||||
|
||||
channel = listener->queue;
|
||||
|
||||
listener->queue = listener->queue->next;
|
||||
if (listener->queue) {
|
||||
listener->queue->prev = NULL;
|
||||
}
|
||||
|
||||
channel->prev = NULL;
|
||||
channel->next = session->channels.head;
|
||||
session->channels.head = channel;
|
||||
|
||||
if (channel->next) {
|
||||
channel->next->prev = channel;
|
||||
} else {
|
||||
session->channels.tail = channel;
|
||||
}
|
||||
listener->queue_size--;
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_channel_setenv_ex
|
||||
* Set an environment variable prior to requesting a shell/program/subsystem
|
||||
*/
|
||||
|
136
src/packet.c
136
src/packet.c
@ -42,6 +42,132 @@
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
|
||||
/* {{{ libssh2_packet_queue_listener
|
||||
* Queue a connection request for a listener
|
||||
*/
|
||||
inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char *data, unsigned long datalen)
|
||||
{
|
||||
/* Look for a matching listener */
|
||||
unsigned char *s = data + (sizeof("forwarded-tcpip") - 1) + 5;
|
||||
unsigned long packet_len = 17 + (sizeof("Forward not requested") - 1);
|
||||
unsigned char *p, packet[17 + (sizeof("Forward not requested") - 1)];
|
||||
/* packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */
|
||||
LIBSSH2_LISTENER *l = session->listeners;
|
||||
char failure_code = 1; /* SSH_OPEN_ADMINISTRATIVELY_PROHIBITED */
|
||||
unsigned long sender_channel, initial_window_size, packet_size;
|
||||
unsigned char *host, *shost;
|
||||
unsigned long port, sport, host_len, shost_len;
|
||||
|
||||
sender_channel = libssh2_ntohu32(s); s += 4;
|
||||
|
||||
initial_window_size = libssh2_ntohu32(s); s += 4;
|
||||
packet_size = libssh2_ntohu32(s); s += 4;
|
||||
|
||||
host_len = libssh2_ntohu32(s); s += 4;
|
||||
host = s; s += host_len;
|
||||
port = libssh2_ntohu32(s); s += 4;
|
||||
|
||||
shost_len = libssh2_ntohu32(s); s += 4;
|
||||
shost = s; s += shost_len;
|
||||
sport = libssh2_ntohu32(s); s += 4;
|
||||
|
||||
while (l) {
|
||||
if ((l->port == port) &&
|
||||
(strlen(l->host) == host_len) &&
|
||||
(memcmp(l->host, host, host_len) == 0)) {
|
||||
/* This is our listener */
|
||||
LIBSSH2_CHANNEL *channel, *last_queued = l->queue;
|
||||
|
||||
if (l->queue_maxsize &&
|
||||
(l->queue_maxsize <= l->queue_size)) {
|
||||
/* Queue is full */
|
||||
failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */
|
||||
break;
|
||||
}
|
||||
|
||||
channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL));
|
||||
if (!channel) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0);
|
||||
failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */
|
||||
break;
|
||||
}
|
||||
memset(channel, 0, sizeof(LIBSSH2_CHANNEL));
|
||||
|
||||
channel->session = session;
|
||||
channel->channel_type_len = sizeof("forwarded-tcpip") - 1;
|
||||
channel->channel_type = LIBSSH2_ALLOC(session, channel->channel_type_len + 1);
|
||||
if (!channel) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0);
|
||||
LIBSSH2_FREE(session, channel);
|
||||
failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */
|
||||
break;
|
||||
}
|
||||
memcpy(channel->channel_type, "forwarded-tcpip", channel->channel_type_len + 1);
|
||||
|
||||
channel->remote.id = sender_channel;
|
||||
channel->remote.window_size_initial = LIBSSH2_CHANNEL_WINDOW_DEFAULT;
|
||||
channel->remote.window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT;
|
||||
channel->remote.packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT;
|
||||
|
||||
channel->local.id = libssh2_channel_nextid(session);
|
||||
channel->local.window_size_initial = initial_window_size;
|
||||
channel->local.window_size = initial_window_size;
|
||||
channel->local.packet_size = packet_size;
|
||||
|
||||
p = packet;
|
||||
*(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION;
|
||||
libssh2_htonu32(p, channel->remote.id); p += 4;
|
||||
libssh2_htonu32(p, channel->local.id); p += 4;
|
||||
libssh2_htonu32(p, channel->remote.window_size_initial); p += 4;
|
||||
libssh2_htonu32(p, channel->remote.packet_size); p += 4;
|
||||
|
||||
if (libssh2_packet_write(session, packet, 17)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel open confirmation", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Link the channel into the end of the queue list */
|
||||
|
||||
if (!last_queued) {
|
||||
l->queue = channel;
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (last_queued->next) last_queued = last_queued->next;
|
||||
|
||||
last_queued->next = channel;
|
||||
channel->prev = last_queued;
|
||||
|
||||
l->queue_size++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
l = l->next;
|
||||
}
|
||||
|
||||
/* We're not listening to you */
|
||||
{
|
||||
|
||||
p = packet;
|
||||
*(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE;
|
||||
libssh2_htonu32(p, sender_channel); p += 4;
|
||||
libssh2_htonu32(p, failure_code); p += 4;
|
||||
libssh2_htonu32(p, sizeof("Forward not requested") - 1); p += 4;
|
||||
memcpy(s, "Forward not requested", sizeof("Forward not requested") - 1); p += sizeof("Forward not requested") - 1;
|
||||
libssh2_htonu32(p, 0);
|
||||
|
||||
if (libssh2_packet_write(session, packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send open failure", 0);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
/* {{{ libssh2_packet_new
|
||||
* Create a new packet and attach it to the brigade
|
||||
*/
|
||||
@ -230,6 +356,16 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case SSH_MSG_CHANNEL_OPEN:
|
||||
if ((datalen >= (sizeof("forwarded-tcpip") + 4)) &&
|
||||
((sizeof("forwarded-tcpip")-1) == libssh2_ntohu32(data + 1)) &&
|
||||
(memcmp(data + 5, "forwarded-tcpip", sizeof("forwarded-tcpip") - 1) == 0)) {
|
||||
int retval = libssh2_packet_queue_listener(session, data, datalen);
|
||||
|
||||
LIBSSH2_FREE(session, data);
|
||||
return retval;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET));
|
||||
|
@ -315,6 +315,10 @@ LIBSSH2_API void libssh2_session_free(LIBSSH2_SESSION *session)
|
||||
}
|
||||
}
|
||||
|
||||
while (session->listeners) {
|
||||
libssh2_channel_forward_cancel(session->listeners);
|
||||
}
|
||||
|
||||
if (session->newkeys) {
|
||||
/* hostkey */
|
||||
if (session->hostkey && session->hostkey->dtor) {
|
||||
|
Загрузка…
Ссылка в новой задаче
Block a user