Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
diff --git a/mux.c b/mux.c
index cdc01bd..8a811a9 100644
--- a/mux.c
+++ b/mux.c
@@ -146,6 +147,7 @@ struct mux_master_state {
#define MUX_C_CLOSE_FWD 0x10000007
#define MUX_C_NEW_STDIO_FWD 0x10000008
#define MUX_C_STOP_LISTENING 0x10000009
+#define MUX_C_FDPASS 0x1000000A
#define MUX_S_OK 0x80000001
#define MUX_S_PERMISSION_DENIED 0x80000002
#define MUX_S_FAILURE 0x80000003
@@ -172,6 +174,15 @@ static int process_mux_close_fwd(u_int, Channel *, Buffer *, Buffer *);
static int process_mux_stdio_fwd(u_int, Channel *, Buffer *, Buffer *);
static int process_mux_stop_listening(u_int, Channel *, Buffer *, Buffer *);
+#ifdef HAVE_CYGWIN
+int muxfd[3];
+char *muxsock[3];
+
+static int mux_client_fdpass(int, int, int);
+static int process_mux_fdpass(int);
+static int mux_redirect(int, int);
+#endif
+
static const struct {
u_int type;
int (*handler)(u_int, Channel *, Buffer *, Buffer *);
@@ -372,11 +383,22 @@ process_mux_new_session(u_int rid, Channel *c, Buffer *m, Buffer *r)
cmd = NULL;
/* Gather fds from client */
+#ifdef HAVE_CYGWIN
+ for(i = 1; i < 3; i++) {
+ if ((new_fd[i] = process_mux_fdpass(c->sock)) >= 0) {
+ new_fd[0] = new_fd[1]; // stdin and stdout is going through the same socket
+ } else {
+#else
for(i = 0; i < 3; i++) {
if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) {
+#endif
error("%s: failed to receive fd %d from slave",
__func__, i);
+#ifdef HAVE_CYGWIN
+ for (j = 1; j < i; j++)
+#else
for (j = 0; j < i; j++)
+#endif
close(new_fd[j]);
for (j = 0; j < env_len; j++)
free(cctx->env[j]);
@@ -434,6 +456,8 @@ process_mux_new_session(u_int rid, Channel *c, Buffer *m, Buffer *r)
/* Try to pick up ttymodes from client before it goes raw */
if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1)
error("%s: tcgetattr: %s", __func__, strerror(errno));
+ // TODO tcgetattr fails on socket. We can send attributes
+ // the same way we send the rest information???
/* enable nonblocking unless tty */
if (!isatty(new_fd[0]))
@@ -958,11 +982,22 @@ process_mux_stdio_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r)
__func__, c->self, chost, cport);
/* Gather fds from client */
+#ifdef HAVE_CYGWIN
+ for(i = 1; i < 2; i++) {
+ if ((new_fd[i] = process_mux_fdpass(c->sock)) >= 0) {
+ new_fd[0] = new_fd[1]; // stdin and stdout is going through the same socket
+ } else {
+#else
for(i = 0; i < 2; i++) {
if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) {
+#endif
error("%s: failed to receive fd %d from slave",
__func__, i);
+#ifdef HAVE_CYGWIN
+ for (j = 1; j < i; j++)
+#else
for (j = 0; j < i; j++)
+#endif
close(new_fd[j]);
free(chost);
@@ -1842,9 +1877,14 @@ mux_client_request_session(int fd)
fatal("%s: write packet: %s", __func__, strerror(errno));
/* Send the stdio file descriptors */
+#ifdef HAVE_CYGWIN
+ if (mux_client_fdpass(fd, STDIN_FILENO, STDOUT_FILENO) == -1 ||
+ mux_client_fdpass(fd, -1, STDERR_FILENO) == -1)
+#else
if (mm_send_fd(fd, STDIN_FILENO) == -1 ||
mm_send_fd(fd, STDOUT_FILENO) == -1 ||
mm_send_fd(fd, STDERR_FILENO) == -1)
+#endif
fatal("%s: send fds failed", __func__);
debug3("%s: session request sent", __func__);
@@ -1894,6 +1934,52 @@ mux_client_request_session(int fd)
if (tty_flag)
enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
+#ifdef HAVE_CYGWIN
+ fd_set readset, activeset;
+ int maxfd, res, fds = 3;
+ FD_ZERO(&readset);
+ FD_SET(STDIN_FILENO, &readset);
+ FD_SET(muxfd[1], &readset);
+ if (muxfd[2] > -1)
+ FD_SET(muxfd[2], &readset);
+ FD_SET(fd, &readset);
+ maxfd = MAX(MAX(muxfd[1], muxfd[2]), fd);
+ for (exitval = 255, exitval_seen = 0;;) {
+ activeset = readset;
+ res = select(maxfd+1, &activeset, NULL, NULL, NULL);
+ //debug3("select returned %d\n", res);
+ if (res < 0) {
+ perror ("select");
+ break;
+ } else {
+ if (FD_ISSET(STDIN_FILENO, &activeset))
+ if (mux_redirect(STDIN_FILENO, muxfd[1]) == 0) {
+ shutdown(muxfd[1], SHUT_WR);
+ FD_CLR(STDIN_FILENO, &readset);
+ fds--;
+ }
+ if (FD_ISSET(muxfd[1], &activeset))
+ if (mux_redirect(muxfd[1], STDOUT_FILENO) == 0) {
+ FD_CLR(muxfd[1], &readset);
+ fds--;
+ }
+ if (FD_ISSET(muxfd[2], &activeset))
+ if (mux_redirect(muxfd[2], STDERR_FILENO) == 0) {
+ FD_CLR(muxfd[2], &readset);
+ fds--;
+ }
+ /*
+ * FAILSAFE: broken protocol? -- the exit message is not always present
+ * jump to cleanup also when there are no other active FDs
+ */
+ if (FD_ISSET(fd, &activeset) || fds < 1) {
+ goto handle_messages;
+ }
+ continue;
+ }
+handle_messages:
+#else
+
/*
* Stick around until the controlee closes the client_fd.
* Before it does, it is expected to write an exit message.
@@ -1902,6 +1988,8 @@ mux_client_request_session(int fd)
* terminate early too (possibly losing data).
*/
for (exitval = 255, exitval_seen = 0;;) {
+#endif
+ //logit("--> Starting to handle messages since I got that read from select");
buffer_clear(&m);
if (mux_client_read_packet(fd, &m) != 0)
break;
@@ -1932,6 +2020,17 @@ mux_client_request_session(int fd)
}
}
+#ifdef HAVE_CYGWIN
+ debug3("%s: cleaning up file descriptors", __func__);
+ for(i = 1; i < 3; i++) {
+ close(muxfd[i]);
+ if (muxsock[i] != NULL) {
+ unlink(muxsock[i]);
+ free(muxsock[i]);
+ }
+ }
+#endif
+
close(fd);
if (rawmode)
leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
@@ -1988,8 +2087,12 @@ mux_client_request_stdio_fwd(int fd)
fatal("%s: write packet: %s", __func__, strerror(errno));
/* Send the stdio file descriptors */
+#ifdef HAVE_CYGWIN
+ if (mux_client_fdpass(fd, STDIN_FILENO, STDOUT_FILENO) == -1)
+#else
if (mm_send_fd(fd, STDIN_FILENO) == -1 ||
mm_send_fd(fd, STDOUT_FILENO) == -1)
+#endif
fatal("%s: send fds failed", __func__);
debug3("%s: stdio forward request sent", __func__);
@@ -2198,3 +2301,128 @@ muxclient(const char *path)
fatal("unrecognised muxclient_command %d", muxclient_command);
}
}
+
+#ifdef HAVE_CYGWIN
+static int
+mux_client_fdpass(int fd, int fdpass_in, int fdpass_out)
+{
+ Buffer m;
+ char rbuf[16+1];
+ char *path;
+ char r;
+ int ffd, sock, t;
+ size_t i;
+ struct sockaddr_un remote;
+
+ debug3("%s: entering", __func__);
+
+ for (i = 0; i < sizeof(rbuf) - 1; i++) {
+ r = arc4random_uniform(26+26+10);
+ rbuf[i] = (r < 26) ? 'a' + r :
+ (r < 26*2) ? 'A' + r - 26 :
+ '0' + r - 26 - 26;
+ }
+ rbuf[sizeof(rbuf) - 1] = '\0';
+ if (asprintf(&path, "%s.fdpass.%s", options.control_path, rbuf) < 0) {
+ error("%s: asprintf: %s", __func__, strerror(errno));
+ return -1;
+ }
+ if ((sock = unix_listener(path, 2, 0)) < 0) {
+ free(path);
+ error("%s: socket: %s", __func__, strerror(errno));
+ return -1;
+ }
+ debug3("%s: emulating fdpass through socket %s", __func__, path);
+
+ buffer_init(&m);
+ buffer_put_int(&m, MUX_C_FDPASS);
+ buffer_put_cstring(&m, path);
+ if (mux_client_write_packet(fd, &m) != 0) {
+ free(path);
+ buffer_free(&m);
+ error("%s: failed to write packet to mux", __func__);
+ return -1;
+ }
+ buffer_free(&m);
+
+ //printf("muxfd[%d] = %d\n", fdpass, ffd);
+ if ((ffd = accept(sock, (struct sockaddr *)&remote, &t)) < 0) {
+ free(path);
+ error("%s: accept: %s", __func__, strerror(errno));
+ return -1;
+ }
+ debug3("%s: accepted client on socket %s", __func__, path);
+
+ muxfd[fdpass_out] = ffd; // stores only 1=>I/O 2=>ERR
+ muxsock[fdpass_out] = path;
+
+ return 0;
+}
+
+static int
+process_mux_fdpass(int fd)
+{
+ Buffer m;
+ int msg_id, sock, len;
+ char *path;
+ u_int n;
+ struct sockaddr_un remote;
+
+ buffer_init(&m);
+ if (mux_client_read_packet(fd, &m) != 0) {
+ buffer_free(&m);
+ return -1;
+ }
+
+ msg_id = buffer_get_int(&m);
+ path = buffer_get_cstring(&m, &n);
+ buffer_free(&m);
+ if (msg_id != MUX_C_FDPASS)
+ error("%s: Received wrong message id instead of passed socket", __func__);
+
+ debug3("%s: received socket %s to emulate fdpass", __func__, path);
+ if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+ error("%s: socket: %s", __func__, strerror(errno));
+ return -1;
+ }
+ memset(&remote, '\0', sizeof(remote));
+ remote.sun_family = AF_UNIX;
+ if (strlcpy(remote.sun_path, path,
+ sizeof(remote.sun_path)) >= sizeof(remote.sun_path)) {
+ error("ControlPath too long");
+ return -1;
+ }
+ len = offsetof(struct sockaddr_un, sun_path) + strlen(path) + 1;
+ if (connect(sock, (struct sockaddr *)&remote, len) < 0) {
+ error("%s: connect: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ free(path);
+ return sock;
+}
+
+static int mux_redirect(int source, int target)
+{
+ static char buffer[SSH_IOBUFSZ+1];
+ int result;
+ do {
+ result = read(source, buffer, SSH_IOBUFSZ);
+ } while (result == -1 && errno == EINTR);
+
+ if (result > 0) {
+ result = write(target, buffer, result);
+ //buffer[result] = 0;
+ //debug3("Redirecting %d -> %d data: %s\n", source, target, buffer);
+ return result; // written size
+ } else if (result == 0) {
+ //close, FD_CLEAR
+ //printf("Closing fd %d\n", source);
+ //close(target); // can't close socket yet since it is both way
+ return result; // propagete information about closing fd
+ } else {
+ printf("Error in read(): %s\n", strerror(errno));
+ return -1;
+ }
+}
+#endif