static int rlogin_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { Rlogin rlogin = (Rlogin) plug; /* * We don't implement independent EOF in each direction for Telnet * connections; as soon as we get word that the remote side has * sent us EOF, we wind up the whole connection. */ if (rlogin->s) { sk_close(rlogin->s); rlogin->s = NULL; if (error_msg) rlogin->closed_on_socket_error = TRUE; notify_remote_exit(rlogin->frontend); } if (error_msg) { /* A socket error has occurred. */ logevent(rlogin->frontend, error_msg); connection_fatal(rlogin->frontend, "%s", error_msg); } /* Otherwise, the remote side closed the connection normally. */ return 0; }
static int raw_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { Raw raw = (Raw) plug; if (error_msg) { /* A socket error has occurred. */ if (raw->s) { sk_close(raw->s); raw->s = NULL; notify_remote_exit(raw->frontend); } logevent(raw->frontend, error_msg); connection_fatal(raw->frontend, "%s", error_msg); } else { /* Otherwise, the remote side closed the connection normally. */ if (!raw->sent_console_eof && from_backend_eof(raw->frontend)) { /* * The front end wants us to close the outgoing side of the * connection as soon as we see EOF from the far end. */ if (!raw->sent_socket_eof) { if (raw->s) sk_write_eof(raw->s); raw->sent_socket_eof= TRUE; } } raw->sent_console_eof = TRUE; raw_check_close(raw); } return 0; }
static void sk_proxy_close(Socket s) { Proxy_Socket ps = (Proxy_Socket) s; sk_close(ps->sub_socket); sk_addr_free(ps->remote_addr); sfree(ps); }
static void adb_free(void *handle) { Adb adb = (Adb) handle; if (adb->s) sk_close(adb->s); sfree(adb); }
static void raw_free(void *handle) { Raw raw = (Raw) handle; if (raw->s) sk_close(raw->s); sfree(raw); }
static void sk_proxy_close (Socket s) { ProxySocket *ps = FROMFIELD(s, ProxySocket, sockvt); sk_close(ps->sub_socket); sk_addr_free(ps->remote_addr); sfree(ps); }
static void sk_proxy_close (Socket *s) { ProxySocket *ps = container_of(s, ProxySocket, sock); sk_close(ps->sub_socket); sk_addr_free(ps->remote_addr); sfree(ps); }
static void rlogin_free(void *handle) { Rlogin rlogin = (Rlogin) handle; if (rlogin->s) sk_close(rlogin->s); sfree(rlogin); }
static void rlogin_free(void *handle) { Rlogin rlogin = (Rlogin) handle; if (rlogin->prompt) free_prompts(rlogin->prompt); if (rlogin->s) sk_close(rlogin->s); conf_free(rlogin->conf); sfree(rlogin); }
void pfd_close(Socket s) { struct PFwdPrivate *pr; if (!s) return; pr = (struct PFwdPrivate *) sk_get_private_ptr(s); sfree(pr); sk_close(s); }
static int rlogin_closing(Plug plug, char *error_msg, int error_code, int calling_back) { if (s) { sk_close(s); s = NULL; } if (error_msg) { /* A socket error has occurred. */ connection_fatal(error_msg); } /* Otherwise, the remote side closed the connection normally. */ return 0; }
static void raw_check_close(Raw raw) { /* * Called after we send EOF on either the socket or the console. * Its job is to wind up the session once we have sent EOF on both. */ if (raw->sent_console_eof && raw->sent_socket_eof) { if (raw->s) { sk_close(raw->s); raw->s = NULL; notify_remote_exit(raw->frontend); } } }
static int rlogin_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { Rlogin rlogin = (Rlogin) plug; if (rlogin->s) { sk_close(rlogin->s); rlogin->s = NULL; } if (error_msg) { /* A socket error has occurred. */ logevent(rlogin->frontend, error_msg); connection_fatal(rlogin->frontend, "%s", error_msg); } /* Otherwise, the remote side closed the connection normally. */ return 0; }
static int raw_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { Raw raw = (Raw) plug; if (raw->s) { sk_close(raw->s); raw->s = NULL; notify_remote_exit(raw->frontend); } if (error_msg) { /* A socket error has occurred. */ logevent(raw->frontend, error_msg); connection_fatal(raw->frontend, "%s", error_msg); } /* Otherwise, the remote side closed the connection normally. */ return 0; }
static int cygterm_closing(Plug plug, const char *error_msg, int error_code, int calling_back) { Local local = (Local)plug; cygterm_debug("top"); if (local->s) { sk_close(local->s); local->s = NULL; } /* check for errors from cthelper */ CloseHandle(local->ctl); /* wait for cthelper */ if (local->pi.hProcess != INVALID_HANDLE_VALUE) { if (WAIT_OBJECT_0 == WaitForSingleObject(local->pi.hProcess, 2000)) { DWORD status; GetExitCodeProcess(local->pi.hProcess, &status); switch (status) { case CthelperSuccess: break; case CthelperInvalidUsage: case CthelperInvalidPort: case CthelperConnectFailed: local->exitcode = INT_MAX; error_msg = "Internal error"; break; case CthelperPtyforkFailure: local->exitcode = INT_MAX; error_msg = "Failed to allocate pseudoterminal"; break; case CthelperExecFailure: local->exitcode = INT_MAX; error_msg = "Failed to execute command"; break; } } } /* this calls cygterm_exitcode() */ notify_remote_exit(local->frontend); if (error_msg) { cygterm_debug("error_msg: %s", error_msg); connection_fatal(local->frontend, "%s", error_msg); } return 0; }
static const char * cygterm_init(void *frontend_handle, void **backend_handle, Conf *conf, char *unused_host, int unused_port, char **realhost, int nodelay, int keepalive) { /* XXX: I'm not sure if it is OK to overload Plug like this. * cygterm_accepting should only be used for the listening socket * (local->a) while the cygterm_closing, cygterm_receive, and cygterm_sent * should be used only for the actual connection (local->s). */ static const struct plug_function_table fn_table = { cygterm_log, cygterm_closing, cygterm_receive, cygterm_sent, cygterm_accepting }; Local local; const char *command; char cmdline[2 * MAX_PATH]; int cport; const char *err; int cmdlinelen; cygterm_debug("top"); local = snew(struct cygterm_backend_data); local->fn = &fn_table; local->a = NULL; local->s = NULL; local->conf = conf; local->editing = 0; local->echoing = 0; local->exitcode = 0; *backend_handle = local; local->frontend = frontend_handle; /* set up listen socket for communication with child */ cygterm_debug("setupCygTerm"); /* let sk use INADDR_LOOPBACK and let WinSock choose a port */ local->a = sk_newlistener(0, 0, (Plug)local, 1, ADDRTYPE_IPV4); if ((err = sk_socket_error(local->a)) != NULL) goto fail_free; /* now, get the port that WinSock chose */ /* XXX: Is there another function in PuTTY to do this? */ cygterm_debug("getting port"); cport = sk_getport(local->a); if (cport == -1) { err = "Failed to get port number for cthelper"; goto fail_close; } if (strchr(conf_get_str(local->conf, CONF_termtype), ' ')) { err = "term type contains spaces"; goto fail_close; } /* Build cthelper command line */ if(conf_get_int(conf, CONF_cygterm64)) { cmdlinelen = sprintf(cmdline, CTHELPER64" %u %s ", cport, conf_get_str(local->conf, CONF_termtype)); } else { cmdlinelen = sprintf(cmdline, CTHELPER" %u %s ", cport, conf_get_str(local->conf, CONF_termtype)); } cmdlinelen += makeAttributes(cmdline + cmdlinelen, conf); command = conf_get_str(conf, CONF_cygcmd); cygterm_debug("command is :%s:", command); /* A command of "." or "-" tells us to pass no command arguments to * cthelper which will then run the user's shell under Cygwin. */ if ((command[0]=='-'||command[0]=='.') && command[1]=='\0') ; else if (cmdlinelen + strlen(command) + 2 > sizeof cmdline) { err = "command is too long"; goto fail_close; } else { cmdlinelen += sprintf(cmdline + cmdlinelen, " %s", command); } /* Add the Cygwin /bin path to the PATH. */ if (conf_get_int(conf, CONF_cygautopath)) { char *cygwinBinPath = getCygwinBin(conf_get_int(conf, CONF_cygterm64)); if (!cygwinBinPath) { /* we'll try anyway */ cygterm_debug("cygwin bin directory not found"); } else { cygterm_debug("found cygwin directory: %s", cygwinBinPath); appendPath(cygwinBinPath); sfree(cygwinBinPath); } } cygterm_debug("starting cthelper: %s", cmdline); if ((err = spawnChild(cmdline, &local->pi, &local->ctl))) goto fail_close; /* This should be set to the local hostname, Apparently, realhost is used * only to set the window title. */ strcpy(*realhost = smalloc(sizeof CYGTERM_NAME), CYGTERM_NAME); cygterm_debug("OK"); return 0; fail_close: sk_close(local->a); fail_free: sfree(local); return err; }
int platform_ssh_share(const char *pi_name, Conf *conf, Plug downplug, Plug upplug, Socket *sock, char **logtext, char **ds_err, char **us_err, int can_upstream, int can_downstream) { char *name, *mutexname, *pipename; HANDLE mutex; Socket retsock; PSECURITY_DESCRIPTOR psd; PACL acl; /* * Transform the platform-independent version of the connection * identifier into the obfuscated version we'll use for our * Windows named pipe and mutex. A side effect of doing this is * that it also eliminates any characters illegal in Windows pipe * names. */ name = obfuscate_name(pi_name); if (!name) { *logtext = dupprintf("Unable to call CryptProtectMemory: %s", win_strerror(GetLastError())); return SHARE_NONE; } /* * Make a mutex name out of the connection identifier, and lock it * while we decide whether to be upstream or downstream. */ { SECURITY_ATTRIBUTES sa; mutexname = make_name(CONNSHARE_MUTEX_PREFIX, name); if (!make_private_security_descriptor(MUTEX_ALL_ACCESS, &psd, &acl, logtext)) { sfree(mutexname); return SHARE_NONE; } memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = psd; sa.bInheritHandle = FALSE; mutex = CreateMutex(&sa, FALSE, mutexname); if (!mutex) { *logtext = dupprintf("CreateMutex(\"%s\") failed: %s", mutexname, win_strerror(GetLastError())); sfree(mutexname); LocalFree(psd); LocalFree(acl); return SHARE_NONE; } sfree(mutexname); LocalFree(psd); LocalFree(acl); WaitForSingleObject(mutex, INFINITE); } pipename = make_name(CONNSHARE_PIPE_PREFIX, name); *logtext = NULL; if (can_downstream) { retsock = new_named_pipe_client(pipename, downplug); if (sk_socket_error(retsock) == NULL) { sfree(*logtext); *logtext = pipename; *sock = retsock; sfree(name); ReleaseMutex(mutex); CloseHandle(mutex); return SHARE_DOWNSTREAM; } sfree(*ds_err); *ds_err = dupprintf("%s: %s", pipename, sk_socket_error(retsock)); sk_close(retsock); } if (can_upstream) { retsock = new_named_pipe_listener(pipename, upplug); if (sk_socket_error(retsock) == NULL) { sfree(*logtext); *logtext = pipename; *sock = retsock; sfree(name); ReleaseMutex(mutex); CloseHandle(mutex); return SHARE_UPSTREAM; } sfree(*us_err); *us_err = dupprintf("%s: %s", pipename, sk_socket_error(retsock)); sk_close(retsock); } /* One of the above clauses ought to have happened. */ assert(*logtext || *ds_err || *us_err); sfree(pipename); sfree(name); ReleaseMutex(mutex); CloseHandle(mutex); return SHARE_NONE; }
int platform_make_x11_server(Plug *plug, const char *progname, int mindisp, const char *screen_number_suffix, ptrlen authproto, ptrlen authdata, Socket **sockets, Conf *conf) { char *tmpdir; char *authfilename = NULL; strbuf *authfiledata = NULL; char *unix_path = NULL; SockAddr *a_tcp = NULL, *a_unix = NULL; int authfd; FILE *authfp; int displayno; authfiledata = strbuf_new_nm(); int nsockets = 0; /* * Look for a free TCP port to run our server on. */ for (displayno = mindisp;; displayno++) { const char *err; int tcp_port = displayno + 6000; int addrtype = ADDRTYPE_IPV4; sockets[nsockets] = new_listener( NULL, tcp_port, plug, false, conf, addrtype); err = sk_socket_error(sockets[nsockets]); if (!err) { char *hostname = get_hostname(); if (hostname) { char *canonicalname = NULL; a_tcp = name_lookup(hostname, tcp_port, &canonicalname, conf, addrtype, NULL, ""); sfree(canonicalname); } sfree(hostname); nsockets++; break; /* success! */ } else { sk_close(sockets[nsockets]); } if (!strcmp(err, strerror(EADDRINUSE))) /* yuck! */ goto out; } if (a_tcp) { x11_format_auth_for_authfile( BinarySink_UPCAST(authfiledata), a_tcp, displayno, authproto, authdata); } /* * Try to establish the Unix-domain analogue. That may or may not * work - file permissions in /tmp may prevent it, for example - * but it's worth a try, and we don't consider it a fatal error if * it doesn't work. */ unix_path = dupprintf("/tmp/.X11-unix/X%d", displayno); a_unix = unix_sock_addr(unix_path); sockets[nsockets] = new_unix_listener(a_unix, plug); if (!sk_socket_error(sockets[nsockets])) { x11_format_auth_for_authfile( BinarySink_UPCAST(authfiledata), a_unix, displayno, authproto, authdata); nsockets++; } else { sk_close(sockets[nsockets]); sfree(unix_path); unix_path = NULL; } /* * Decide where the authority data will be written. */ tmpdir = getenv("TMPDIR"); if (!tmpdir || !*tmpdir) tmpdir = "/tmp"; authfilename = dupcat(tmpdir, "/", progname, "-Xauthority-XXXXXX", NULL); { int oldumask = umask(077); authfd = mkstemp(authfilename); umask(oldumask); } if (authfd < 0) { while (nsockets-- > 0) sk_close(sockets[nsockets]); goto out; } /* * Spawn a subprocess which will try to reliably delete our * auth file when we terminate, in case we die unexpectedly. */ { int cleanup_pipe[2]; pid_t pid; /* Don't worry if pipe or fork fails; it's not _that_ critical. */ if (!pipe(cleanup_pipe)) { if ((pid = fork()) == 0) { int buf[1024]; /* * Our parent process holds the writing end of * this pipe, and writes nothing to it. Hence, * we expect read() to return EOF as soon as * that process terminates. */ close(0); close(1); close(2); setpgid(0, 0); close(cleanup_pipe[1]); close(authfd); while (read(cleanup_pipe[0], buf, sizeof(buf)) > 0); unlink(authfilename); if (unix_path) unlink(unix_path); _exit(0); } else if (pid < 0) { close(cleanup_pipe[0]); close(cleanup_pipe[1]); } else { close(cleanup_pipe[0]); cloexec(cleanup_pipe[1]); } } } authfp = fdopen(authfd, "wb"); fwrite(authfiledata->u, 1, authfiledata->len, authfp); fclose(authfp); { char *display = dupprintf(":%d%s", displayno, screen_number_suffix); conf_set_str_str(conf, CONF_environmt, "DISPLAY", display); sfree(display); } conf_set_str_str(conf, CONF_environmt, "XAUTHORITY", authfilename); /* * FIXME: return at least the DISPLAY and XAUTHORITY env settings, * and perhaps also the display number */ out: if (a_tcp) sk_addr_free(a_tcp); /* a_unix doesn't need freeing, because new_unix_listener took it over */ sfree(authfilename); strbuf_free(authfiledata); sfree(unix_path); return nsockets; }
struct X11Display *x11_setup_display(char *display, int authtype, Conf *conf) { struct X11Display *disp = snew(struct X11Display); char *localcopy; int i; if (!display || !*display) { localcopy = platform_get_x_display(); if (!localcopy || !*localcopy) { sfree(localcopy); localcopy = dupstr(":0"); /* plausible default for any platform */ } } else localcopy = dupstr(display); /* * Parse the display name. * * We expect this to have one of the following forms: * * - the standard X format which looks like * [ [ protocol '/' ] host ] ':' displaynumber [ '.' screennumber ] * (X11 also permits a double colon to indicate DECnet, but * that's not our problem, thankfully!) * * - only seen in the wild on MacOS (so far): a pathname to a * Unix-domain socket, which will typically and confusingly * end in ":0", and which I'm currently distinguishing from * the standard scheme by noting that it starts with '/'. */ if (localcopy[0] == '/') { disp->unixsocketpath = localcopy; disp->unixdomain = TRUE; disp->hostname = NULL; disp->displaynum = -1; disp->screennum = 0; disp->addr = NULL; } else { char *colon, *dot, *slash; char *protocol, *hostname; colon = strrchr(localcopy, ':'); if (!colon) { sfree(disp); sfree(localcopy); return NULL; /* FIXME: report a specific error? */ } *colon++ = '\0'; dot = strchr(colon, '.'); if (dot) *dot++ = '\0'; disp->displaynum = atoi(colon); if (dot) disp->screennum = atoi(dot); else disp->screennum = 0; protocol = NULL; hostname = localcopy; if (colon > localcopy) { slash = strchr(localcopy, '/'); if (slash) { *slash++ = '\0'; protocol = localcopy; hostname = slash; } } disp->hostname = *hostname ? dupstr(hostname) : NULL; if (protocol) disp->unixdomain = (!strcmp(protocol, "local") || !strcmp(protocol, "unix")); else if (!*hostname || !strcmp(hostname, "unix")) disp->unixdomain = platform_uses_x11_unix_by_default; else disp->unixdomain = FALSE; if (!disp->hostname && !disp->unixdomain) disp->hostname = dupstr("localhost"); disp->unixsocketpath = NULL; disp->addr = NULL; sfree(localcopy); } /* * Look up the display hostname, if we need to. */ if (!disp->unixdomain) { const char *err; disp->port = 6000 + disp->displaynum; disp->addr = name_lookup(disp->hostname, disp->port, &disp->realhost, conf, ADDRTYPE_UNSPEC); if ((err = sk_addr_error(disp->addr)) != NULL) { sk_addr_free(disp->addr); sfree(disp->hostname); sfree(disp->unixsocketpath); sfree(disp); return NULL; /* FIXME: report an error */ } } /* * Try upgrading an IP-style localhost display to a Unix-socket * display (as the standard X connection libraries do). */ if (!disp->unixdomain && sk_address_is_local(disp->addr)) { SockAddr ux = platform_get_x11_unix_address(NULL, disp->displaynum); const char *err = sk_addr_error(ux); if (!err) { /* Create trial connection to see if there is a useful Unix-domain * socket */ const struct plug_function_table *dummy = &dummy_plug; Socket s = putty_sk_new(sk_addr_dup(ux), 0, 0, 0, 0, 0, (Plug)&dummy #ifdef MPEXT , 0, 0 #endif ); err = sk_socket_error(s); sk_close(s); } if (err) { sk_addr_free(ux); } else { sk_addr_free(disp->addr); disp->unixdomain = TRUE; disp->addr = ux; /* Fill in the rest in a moment */ } } if (disp->unixdomain) { if (!disp->addr) disp->addr = platform_get_x11_unix_address(disp->unixsocketpath, disp->displaynum); if (disp->unixsocketpath) disp->realhost = dupstr(disp->unixsocketpath); else disp->realhost = dupprintf("unix:%d", disp->displaynum); disp->port = 0; } /* * Invent the remote authorisation details. */ if (authtype == X11_MIT) { disp->remoteauthproto = X11_MIT; /* MIT-MAGIC-COOKIE-1. Cookie size is 128 bits (16 bytes). */ disp->remoteauthdata = snewn(16, unsigned char); for (i = 0; i < 16; i++) disp->remoteauthdata[i] = random_byte(); disp->remoteauthdatalen = 16; disp->xdmseen = NULL; } else {
int platform_ssh_share(const char *pi_name, Conf *conf, Plug downplug, Plug upplug, Socket *sock, char **logtext, char **ds_err, char **us_err, int can_upstream, int can_downstream) { char *dirname, *lockname, *sockname, *err; int lockfd; Socket retsock; /* * Sort out what we're going to call the directory in which we * keep the socket. This has the side effect of potentially * creating its top-level containing dir and/or the salt file * within that, if they don't already exist. */ dirname = make_dirname(pi_name, logtext); if (!dirname) { return SHARE_NONE; } /* * Now make sure the subdirectory exists. */ if ((err = make_dir_and_check_ours(dirname)) != NULL) { *logtext = err; sfree(dirname); return SHARE_NONE; } /* * Acquire a lock on a file in that directory. */ lockname = dupcat(dirname, "/lock", (char *)NULL); lockfd = open(lockname, O_CREAT | O_RDWR | O_TRUNC, 0600); if (lockfd < 0) { *logtext = dupprintf("%s: open: %s", lockname, strerror(errno)); sfree(dirname); sfree(lockname); return SHARE_NONE; } if (flock(lockfd, LOCK_EX) < 0) { *logtext = dupprintf("%s: flock(LOCK_EX): %s", lockname, strerror(errno)); sfree(dirname); sfree(lockname); close(lockfd); return SHARE_NONE; } sockname = dupprintf("%s/socket", dirname); *logtext = NULL; if (can_downstream) { retsock = new_connection(unix_sock_addr(sockname), "", 0, 0, 1, 0, 0, downplug, conf); if (sk_socket_error(retsock) == NULL) { sfree(*logtext); *logtext = sockname; *sock = retsock; sfree(dirname); sfree(lockname); close(lockfd); return SHARE_DOWNSTREAM; } sfree(*ds_err); *ds_err = dupprintf("%s: %s", sockname, sk_socket_error(retsock)); sk_close(retsock); } if (can_upstream) { retsock = new_unix_listener(unix_sock_addr(sockname), upplug); if (sk_socket_error(retsock) == NULL) { sfree(*logtext); *logtext = sockname; *sock = retsock; sfree(dirname); sfree(lockname); close(lockfd); return SHARE_UPSTREAM; } sfree(*us_err); *us_err = dupprintf("%s: %s", sockname, sk_socket_error(retsock)); sk_close(retsock); } /* One of the above clauses ought to have happened. */ assert(*logtext || *ds_err || *us_err); sfree(dirname); sfree(lockname); sfree(sockname); close(lockfd); return SHARE_NONE; }