/* * $Id; stfufs-client.c $ * * Author: Tomi Ollila -- too ät iki piste fi * * Copyright (c) 2007 Tomi Ollila * All rights reserved * * Created: Sun 26 Aug 2007 08:14:57 AM EEST too * Last modified: Thu Jan 31 20:03:05 EET 2008 too */ #include #include #include #include #include #include #include #include #include "util.h" #include "pretocol.h" #include "random.h" #include "version.h" #define STFUFS_CLIENT 1 #include "stfufs.h" static int getport(const char * s) { int i; i = atoi(s); if (i > 0 && i < 65536) { const char * p = s; do { if (*p < '0' || *p > '9') break; } while (*++p); if (*p == '\0') return i; } die("Illegal port %s", s); } static void gethostport(const char * s, char * host, int * port) { char * p; #if 0 if (port != 0) die("Port %d not 0, reused / mutually exclusive argument ?", port); #endif p = strrchr(s, ':'); if (p) { /* XXX bounds check */ memcpy(host, s, p - s); host[p - s] = '\0'; *port = getport(p + 1); } else { *port = getport(s); host[0] = '\0'; } } static bool isdir(const char * path) { struct stat st; if (lstat(path, &st) < 0) /* stat -> lstat: symlink does not do it ! */ return false; return S_ISDIR(st.st_mode); } static char * pusharg(char ** pp, const char * s, int len) { char * p = *pp; if (len < 0) { len = strlen(s) + 1; memcpy(p, s, len); } else { memcpy(p, s, len); p[len++] = '\0'; } *pp += len; return p; } static void sshcmd_die_hook(void * data) { d1(("sshcmd_die_hook()")); close((int)data); wait(null); } static void exitsshcmd(void) { d1(("waiting for ssh to exit")); /* maybe message */ wait(null); die_hook.func = null; } static int runsshcmd(const char * host, int port) { int fds[2]; char * args[10]; char argbuf[1024], *p = argbuf; int i = 0; int hostlen = strlen(host); if (hostlen > 768) die("host name too long"); args[i++] = pusharg(&p, "ssh", -1); args[i++] = pusharg(&p, host, hostlen); if (port) { args[i++] = pusharg(&p, "-p", -1); sprintf(p, "%d", port); args[i++] = p; p += strlen(p) + 1; } args[i++] = pusharg(&p, "stfufs-server", -1); args[i++] = pusharg(&p, PROTVER, -1); args[i++] = pusharg(&p, VERSION, -1); args[i] = null; if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) die("Socketpair failed:"); switch (fork()) { case -1: die("Fork failed:"); case 0: /* child */ close(fds[0]); dup2(fds[1], 0); dup2(fds[1], 1); if (fds[1] > 2) close(fds[1]); execvp("ssh", args); /* urgh... */ die("exec failed:"); default: /* parent */ break; } close(fds[1]); die_hook.func = sshcmd_die_hook; die_hook.data = (void *)fds[0]; return fds[0]; } void stfufs_client_args_init(struct clientargs * cargs) { memset(cargs, 0, sizeof *cargs); cargs->doconn = 1; } int stfufs_client_remotehost_arg(struct clientargs * cargs, const char * arg) { const char * p, *s; unsigned int l; if (cargs->sshhost[0]) { if (! isdir(arg)) die ("%s not directory in local host (%d)", arg, cargs->sshhost[0]); return 1; /* returning one gives arg for fuse for mountpoint ? */ } s = p = arg; p = strchr(p, ':'); if (p == null) die("No path component in remote directory path %s", cargs->sshhost); l = p - s; if (l >= sizeof cargs->sshhost) die("remote host name too long"); memcpy(cargs->sshhost, s, l); cargs->sshhost[l] = '\0'; l = strlen(p + 1) + 1; if (l > sizeof cargs->sshpath) die("remote path name too long"); memcpy(cargs->sshpath, p + 1, l); return 0; } int stfufs_client_conn_arg(struct clientargs * cargs, const char * arg) { cargs->port = getport(arg + 2); cargs->doconn = 1; return 0; } int stfufs_client_bind_arg(struct clientargs * cargs, const char * arg) { cargs->port = getport(arg + 2); cargs->doconn = 0; return 0; } int stfufs_client_proxy_arg(struct clientargs * cargs, const char * arg) { gethostport(arg + 2, cargs->proxyhost, &cargs->proxyport); return 0; } int stfufs_client_create_connection(const char * prgname, struct clientargs * cargs, int * maxmsgsize) { unsigned char buf[260]; int fd, i, len, ssd; char c; d0(("%*s %d %s", cargs->sshhostlen, cargs->sshhost, cargs->sshport, cargs->sshpath)); if (cargs->sshhost[0] == '\0') { die("missing host\nsee `%s -h' for usage", prgname); exit(1); } fd = runsshcmd(cargs->sshhost, cargs->sshport); i = recvident(fd, 2 * 60 * 1000); if (i >= 0) die("Ack %d too early", i); sendident(fd); write(fd, "", 1); /* our ack, '\0'-byte */ randombytes(buf, 4); readwt(fd, &c, 1, 10 * 1000); /* read ack */ if (c != 0) die("Server did not accept our ident"); /* XXX error msg via fd 2 ? */ /* remote directory to be mounted */ writehsmsg(fd, S2U(cargs->sshpath,char,[],*), strlen(cargs->sshpath) + 1); readwt(fd, &c, 1, 10 * 1000); /* read ack */ if (c != 0) exit(1); /* error message via fd 2 */ buf[4] = cargs->port >> 8; buf[5] = cargs->port; if (cargs->doconn) { len = 6; } else { if (cargs->proxyport) { buf[4] = cargs->proxyport >> 8; buf[5] = cargs->proxyport; } if (cargs->proxyhost) strcpy(U2S(buf, char, [], *) + 6, cargs->proxyhost); else buf[6] = '\0'; len = 6 + strlen(U2S(buf, char, [], *) + 6); ssd = dobindandlisten(cargs->port, true); } writehsmsg(fd, buf, len); /* random, port + optionally proxy info */ i = readhsmsg(fd, buf + 4); if (i != 10) /* 4 byte random + 2 byte port + 4 bytes max read/write size */ die("Unexpected message from server"); /* ssh has done it's work */ close(fd); /* are we happy with this implementation ? (hacked in last minute) */ *maxmsgsize = (buf[10] << 24) + (buf[11] << 16) + (buf[12] << 8) + buf[13]; if (cargs->doconn) { char * tcphost = strrchr(cargs->sshhost, '@'); if (tcphost == null) tcphost = cargs->sshhost; else tcphost++; if (cargs->proxyport) { if (cargs->proxyhost == null) fd = doconnect(buf, tcphost, cargs->proxyport); else fd = doconnect(buf, cargs->proxyhost, cargs->proxyport); } else { if (cargs->port == 0) cargs->port = (buf[8] << 8) + buf[9]; fd = doconnect(buf, tcphost, cargs->port); } } else fd = doaccept(buf, ssd); exitsshcmd(); d1(("ssh exited")); /* maybe message */ return fd; } static void futsupp(const char * component) { die("%s version " VERSION " does not support requested protocol", component); } int main(int argc, char ** argv) { util_init("Client"); if (argc > 1) { if (strcmp(argv[1], "--client") == 0) futsupp("Client"); if (strcmp(argv[1], "--server") == 0) futsupp("Server"); } stfufs_client_fuse_main(argc, argv); exit(0); } /* * Local variables: * mode: c * c-file-style: "stroustrup" * tab-width: 8 * End: */