mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 01:48:00 +00:00
Move transient host key cache into its own file.
This is a nice standalone piece of code which doesn't really have to appear in the middle of ssh2transport.
This commit is contained in:
parent
79c4d3f3ee
commit
fe26ddb1d9
2
Recipe
2
Recipe
@ -256,7 +256,7 @@ SSH = ssh sshcommon ssh1bpp ssh2bpp ssh2bpp-bare ssh1censor ssh2censor
|
|||||||
+ sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
|
+ sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
|
||||||
+ sshaes sshccp sshsh256 sshsh512 sshbn wildcard pinger ssharcf
|
+ sshaes sshccp sshsh256 sshsh512 sshbn wildcard pinger ssharcf
|
||||||
+ sshgssc pgssapi sshshare sshecc aqsync marshal nullplug agentf
|
+ sshgssc pgssapi sshshare sshecc aqsync marshal nullplug agentf
|
||||||
+ sshmac mainchan
|
+ sshmac mainchan ssh2transhk
|
||||||
WINSSH = SSH winnoise wincapi winpgntc wingss winshare winnps winnpc
|
WINSSH = SSH winnoise wincapi winpgntc wingss winshare winnps winnpc
|
||||||
+ winhsock errsock
|
+ winhsock errsock
|
||||||
UXSSH = SSH uxnoise uxagentc uxgss uxshare
|
UXSSH = SSH uxnoise uxagentc uxgss uxshare
|
||||||
|
11
ssh.h
11
ssh.h
@ -1482,3 +1482,14 @@ void add_to_commasep(strbuf *buf, const char *data);
|
|||||||
|
|
||||||
int verify_ssh_manual_host_key(
|
int verify_ssh_manual_host_key(
|
||||||
Conf *conf, const char *fingerprint, ssh_key *key);
|
Conf *conf, const char *fingerprint, ssh_key *key);
|
||||||
|
|
||||||
|
typedef struct ssh_transient_hostkey_cache ssh_transient_hostkey_cache;
|
||||||
|
ssh_transient_hostkey_cache *ssh_transient_hostkey_cache_new(void);
|
||||||
|
void ssh_transient_hostkey_cache_free(ssh_transient_hostkey_cache *thc);
|
||||||
|
void ssh_transient_hostkey_cache_add(
|
||||||
|
ssh_transient_hostkey_cache *thc, ssh_key *key);
|
||||||
|
int ssh_transient_hostkey_cache_verify(
|
||||||
|
ssh_transient_hostkey_cache *thc, ssh_key *key);
|
||||||
|
int ssh_transient_hostkey_cache_has(
|
||||||
|
ssh_transient_hostkey_cache *thc, const ssh_keyalg *alg);
|
||||||
|
int ssh_transient_hostkey_cache_non_empty(ssh_transient_hostkey_cache *thc);
|
||||||
|
124
ssh2transhk.c
Normal file
124
ssh2transhk.c
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Data structure managing host keys in sessions based on GSSAPI KEX.
|
||||||
|
*
|
||||||
|
* In a session we started with a GSSAPI key exchange, the concept of
|
||||||
|
* 'host key' has completely different lifetime and security semantics
|
||||||
|
* from the usual ones. Per RFC 4462 section 2.1, we assume that any
|
||||||
|
* host key delivered to us in the course of a GSSAPI key exchange is
|
||||||
|
* _solely_ there to use as a transient fallback within the same
|
||||||
|
* session, if at the time of a subsequent rekey the GSS credentials
|
||||||
|
* are temporarily invalid and so a non-GSS KEX method has to be used.
|
||||||
|
*
|
||||||
|
* In particular, in a GSS-based SSH deployment, host keys may not
|
||||||
|
* even _be_ persistent identities for the server; it would be
|
||||||
|
* legitimate for a server to generate a fresh one routinely if it
|
||||||
|
* wanted to, like SSH-1 server keys.
|
||||||
|
*
|
||||||
|
* So, in this mode, we never touch the persistent host key cache at
|
||||||
|
* all, either to check keys against it _or_ to store keys in it.
|
||||||
|
* Instead, we maintain an in-memory cache of host keys that have been
|
||||||
|
* mentioned in GSS key exchanges within this particular session, and
|
||||||
|
* we permit precisely those host keys in non-GSS rekeys.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
#include "ssh.h"
|
||||||
|
|
||||||
|
struct ssh_transient_hostkey_cache {
|
||||||
|
tree234 *cache;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ssh_transient_hostkey_cache_entry {
|
||||||
|
const ssh_keyalg *alg;
|
||||||
|
strbuf *pub_blob;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ssh_transient_hostkey_cache_cmp(void *av, void *bv)
|
||||||
|
{
|
||||||
|
const struct ssh_transient_hostkey_cache_entry
|
||||||
|
*a = (const struct ssh_transient_hostkey_cache_entry *)av,
|
||||||
|
*b = (const struct ssh_transient_hostkey_cache_entry *)bv;
|
||||||
|
return strcmp(a->alg->ssh_id, b->alg->ssh_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ssh_transient_hostkey_cache_find(void *av, void *bv)
|
||||||
|
{
|
||||||
|
const ssh_keyalg *aalg = (const ssh_keyalg *)av;
|
||||||
|
const struct ssh_transient_hostkey_cache_entry
|
||||||
|
*b = (const struct ssh_transient_hostkey_cache_entry *)bv;
|
||||||
|
return strcmp(aalg->ssh_id, b->alg->ssh_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssh_transient_hostkey_cache *ssh_transient_hostkey_cache_new(void)
|
||||||
|
{
|
||||||
|
ssh_transient_hostkey_cache *thc = snew(ssh_transient_hostkey_cache);
|
||||||
|
thc->cache = newtree234(ssh_transient_hostkey_cache_cmp);
|
||||||
|
return thc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ssh_transient_hostkey_cache_free(ssh_transient_hostkey_cache *thc)
|
||||||
|
{
|
||||||
|
struct ssh_transient_hostkey_cache_entry *ent;
|
||||||
|
while ((ent = delpos234(thc->cache, 0)) != NULL) {
|
||||||
|
strbuf_free(ent->pub_blob);
|
||||||
|
sfree(ent);
|
||||||
|
}
|
||||||
|
freetree234(thc->cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ssh_transient_hostkey_cache_add(
|
||||||
|
ssh_transient_hostkey_cache *thc, ssh_key *key)
|
||||||
|
{
|
||||||
|
struct ssh_transient_hostkey_cache_entry *ent, *retd;
|
||||||
|
|
||||||
|
if ((ent = find234(thc->cache, (void *)ssh_key_alg(key),
|
||||||
|
ssh_transient_hostkey_cache_find)) != NULL) {
|
||||||
|
strbuf_free(ent->pub_blob);
|
||||||
|
sfree(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
ent = snew(struct ssh_transient_hostkey_cache_entry);
|
||||||
|
ent->alg = ssh_key_alg(key);
|
||||||
|
ent->pub_blob = strbuf_new();
|
||||||
|
ssh_key_public_blob(key, BinarySink_UPCAST(ent->pub_blob));
|
||||||
|
retd = add234(thc->cache, ent);
|
||||||
|
assert(retd == ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ssh_transient_hostkey_cache_verify(
|
||||||
|
ssh_transient_hostkey_cache *thc, ssh_key *key)
|
||||||
|
{
|
||||||
|
struct ssh_transient_hostkey_cache_entry *ent;
|
||||||
|
int toret = FALSE;
|
||||||
|
|
||||||
|
if ((ent = find234(thc->cache, (void *)ssh_key_alg(key),
|
||||||
|
ssh_transient_hostkey_cache_find)) != NULL) {
|
||||||
|
strbuf *this_blob = strbuf_new();
|
||||||
|
ssh_key_public_blob(key, BinarySink_UPCAST(this_blob));
|
||||||
|
|
||||||
|
if (this_blob->len == ent->pub_blob->len &&
|
||||||
|
!memcmp(this_blob->s, ent->pub_blob->s,
|
||||||
|
this_blob->len))
|
||||||
|
toret = TRUE;
|
||||||
|
|
||||||
|
strbuf_free(this_blob);
|
||||||
|
}
|
||||||
|
|
||||||
|
return toret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ssh_transient_hostkey_cache_has(
|
||||||
|
ssh_transient_hostkey_cache *thc, const ssh_keyalg *alg)
|
||||||
|
{
|
||||||
|
struct ssh_transient_hostkey_cache_entry *ent =
|
||||||
|
find234(thc->cache, (void *)alg,
|
||||||
|
ssh_transient_hostkey_cache_find);
|
||||||
|
return ent != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ssh_transient_hostkey_cache_non_empty(ssh_transient_hostkey_cache *thc)
|
||||||
|
{
|
||||||
|
return count234(thc->cache) > 0;
|
||||||
|
}
|
149
ssh2transport.c
149
ssh2transport.c
@ -192,8 +192,8 @@ struct ssh2_transport_state {
|
|||||||
int gss_status;
|
int gss_status;
|
||||||
time_t gss_cred_expiry; /* Re-delegate if newer */
|
time_t gss_cred_expiry; /* Re-delegate if newer */
|
||||||
unsigned long gss_ctxt_lifetime; /* Re-delegate when short */
|
unsigned long gss_ctxt_lifetime; /* Re-delegate when short */
|
||||||
tree234 *transient_hostkey_cache;
|
|
||||||
#endif
|
#endif
|
||||||
|
ssh_transient_hostkey_cache *thc;
|
||||||
|
|
||||||
int gss_kex_used;
|
int gss_kex_used;
|
||||||
|
|
||||||
@ -287,16 +287,6 @@ static const struct PacketProtocolLayerVtable ssh2_transport_vtable = {
|
|||||||
#ifndef NO_GSSAPI
|
#ifndef NO_GSSAPI
|
||||||
static void ssh2_transport_gss_update(struct ssh2_transport_state *s,
|
static void ssh2_transport_gss_update(struct ssh2_transport_state *s,
|
||||||
int definitely_rekeying);
|
int definitely_rekeying);
|
||||||
static void ssh_init_transient_hostkey_store(struct ssh2_transport_state *);
|
|
||||||
static void ssh_cleanup_transient_hostkey_store(struct ssh2_transport_state *);
|
|
||||||
static void ssh_store_transient_hostkey(
|
|
||||||
struct ssh2_transport_state *s, ssh_key *key);
|
|
||||||
static int ssh_verify_transient_hostkey(
|
|
||||||
struct ssh2_transport_state *s, ssh_key *key);
|
|
||||||
static int ssh_have_transient_hostkey(
|
|
||||||
struct ssh2_transport_state *s, const ssh_keyalg *alg);
|
|
||||||
static int ssh_have_any_transient_hostkey(
|
|
||||||
struct ssh2_transport_state *s);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int ssh2_transport_timer_update(struct ssh2_transport_state *s,
|
static int ssh2_transport_timer_update(struct ssh2_transport_state *s,
|
||||||
@ -347,8 +337,8 @@ PacketProtocolLayer *ssh2_transport_new(
|
|||||||
s->gss_cred_expiry = GSS_NO_EXPIRATION;
|
s->gss_cred_expiry = GSS_NO_EXPIRATION;
|
||||||
s->shgss->srv_name = GSS_C_NO_NAME;
|
s->shgss->srv_name = GSS_C_NO_NAME;
|
||||||
s->shgss->ctx = NULL;
|
s->shgss->ctx = NULL;
|
||||||
ssh_init_transient_hostkey_store(s);
|
|
||||||
#endif
|
#endif
|
||||||
|
s->thc = ssh_transient_hostkey_cache_new();
|
||||||
s->gss_kex_used = FALSE;
|
s->gss_kex_used = FALSE;
|
||||||
|
|
||||||
ssh2_transport_set_max_data_size(s);
|
ssh2_transport_set_max_data_size(s);
|
||||||
@ -407,9 +397,7 @@ static void ssh2_transport_free(PacketProtocolLayer *ppl)
|
|||||||
ssh_ecdhkex_freekey(s->ecdh_key);
|
ssh_ecdhkex_freekey(s->ecdh_key);
|
||||||
if (s->exhash)
|
if (s->exhash)
|
||||||
ssh_hash_free(s->exhash);
|
ssh_hash_free(s->exhash);
|
||||||
#ifndef NO_GSSAPI
|
ssh_transient_hostkey_cache_free(s->thc);
|
||||||
ssh_cleanup_transient_hostkey_store(s);
|
|
||||||
#endif
|
|
||||||
sfree(s);
|
sfree(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -860,7 +848,8 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
|
|||||||
for (j = 0; j < lenof(hostkey_algs); j++) {
|
for (j = 0; j < lenof(hostkey_algs); j++) {
|
||||||
if (hostkey_algs[j].id != s->preferred_hk[i])
|
if (hostkey_algs[j].id != s->preferred_hk[i])
|
||||||
continue;
|
continue;
|
||||||
if (ssh_have_transient_hostkey(s, hostkey_algs[j].alg)) {
|
if (ssh_transient_hostkey_cache_has(
|
||||||
|
s->thc, hostkey_algs[j].alg)) {
|
||||||
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
|
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
|
||||||
hostkey_algs[j].alg->ssh_id);
|
hostkey_algs[j].alg->ssh_id);
|
||||||
alg->u.hk.hostkey = hostkey_algs[j].alg;
|
alg->u.hk.hostkey = hostkey_algs[j].alg;
|
||||||
@ -1884,8 +1873,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
|
|||||||
if (s->gss_kex_used) {
|
if (s->gss_kex_used) {
|
||||||
/*
|
/*
|
||||||
* In a GSS-based session, check the host key (if any) against
|
* In a GSS-based session, check the host key (if any) against
|
||||||
* the transient host key cache. See comment above, at the
|
* the transient host key cache.
|
||||||
* definition of ssh_transient_hostkey_cache_entry.
|
|
||||||
*/
|
*/
|
||||||
if (s->kex_alg->main_type == KEXTYPE_GSS) {
|
if (s->kex_alg->main_type == KEXTYPE_GSS) {
|
||||||
|
|
||||||
@ -1899,8 +1887,8 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
|
|||||||
ppl_logevent(("%s", s->fingerprint));
|
ppl_logevent(("%s", s->fingerprint));
|
||||||
sfree(s->fingerprint);
|
sfree(s->fingerprint);
|
||||||
s->fingerprint = NULL;
|
s->fingerprint = NULL;
|
||||||
ssh_store_transient_hostkey(s, s->hkey);
|
ssh_transient_hostkey_cache_add(s->thc, s->hkey);
|
||||||
} else if (!ssh_have_any_transient_hostkey(s)) {
|
} else if (!ssh_transient_hostkey_cache_non_empty(s->thc)) {
|
||||||
/*
|
/*
|
||||||
* But if it didn't, then we currently have no
|
* But if it didn't, then we currently have no
|
||||||
* fallback host key to use in subsequent non-GSS
|
* fallback host key to use in subsequent non-GSS
|
||||||
@ -1957,9 +1945,9 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
|
|||||||
if (s->need_gss_transient_hostkey) {
|
if (s->need_gss_transient_hostkey) {
|
||||||
ppl_logevent(("Post-GSS rekey provided fallback host key:"));
|
ppl_logevent(("Post-GSS rekey provided fallback host key:"));
|
||||||
ppl_logevent(("%s", s->fingerprint));
|
ppl_logevent(("%s", s->fingerprint));
|
||||||
ssh_store_transient_hostkey(s, s->hkey);
|
ssh_transient_hostkey_cache_add(s->thc, s->hkey);
|
||||||
s->need_gss_transient_hostkey = FALSE;
|
s->need_gss_transient_hostkey = FALSE;
|
||||||
} else if (!ssh_verify_transient_hostkey(s, s->hkey)) {
|
} else if (!ssh_transient_hostkey_cache_verify(s->thc, s->hkey)) {
|
||||||
ppl_logevent(("Non-GSS rekey after initial GSS kex "
|
ppl_logevent(("Non-GSS rekey after initial GSS kex "
|
||||||
"used host key:"));
|
"used host key:"));
|
||||||
ppl_logevent(("%s", s->fingerprint));
|
ppl_logevent(("%s", s->fingerprint));
|
||||||
@ -2584,123 +2572,6 @@ static void ssh2_transport_gss_update(struct ssh2_transport_state *s,
|
|||||||
s->gss_status |= GSS_CTXT_EXPIRES;
|
s->gss_status |= GSS_CTXT_EXPIRES;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Data structure managing host keys in sessions based on GSSAPI KEX.
|
|
||||||
*
|
|
||||||
* In a session we started with a GSSAPI key exchange, the concept of
|
|
||||||
* 'host key' has completely different lifetime and security semantics
|
|
||||||
* from the usual ones. Per RFC 4462 section 2.1, we assume that any
|
|
||||||
* host key delivered to us in the course of a GSSAPI key exchange is
|
|
||||||
* _solely_ there to use as a transient fallback within the same
|
|
||||||
* session, if at the time of a subsequent rekey the GSS credentials
|
|
||||||
* are temporarily invalid and so a non-GSS KEX method has to be used.
|
|
||||||
*
|
|
||||||
* In particular, in a GSS-based SSH deployment, host keys may not
|
|
||||||
* even _be_ persistent identities for the server; it would be
|
|
||||||
* legitimate for a server to generate a fresh one routinely if it
|
|
||||||
* wanted to, like SSH-1 server keys.
|
|
||||||
*
|
|
||||||
* So, in this mode, we never touch the persistent host key cache at
|
|
||||||
* all, either to check keys against it _or_ to store keys in it.
|
|
||||||
* Instead, we maintain an in-memory cache of host keys that have been
|
|
||||||
* mentioned in GSS key exchanges within this particular session, and
|
|
||||||
* we permit precisely those host keys in non-GSS rekeys.
|
|
||||||
*/
|
|
||||||
struct ssh_transient_hostkey_cache_entry {
|
|
||||||
const ssh_keyalg *alg;
|
|
||||||
strbuf *pub_blob;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int ssh_transient_hostkey_cache_cmp(void *av, void *bv)
|
|
||||||
{
|
|
||||||
const struct ssh_transient_hostkey_cache_entry
|
|
||||||
*a = (const struct ssh_transient_hostkey_cache_entry *)av,
|
|
||||||
*b = (const struct ssh_transient_hostkey_cache_entry *)bv;
|
|
||||||
return strcmp(a->alg->ssh_id, b->alg->ssh_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ssh_transient_hostkey_cache_find(void *av, void *bv)
|
|
||||||
{
|
|
||||||
const ssh_keyalg *aalg = (const ssh_keyalg *)av;
|
|
||||||
const struct ssh_transient_hostkey_cache_entry
|
|
||||||
*b = (const struct ssh_transient_hostkey_cache_entry *)bv;
|
|
||||||
return strcmp(aalg->ssh_id, b->alg->ssh_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ssh_init_transient_hostkey_store(
|
|
||||||
struct ssh2_transport_state *s)
|
|
||||||
{
|
|
||||||
s->transient_hostkey_cache =
|
|
||||||
newtree234(ssh_transient_hostkey_cache_cmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ssh_cleanup_transient_hostkey_store(
|
|
||||||
struct ssh2_transport_state *s)
|
|
||||||
{
|
|
||||||
struct ssh_transient_hostkey_cache_entry *ent;
|
|
||||||
while ((ent = delpos234(s->transient_hostkey_cache, 0)) != NULL) {
|
|
||||||
strbuf_free(ent->pub_blob);
|
|
||||||
sfree(ent);
|
|
||||||
}
|
|
||||||
freetree234(s->transient_hostkey_cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ssh_store_transient_hostkey(
|
|
||||||
struct ssh2_transport_state *s, ssh_key *key)
|
|
||||||
{
|
|
||||||
struct ssh_transient_hostkey_cache_entry *ent, *retd;
|
|
||||||
|
|
||||||
if ((ent = find234(s->transient_hostkey_cache, (void *)ssh_key_alg(key),
|
|
||||||
ssh_transient_hostkey_cache_find)) != NULL) {
|
|
||||||
strbuf_free(ent->pub_blob);
|
|
||||||
sfree(ent);
|
|
||||||
}
|
|
||||||
|
|
||||||
ent = snew(struct ssh_transient_hostkey_cache_entry);
|
|
||||||
ent->alg = ssh_key_alg(key);
|
|
||||||
ent->pub_blob = strbuf_new();
|
|
||||||
ssh_key_public_blob(key, BinarySink_UPCAST(ent->pub_blob));
|
|
||||||
retd = add234(s->transient_hostkey_cache, ent);
|
|
||||||
assert(retd == ent);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ssh_verify_transient_hostkey(
|
|
||||||
struct ssh2_transport_state *s, ssh_key *key)
|
|
||||||
{
|
|
||||||
struct ssh_transient_hostkey_cache_entry *ent;
|
|
||||||
int toret = FALSE;
|
|
||||||
|
|
||||||
if ((ent = find234(s->transient_hostkey_cache, (void *)ssh_key_alg(key),
|
|
||||||
ssh_transient_hostkey_cache_find)) != NULL) {
|
|
||||||
strbuf *this_blob = strbuf_new();
|
|
||||||
ssh_key_public_blob(key, BinarySink_UPCAST(this_blob));
|
|
||||||
|
|
||||||
if (this_blob->len == ent->pub_blob->len &&
|
|
||||||
!memcmp(this_blob->s, ent->pub_blob->s,
|
|
||||||
this_blob->len))
|
|
||||||
toret = TRUE;
|
|
||||||
|
|
||||||
strbuf_free(this_blob);
|
|
||||||
}
|
|
||||||
|
|
||||||
return toret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ssh_have_transient_hostkey(
|
|
||||||
struct ssh2_transport_state *s, const ssh_keyalg *alg)
|
|
||||||
{
|
|
||||||
struct ssh_transient_hostkey_cache_entry *ent =
|
|
||||||
find234(s->transient_hostkey_cache, (void *)alg,
|
|
||||||
ssh_transient_hostkey_cache_find);
|
|
||||||
return ent != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ssh_have_any_transient_hostkey(
|
|
||||||
struct ssh2_transport_state *s)
|
|
||||||
{
|
|
||||||
return count234(s->transient_hostkey_cache) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ptrlen ssh2_transport_get_session_id(PacketProtocolLayer *ppl)
|
ptrlen ssh2_transport_get_session_id(PacketProtocolLayer *ppl)
|
||||||
{
|
{
|
||||||
struct ssh2_transport_state *s;
|
struct ssh2_transport_state *s;
|
||||||
|
Loading…
Reference in New Issue
Block a user