| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182 | 
							- /*
 
-  * Support for SSH connection sharing, i.e. permitting one PuTTY to
 
-  * open its own channels over the SSH session being run by another.
 
-  */
 
- /*
 
-  * Discussion and technical documentation
 
-  * ======================================
 
-  *
 
-  * The basic strategy for PuTTY's implementation of SSH connection
 
-  * sharing is to have a single 'upstream' PuTTY process, which manages
 
-  * the real SSH connection and all the cryptography, and then zero or
 
-  * more 'downstream' PuTTYs, which never talk to the real host but
 
-  * only talk to the upstream through local IPC (Unix-domain sockets or
 
-  * Windows named pipes).
 
-  *
 
-  * The downstreams communicate with the upstream using a protocol
 
-  * derived from SSH itself, which I'll document in detail below. In
 
-  * brief, though: the downstream->upstream protocol uses a trivial
 
-  * binary packet protocol (just length/type/data) to encapsulate
 
-  * unencrypted SSH messages, and downstreams talk to the upstream more
 
-  * or less as if it was an SSH server itself. (So downstreams can
 
-  * themselves open multiple SSH channels, for example, by sending
 
-  * multiple SSH2_MSG_CHANNEL_OPENs; they can send CHANNEL_REQUESTs of
 
-  * their choice within each channel, and they handle their own
 
-  * WINDOW_ADJUST messages.)
 
-  *
 
-  * The upstream would ideally handle these downstreams by just putting
 
-  * their messages into the queue for proper SSH-2 encapsulation and
 
-  * encryption and sending them straight on to the server. However,
 
-  * that's not quite feasible as written, because client-side channel
 
-  * IDs could easily conflict (between multiple downstreams, or between
 
-  * a downstream and the upstream). To protect against that, the
 
-  * upstream rewrites the client-side channel IDs in messages it passes
 
-  * on to the server, so that it's performing what you might describe
 
-  * as 'channel-number NAT'. Then the upstream remembers which of its
 
-  * own channel IDs are channels it's managing itself, and which are
 
-  * placeholders associated with a particular downstream, so that when
 
-  * replies come in from the server they can be sent on to the relevant
 
-  * downstream (after un-NATting the channel number, of course).
 
-  *
 
-  * Global requests from downstreams are only accepted if the upstream
 
-  * knows what to do about them; currently the only such requests are
 
-  * the ones having to do with remote-to-local port forwarding (in
 
-  * which, again, the upstream remembers that some of the forwardings
 
-  * it's asked the server to set up were on behalf of particular
 
-  * downstreams, and sends the incoming CHANNEL_OPENs to those
 
-  * downstreams when connections come in).
 
-  *
 
-  * Other fiddly pieces of this mechanism are X forwarding and
 
-  * (OpenSSH-style) agent forwarding. Both of these have a fundamental
 
-  * problem arising from the protocol design: that the CHANNEL_OPEN
 
-  * from the server introducing a forwarded connection does not carry
 
-  * any indication of which session channel gave rise to it; so if
 
-  * session channels from multiple downstreams enable those forwarding
 
-  * methods, it's hard for the upstream to know which downstream to
 
-  * send the resulting connections back to.
 
-  *
 
-  * For X forwarding, we can work around this in a really painful way
 
-  * by using the fake X11 authorisation data sent to the server as part
 
-  * of the forwarding setup: upstream ensures that every X forwarding
 
-  * request carries distinguishable fake auth data, and then when X
 
-  * connections come in it waits to see the auth data in the X11 setup
 
-  * message before it decides which downstream to pass the connection
 
-  * on to.
 
-  *
 
-  * For agent forwarding, that workaround is unavailable. As a result,
 
-  * this system (and, as far as I can think of, any other system too)
 
-  * has the fundamental constraint that it can only forward one SSH
 
-  * agent - it can't forward two agents to different session channels.
 
-  * So downstreams can request agent forwarding if they like, but if
 
-  * they do, they'll get whatever SSH agent is known to the upstream
 
-  * (if any) forwarded to their sessions.
 
-  *
 
-  * Downstream-to-upstream protocol
 
-  * -------------------------------
 
-  *
 
-  * Here I document in detail the protocol spoken between PuTTY
 
-  * downstreams and upstreams over local IPC. The IPC mechanism can
 
-  * vary between host platforms, but the protocol is the same.
 
-  *
 
-  * The protocol commences with a version exchange which is exactly
 
-  * like the SSH-2 one, in that each side sends a single line of text
 
-  * of the form
 
-  *
 
-  *   <protocol>-<version>-<softwareversion> [comments] \r\n
 
-  *
 
-  * The only difference is that in real SSH-2, <protocol> is the string
 
-  * "SSH", whereas in this protocol the string is
 
-  * "[email protected]".
 
-  *
 
-  * (The SSH RFCs allow many protocol-level identifier namespaces to be
 
-  * extended by implementors without central standardisation as long as
 
-  * they suffix "@" and a domain name they control to their new ids.
 
-  * RFC 4253 does not define this particular name to be changeable at
 
-  * all, but I like to think this is obviously how it would have done
 
-  * so if the working group had foreseen the need :-)
 
-  *
 
-  * Thereafter, all data exchanged consists of a sequence of binary
 
-  * packets concatenated end-to-end, each of which is of the form
 
-  *
 
-  *     uint32     length of packet, N
 
-  *     byte[N]    N bytes of packet data
 
-  *
 
-  * and, since these are SSH-2 messages, the first data byte is taken
 
-  * to be the packet type code.
 
-  *
 
-  * These messages are interpreted as those of an SSH connection, after
 
-  * userauth completes, and without any repeat key exchange.
 
-  * Specifically, any message from the SSH Connection Protocol is
 
-  * permitted, and also SSH_MSG_IGNORE, SSH_MSG_DEBUG,
 
-  * SSH_MSG_DISCONNECT and SSH_MSG_UNIMPLEMENTED from the SSH Transport
 
-  * Protocol.
 
-  *
 
-  * This protocol imposes a few additional requirements, over and above
 
-  * those of the standard SSH Connection Protocol:
 
-  *
 
-  * Message sizes are not permitted to exceed 0x4010 (16400) bytes,
 
-  * including their length header.
 
-  *
 
-  * When the server (i.e. really the PuTTY upstream) sends
 
-  * SSH_MSG_CHANNEL_OPEN with channel type "x11", and the client
 
-  * (downstream) responds with SSH_MSG_CHANNEL_OPEN_CONFIRMATION, that
 
-  * confirmation message MUST include an initial window size of at
 
-  * least 256. (Rationale: this is a bit of a fudge which makes it
 
-  * easier, by eliminating the possibility of nasty edge cases, for an
 
-  * upstream to arrange not to pass the CHANNEL_OPEN on to downstream
 
-  * until after it's seen the X11 auth data to decide which downstream
 
-  * it needs to go to.)
 
-  */
 
- #include <stdio.h>
 
- #include <stdlib.h>
 
- #include <assert.h>
 
- #include <limits.h>
 
- #include <errno.h>
 
- #include "putty.h"
 
- #include "tree234.h"
 
- #include "ssh.h"
 
- struct ssh_sharing_state {
 
-     char *sockname;                  /* the socket name, kept for cleanup */
 
-     Socket *listensock;              /* the master listening Socket */
 
-     tree234 *connections;            /* holds ssh_sharing_connstates */
 
-     unsigned nextid;                 /* preferred id for next connstate */
 
-     ConnectionLayer *cl;             /* instance of the ssh connection layer */
 
-     char *server_verstring;          /* server version string after "SSH-" */
 
-     Plug plug;
 
- };
 
- struct share_globreq;
 
- struct ssh_sharing_connstate {
 
-     unsigned id;    /* used to identify this downstream in log messages */
 
-     Socket *sock;                     /* the Socket for this connection */
 
-     struct ssh_sharing_state *parent;
 
-     int crLine;                        /* coroutine state for share_receive */
 
-     int sent_verstring, got_verstring, curr_packetlen;
 
-     unsigned char recvbuf[0x4010];
 
-     int recvlen;
 
-     /*
 
-      * Assorted state we have to remember about this downstream, so
 
-      * that we can clean it up appropriately when the downstream goes
 
-      * away.
 
-      */
 
-     /* Channels which don't have a downstream id, i.e. we've passed a
 
-      * CHANNEL_OPEN down from the server but not had an
 
-      * OPEN_CONFIRMATION or OPEN_FAILURE back. If downstream goes
 
-      * away, we respond to all of these with OPEN_FAILURE. */
 
-     tree234 *halfchannels;         /* stores 'struct share_halfchannel' */
 
-     /* Channels which do have a downstream id. We need to index these
 
-      * by both server id and upstream id, so we can find a channel
 
-      * when handling either an upward or a downward message referring
 
-      * to it. */
 
-     tree234 *channels_by_us;       /* stores 'struct share_channel' */
 
-     tree234 *channels_by_server;   /* stores 'struct share_channel' */
 
-     /* Another class of channel which doesn't have a downstream id.
 
-      * The difference between these and halfchannels is that xchannels
 
-      * do have an *upstream* id, because upstream has already accepted
 
-      * the channel request from the server. This arises in the case of
 
-      * X forwarding, where we have to accept the request and read the
 
-      * X authorisation data before we know whether the channel needs
 
-      * to be forwarded to a downstream. */
 
-     tree234 *xchannels_by_us;     /* stores 'struct share_xchannel' */
 
-     tree234 *xchannels_by_server; /* stores 'struct share_xchannel' */
 
-     /* Remote port forwarding requests in force. */
 
-     tree234 *forwardings;          /* stores 'struct share_forwarding' */
 
-     /* Global requests we've sent on to the server, pending replies. */
 
-     struct share_globreq *globreq_head, *globreq_tail;
 
-     Plug plug;
 
- };
 
- struct share_halfchannel {
 
-     unsigned server_id;
 
- };
 
- /* States of a share_channel. */
 
- enum {
 
-     OPEN,
 
-     SENT_CLOSE,
 
-     RCVD_CLOSE,
 
-     /* Downstream has sent CHANNEL_OPEN but server hasn't replied yet.
 
-      * If downstream goes away when a channel is in this state, we
 
-      * must wait for the server's response before starting to send
 
-      * CLOSE. Channels in this state are also not held in
 
-      * channels_by_server, because their server_id field is
 
-      * meaningless. */
 
-     UNACKNOWLEDGED
 
- };
 
- struct share_channel {
 
-     unsigned downstream_id, upstream_id, server_id;
 
-     int downstream_maxpkt;
 
-     int state;
 
-     /*
 
-      * Some channels (specifically, channels on which downstream has
 
-      * sent "x11-req") have the additional function of storing a set
 
-      * of downstream X authorisation data and a handle to an upstream
 
-      * fake set.
 
-      */
 
-     struct X11FakeAuth *x11_auth_upstream;
 
-     int x11_auth_proto;
 
-     char *x11_auth_data;
 
-     int x11_auth_datalen;
 
-     int x11_one_shot;
 
- };
 
- struct share_forwarding {
 
-     char *host;
 
-     int port;
 
-     int active;             /* has the server sent REQUEST_SUCCESS? */
 
-     struct ssh_rportfwd *rpf;
 
- };
 
- struct share_xchannel_message {
 
-     struct share_xchannel_message *next;
 
-     int type;
 
-     unsigned char *data;
 
-     int datalen;
 
- };
 
- struct share_xchannel {
 
-     unsigned upstream_id, server_id;
 
-     /*
 
-      * xchannels come in two flavours: live and dead. Live ones are
 
-      * waiting for an OPEN_CONFIRMATION or OPEN_FAILURE from
 
-      * downstream; dead ones have had an OPEN_FAILURE, so they only
 
-      * exist as a means of letting us conveniently respond to further
 
-      * channel messages from the server until such time as the server
 
-      * sends us CHANNEL_CLOSE.
 
-      */
 
-     int live;
 
-     /*
 
-      * When we receive OPEN_CONFIRMATION, we will need to send a
 
-      * WINDOW_ADJUST to the server to synchronise the windows. For
 
-      * this purpose we need to know what window we have so far offered
 
-      * the server. We record this as exactly the value in the
 
-      * OPEN_CONFIRMATION that upstream sent us, adjusted by the amount
 
-      * by which the two X greetings differed in length.
 
-      */
 
-     int window;
 
-     /*
 
-      * Linked list of SSH messages from the server relating to this
 
-      * channel, which we queue up until downstream sends us an
 
-      * OPEN_CONFIRMATION and we can belatedly send them all on.
 
-      */
 
-     struct share_xchannel_message *msghead, *msgtail;
 
- };
 
- enum {
 
-     GLOBREQ_TCPIP_FORWARD,
 
-     GLOBREQ_CANCEL_TCPIP_FORWARD
 
- };
 
- struct share_globreq {
 
-     struct share_globreq *next;
 
-     int type;
 
-     int want_reply;
 
-     struct share_forwarding *fwd;
 
- };
 
- static int share_connstate_cmp(void *av, void *bv)
 
- {
 
-     const struct ssh_sharing_connstate *a =
 
-         (const struct ssh_sharing_connstate *)av;
 
-     const struct ssh_sharing_connstate *b =
 
-         (const struct ssh_sharing_connstate *)bv;
 
-     if (a->id < b->id)
 
-         return -1;
 
-     else if (a->id > b->id)
 
-         return +1;
 
-     else
 
-         return 0;
 
- }
 
- static unsigned share_find_unused_id
 
- (struct ssh_sharing_state *sharestate, unsigned first)
 
- {
 
-     int low_orig, low, mid, high, high_orig;
 
-     struct ssh_sharing_connstate *cs;
 
-     unsigned ret;
 
-     /*
 
-      * Find the lowest unused downstream ID greater or equal to
 
-      * 'first'.
 
-      *
 
-      * Begin by seeing if 'first' itself is available. If it is, we'll
 
-      * just return it; if it's already in the tree, we'll find the
 
-      * tree index where it appears and use that for the next stage.
 
-      */
 
-     {
 
-         struct ssh_sharing_connstate dummy;
 
-         dummy.id = first;
 
-         cs = findrelpos234(sharestate->connections, &dummy, NULL,
 
-                            REL234_GE, &low_orig);
 
-         if (!cs)
 
-             return first;
 
-     }
 
-     /*
 
-      * Now binary-search using the counted B-tree, to find the largest
 
-      * ID which is in a contiguous sequence from the beginning of that
 
-      * range.
 
-      */
 
-     low = low_orig;
 
-     high = high_orig = count234(sharestate->connections);
 
-     while (high - low > 1) {
 
- 	mid = (high + low) / 2;
 
- 	cs = index234(sharestate->connections, mid);
 
- 	if (cs->id == first + (mid - low_orig))
 
- 	    low = mid;		       /* this one is still in the sequence */
 
- 	else
 
- 	    high = mid;		       /* this one is past the end */
 
-     }
 
-     /*
 
-      * Now low is the tree index of the largest ID in the initial
 
-      * sequence. So the return value is one more than low's id, and we
 
-      * know low's id is given by the formula in the binary search loop
 
-      * above.
 
-      *
 
-      * (If an SSH connection went on for _enormously_ long, we might
 
-      * reach a point where all ids from 'first' to UINT_MAX were in
 
-      * use. In that situation the formula below would wrap round by
 
-      * one and return zero, which is conveniently the right way to
 
-      * signal 'no id available' from this function.)
 
-      */
 
-     ret = first + (low - low_orig) + 1;
 
-     {
 
-         struct ssh_sharing_connstate dummy;
 
-         dummy.id = ret;
 
- 	assert(NULL == find234(sharestate->connections, &dummy, NULL));
 
-     }
 
-     return ret;
 
- }
 
- static int share_halfchannel_cmp(void *av, void *bv)
 
- {
 
-     const struct share_halfchannel *a = (const struct share_halfchannel *)av;
 
-     const struct share_halfchannel *b = (const struct share_halfchannel *)bv;
 
-     if (a->server_id < b->server_id)
 
-         return -1;
 
-     else if (a->server_id > b->server_id)
 
-         return +1;
 
-     else
 
-         return 0;
 
- }
 
- static int share_channel_us_cmp(void *av, void *bv)
 
- {
 
-     const struct share_channel *a = (const struct share_channel *)av;
 
-     const struct share_channel *b = (const struct share_channel *)bv;
 
-     if (a->upstream_id < b->upstream_id)
 
-         return -1;
 
-     else if (a->upstream_id > b->upstream_id)
 
-         return +1;
 
-     else
 
-         return 0;
 
- }
 
- static int share_channel_server_cmp(void *av, void *bv)
 
- {
 
-     const struct share_channel *a = (const struct share_channel *)av;
 
-     const struct share_channel *b = (const struct share_channel *)bv;
 
-     if (a->server_id < b->server_id)
 
-         return -1;
 
-     else if (a->server_id > b->server_id)
 
-         return +1;
 
-     else
 
-         return 0;
 
- }
 
- static int share_xchannel_us_cmp(void *av, void *bv)
 
- {
 
-     const struct share_xchannel *a = (const struct share_xchannel *)av;
 
-     const struct share_xchannel *b = (const struct share_xchannel *)bv;
 
-     if (a->upstream_id < b->upstream_id)
 
-         return -1;
 
-     else if (a->upstream_id > b->upstream_id)
 
-         return +1;
 
-     else
 
-         return 0;
 
- }
 
- static int share_xchannel_server_cmp(void *av, void *bv)
 
- {
 
-     const struct share_xchannel *a = (const struct share_xchannel *)av;
 
-     const struct share_xchannel *b = (const struct share_xchannel *)bv;
 
-     if (a->server_id < b->server_id)
 
-         return -1;
 
-     else if (a->server_id > b->server_id)
 
-         return +1;
 
-     else
 
-         return 0;
 
- }
 
- static int share_forwarding_cmp(void *av, void *bv)
 
- {
 
-     const struct share_forwarding *a = (const struct share_forwarding *)av;
 
-     const struct share_forwarding *b = (const struct share_forwarding *)bv;
 
-     int i;
 
-     if ((i = strcmp(a->host, b->host)) != 0)
 
-         return i;
 
-     else if (a->port < b->port)
 
-         return -1;
 
-     else if (a->port > b->port)
 
-         return +1;
 
-     else
 
-         return 0;
 
- }
 
- static void share_xchannel_free(struct share_xchannel *xc)
 
- {
 
-     while (xc->msghead) {
 
-         struct share_xchannel_message *tmp = xc->msghead;
 
-         xc->msghead = tmp->next;
 
-         sfree(tmp);
 
-     }
 
-     sfree(xc);
 
- }
 
- static void share_connstate_free(struct ssh_sharing_connstate *cs)
 
- {
 
-     struct share_halfchannel *hc;
 
-     struct share_xchannel *xc;
 
-     struct share_channel *chan;
 
-     struct share_forwarding *fwd;
 
-     while ((hc = (struct share_halfchannel *)
 
-             delpos234(cs->halfchannels, 0)) != NULL)
 
-         sfree(hc);
 
-     freetree234(cs->halfchannels);
 
-     /* All channels live in 'channels_by_us' but only some in
 
-      * 'channels_by_server', so we use the former to find the list of
 
-      * ones to free */
 
-     freetree234(cs->channels_by_server);
 
-     while ((chan = (struct share_channel *)
 
-             delpos234(cs->channels_by_us, 0)) != NULL)
 
-         sfree(chan);
 
-     freetree234(cs->channels_by_us);
 
-     /* But every xchannel is in both trees, so it doesn't matter which
 
-      * we use to free them. */
 
-     while ((xc = (struct share_xchannel *)
 
-             delpos234(cs->xchannels_by_us, 0)) != NULL)
 
-         share_xchannel_free(xc);
 
-     freetree234(cs->xchannels_by_us);
 
-     freetree234(cs->xchannels_by_server);
 
-     while ((fwd = (struct share_forwarding *)
 
-             delpos234(cs->forwardings, 0)) != NULL)
 
-         sfree(fwd);
 
-     freetree234(cs->forwardings);
 
-     while (cs->globreq_head) {
 
-         struct share_globreq *globreq = cs->globreq_head;
 
-         cs->globreq_head = cs->globreq_head->next;
 
-         sfree(globreq);
 
-     }
 
-     if (cs->sock)
 
-         sk_close(cs->sock);
 
-     sfree(cs);
 
- }
 
- void sharestate_free(ssh_sharing_state *sharestate)
 
- {
 
-     struct ssh_sharing_connstate *cs;
 
-     platform_ssh_share_cleanup(sharestate->sockname);
 
-     while ((cs = (struct ssh_sharing_connstate *)
 
-             delpos234(sharestate->connections, 0)) != NULL) {
 
-         share_connstate_free(cs);
 
-     }
 
-     freetree234(sharestate->connections);
 
-     if (sharestate->listensock) {
 
-         sk_close(sharestate->listensock);
 
-         sharestate->listensock = NULL;
 
-     }
 
-     sfree(sharestate->server_verstring);
 
-     sfree(sharestate->sockname);
 
-     sfree(sharestate);
 
- }
 
- static struct share_halfchannel *share_add_halfchannel
 
-     (struct ssh_sharing_connstate *cs, unsigned server_id)
 
- {
 
-     struct share_halfchannel *hc = snew(struct share_halfchannel);
 
-     hc->server_id = server_id;
 
-     if (add234(cs->halfchannels, hc) != hc) {
 
-         /* Duplicate?! */
 
-         sfree(hc);
 
-         return NULL;
 
-     } else {
 
-         return hc;
 
-     }
 
- }
 
- static struct share_halfchannel *share_find_halfchannel
 
-     (struct ssh_sharing_connstate *cs, unsigned server_id)
 
- {
 
-     struct share_halfchannel dummyhc;
 
-     dummyhc.server_id = server_id;
 
-     return find234(cs->halfchannels, &dummyhc, NULL);
 
- }
 
- static void share_remove_halfchannel(struct ssh_sharing_connstate *cs,
 
-                                      struct share_halfchannel *hc)
 
- {
 
-     del234(cs->halfchannels, hc);
 
-     sfree(hc);
 
- }
 
- static struct share_channel *share_add_channel
 
-     (struct ssh_sharing_connstate *cs, unsigned downstream_id,
 
-      unsigned upstream_id, unsigned server_id, int state, int maxpkt)
 
- {
 
-     struct share_channel *chan = snew(struct share_channel);
 
-     chan->downstream_id = downstream_id;
 
-     chan->upstream_id = upstream_id;
 
-     chan->server_id = server_id;
 
-     chan->state = state;
 
-     chan->downstream_maxpkt = maxpkt;
 
-     chan->x11_auth_upstream = NULL;
 
-     chan->x11_auth_data = NULL;
 
-     chan->x11_auth_proto = -1;
 
-     chan->x11_auth_datalen = 0;
 
-     chan->x11_one_shot = 0;
 
-     if (add234(cs->channels_by_us, chan) != chan) {
 
-         sfree(chan);
 
-         return NULL;
 
-     }
 
-     if (chan->state != UNACKNOWLEDGED) {
 
-         if (add234(cs->channels_by_server, chan) != chan) {
 
-             del234(cs->channels_by_us, chan);
 
-             sfree(chan);            
 
-             return NULL;
 
-         }
 
-     }
 
-     return chan;
 
- }
 
- static void share_channel_set_server_id(struct ssh_sharing_connstate *cs,
 
-                                         struct share_channel *chan,
 
-                                         unsigned server_id, int newstate)
 
- {
 
-     chan->server_id = server_id;
 
-     chan->state = newstate;
 
-     assert(newstate != UNACKNOWLEDGED);
 
-     add234(cs->channels_by_server, chan);
 
- }
 
- static struct share_channel *share_find_channel_by_upstream
 
-     (struct ssh_sharing_connstate *cs, unsigned upstream_id)
 
- {
 
-     struct share_channel dummychan;
 
-     dummychan.upstream_id = upstream_id;
 
-     return find234(cs->channels_by_us, &dummychan, NULL);
 
- }
 
- static struct share_channel *share_find_channel_by_server
 
-     (struct ssh_sharing_connstate *cs, unsigned server_id)
 
- {
 
-     struct share_channel dummychan;
 
-     dummychan.server_id = server_id;
 
-     return find234(cs->channels_by_server, &dummychan, NULL);
 
- }
 
- static void share_remove_channel(struct ssh_sharing_connstate *cs,
 
-                                  struct share_channel *chan)
 
- {
 
-     del234(cs->channels_by_us, chan);
 
-     del234(cs->channels_by_server, chan);
 
-     if (chan->x11_auth_upstream)
 
-         ssh_remove_sharing_x11_display(cs->parent->cl,
 
-                                        chan->x11_auth_upstream);
 
-     sfree(chan->x11_auth_data);
 
-     sfree(chan);
 
- }
 
- static struct share_xchannel *share_add_xchannel
 
-     (struct ssh_sharing_connstate *cs,
 
-      unsigned upstream_id, unsigned server_id)
 
- {
 
-     struct share_xchannel *xc = snew(struct share_xchannel);
 
-     xc->upstream_id = upstream_id;
 
-     xc->server_id = server_id;
 
-     xc->live = TRUE;
 
-     xc->msghead = xc->msgtail = NULL;
 
-     if (add234(cs->xchannels_by_us, xc) != xc) {
 
-         sfree(xc);
 
-         return NULL;
 
-     }
 
-     if (add234(cs->xchannels_by_server, xc) != xc) {
 
-         del234(cs->xchannels_by_us, xc);
 
-         sfree(xc);
 
-         return NULL;
 
-     }
 
-     return xc;
 
- }
 
- static struct share_xchannel *share_find_xchannel_by_upstream
 
-     (struct ssh_sharing_connstate *cs, unsigned upstream_id)
 
- {
 
-     struct share_xchannel dummyxc;
 
-     dummyxc.upstream_id = upstream_id;
 
-     return find234(cs->xchannels_by_us, &dummyxc, NULL);
 
- }
 
- static struct share_xchannel *share_find_xchannel_by_server
 
-     (struct ssh_sharing_connstate *cs, unsigned server_id)
 
- {
 
-     struct share_xchannel dummyxc;
 
-     dummyxc.server_id = server_id;
 
-     return find234(cs->xchannels_by_server, &dummyxc, NULL);
 
- }
 
- static void share_remove_xchannel(struct ssh_sharing_connstate *cs,
 
-                                  struct share_xchannel *xc)
 
- {
 
-     del234(cs->xchannels_by_us, xc);
 
-     del234(cs->xchannels_by_server, xc);
 
-     share_xchannel_free(xc);
 
- }
 
- static struct share_forwarding *share_add_forwarding
 
-     (struct ssh_sharing_connstate *cs,
 
-      const char *host, int port)
 
- {
 
-     struct share_forwarding *fwd = snew(struct share_forwarding);
 
-     fwd->host = dupstr(host);
 
-     fwd->port = port;
 
-     fwd->active = FALSE;
 
-     if (add234(cs->forwardings, fwd) != fwd) {
 
-         /* Duplicate?! */
 
-         sfree(fwd);
 
-         return NULL;
 
-     }
 
-     return fwd;
 
- }
 
- static struct share_forwarding *share_find_forwarding
 
-     (struct ssh_sharing_connstate *cs, const char *host, int port)
 
- {
 
-     struct share_forwarding dummyfwd, *ret;
 
-     dummyfwd.host = dupstr(host);
 
-     dummyfwd.port = port;
 
-     ret = find234(cs->forwardings, &dummyfwd, NULL);
 
-     sfree(dummyfwd.host);
 
-     return ret;
 
- }
 
- static void share_remove_forwarding(struct ssh_sharing_connstate *cs,
 
-                                     struct share_forwarding *fwd)
 
- {
 
-     del234(cs->forwardings, fwd);
 
-     sfree(fwd);
 
- }
 
- static void log_downstream(struct ssh_sharing_connstate *cs,
 
-                            const char *logfmt, ...)
 
- {
 
-     va_list ap;
 
-     char *buf;
 
-     va_start(ap, logfmt);
 
-     buf = dupvprintf(logfmt, ap);
 
-     va_end(ap);
 
-     logeventf(cs->parent->cl->logctx,
 
-               "Connection sharing downstream #%u: %s", cs->id, buf);
 
-     sfree(buf);
 
- }
 
- static void log_general(struct ssh_sharing_state *sharestate,
 
-                         const char *logfmt, ...)
 
- {
 
-     va_list ap;
 
-     char *buf;
 
-     va_start(ap, logfmt);
 
-     buf = dupvprintf(logfmt, ap);
 
-     va_end(ap);
 
-     logeventf(sharestate->cl->logctx, "Connection sharing: %s", buf);
 
-     sfree(buf);
 
- }
 
- static void send_packet_to_downstream(struct ssh_sharing_connstate *cs,
 
-                                       int type, const void *pkt, int pktlen,
 
-                                       struct share_channel *chan)
 
- {
 
-     strbuf *packet;
 
-     if (!cs->sock) /* throw away all packets destined for a dead downstream */
 
-         return;
 
-     if (type == SSH2_MSG_CHANNEL_DATA) {
 
-         /*
 
-          * Special case which we take care of at a low level, so as to
 
-          * be sure to apply it in all cases. On rare occasions we
 
-          * might find that we have a channel for which the
 
-          * downstream's maximum packet size exceeds the max packet
 
-          * size we presented to the server on its behalf. (This can
 
-          * occur in X11 forwarding, where we have to send _our_
 
-          * CHANNEL_OPEN_CONFIRMATION before we discover which if any
 
-          * downstream the channel is destined for, so if that
 
-          * downstream turns out to present a smaller max packet size
 
-          * then we're in this situation.)
 
-          *
 
-          * If that happens, we just chop up the packet into pieces and
 
-          * send them as separate CHANNEL_DATA packets.
 
-          */
 
-         BinarySource src[1];
 
-         unsigned channel;
 
-         ptrlen data;
 
-         BinarySource_BARE_INIT(src, pkt, pktlen);
 
-         channel = get_uint32(src);
 
-         data = get_string(src);
 
-         do {
 
-             int this_len = (data.len > chan->downstream_maxpkt ?
 
-                             chan->downstream_maxpkt : data.len);
 
-             packet = strbuf_new();
 
-             put_uint32(packet, 0);     /* placeholder for length field */
 
-             put_byte(packet, type);
 
-             put_uint32(packet, channel);
 
-             put_uint32(packet, this_len);
 
-             put_data(packet, data.ptr, this_len);
 
-             data.ptr = (const char *)data.ptr + this_len;
 
-             data.len -= this_len;
 
-             PUT_32BIT(packet->s, packet->len-4);
 
-             sk_write(cs->sock, packet->s, packet->len);
 
-             strbuf_free(packet);
 
-         } while (data.len > 0);
 
-     } else {
 
-         /*
 
-          * Just do the obvious thing.
 
-          */
 
-         packet = strbuf_new();
 
-         put_uint32(packet, 0);     /* placeholder for length field */
 
-         put_byte(packet, type);
 
-         put_data(packet, pkt, pktlen);
 
-         PUT_32BIT(packet->s, packet->len-4);
 
-         sk_write(cs->sock, packet->s, packet->len);
 
-         strbuf_free(packet);
 
-     }
 
- }
 
- static void share_try_cleanup(struct ssh_sharing_connstate *cs)
 
- {
 
-     int i;
 
-     struct share_halfchannel *hc;
 
-     struct share_channel *chan;
 
-     struct share_forwarding *fwd;
 
-     /*
 
-      * Any half-open channels, i.e. those for which we'd received
 
-      * CHANNEL_OPEN from the server but not passed back a response
 
-      * from downstream, should be responded to with OPEN_FAILURE.
 
-      */
 
-     while ((hc = (struct share_halfchannel *)
 
-             index234(cs->halfchannels, 0)) != NULL) {
 
-         static const char reason[] = "PuTTY downstream no longer available";
 
-         static const char lang[] = "en";
 
-         strbuf *packet;
 
-         packet = strbuf_new();
 
-         put_uint32(packet, hc->server_id);
 
-         put_uint32(packet, SSH2_OPEN_CONNECT_FAILED);
 
-         put_stringz(packet, reason);
 
-         put_stringz(packet, lang);
 
-         ssh_send_packet_from_downstream(
 
-             cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_OPEN_FAILURE,
 
-             packet->s, packet->len,
 
-             "cleanup after downstream went away");
 
-         strbuf_free(packet);
 
-         share_remove_halfchannel(cs, hc);
 
-     }
 
-     /*
 
-      * Any actually open channels should have a CHANNEL_CLOSE sent for
 
-      * them, unless we've already done so. We won't be able to
 
-      * actually clean them up until CHANNEL_CLOSE comes back from the
 
-      * server, though (unless the server happens to have sent a CLOSE
 
-      * already).
 
-      *
 
-      * Another annoying exception is UNACKNOWLEDGED channels, i.e.
 
-      * we've _sent_ a CHANNEL_OPEN to the server but not received an
 
-      * OPEN_CONFIRMATION or OPEN_FAILURE. We must wait for a reply
 
-      * before closing the channel, because until we see that reply we
 
-      * won't have the server's channel id to put in the close message.
 
-      */
 
-     for (i = 0; (chan = (struct share_channel *)
 
-                  index234(cs->channels_by_us, i)) != NULL; i++) {
 
-         strbuf *packet;
 
-         if (chan->state != SENT_CLOSE && chan->state != UNACKNOWLEDGED) {
 
-             packet = strbuf_new();
 
-             put_uint32(packet, chan->server_id);
 
-             ssh_send_packet_from_downstream(
 
-                 cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_CLOSE,
 
-                 packet->s, packet->len,
 
-                 "cleanup after downstream went away");
 
-             strbuf_free(packet);
 
-             if (chan->state != RCVD_CLOSE) {
 
-                 chan->state = SENT_CLOSE;
 
-             } else {
 
-                 /* In this case, we _can_ clear up the channel now. */
 
-                 ssh_delete_sharing_channel(cs->parent->cl, chan->upstream_id);
 
-                 share_remove_channel(cs, chan);
 
-                 i--;    /* don't accidentally skip one as a result */
 
-             }
 
-         }
 
-     }
 
-     /*
 
-      * Any remote port forwardings we're managing on behalf of this
 
-      * downstream should be cancelled. Again, we must defer those for
 
-      * which we haven't yet seen REQUEST_SUCCESS/FAILURE.
 
-      *
 
-      * We take a fire-and-forget approach during cleanup, not
 
-      * bothering to set want_reply.
 
-      */
 
-     for (i = 0; (fwd = (struct share_forwarding *)
 
-                  index234(cs->forwardings, i)) != NULL; i++) {
 
-         if (fwd->active) {
 
-             strbuf *packet = strbuf_new();
 
-             put_stringz(packet, "cancel-tcpip-forward");
 
-             put_bool(packet, FALSE);       /* !want_reply */
 
-             put_stringz(packet, fwd->host);
 
-             put_uint32(packet, fwd->port);
 
-             ssh_send_packet_from_downstream(
 
-                 cs->parent->cl, cs->id, SSH2_MSG_GLOBAL_REQUEST,
 
-                 packet->s, packet->len,
 
-                 "cleanup after downstream went away");
 
-             strbuf_free(packet);
 
-             ssh_rportfwd_remove(cs->parent->cl, fwd->rpf);
 
-             share_remove_forwarding(cs, fwd);
 
-             i--;    /* don't accidentally skip one as a result */
 
-         }
 
-     }
 
-     if (count234(cs->halfchannels) == 0 &&
 
-         count234(cs->channels_by_us) == 0 &&
 
-         count234(cs->forwardings) == 0) {
 
-         struct ssh_sharing_state *sharestate = cs->parent;
 
-         /*
 
-          * Now we're _really_ done, so we can get rid of cs completely.
 
-          */
 
-         del234(sharestate->connections, cs);
 
-         log_downstream(cs, "disconnected");
 
-         share_connstate_free(cs);
 
-         /*
 
-          * And if this was the last downstream, notify the connection
 
-          * layer, because it might now be time to wind up the whole
 
-          * SSH connection.
 
-          */
 
-         if (count234(sharestate->connections) == 0 && sharestate->cl)
 
-             ssh_sharing_no_more_downstreams(sharestate->cl);
 
-     }
 
- }
 
- static void share_begin_cleanup(struct ssh_sharing_connstate *cs)
 
- {
 
-     sk_close(cs->sock);
 
-     cs->sock = NULL;
 
-     share_try_cleanup(cs);
 
- }
 
- static void share_disconnect(struct ssh_sharing_connstate *cs,
 
-                              const char *message)
 
- {
 
-     strbuf *packet = strbuf_new();
 
-     put_uint32(packet, SSH2_DISCONNECT_PROTOCOL_ERROR);
 
-     put_stringz(packet, message);
 
-     put_stringz(packet, "en");         /* language */
 
-     send_packet_to_downstream(cs, SSH2_MSG_DISCONNECT,
 
-                               packet->s, packet->len, NULL);
 
-     strbuf_free(packet);
 
-     share_begin_cleanup(cs);
 
- }
 
- static void share_closing(Plug *plug, const char *error_msg, int error_code,
 
- 			  int calling_back)
 
- {
 
-     struct ssh_sharing_connstate *cs = container_of(
 
-         plug, struct ssh_sharing_connstate, plug);
 
-     if (error_msg) {
 
- #ifdef BROKEN_PIPE_ERROR_CODE
 
-         /*
 
-          * Most of the time, we log what went wrong when a downstream
 
-          * disappears with a socket error. One exception, though, is
 
-          * receiving EPIPE when we haven't received a protocol version
 
-          * string from the downstream, because that can happen as a result
 
-          * of plink -shareexists (opening the connection and instantly
 
-          * closing it again without bothering to read our version string).
 
-          * So that one case is not treated as a log-worthy error.
 
-          */
 
-         if (error_code == BROKEN_PIPE_ERROR_CODE && !cs->got_verstring)
 
-             /* do nothing */;
 
-         else
 
- #endif
 
-             log_downstream(cs, "Socket error: %s", error_msg);
 
-     }
 
-     share_begin_cleanup(cs);
 
- }
 
- /*
 
-  * Append a message to the end of an xchannel's queue.
 
-  */
 
- static void share_xchannel_add_message(
 
-     struct share_xchannel *xc, int type, const void *data, int len)
 
- {
 
-     struct share_xchannel_message *msg;
 
-     /*
 
-      * Allocate the 'struct share_xchannel_message' and the actual
 
-      * data in one unit.
 
-      */
 
-     msg = snew_plus(struct share_xchannel_message, len);
 
-     msg->data = snew_plus_get_aux(msg);
 
-     msg->datalen = len;
 
-     msg->type = type;
 
-     memcpy(msg->data, data, len);
 
-     /*
 
-      * Queue it in the xchannel.
 
-      */
 
-     if (xc->msgtail)
 
-         xc->msgtail->next = msg;
 
-     else
 
-         xc->msghead = msg;
 
-     msg->next = NULL;
 
-     xc->msgtail = msg;
 
- }
 
- void share_dead_xchannel_respond(struct ssh_sharing_connstate *cs,
 
-                                  struct share_xchannel *xc)
 
- {
 
-     /*
 
-      * Handle queued incoming messages from the server destined for an
 
-      * xchannel which is dead (i.e. downstream sent OPEN_FAILURE).
 
-      */
 
-     int delete = FALSE;
 
-     while (xc->msghead) {
 
-         struct share_xchannel_message *msg = xc->msghead;
 
-         xc->msghead = msg->next;
 
-         if (msg->type == SSH2_MSG_CHANNEL_REQUEST && msg->datalen > 4) {
 
-             /*
 
-              * A CHANNEL_REQUEST is responded to by sending
 
-              * CHANNEL_FAILURE, if it has want_reply set.
 
-              */
 
-             BinarySource src[1];
 
-             BinarySource_BARE_INIT(src, msg->data, msg->datalen);
 
-             get_uint32(src);           /* skip channel id */
 
-             get_string(src);           /* skip request type */
 
-             if (get_bool(src)) {
 
-                 strbuf *packet = strbuf_new();
 
-                 put_uint32(packet, xc->server_id);
 
-                 ssh_send_packet_from_downstream
 
-                     (cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_FAILURE,
 
-                      packet->s, packet->len,
 
-                      "downstream refused X channel open");
 
-                 strbuf_free(packet);
 
-             }
 
-         } else if (msg->type == SSH2_MSG_CHANNEL_CLOSE) {
 
-             /*
 
-              * On CHANNEL_CLOSE we can discard the channel completely.
 
-              */
 
-             delete = TRUE;
 
-         }
 
-         sfree(msg);
 
-     }
 
-     xc->msgtail = NULL;
 
-     if (delete) {
 
-         ssh_delete_sharing_channel(cs->parent->cl, xc->upstream_id);
 
-         share_remove_xchannel(cs, xc);
 
-     }
 
- }
 
- void share_xchannel_confirmation(struct ssh_sharing_connstate *cs,
 
-                                  struct share_xchannel *xc,
 
-                                  struct share_channel *chan,
 
-                                  unsigned downstream_window)
 
- {
 
-     strbuf *packet;
 
-     /*
 
-      * Send all the queued messages downstream.
 
-      */
 
-     while (xc->msghead) {
 
-         struct share_xchannel_message *msg = xc->msghead;
 
-         xc->msghead = msg->next;
 
-         if (msg->datalen >= 4)
 
-             PUT_32BIT(msg->data, chan->downstream_id);
 
-         send_packet_to_downstream(cs, msg->type,
 
-                                   msg->data, msg->datalen, chan);
 
-         sfree(msg);
 
-     }
 
-     /*
 
-      * Send a WINDOW_ADJUST back upstream, to synchronise the window
 
-      * size downstream thinks it's presented with the one we've
 
-      * actually presented.
 
-      */
 
-     packet = strbuf_new();
 
-     put_uint32(packet, xc->server_id);
 
-     put_uint32(packet, downstream_window - xc->window);
 
-     ssh_send_packet_from_downstream(
 
-         cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_WINDOW_ADJUST,
 
-         packet->s, packet->len,
 
-         "window adjustment after downstream accepted X channel");
 
-     strbuf_free(packet);
 
- }
 
- void share_xchannel_failure(struct ssh_sharing_connstate *cs,
 
-                             struct share_xchannel *xc)
 
- {
 
-     /*
 
-      * If downstream refuses to open our X channel at all for some
 
-      * reason, we must respond by sending an emergency CLOSE upstream.
 
-      */
 
-     strbuf *packet = strbuf_new();
 
-     put_uint32(packet, xc->server_id);
 
-     ssh_send_packet_from_downstream(
 
-         cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_CLOSE,
 
-         packet->s, packet->len,
 
-         "downstream refused X channel open");
 
-     strbuf_free(packet);
 
-     /*
 
-      * Now mark the xchannel as dead, and respond to anything sent on
 
-      * it until we see CLOSE for it in turn.
 
-      */
 
-     xc->live = FALSE;
 
-     share_dead_xchannel_respond(cs, xc);
 
- }
 
- void share_setup_x11_channel(ssh_sharing_connstate *cs, share_channel *chan,
 
-                              unsigned upstream_id, unsigned server_id,
 
-                              unsigned server_currwin, unsigned server_maxpkt,
 
-                              unsigned client_adjusted_window,
 
-                              const char *peer_addr, int peer_port, int endian,
 
-                              int protomajor, int protominor,
 
-                              const void *initial_data, int initial_len)
 
- {
 
-     struct share_xchannel *xc;
 
-     void *greeting;
 
-     int greeting_len;
 
-     strbuf *packet;
 
-     /*
 
-      * Create an xchannel containing data we've already received from
 
-      * the X client, and preload it with a CHANNEL_DATA message
 
-      * containing our own made-up authorisation greeting and any
 
-      * additional data sent from the server so far.
 
-      */
 
-     xc = share_add_xchannel(cs, upstream_id, server_id);
 
-     greeting = x11_make_greeting(endian, protomajor, protominor,
 
-                                  chan->x11_auth_proto,
 
-                                  chan->x11_auth_data, chan->x11_auth_datalen,
 
-                                  peer_addr, peer_port, &greeting_len);
 
-     packet = strbuf_new();
 
-     put_uint32(packet, 0); /* leave the channel id field unfilled - we
 
-                             * don't know the downstream id yet */
 
-     put_uint32(packet, greeting_len + initial_len);
 
-     put_data(packet, greeting, greeting_len);
 
-     put_data(packet, initial_data, initial_len);
 
-     sfree(greeting);
 
-     share_xchannel_add_message(xc, SSH2_MSG_CHANNEL_DATA,
 
-                                packet->s, packet->len);
 
-     strbuf_free(packet);
 
-     xc->window = client_adjusted_window + greeting_len;
 
-     /*
 
-      * Send on a CHANNEL_OPEN to downstream.
 
-      */
 
-     packet = strbuf_new();
 
-     put_stringz(packet, "x11");
 
-     put_uint32(packet, server_id);
 
-     put_uint32(packet, server_currwin);
 
-     put_uint32(packet, server_maxpkt);
 
-     put_stringz(packet, peer_addr);
 
-     put_uint32(packet, peer_port);
 
-     send_packet_to_downstream(cs, SSH2_MSG_CHANNEL_OPEN,
 
-                               packet->s, packet->len, NULL);
 
-     strbuf_free(packet);
 
-     /*
 
-      * If this was a once-only X forwarding, clean it up now.
 
-      */
 
-     if (chan->x11_one_shot) {
 
-         ssh_remove_sharing_x11_display(cs->parent->cl,
 
-                                        chan->x11_auth_upstream);
 
-         chan->x11_auth_upstream = NULL;
 
-         sfree(chan->x11_auth_data);
 
-         chan->x11_auth_proto = -1;
 
-         chan->x11_auth_datalen = 0;
 
-         chan->x11_one_shot = 0;
 
-     }
 
- }
 
- void share_got_pkt_from_server(ssh_sharing_connstate *cs, int type,
 
-                                const void *vpkt, int pktlen)
 
- {
 
-     const unsigned char *pkt = (const unsigned char *)vpkt;
 
-     struct share_globreq *globreq;
 
-     size_t id_pos;
 
-     unsigned upstream_id, server_id;
 
-     struct share_channel *chan;
 
-     struct share_xchannel *xc;
 
-     BinarySource src[1];
 
-     BinarySource_BARE_INIT(src, pkt, pktlen);
 
-     switch (type) {
 
-       case SSH2_MSG_REQUEST_SUCCESS:
 
-       case SSH2_MSG_REQUEST_FAILURE:
 
-         globreq = cs->globreq_head;
 
-         assert(globreq);         /* should match the queue in ssh.c */
 
-         if (globreq->type == GLOBREQ_TCPIP_FORWARD) {
 
-             if (type == SSH2_MSG_REQUEST_FAILURE) {
 
-                 share_remove_forwarding(cs, globreq->fwd);
 
-             } else {
 
-                 globreq->fwd->active = TRUE;
 
-             }
 
-         } else if (globreq->type == GLOBREQ_CANCEL_TCPIP_FORWARD) {
 
-             if (type == SSH2_MSG_REQUEST_SUCCESS) {
 
-                 share_remove_forwarding(cs, globreq->fwd);
 
-             }
 
-         }
 
-         if (globreq->want_reply) {
 
-             send_packet_to_downstream(cs, type, pkt, pktlen, NULL);
 
-         }
 
-         cs->globreq_head = globreq->next;
 
-         sfree(globreq);
 
-         if (cs->globreq_head == NULL)
 
-             cs->globreq_tail = NULL;
 
-         if (!cs->sock) {
 
-             /* Retry cleaning up this connection, in case that reply
 
-              * was the last thing we were waiting for. */
 
-             share_try_cleanup(cs);
 
-         }
 
-         break;
 
-       case SSH2_MSG_CHANNEL_OPEN:
 
-         get_string(src);
 
-         server_id = get_uint32(src);
 
-         assert(!get_err(src));
 
-         share_add_halfchannel(cs, server_id);
 
-         send_packet_to_downstream(cs, type, pkt, pktlen, NULL);
 
-         break;
 
-       case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
 
-       case SSH2_MSG_CHANNEL_OPEN_FAILURE:
 
-       case SSH2_MSG_CHANNEL_CLOSE:
 
-       case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
 
-       case SSH2_MSG_CHANNEL_DATA:
 
-       case SSH2_MSG_CHANNEL_EXTENDED_DATA:
 
-       case SSH2_MSG_CHANNEL_EOF:
 
-       case SSH2_MSG_CHANNEL_REQUEST:
 
-       case SSH2_MSG_CHANNEL_SUCCESS:
 
-       case SSH2_MSG_CHANNEL_FAILURE:
 
-         /*
 
-          * All these messages have the recipient channel id as the
 
-          * first uint32 field in the packet. Substitute the downstream
 
-          * channel id for our one and pass the packet downstream.
 
-          */
 
-         id_pos = src->pos;
 
-         upstream_id = get_uint32(src);
 
-         if ((chan = share_find_channel_by_upstream(cs, upstream_id)) != NULL) {
 
-             /*
 
-              * The normal case: this id refers to an open channel.
 
-              */
 
-             unsigned char *rewritten = snewn(pktlen, unsigned char);
 
-             memcpy(rewritten, pkt, pktlen);
 
-             PUT_32BIT(rewritten + id_pos, chan->downstream_id);
 
-             send_packet_to_downstream(cs, type, rewritten, pktlen, chan);
 
-             sfree(rewritten);
 
-             /*
 
-              * Update the channel state, for messages that need it.
 
-              */
 
-             if (type == SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
 
-                 if (chan->state == UNACKNOWLEDGED && pktlen >= 8) {
 
-                     share_channel_set_server_id(cs, chan, GET_32BIT(pkt+4),
 
-                                                 OPEN);
 
-                     if (!cs->sock) {
 
-                         /* Retry cleaning up this connection, so that we
 
-                          * can send an immediate CLOSE on this channel for
 
-                          * which we now know the server id. */
 
-                         share_try_cleanup(cs);
 
-                     }
 
-                 }
 
-             } else if (type == SSH2_MSG_CHANNEL_OPEN_FAILURE) {
 
-                 ssh_delete_sharing_channel(cs->parent->cl, chan->upstream_id);
 
-                 share_remove_channel(cs, chan);
 
-             } else if (type == SSH2_MSG_CHANNEL_CLOSE) {
 
-                 if (chan->state == SENT_CLOSE) {
 
-                     ssh_delete_sharing_channel(cs->parent->cl,
 
-                                                chan->upstream_id);
 
-                     share_remove_channel(cs, chan);
 
-                     if (!cs->sock) {
 
-                         /* Retry cleaning up this connection, in case this
 
-                          * channel closure was the last thing we were
 
-                          * waiting for. */
 
-                         share_try_cleanup(cs);
 
-                     }
 
-                 } else {
 
-                     chan->state = RCVD_CLOSE;
 
-                 }
 
-             }
 
-         } else if ((xc = share_find_xchannel_by_upstream(cs, upstream_id))
 
-                    != NULL) {
 
-             /*
 
-              * The unusual case: this id refers to an xchannel. Add it
 
-              * to the xchannel's queue.
 
-              */
 
-             share_xchannel_add_message(xc, type, pkt, pktlen);
 
-             /* If the xchannel is dead, then also respond to it (which
 
-              * may involve deleting the channel). */
 
-             if (!xc->live)
 
-                 share_dead_xchannel_respond(cs, xc);
 
-         }
 
-         break;
 
-       default:
 
-         assert(!"This packet type should never have come from ssh.c");
 
-         break;
 
-     }
 
- }
 
- static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
 
-                                           int type,
 
-                                           unsigned char *pkt, int pktlen)
 
- {
 
-     ptrlen request_name;
 
-     struct share_forwarding *fwd;
 
-     size_t id_pos;
 
-     unsigned maxpkt;
 
-     unsigned old_id, new_id, server_id;
 
-     struct share_globreq *globreq;
 
-     struct share_channel *chan;
 
-     struct share_halfchannel *hc;
 
-     struct share_xchannel *xc;
 
-     strbuf *packet;
 
-     char *err = NULL;
 
-     BinarySource src[1];
 
-     size_t wantreplypos;
 
-     int orig_wantreply;
 
-     BinarySource_BARE_INIT(src, pkt, pktlen);
 
-     switch (type) {
 
-       case SSH2_MSG_DISCONNECT:
 
-         /*
 
-          * This message stops here: if downstream is disconnecting
 
-          * from us, that doesn't mean we want to disconnect from the
 
-          * SSH server. Close the downstream connection and start
 
-          * cleanup.
 
-          */
 
-         share_begin_cleanup(cs);
 
-         break;
 
-       case SSH2_MSG_GLOBAL_REQUEST:
 
-         /*
 
-          * The only global requests we understand are "tcpip-forward"
 
-          * and "cancel-tcpip-forward". Since those require us to
 
-          * maintain state, we must assume that other global requests
 
-          * will probably require that too, and so we don't forward on
 
-          * any request we don't understand.
 
-          */
 
-         request_name = get_string(src);
 
-         wantreplypos = src->pos;
 
-         orig_wantreply = get_bool(src);
 
-         if (ptrlen_eq_string(request_name, "tcpip-forward")) {
 
-             ptrlen hostpl;
 
-             char *host;
 
-             int port;
 
-             struct ssh_rportfwd *rpf;
 
-             /*
 
-              * Pick the packet apart to find the want_reply field and
 
-              * the host/port we're going to ask to listen on.
 
-              */
 
-             hostpl = get_string(src);
 
-             port = toint(get_uint32(src));
 
-             if (get_err(src)) {
 
-                 err = dupprintf("Truncated GLOBAL_REQUEST packet");
 
-                 goto confused;
 
-             }
 
-             host = mkstr(hostpl);
 
-             /*
 
-              * See if we can allocate space in ssh.c's tree of remote
 
-              * port forwardings. If we can't, it's because another
 
-              * client sharing this connection has already allocated
 
-              * the identical port forwarding, so we take it on
 
-              * ourselves to manufacture a failure packet and send it
 
-              * back to downstream.
 
-              */
 
-             rpf = ssh_rportfwd_alloc(
 
-                 cs->parent->cl, host, port, NULL, 0, 0, NULL, NULL, cs);
 
-             if (!rpf) {
 
-                 if (orig_wantreply) {
 
-                     send_packet_to_downstream(cs, SSH2_MSG_REQUEST_FAILURE,
 
-                                               "", 0, NULL);
 
-                 }
 
-             } else {
 
-                 /*
 
-                  * We've managed to make space for this forwarding
 
-                  * locally. Pass the request on to the SSH server, but
 
-                  * set want_reply even if it wasn't originally set, so
 
-                  * that we know whether this forwarding needs to be
 
-                  * cleaned up if downstream goes away.
 
-                  */
 
-                 pkt[wantreplypos] = 1;
 
-                 ssh_send_packet_from_downstream
 
-                     (cs->parent->cl, cs->id, type, pkt, pktlen,
 
-                      orig_wantreply ? NULL : "upstream added want_reply flag");
 
-                 fwd = share_add_forwarding(cs, host, port);
 
-                 ssh_sharing_queue_global_request(cs->parent->cl, cs);
 
-                 if (fwd) {
 
-                     globreq = snew(struct share_globreq);
 
-                     globreq->next = NULL;
 
-                     if (cs->globreq_tail)
 
-                         cs->globreq_tail->next = globreq;
 
-                     else
 
-                         cs->globreq_head = globreq;
 
-                     globreq->fwd = fwd;
 
-                     globreq->want_reply = orig_wantreply;
 
-                     globreq->type = GLOBREQ_TCPIP_FORWARD;
 
-                     fwd->rpf = rpf;
 
-                 }
 
-             }
 
-             sfree(host);
 
-         } else if (ptrlen_eq_string(request_name, "cancel-tcpip-forward")) {
 
-             ptrlen hostpl;
 
-             char *host;
 
-             int port;
 
-             struct share_forwarding *fwd;
 
-             /*
 
-              * Pick the packet apart to find the want_reply field and
 
-              * the host/port we're going to ask to listen on.
 
-              */
 
-             hostpl = get_string(src);
 
-             port = toint(get_uint32(src));
 
-             if (get_err(src)) {
 
-                 err = dupprintf("Truncated GLOBAL_REQUEST packet");
 
-                 goto confused;
 
-             }
 
-             host = mkstr(hostpl);
 
-             /*
 
-              * Look up the existing forwarding with these details.
 
-              */
 
-             fwd = share_find_forwarding(cs, host, port);
 
-             if (!fwd) {
 
-                 if (orig_wantreply) {
 
-                     send_packet_to_downstream(cs, SSH2_MSG_REQUEST_FAILURE,
 
-                                               "", 0, NULL);
 
-                 }
 
-             } else {
 
-                 /*
 
-                  * Tell ssh.c to stop sending us channel-opens for
 
-                  * this forwarding.
 
-                  */
 
-                 ssh_rportfwd_remove(cs->parent->cl, fwd->rpf);
 
-                 /*
 
-                  * Pass the cancel request on to the SSH server, but
 
-                  * set want_reply even if it wasn't originally set, so
 
-                  * that _we_ know whether the forwarding has been
 
-                  * deleted even if downstream doesn't want to know.
 
-                  */
 
-                 pkt[wantreplypos] = 1;
 
-                 ssh_send_packet_from_downstream
 
-                     (cs->parent->cl, cs->id, type, pkt, pktlen,
 
-                      orig_wantreply ? NULL : "upstream added want_reply flag");
 
-                 ssh_sharing_queue_global_request(cs->parent->cl, cs);
 
-                 /*
 
-                  * And queue a globreq so that when the reply comes
 
-                  * back we know to cancel it.
 
-                  */
 
-                 globreq = snew(struct share_globreq);
 
-                 globreq->next = NULL;
 
-                 if (cs->globreq_tail)
 
-                     cs->globreq_tail->next = globreq;
 
-                 else
 
-                     cs->globreq_head = globreq;
 
-                 globreq->fwd = fwd;
 
-                 globreq->want_reply = orig_wantreply;
 
-                 globreq->type = GLOBREQ_CANCEL_TCPIP_FORWARD;
 
-             }
 
-             sfree(host);
 
-         } else {
 
-             /*
 
-              * Request we don't understand. Manufacture a failure
 
-              * message if an answer was required.
 
-              */
 
-             if (orig_wantreply)
 
-                 send_packet_to_downstream(cs, SSH2_MSG_REQUEST_FAILURE,
 
-                                           "", 0, NULL);
 
-         }
 
-         break;
 
-       case SSH2_MSG_CHANNEL_OPEN:
 
-         /* Sender channel id comes after the channel type string */
 
-         get_string(src);
 
-         id_pos = src->pos;
 
-         old_id = get_uint32(src);
 
-         new_id = ssh_alloc_sharing_channel(cs->parent->cl, cs);
 
-         get_uint32(src);               /* skip initial window size */
 
-         maxpkt = get_uint32(src);
 
-         if (get_err(src)) {
 
-             err = dupprintf("Truncated CHANNEL_OPEN packet");
 
-             goto confused;
 
-         }
 
-         share_add_channel(cs, old_id, new_id, 0, UNACKNOWLEDGED, maxpkt);
 
-         PUT_32BIT(pkt + id_pos, new_id);
 
-         ssh_send_packet_from_downstream(cs->parent->cl, cs->id,
 
-                                         type, pkt, pktlen, NULL);
 
-         break;
 
-       case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
 
-         if (pktlen < 16) {
 
-             err = dupprintf("Truncated CHANNEL_OPEN_CONFIRMATION packet");
 
-             goto confused;
 
-         }
 
-         server_id = get_uint32(src);
 
-         id_pos = src->pos;
 
-         old_id = get_uint32(src);
 
-         get_uint32(src);               /* skip initial window size */
 
-         maxpkt = get_uint32(src);
 
-         if (get_err(src)) {
 
-             err = dupprintf("Truncated CHANNEL_OPEN_CONFIRMATION packet");
 
-             goto confused;
 
-         }
 
-         /* This server id may refer to either a halfchannel or an xchannel. */
 
-         hc = NULL, xc = NULL;          /* placate optimiser */
 
-         if ((hc = share_find_halfchannel(cs, server_id)) != NULL) {
 
-             new_id = ssh_alloc_sharing_channel(cs->parent->cl, cs);
 
-         } else if ((xc = share_find_xchannel_by_server(cs, server_id))
 
-                    != NULL) {
 
-             new_id = xc->upstream_id;
 
-         } else {
 
-             err = dupprintf("CHANNEL_OPEN_CONFIRMATION packet cited unknown channel %u", (unsigned)server_id);
 
-             goto confused;
 
-         }
 
-             
 
-         PUT_32BIT(pkt + id_pos, new_id);
 
-         chan = share_add_channel(cs, old_id, new_id, server_id, OPEN, maxpkt);
 
-         if (hc) {
 
-             ssh_send_packet_from_downstream(cs->parent->cl, cs->id,
 
-                                             type, pkt, pktlen, NULL);
 
-             share_remove_halfchannel(cs, hc);
 
-         } else if (xc) {
 
-             unsigned downstream_window = GET_32BIT(pkt + 8);
 
-             if (downstream_window < 256) {
 
-                 err = dupprintf("Initial window size for x11 channel must be at least 256 (got %u)", downstream_window);
 
-                 goto confused;
 
-             }
 
-             share_xchannel_confirmation(cs, xc, chan, downstream_window);
 
-             share_remove_xchannel(cs, xc);
 
-         }
 
-         break;
 
-       case SSH2_MSG_CHANNEL_OPEN_FAILURE:
 
-         server_id = get_uint32(src);
 
-         if (get_err(src)) {
 
-             err = dupprintf("Truncated CHANNEL_OPEN_FAILURE packet");
 
-             goto confused;
 
-         }
 
-         /* This server id may refer to either a halfchannel or an xchannel. */
 
-         if ((hc = share_find_halfchannel(cs, server_id)) != NULL) {
 
-             ssh_send_packet_from_downstream(cs->parent->cl, cs->id,
 
-                                             type, pkt, pktlen, NULL);
 
-             share_remove_halfchannel(cs, hc);
 
-         } else if ((xc = share_find_xchannel_by_server(cs, server_id))
 
-                    != NULL) {
 
-             share_xchannel_failure(cs, xc);
 
-         } else {
 
-             err = dupprintf("CHANNEL_OPEN_FAILURE packet cited unknown channel %u", (unsigned)server_id);
 
-             goto confused;
 
-         }
 
-         break;
 
-       case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
 
-       case SSH2_MSG_CHANNEL_DATA:
 
-       case SSH2_MSG_CHANNEL_EXTENDED_DATA:
 
-       case SSH2_MSG_CHANNEL_EOF:
 
-       case SSH2_MSG_CHANNEL_CLOSE:
 
-       case SSH2_MSG_CHANNEL_REQUEST:
 
-       case SSH2_MSG_CHANNEL_SUCCESS:
 
-       case SSH2_MSG_CHANNEL_FAILURE:
 
-       case SSH2_MSG_IGNORE:
 
-       case SSH2_MSG_DEBUG:
 
-         server_id = get_uint32(src);
 
-         if (type == SSH2_MSG_CHANNEL_REQUEST) {
 
-             request_name = get_string(src);
 
-             /*
 
-              * Agent forwarding requests from downstream are treated
 
-              * specially. Because OpenSSHD doesn't let us enable agent
 
-              * forwarding independently per session channel, and in
 
-              * particular because the OpenSSH-defined agent forwarding
 
-              * protocol does not mark agent-channel requests with the
 
-              * id of the session channel they originate from, the only
 
-              * way we can implement agent forwarding in a
 
-              * connection-shared PuTTY is to forward the _upstream_
 
-              * agent. Hence, we unilaterally deny agent forwarding
 
-              * requests from downstreams if we aren't prepared to
 
-              * forward an agent ourselves.
 
-              *
 
-              * (If we are, then we dutifully pass agent forwarding
 
-              * requests upstream. OpenSSHD has the curious behaviour
 
-              * that all but the first such request will be rejected,
 
-              * but all session channels opened after the first request
 
-              * get agent forwarding enabled whether they ask for it or
 
-              * not; but that's not our concern, since other SSH
 
-              * servers supporting the same piece of protocol might in
 
-              * principle at least manage to enable agent forwarding on
 
-              * precisely the channels that requested it, even if the
 
-              * subsequent CHANNEL_OPENs still can't be associated with
 
-              * a parent session channel.)
 
-              */
 
-             if (ptrlen_eq_string(request_name, "[email protected]") &&
 
-                 !ssh_agent_forwarding_permitted(cs->parent->cl)) {
 
-                 chan = share_find_channel_by_server(cs, server_id);
 
-                 if (chan) {
 
-                     packet = strbuf_new();
 
-                     put_uint32(packet, chan->downstream_id);
 
-                     send_packet_to_downstream(
 
-                         cs, SSH2_MSG_CHANNEL_FAILURE,
 
-                         packet->s, packet->len, NULL);
 
-                     strbuf_free(packet);
 
-                 } else {
 
-                     char *buf = dupprintf("Agent forwarding request for "
 
-                                           "unrecognised channel %u", server_id);
 
-                     share_disconnect(cs, buf);
 
-                     sfree(buf);
 
-                     return;
 
-                 }
 
-                 break;
 
-             }
 
-             /*
 
-              * Another thing we treat specially is X11 forwarding
 
-              * requests. For these, we have to make up another set of
 
-              * X11 auth data, and enter it into our SSH connection's
 
-              * list of possible X11 authorisation credentials so that
 
-              * when we see an X11 channel open request we can know
 
-              * whether it's one to handle locally or one to pass on to
 
-              * a downstream, and if the latter, which one.
 
-              */
 
-             if (ptrlen_eq_string(request_name, "x11-req")) {
 
-                 int want_reply, single_connection, screen;
 
-                 ptrlen auth_data;
 
-                 int auth_proto;
 
-                 chan = share_find_channel_by_server(cs, server_id);
 
-                 if (!chan) {
 
-                     char *buf = dupprintf("X11 forwarding request for "
 
-                                           "unrecognised channel %u", server_id);
 
-                     share_disconnect(cs, buf);
 
-                     sfree(buf);
 
-                     return;
 
-                 }
 
-                 /*
 
-                  * Pick apart the whole message to find the downstream
 
-                  * auth details.
 
-                  */
 
-                 want_reply = get_bool(src);
 
-                 single_connection = get_bool(src);
 
-                 auth_proto = x11_identify_auth_proto(get_string(src));
 
-                 auth_data = get_string(src);
 
-                 screen = toint(get_uint32(src));
 
-                 if (get_err(src)) {
 
-                     err = dupprintf("Truncated CHANNEL_REQUEST(\"x11-req\")"
 
-                                     " packet");
 
-                     goto confused;
 
-                 }
 
-                 if (auth_proto < 0) {
 
-                     /* Reject due to not understanding downstream's
 
-                      * requested authorisation method. */
 
-                     packet = strbuf_new();
 
-                     put_uint32(packet, chan->downstream_id);
 
-                     send_packet_to_downstream(
 
-                         cs, SSH2_MSG_CHANNEL_FAILURE,
 
-                         packet->s, packet->len, NULL);
 
-                     strbuf_free(packet);
 
-                     break;
 
-                 }
 
-                 chan->x11_auth_proto = auth_proto;
 
-                 chan->x11_auth_data = x11_dehexify(auth_data,
 
-                                                    &chan->x11_auth_datalen);
 
-                 chan->x11_auth_upstream =
 
-                     ssh_add_sharing_x11_display(cs->parent->cl, auth_proto,
 
-                                                 cs, chan);
 
-                 chan->x11_one_shot = single_connection;
 
-                 /*
 
-                  * Now construct a replacement X forwarding request,
 
-                  * containing our own auth data, and send that to the
 
-                  * server.
 
-                  */
 
-                 packet = strbuf_new();
 
-                 put_uint32(packet, server_id);
 
-                 put_stringz(packet, "x11-req");
 
-                 put_bool(packet, want_reply);
 
-                 put_bool(packet, single_connection);
 
-                 put_stringz(packet, chan->x11_auth_upstream->protoname);
 
-                 put_stringz(packet, chan->x11_auth_upstream->datastring);
 
-                 put_uint32(packet, screen);
 
-                 ssh_send_packet_from_downstream(
 
-                     cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_REQUEST,
 
-                     packet->s, packet->len, NULL);
 
-                 strbuf_free(packet);
 
-                 break;
 
-             }
 
-         }
 
-         ssh_send_packet_from_downstream(cs->parent->cl, cs->id,
 
-                                         type, pkt, pktlen, NULL);
 
-         if (type == SSH2_MSG_CHANNEL_CLOSE && pktlen >= 4) {
 
-             chan = share_find_channel_by_server(cs, server_id);
 
-             if (chan) {
 
-                 if (chan->state == RCVD_CLOSE) {
 
-                     ssh_delete_sharing_channel(cs->parent->cl,
 
-                                                chan->upstream_id);
 
-                     share_remove_channel(cs, chan);
 
-                 } else {
 
-                     chan->state = SENT_CLOSE;
 
-                 }
 
-             }
 
-         }
 
-         break;
 
-       default:
 
-         err = dupprintf("Unexpected packet type %d\n", type);
 
-         goto confused;
 
-         /*
 
-          * Any other packet type is unexpected. In particular, we
 
-          * never pass GLOBAL_REQUESTs downstream, so we never expect
 
-          * to see SSH2_MSG_REQUEST_{SUCCESS,FAILURE}.
 
-          */
 
-       confused:
 
-         assert(err != NULL);
 
-         share_disconnect(cs, err);
 
-         sfree(err);
 
-         break;
 
-     }
 
- }
 
- /*
 
-  * Coroutine macros similar to, but simplified from, those in ssh.c.
 
-  */
 
- #define crBegin(v)	{ int *crLine = &v; switch(v) { case 0:;
 
- #define crFinishV	} *crLine = 0; return; }
 
- #define crGetChar(c) do                                         \
 
-     {                                                           \
 
-         while (len == 0) {                                      \
 
-             *crLine =__LINE__; return; case __LINE__:;          \
 
-         }                                                       \
 
-         len--;                                                  \
 
-         (c) = (unsigned char)*data++;                           \
 
-     } while (0)
 
- static void share_receive(Plug *plug, int urgent, char *data, int len)
 
- {
 
-     ssh_sharing_connstate *cs = container_of(
 
-         plug, ssh_sharing_connstate, plug);
 
-     static const char expected_verstring_prefix[] =
 
-         "[email protected]";
 
-     unsigned char c;
 
-     crBegin(cs->crLine);
 
-     /*
 
-      * First read the version string from downstream.
 
-      */
 
-     cs->recvlen = 0;
 
-     while (1) {
 
-         crGetChar(c);
 
-         if (c == '\012')
 
-             break;
 
-         if (cs->recvlen >= sizeof(cs->recvbuf)) {
 
-             char *buf = dupprintf("Version string far too long\n");
 
-             share_disconnect(cs, buf);
 
-             sfree(buf);
 
-             goto dead;
 
-         }
 
-         cs->recvbuf[cs->recvlen++] = c;
 
-     }
 
-     /*
 
-      * Now parse the version string to make sure it's at least vaguely
 
-      * sensible, and log it.
 
-      */
 
-     if (cs->recvlen < sizeof(expected_verstring_prefix)-1 ||
 
-         memcmp(cs->recvbuf, expected_verstring_prefix,
 
-                sizeof(expected_verstring_prefix) - 1)) {
 
-         char *buf = dupprintf("Version string did not have expected prefix\n");
 
-         share_disconnect(cs, buf);
 
-         sfree(buf);
 
-         goto dead;
 
-     }
 
-     if (cs->recvlen > 0 && cs->recvbuf[cs->recvlen-1] == '\015')
 
-         cs->recvlen--;                 /* trim off \r before \n */
 
-     log_downstream(cs, "Downstream version string: %.*s",
 
-                    cs->recvlen, cs->recvbuf);
 
-     cs->got_verstring = TRUE;
 
-     /*
 
-      * Loop round reading packets.
 
-      */
 
-     while (1) {
 
-         cs->recvlen = 0;
 
-         while (cs->recvlen < 4) {
 
-             crGetChar(c);
 
-             cs->recvbuf[cs->recvlen++] = c;
 
-         }
 
-         cs->curr_packetlen = toint(GET_32BIT(cs->recvbuf) + 4);
 
-         if (cs->curr_packetlen < 5 ||
 
-             cs->curr_packetlen > sizeof(cs->recvbuf)) {
 
-             char *buf = dupprintf("Bad packet length %u\n",
 
-                                   (unsigned)cs->curr_packetlen);
 
-             share_disconnect(cs, buf);
 
-             sfree(buf);
 
-             goto dead;
 
-         }
 
-         while (cs->recvlen < cs->curr_packetlen) {
 
-             crGetChar(c);
 
-             cs->recvbuf[cs->recvlen++] = c;
 
-         }
 
-         share_got_pkt_from_downstream(cs, cs->recvbuf[4],
 
-                                       cs->recvbuf + 5, cs->recvlen - 5);
 
-     }
 
-   dead:;
 
-     crFinishV;
 
- }
 
- static void share_sent(Plug *plug, int bufsize)
 
- {
 
-     /* ssh_sharing_connstate *cs = container_of(
 
-         plug, ssh_sharing_connstate, plug); */
 
-     /*
 
-      * We do nothing here, because we expect that there won't be a
 
-      * need to throttle and unthrottle the connection to a downstream.
 
-      * It should automatically throttle itself: if the SSH server
 
-      * sends huge amounts of data on all channels then it'll run out
 
-      * of window until our downstream sends it back some
 
-      * WINDOW_ADJUSTs.
 
-      */
 
- }
 
- static void share_listen_closing(Plug *plug, const char *error_msg,
 
- 				 int error_code, int calling_back)
 
- {
 
-     ssh_sharing_state *sharestate =
 
-         container_of(plug, ssh_sharing_state, plug);
 
-     if (error_msg)
 
-         log_general(sharestate, "listening socket: %s", error_msg);
 
-     sk_close(sharestate->listensock);
 
-     sharestate->listensock = NULL;
 
- }
 
- static void share_send_verstring(ssh_sharing_connstate *cs)
 
- {
 
-     char *fullstring = dupcat("[email protected]",
 
-                               cs->parent->server_verstring, "\015\012", NULL);
 
-     sk_write(cs->sock, fullstring, strlen(fullstring));
 
-     sfree(fullstring);
 
-     cs->sent_verstring = TRUE;
 
- }
 
- int share_ndownstreams(ssh_sharing_state *sharestate)
 
- {
 
-     return count234(sharestate->connections);
 
- }
 
- void share_activate(ssh_sharing_state *sharestate,
 
-                     const char *server_verstring)
 
- {
 
-     /*
 
-      * Indication from ssh.c that we are now ready to begin serving
 
-      * any downstreams that have already connected to us.
 
-      */
 
-     struct ssh_sharing_connstate *cs;
 
-     int i;
 
-     /*
 
-      * Trim the server's version string down to just the software
 
-      * version component, removing "SSH-2.0-" or whatever at the
 
-      * front.
 
-      */
 
-     for (i = 0; i < 2; i++) {
 
-         server_verstring += strcspn(server_verstring, "-");
 
-         if (*server_verstring)
 
-             server_verstring++;
 
-     }
 
-     sharestate->server_verstring = dupstr(server_verstring);
 
-     for (i = 0; (cs = (struct ssh_sharing_connstate *)
 
-                  index234(sharestate->connections, i)) != NULL; i++) {
 
-         assert(!cs->sent_verstring);
 
-         share_send_verstring(cs);
 
-     }
 
- }
 
- static const PlugVtable ssh_sharing_conn_plugvt = {
 
-     NULL, /* no log function, because that's for outgoing connections */
 
-     share_closing,
 
-     share_receive,
 
-     share_sent,
 
-     NULL /* no accepting function, because we've already done it */
 
- };
 
- static int share_listen_accepting(Plug *plug,
 
-                                   accept_fn_t constructor, accept_ctx_t ctx)
 
- {
 
-     struct ssh_sharing_state *sharestate = container_of(
 
-         plug, struct ssh_sharing_state, plug);
 
-     struct ssh_sharing_connstate *cs;
 
-     const char *err;
 
-     SocketPeerInfo *peerinfo;
 
-     /*
 
-      * A new downstream has connected to us.
 
-      */
 
-     cs = snew(struct ssh_sharing_connstate);
 
-     cs->plug.vt = &ssh_sharing_conn_plugvt;
 
-     cs->parent = sharestate;
 
-     if ((cs->id = share_find_unused_id(sharestate, sharestate->nextid)) == 0 &&
 
-         (cs->id = share_find_unused_id(sharestate, 1)) == 0) {
 
-         sfree(cs);
 
-         return 1;
 
-     }
 
-     sharestate->nextid = cs->id + 1;
 
-     if (sharestate->nextid == 0)
 
-         sharestate->nextid++; /* only happens in VERY long-running upstreams */
 
-     cs->sock = constructor(ctx, &cs->plug);
 
-     if ((err = sk_socket_error(cs->sock)) != NULL) {
 
-         sfree(cs);
 
- 	return err != NULL;
 
-     }
 
-     sk_set_frozen(cs->sock, 0);
 
-     add234(cs->parent->connections, cs);
 
-     cs->sent_verstring = FALSE;
 
-     if (sharestate->server_verstring)
 
-         share_send_verstring(cs);
 
-     cs->got_verstring = FALSE;
 
-     cs->recvlen = 0;
 
-     cs->crLine = 0;
 
-     cs->halfchannels = newtree234(share_halfchannel_cmp);
 
-     cs->channels_by_us = newtree234(share_channel_us_cmp);
 
-     cs->channels_by_server = newtree234(share_channel_server_cmp);
 
-     cs->xchannels_by_us = newtree234(share_xchannel_us_cmp);
 
-     cs->xchannels_by_server = newtree234(share_xchannel_server_cmp);
 
-     cs->forwardings = newtree234(share_forwarding_cmp);
 
-     cs->globreq_head = cs->globreq_tail = NULL;
 
-     peerinfo = sk_peer_info(cs->sock);
 
-     log_downstream(cs, "connected%s%s",
 
-                    (peerinfo && peerinfo->log_text ? " from " : ""),
 
-                    (peerinfo && peerinfo->log_text ? peerinfo->log_text : ""));
 
-     sk_free_peer_info(peerinfo);
 
-     return 0;
 
- }
 
- /* Per-application overrides for what roles we can take (e.g. pscp
 
-  * will never be an upstream) */
 
- extern const int share_can_be_downstream;
 
- extern const int share_can_be_upstream;
 
- /*
 
-  * Decide on the string used to identify the connection point between
 
-  * upstream and downstream (be it a Windows named pipe or a
 
-  * Unix-domain socket or whatever else).
 
-  *
 
-  * I wondered about making this a SHA hash of all sorts of pieces of
 
-  * the PuTTY configuration - essentially everything PuTTY uses to know
 
-  * where and how to make a connection, including all the proxy details
 
-  * (or rather, all the _relevant_ ones - only including settings that
 
-  * other settings didn't prevent from having any effect), plus the
 
-  * username. However, I think it's better to keep it really simple:
 
-  * the connection point identifier is derived from the hostname and
 
-  * port used to index the host-key cache (not necessarily where we
 
-  * _physically_ connected to, in cases involving proxies or
 
-  * CONF_loghost), plus the username if one is specified.
 
-  *
 
-  * The per-platform code will quite likely hash or obfuscate this name
 
-  * in turn, for privacy from other users; failing that, it might
 
-  * transform it to avoid dangerous filename characters and so on. But
 
-  * that doesn't matter to us: for us, the point is that two session
 
-  * configurations which return the same string from this function will
 
-  * be treated as potentially shareable with each other.
 
-  */
 
- char *ssh_share_sockname(const char *host, int port, Conf *conf)
 
- {
 
-     char *username = get_remote_username(conf);
 
-     char *sockname;
 
-     if (port == 22) {
 
-         if (username)
 
-             sockname = dupprintf("%s@%s", username, host);
 
-         else
 
-             sockname = dupprintf("%s", host);
 
-     } else {
 
-         if (username)
 
-             sockname = dupprintf("%s@%s:%d", username, host, port);
 
-         else
 
-             sockname = dupprintf("%s:%d", host, port);
 
-     }
 
-     sfree(username);
 
-     return sockname;
 
- }
 
- int ssh_share_test_for_upstream(const char *host, int port, Conf *conf)
 
- {
 
-     char *sockname, *logtext, *ds_err, *us_err;
 
-     int result;
 
-     Socket *sock;
 
-     sockname = ssh_share_sockname(host, port, conf);
 
-     sock = NULL;
 
-     logtext = ds_err = us_err = NULL;
 
-     result = platform_ssh_share(sockname, conf, nullplug, (Plug *)NULL, &sock,
 
-                                 &logtext, &ds_err, &us_err, FALSE, TRUE);
 
-     sfree(logtext);
 
-     sfree(ds_err);
 
-     sfree(us_err);
 
-     sfree(sockname);
 
-     if (result == SHARE_NONE) {
 
-         assert(sock == NULL);
 
-         return FALSE;
 
-     } else {
 
-         assert(result == SHARE_DOWNSTREAM);
 
-         sk_close(sock);
 
-         return TRUE;
 
-     }
 
- }
 
- static const PlugVtable ssh_sharing_listen_plugvt = {
 
-     NULL, /* no log function, because that's for outgoing connections */
 
-     share_listen_closing,
 
-     NULL, /* no receive function on a listening socket */
 
-     NULL, /* no sent function on a listening socket */
 
-     share_listen_accepting
 
- };
 
- void ssh_connshare_provide_connlayer(ssh_sharing_state *sharestate,
 
-                                      ConnectionLayer *cl)
 
- {
 
-     sharestate->cl = cl;
 
- }
 
- /*
 
-  * Init function for connection sharing. We either open a listening
 
-  * socket and become an upstream, or connect to an existing one and
 
-  * become a downstream, or do neither. We are responsible for deciding
 
-  * which of these to do (including checking the Conf to see if
 
-  * connection sharing is even enabled in the first place). If we
 
-  * become a downstream, we return the Socket with which we connected
 
-  * to the upstream; otherwise (whether or not we have established an
 
-  * upstream) we return NULL.
 
-  */
 
- Socket *ssh_connection_sharing_init(
 
-     const char *host, int port, Conf *conf, LogContext *logctx,
 
-     Plug *sshplug, ssh_sharing_state **state)
 
- {
 
-     int result, can_upstream, can_downstream;
 
-     char *logtext, *ds_err, *us_err;
 
-     char *sockname;
 
-     Socket *sock, *toret = NULL;
 
-     struct ssh_sharing_state *sharestate;
 
-     if (!conf_get_int(conf, CONF_ssh_connection_sharing))
 
-         return NULL;                   /* do not share anything */
 
-     can_upstream = share_can_be_upstream &&
 
-         conf_get_int(conf, CONF_ssh_connection_sharing_upstream);
 
-     can_downstream = share_can_be_downstream &&
 
-         conf_get_int(conf, CONF_ssh_connection_sharing_downstream);
 
-     if (!can_upstream && !can_downstream)
 
-         return NULL;
 
-     sockname = ssh_share_sockname(host, port, conf);
 
-     /*
 
-      * Create a data structure for the listening plug if we turn out
 
-      * to be an upstream.
 
-      */
 
-     sharestate = snew(struct ssh_sharing_state);
 
-     sharestate->plug.vt = &ssh_sharing_listen_plugvt;
 
-     sharestate->listensock = NULL;
 
-     sharestate->cl = NULL;
 
-     /*
 
-      * Now hand off to a per-platform routine that either connects to
 
-      * an existing upstream (using 'ssh' as the plug), establishes our
 
-      * own upstream (using 'sharestate' as the plug), or forks off a
 
-      * separate upstream and then connects to that. It will return a
 
-      * code telling us which kind of socket it put in 'sock'.
 
-      */
 
-     sock = NULL;
 
-     logtext = ds_err = us_err = NULL;
 
-     result = platform_ssh_share(
 
-         sockname, conf, sshplug, &sharestate->plug, &sock, &logtext,
 
-         &ds_err, &us_err, can_upstream, can_downstream);
 
-     switch (result) {
 
-       case SHARE_NONE:
 
-         /*
 
-          * We aren't sharing our connection at all (e.g. something
 
-          * went wrong setting the socket up). Free the upstream
 
-          * structure and return NULL.
 
-          */
 
-         if (logtext) {
 
-             /* For this result, if 'logtext' is not NULL then it is an
 
-              * error message indicating a reason why connection sharing
 
-              * couldn't be set up _at all_ */
 
-             logeventf(logctx,
 
-                       "Could not set up connection sharing: %s", logtext);
 
-         } else {
 
-             /* Failing that, ds_err and us_err indicate why we
 
-              * couldn't be a downstream and an upstream respectively */
 
-             if (ds_err)
 
-                 logeventf(logctx, "Could not set up connection sharing"
 
-                           " as downstream: %s", ds_err);
 
-             if (us_err)
 
-                 logeventf(logctx, "Could not set up connection sharing"
 
-                           " as upstream: %s", us_err);
 
-         }
 
-         assert(sock == NULL);
 
-         *state = NULL;
 
-         sfree(sharestate);
 
-         sfree(sockname);
 
-         break;
 
-       case SHARE_DOWNSTREAM:
 
-         /*
 
-          * We are downstream, so free sharestate which it turns out we
 
-          * don't need after all, and return the downstream socket as a
 
-          * replacement for an ordinary SSH connection.
 
-          */
 
-         /* 'logtext' is a local endpoint address */
 
-         logeventf(logctx, "Using existing shared connection at %s", logtext);
 
-         *state = NULL;
 
-         sfree(sharestate);
 
-         sfree(sockname);
 
-         toret = sock;
 
-         break;
 
-       case SHARE_UPSTREAM:
 
-         /*
 
-          * We are upstream. Set up sharestate properly and pass a copy
 
-          * to the caller; return NULL, to tell ssh.c that it has to
 
-          * make an ordinary connection after all.
 
-          */
 
-         /* 'logtext' is a local endpoint address */
 
-         logeventf(logctx, "Sharing this connection at %s", logtext);
 
-         *state = sharestate;
 
-         sharestate->listensock = sock;
 
-         sharestate->connections = newtree234(share_connstate_cmp);
 
-         sharestate->server_verstring = NULL;
 
-         sharestate->sockname = sockname;
 
-         sharestate->nextid = 1;
 
-         break;
 
-     }
 
-     sfree(logtext);
 
-     sfree(ds_err);
 
-     sfree(us_err);
 
-     return toret;
 
- }
 
 
  |