1
0
mirror of https://github.com/libssh2/libssh2.git synced 2025-11-20 02:42:09 +03:00
Files
libssh2/example/tcpip-forward.c
Viktor Szakats ec0feae792 build: speed up and extend picky compiler options
Implement picky warnings with clang in autotools. Extend picky gcc
warnings, sync them between build tools and compilers and greatly
speed up detection in CMake.

- autotools: enable clang compiler warnings with `--enable-debug`.

- autotools: enable more gcc compiler warnings with `--enable-debug`.

- autotools/cmake: sync compiler warning options between gcc and clang.

- sync compiler warning options between autotools and cmake.

- cmake: reduce option-checks to speed up the detection phase.
  Bring them down to 3 (from 35). Leaving some checks to keep the
  CMake logic alive and for an easy way to add new options.

  clang 3.0 (2011-11-29) and gcc 2.95 (1999-07-31) now required.

- autotools logic copied from curl, with these differences:

  - delete `-Wimplicit-fallthrough=4` due to a false positive.

  - reduce `-Wformat-truncation=2` to `1` due to a false positive.

  - simplify MinGW detection for `-Wno-pedantic-ms-format`.

- cmake: show enabled picky compiler options (like autotools).

- cmake: do compile `tests/simple.c` and `tests/ssh2.c`.

- fix new compiler warnings.

- `tests/CMakeLists.txt`: fix indentation.

Original source of autotools logic:
- a8fbdb461c/acinclude.m4
- a8fbdb461c/m4/curl-compilers.m4

Notice that the autotools implementation considers Apple clang as
legacy clang 3.7. CMake detection works more accurately, at the same
time more error-prone and difficult to update due to the sparsely
documented nature of Apple clang option evolution.

Closes #952
2023-04-13 11:12:22 +00:00

327 lines
9.6 KiB
C

#include "libssh2_setup.h"
#include <libssh2.h>
#ifdef WIN32
#include <ws2tcpip.h> /* for socklen_t */
#define recv(s, b, l, f) recv((s), (b), (int)(l), (f))
#define send(s, b, l, f) send((s), (b), (int)(l), (f))
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef INADDR_NONE
#define INADDR_NONE (in_addr_t)~0
#endif
static const char *pubkey = "/home/username/.ssh/id_rsa.pub";
static const char *privkey = "/home/username/.ssh/id_rsa";
static const char *username = "username";
static const char *password = "";
static const char *server_ip = "127.0.0.1";
/* resolved by the server */
static const char *remote_listenhost = "localhost";
static int remote_wantport = 2222;
static int remote_listenport;
static const char *local_destip = "127.0.0.1";
static int local_destport = 22;
enum {
AUTH_NONE = 0,
AUTH_PASSWORD,
AUTH_PUBLICKEY
};
int main(int argc, char *argv[])
{
int rc, i, auth = AUTH_NONE;
struct sockaddr_in sin;
socklen_t sinlen = sizeof(sin);
const char *fingerprint;
char *userauthlist;
LIBSSH2_SESSION *session;
LIBSSH2_LISTENER *listener = NULL;
LIBSSH2_CHANNEL *channel = NULL;
fd_set fds;
struct timeval tv;
ssize_t len, wr;
char buf[16384];
libssh2_socket_t sock = LIBSSH2_INVALID_SOCKET;
libssh2_socket_t forwardsock = LIBSSH2_INVALID_SOCKET;
#ifdef WIN32
WSADATA wsadata;
int err;
err = WSAStartup(MAKEWORD(2, 0), &wsadata);
if(err != 0) {
fprintf(stderr, "WSAStartup failed with error: %d\n", err);
return 1;
}
#endif
if(argc > 1)
server_ip = argv[1];
if(argc > 2)
username = argv[2];
if(argc > 3)
password = argv[3];
if(argc > 4)
remote_listenhost = argv[4];
if(argc > 5)
remote_wantport = atoi(argv[5]);
if(argc > 6)
local_destip = argv[6];
if(argc > 7)
local_destport = atoi(argv[7]);
rc = libssh2_init(0);
if(rc != 0) {
fprintf(stderr, "libssh2 initialization failed (%d)\n", rc);
return 1;
}
/* Connect to SSH server */
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock == LIBSSH2_INVALID_SOCKET) {
fprintf(stderr, "failed to open socket!\n");
return -1;
}
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(server_ip);
if(INADDR_NONE == sin.sin_addr.s_addr) {
fprintf(stderr, "inet_addr: Invalid IP address \"%s\"\n", server_ip);
return -1;
}
sin.sin_port = htons(22);
if(connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in))) {
fprintf(stderr, "Failed to connect to %s!\n", inet_ntoa(sin.sin_addr));
return -1;
}
/* Create a session instance */
session = libssh2_session_init();
if(!session) {
fprintf(stderr, "Could not initialize SSH session!\n");
return -1;
}
/* ... start it up. This will trade welcome banners, exchange keys,
* and setup crypto, compression, and MAC layers
*/
rc = libssh2_session_handshake(session, sock);
if(rc) {
fprintf(stderr, "Error when starting up SSH session: %d\n", rc);
return -1;
}
/* At this point we have not yet authenticated. The first thing to do
* is check the hostkey's fingerprint against our known hosts Your app
* may have it hard coded, may go to a file, may present it to the
* user, that's your call
*/
fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
fprintf(stderr, "Fingerprint: ");
for(i = 0; i < 20; i++)
fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]);
fprintf(stderr, "\n");
/* check what authentication methods are available */
userauthlist = libssh2_userauth_list(session, username,
(unsigned int)strlen(username));
fprintf(stderr, "Authentication methods: %s\n", userauthlist);
if(strstr(userauthlist, "password"))
auth |= AUTH_PASSWORD;
if(strstr(userauthlist, "publickey"))
auth |= AUTH_PUBLICKEY;
/* check for options */
if(argc > 8) {
if((auth & AUTH_PASSWORD) && !strcmp(argv[8], "-p"))
auth = AUTH_PASSWORD;
if((auth & AUTH_PUBLICKEY) && !strcmp(argv[8], "-k"))
auth = AUTH_PUBLICKEY;
}
if(auth & AUTH_PASSWORD) {
if(libssh2_userauth_password(session, username, password)) {
fprintf(stderr, "Authentication by password failed.\n");
goto shutdown;
}
}
else if(auth & AUTH_PUBLICKEY) {
if(libssh2_userauth_publickey_fromfile(session, username,
pubkey, privkey,
password)) {
fprintf(stderr, "\tAuthentication by public key failed!\n");
goto shutdown;
}
fprintf(stderr, "\tAuthentication by public key succeeded.\n");
}
else {
fprintf(stderr, "No supported authentication methods found!\n");
goto shutdown;
}
fprintf(stderr, "Asking server to listen on remote %s:%d\n",
remote_listenhost, remote_wantport);
listener = libssh2_channel_forward_listen_ex(session, remote_listenhost,
remote_wantport, &remote_listenport, 1);
if(!listener) {
fprintf(stderr, "Could not start the tcpip-forward listener!\n"
"(Note that this can be a problem at the server!"
" Please review the server logs.)\n");
goto shutdown;
}
fprintf(stderr, "Server is listening on %s:%d\n", remote_listenhost,
remote_listenport);
fprintf(stderr, "Waiting for remote connection\n");
channel = libssh2_channel_forward_accept(listener);
if(!channel) {
fprintf(stderr, "Could not accept connection!\n"
"(Note that this can be a problem at the server!"
" Please review the server logs.)\n");
goto shutdown;
}
fprintf(stderr,
"Accepted remote connection. Connecting to local server %s:%d\n",
local_destip, local_destport);
forwardsock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(forwardsock == LIBSSH2_INVALID_SOCKET) {
fprintf(stderr, "failed to open forward socket!\n");
goto shutdown;
}
sin.sin_family = AF_INET;
sin.sin_port = htons((unsigned short)local_destport);
sin.sin_addr.s_addr = inet_addr(local_destip);
if(INADDR_NONE == sin.sin_addr.s_addr) {
perror("inet_addr");
goto shutdown;
}
if(-1 == connect(forwardsock, (struct sockaddr *)&sin, sinlen)) {
perror("connect");
goto shutdown;
}
fprintf(stderr, "Forwarding connection from remote %s:%d to local %s:%d\n",
remote_listenhost, remote_listenport, local_destip, local_destport);
/* Must use non-blocking IO hereafter due to the current libssh2 API */
libssh2_session_set_blocking(session, 0);
for(;;) {
FD_ZERO(&fds);
FD_SET(forwardsock, &fds);
tv.tv_sec = 0;
tv.tv_usec = 100000;
rc = select((int)(forwardsock + 1), &fds, NULL, NULL, &tv);
if(-1 == rc) {
perror("select");
goto shutdown;
}
if(rc && FD_ISSET(forwardsock, &fds)) {
ssize_t nwritten;
len = recv(forwardsock, buf, sizeof(buf), 0);
if(len < 0) {
perror("read");
goto shutdown;
}
else if(0 == len) {
fprintf(stderr, "The local server at %s:%d disconnected!\n",
local_destip, local_destport);
goto shutdown;
}
wr = 0;
do {
nwritten = libssh2_channel_write(channel, buf, len);
if(nwritten < 0) {
fprintf(stderr, "libssh2_channel_write: %d\n",
(int)nwritten);
goto shutdown;
}
wr += nwritten;
} while(nwritten > 0 && wr < len);
}
for(;;) {
ssize_t nsent;
len = libssh2_channel_read(channel, buf, sizeof(buf));
if(LIBSSH2_ERROR_EAGAIN == len)
break;
else if(len < 0) {
fprintf(stderr, "libssh2_channel_read: %d",
(int)len);
goto shutdown;
}
wr = 0;
while(wr < len) {
nsent = send(forwardsock, buf + wr, len - wr, 0);
if(nsent <= 0) {
perror("write");
goto shutdown;
}
wr += nsent;
}
if(libssh2_channel_eof(channel)) {
fprintf(stderr, "The remote client at %s:%d disconnected!\n",
remote_listenhost, remote_listenport);
goto shutdown;
}
}
}
shutdown:
#ifdef WIN32
closesocket(forwardsock);
#else
close(forwardsock);
#endif
if(channel)
libssh2_channel_free(channel);
if(listener)
libssh2_channel_forward_cancel(listener);
libssh2_session_disconnect(session, "Client disconnecting normally");
libssh2_session_free(session);
#ifdef WIN32
closesocket(sock);
#else
close(sock);
#endif
libssh2_exit();
return 0;
}