1
0
mirror of https://github.com/libssh2/libssh2.git synced 2025-11-20 02:42:09 +03:00
Files
libssh2/example/x11.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

505 lines
13 KiB
C

/*
* Sample showing how to makes SSH2 with X11 Forwarding works.
*
* Usage:
* "ssh2 host user password [DEBUG]"
*/
#include "libssh2_setup.h"
#include <libssh2.h>
#include <stdio.h>
#ifdef HAVE_SYS_UN_H
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#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_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <termios.h>
#define _PATH_UNIX_X "/tmp/.X11-unix/X%d"
/*
* Chained list that contains channels and associated X11 socket for each X11
* connections
*/
struct chan_X11_list {
LIBSSH2_CHANNEL *chan;
int sock;
struct chan_X11_list *next;
};
static struct chan_X11_list * gp_x11_chan = NULL;
static struct termios _saved_tio;
/*
* Utility function to remove a Node of the chained list
*/
static void remove_node(struct chan_X11_list *elem)
{
struct chan_X11_list *current_node = NULL;
current_node = gp_x11_chan;
if(gp_x11_chan == elem) {
gp_x11_chan = gp_x11_chan->next;
free(current_node);
return;
}
while(current_node->next != NULL) {
if(current_node->next == elem) {
current_node->next = current_node->next->next;
current_node = current_node->next;
free(current_node);
break;
}
}
}
static void session_shutdown(LIBSSH2_SESSION *session)
{
libssh2_session_disconnect(session, "Session Shutdown");
libssh2_session_free(session);
}
static int _raw_mode(void)
{
int rc;
struct termios tio;
rc = tcgetattr(fileno(stdin), &tio);
if(rc != -1) {
_saved_tio = tio;
/* do the equivalent of cfmakeraw() manually, to build on Solaris */
tio.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
tio.c_oflag &= ~OPOST;
tio.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
tio.c_cflag &= ~(CSIZE|PARENB);
tio.c_cflag |= CS8;
rc = tcsetattr(fileno(stdin), TCSADRAIN, &tio);
}
return rc;
}
static int _normal_mode(void)
{
int rc;
rc = tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio);
return rc;
}
/*
* CallBack to initialize the forwarding.
* Save the channel to loop on it, save the X11 forwarded socket to send
* and receive info from our X server.
*/
static void x11_callback(LIBSSH2_SESSION *session, LIBSSH2_CHANNEL *channel,
char *shost, int sport, void **abstract)
{
const char *display = NULL;
char *ptr = NULL;
char *temp_buff = NULL;
int display_port = 0;
int rc = 0;
libssh2_socket_t sock = LIBSSH2_INVALID_SOCKET;
struct sockaddr_un addr;
struct chan_X11_list *new;
struct chan_X11_list *chan_iter;
(void)session;
(void)shost;
(void)sport;
(void)abstract;
/*
* Connect to the display
* Inspired by x11_connect_display in openssh
*/
display = getenv("DISPLAY");
if(display != NULL) {
if(strncmp(display, "unix:", 5) == 0 ||
display[0] == ':') {
/* Connect to the local unix domain */
ptr = strrchr(display, ':');
temp_buff = (char *) calloc(strlen(ptr + 1) + 1, sizeof(char));
if(!temp_buff) {
perror("calloc");
return;
}
memcpy(temp_buff, ptr + 1, strlen(ptr + 1));
display_port = atoi(temp_buff);
free(temp_buff);
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if(sock == LIBSSH2_INVALID_SOCKET)
return;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path),
_PATH_UNIX_X, display_port);
rc = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
if(rc != -1) {
/* Connection Successful */
if(gp_x11_chan == NULL) {
/* Calloc ensure that gp_X11_chan is full of 0 */
gp_x11_chan = (struct chan_X11_list *)
calloc(1, sizeof(struct chan_X11_list));
gp_x11_chan->sock = sock;
gp_x11_chan->chan = channel;
gp_x11_chan->next = NULL;
}
else {
chan_iter = gp_x11_chan;
while(chan_iter->next != NULL)
chan_iter = chan_iter->next;
/* Create the new Node */
new = (struct chan_X11_list *)
malloc(sizeof(struct chan_X11_list));
new->sock = sock;
new->chan = channel;
new->next = NULL;
chan_iter->next = new;
}
}
else
close(sock);
}
}
return;
}
/*
* Send and receive Data for the X11 channel.
* If the connection is closed, returns -1, 0 either.
*/
static int x11_send_receive(LIBSSH2_CHANNEL *channel, int sock)
{
char *buf = NULL;
int bufsize = 8192;
int rc = 0;
int nfds = 1;
LIBSSH2_POLLFD *fds = NULL;
fd_set set;
struct timeval timeval_out;
timeval_out.tv_sec = 0;
timeval_out.tv_usec = 0;
FD_ZERO(&set);
FD_SET(sock, &set);
buf = calloc(bufsize, sizeof(char));
if(!buf)
return 0;
fds = malloc(sizeof(LIBSSH2_POLLFD));
if(!fds) {
free(buf);
return 0;
}
fds[0].type = LIBSSH2_POLLFD_CHANNEL;
fds[0].fd.channel = channel;
fds[0].events = LIBSSH2_POLLFD_POLLIN;
fds[0].revents = LIBSSH2_POLLFD_POLLIN;
rc = libssh2_poll(fds, nfds, 0);
if(rc > 0) {
ssize_t nread;
nread = libssh2_channel_read(channel, buf, bufsize);
write(sock, buf, nread);
}
rc = select((int)(sock + 1), &set, NULL, NULL, &timeval_out);
if(rc > 0) {
ssize_t nread;
memset((void *)buf, 0, bufsize);
/* Data in sock */
nread = read(sock, buf, bufsize);
if(nread > 0) {
libssh2_channel_write(channel, buf, nread);
}
else {
free(buf);
return -1;
}
}
free(fds);
free(buf);
if(libssh2_channel_eof(channel) == 1) {
return -1;
}
return 0;
}
/*
* Main, more than inspired by ssh2.c by Bagder
*/
int
main(int argc, char *argv[])
{
uint32_t hostaddr = 0;
int rc = 0;
libssh2_socket_t sock = LIBSSH2_INVALID_SOCKET;
struct sockaddr_in sin;
LIBSSH2_SESSION *session;
LIBSSH2_CHANNEL *channel;
char *username = NULL;
char *password = NULL;
size_t bufsiz = 8193;
char *buf = NULL;
int set_debug_on = 0;
int nfds = 1;
LIBSSH2_POLLFD *fds = NULL;
/* Chan List struct */
struct chan_X11_list *current_node = NULL;
/* Struct winsize for term size */
struct winsize w_size;
struct winsize w_size_bck;
/* For select on stdin */
fd_set set;
struct timeval timeval_out;
timeval_out.tv_sec = 0;
timeval_out.tv_usec = 10;
if(argc > 3) {
hostaddr = inet_addr(argv[1]);
username = argv[2];
password = argv[3];
}
else {
fprintf(stderr, "Usage: %s destination username password",
argv[0]);
return -1;
}
if(argc > 4) {
set_debug_on = 1;
fprintf(stderr, "DEBUG is ON: %d\n", set_debug_on);
}
rc = libssh2_init(0);
if(rc != 0) {
fprintf(stderr, "libssh2 initialization failed (%d)\n", rc);
return 1;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == LIBSSH2_INVALID_SOCKET) {
perror("socket");
return -1;
}
sin.sin_family = AF_INET;
sin.sin_port = htons(22);
sin.sin_addr.s_addr = hostaddr;
if(connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in))) {
fprintf(stderr, "Failed to established connection!\n");
return -1;
}
/* Open a session */
session = libssh2_session_init();
rc = libssh2_session_handshake(session, sock);
if(rc != 0) {
fprintf(stderr, "Failed Start the SSH session\n");
return -1;
}
if(set_debug_on == 1)
libssh2_trace(session, LIBSSH2_TRACE_CONN);
/* ignore pedantic warnings by gcc on the callback argument */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
/* Set X11 Callback */
libssh2_session_callback_set(session, LIBSSH2_CALLBACK_X11,
(void *)x11_callback);
#pragma GCC diagnostic pop
/* Authenticate via password */
rc = libssh2_userauth_password(session, username, password);
if(rc != 0) {
fprintf(stderr, "Failed to authenticate\n");
session_shutdown(session);
close(sock);
return -1;
}
/* Open a channel */
channel = libssh2_channel_open_session(session);
if(channel == NULL) {
fprintf(stderr, "Failed to open a new channel\n");
session_shutdown(session);
close(sock);
return -1;
}
/* Request a PTY */
rc = libssh2_channel_request_pty(channel, "xterm");
if(rc != 0) {
fprintf(stderr, "Failed to request a pty\n");
session_shutdown(session);
close(sock);
return -1;
}
/* Request X11 */
rc = libssh2_channel_x11_req(channel, 0);
if(rc != 0) {
fprintf(stderr, "Failed to request X11 forwarding\n");
session_shutdown(session);
close(sock);
return -1;
}
/* Request a shell */
rc = libssh2_channel_shell(channel);
if(rc != 0) {
fprintf(stderr, "Failed to open a shell\n");
session_shutdown(session);
close(sock);
return -1;
}
rc = _raw_mode();
if(rc != 0) {
fprintf(stderr, "Failed to entered in raw mode\n");
session_shutdown(session);
close(sock);
return -1;
}
memset(&w_size, 0, sizeof(struct winsize));
memset(&w_size_bck, 0, sizeof(struct winsize));
for(;;) {
FD_ZERO(&set);
FD_SET(fileno(stdin), &set);
/* Search if a resize pty has to be send */
ioctl(fileno(stdin), TIOCGWINSZ, &w_size);
if((w_size.ws_row != w_size_bck.ws_row) ||
(w_size.ws_col != w_size_bck.ws_col)) {
w_size_bck = w_size;
libssh2_channel_request_pty_size(channel,
w_size.ws_col,
w_size.ws_row);
}
buf = calloc(bufsiz, sizeof(char));
if(buf == NULL)
break;
fds = malloc(sizeof(LIBSSH2_POLLFD));
if(fds == NULL) {
free(buf);
break;
}
fds[0].type = LIBSSH2_POLLFD_CHANNEL;
fds[0].fd.channel = channel;
fds[0].events = LIBSSH2_POLLFD_POLLIN;
fds[0].revents = LIBSSH2_POLLFD_POLLIN;
rc = libssh2_poll(fds, nfds, 0);
if(rc >0) {
libssh2_channel_read(channel, buf, sizeof(buf));
fprintf(stdout, "%s", buf);
fflush(stdout);
}
/* Looping on X clients */
if(gp_x11_chan != NULL) {
current_node = gp_x11_chan;
}
else
current_node = NULL;
while(current_node != NULL) {
struct chan_X11_list *next_node;
rc = x11_send_receive(current_node->chan, current_node->sock);
next_node = current_node->next;
if(rc == -1) {
shutdown(current_node->sock, SHUT_RDWR);
close(current_node->sock);
remove_node(current_node);
}
current_node = next_node;
}
rc = select((int)(fileno(stdin) + 1), &set, NULL, NULL, &timeval_out);
if(rc > 0) {
ssize_t nread;
/* Data in stdin */
nread = read(fileno(stdin), buf, 1);
if(nread > 0)
libssh2_channel_write(channel, buf, sizeof(buf));
}
free(fds);
free(buf);
if(libssh2_channel_eof(channel) == 1) {
break;
}
}
if(channel) {
libssh2_channel_free(channel);
channel = NULL;
}
_normal_mode();
libssh2_exit();
return 0;
}
#else
int
main(void)
{
printf("Sorry, this platform is not supported.");
return 1;
}
#endif /* HAVE_SYS_UN_H */