diff --git a/proxy/interactor.c b/proxy/interactor.c index ccd3e42e..49359c1c 100644 --- a/proxy/interactor.c +++ b/proxy/interactor.c @@ -32,6 +32,13 @@ void interactor_return_seat(Interactor *itr) interactor_set_seat(itr, realseat); tempseat_free(tempseat); + /* + * If we have a parent Interactor, and anyone has ever called + * interactor_announce, then all Interactors from now on will + * announce themselves even if they have nothing to say. + */ + interactor_announce(itr); + /* * We're about to hand this seat back to the parent Interactor to * do its own thing with. It will typically expect to start in the @@ -44,12 +51,55 @@ void interactor_return_seat(Interactor *itr) InteractionReadySeat interactor_announce(Interactor *itr) { Seat *seat = interactor_get_seat(itr); - - /* TODO: print an announcement of this Interactor's identity, when - * appropriate */ + assert(!is_tempseat(seat) && + "Shouldn't call announce when someone else is using our seat"); InteractionReadySeat iseat; iseat.seat = seat; + /* + * Find the Interactor at the top of the chain, so that all the + * Interactors in a stack can share that one's last-to-talk field. + * Also, count how far we had to go to get to it, to put in the + * message. + */ + Interactor *itr_top = itr; + unsigned level = 0; + while (itr_top->parent) { + itr_top = itr_top->parent; + level++; + } + + /* + * Generally, we should announce ourself if the previous + * Interactor that said anything was not us. That includes if + * there was no previous Interactor to talk (i.e. if we're the + * first to say anything) - *except* that the primary Interactor + * doesn't need to announce itself, if no proxy has intervened + * before it. + */ + bool need_announcement = (itr_top->last_to_talk != itr); + if (!itr->parent && !itr_top->last_to_talk) + need_announcement = false; + + if (need_announcement) { + const char *prefix = ""; + if (itr_top->last_to_talk != NULL) + prefix = "\r\n"; + + char *desc = interactor_description(itr); + char *adjective = (level == 0 ? dupstr("primary") : + level == 1 ? dupstr("proxy") : + dupprintf("proxy^%u", level)); + char *msg = dupprintf("%sMaking %s %s", prefix, adjective, desc); + sfree(adjective); + sfree(desc); + + seat_antispoof_msg(iseat, msg); + sfree(msg); + + itr_top->last_to_talk = itr; + } + return iseat; } diff --git a/proxy/sshproxy.c b/proxy/sshproxy.c index 5ab81e61..ff830931 100644 --- a/proxy/sshproxy.c +++ b/proxy/sshproxy.c @@ -16,14 +16,6 @@ const bool ssh_proxy_supported = true; /* * TODO for future work: * - * All the interactive prompts we present to the main Seat - the host - * key and weak-crypto dialog boxes, and all prompts presented via the - * userpass_input system - need adjusting so that it's clear to the - * user _which_ SSH connection they come from. At the moment, you just - * get shown a host key fingerprint or a cryptic "login as:" prompt, - * and you have to guess which server you're currently supposed to be - * interpreting it relative to. - * * If the user manually aborts the attempt to make the proxy SSH * connection (e.g. by hitting ^C at a userpass prompt, or refusing to * accept the proxy server's host key), then an assertion failure @@ -607,6 +599,7 @@ Socket *sshproxy_new_connection(SockAddr *addr, const char *hostname, */ if (clientitr) { sp->clientitr = clientitr; + interactor_set_child(sp->clientitr, sp->backend->interactor); sp->clientlp = interactor_logpolicy(clientitr); diff --git a/putty.h b/putty.h index edfd1171..0047156f 100644 --- a/putty.h +++ b/putty.h @@ -661,6 +661,19 @@ struct InteractionReadySeat { */ struct Interactor { const InteractorVtable *vt; + + /* The parent Interactor that we are a proxy for, if any. */ + Interactor *parent; + + /* + * If we're the top-level Interactor (parent==NULL), then this + * field records the last Interactor that actually did anything + * interactive, so that we know when to announce a changeover + * between levels of proxying. + * + * If parent != NULL, this field is not used. + */ + Interactor *last_to_talk; }; struct InteractorVtable { @@ -706,6 +719,8 @@ static inline Seat *interactor_get_seat(Interactor *itr) static inline void interactor_set_seat(Interactor *itr, Seat *seat) { itr->vt->set_seat(itr, seat); } +static inline void interactor_set_child(Interactor *parent, Interactor *child) +{ child->parent = parent; } Seat *interactor_borrow_seat(Interactor *itr); void interactor_return_seat(Interactor *itr); InteractionReadySeat interactor_announce(Interactor *itr);