Added CREDENTIAL frame support.
This commit is contained in:
parent
93953c102b
commit
d83d1cd33a
|
@ -34,13 +34,15 @@ lib_LTLIBRARIES = libspdylay.la
|
|||
OBJECTS = spdylay_pq.c spdylay_map.c spdylay_queue.c \
|
||||
spdylay_buffer.c spdylay_frame.c spdylay_zlib.c \
|
||||
spdylay_session.c spdylay_helper.c spdylay_stream.c spdylay_npn.c \
|
||||
spdylay_submit.c spdylay_outbound_item.c
|
||||
spdylay_submit.c spdylay_outbound_item.c \
|
||||
spdylay_client_cert_vector.c
|
||||
|
||||
HFILES = spdylay_pq.h spdylay_int.h spdylay_map.h spdylay_queue.h \
|
||||
spdylay_buffer.h spdylay_frame.h spdylay_zlib.h \
|
||||
spdylay_session.h spdylay_helper.h spdylay_stream.h spdylay_int.h \
|
||||
spdylay_npn.h \
|
||||
spdylay_submit.h spdylay_outbound_item.h \
|
||||
spdylay_client_cert_vector.h \
|
||||
spdylay_net.h
|
||||
|
||||
libspdylay_la_SOURCES = $(HFILES) $(OBJECTS)
|
||||
|
|
|
@ -154,6 +154,11 @@ typedef enum {
|
|||
* multiple in-sequence NUL bytes).
|
||||
*/
|
||||
SPDYLAY_ERR_INVALID_HEADER_BLOCK = -518,
|
||||
/**
|
||||
* Indicates that the context is not suitable to perform the
|
||||
* requested operation.
|
||||
*/
|
||||
SPDYLAY_ERR_INVALID_STATE = -519,
|
||||
/**
|
||||
* The errors < :enum:`SPDYLAY_ERR_FATAL` mean that the library is
|
||||
* under unexpected condition and cannot process any further data
|
||||
|
@ -214,7 +219,11 @@ typedef enum {
|
|||
/**
|
||||
* The WINDOW_UPDATE control frame. This first appeared in SPDY/3.
|
||||
*/
|
||||
SPDYLAY_WINDOW_UPDATE = 9
|
||||
SPDYLAY_WINDOW_UPDATE = 9,
|
||||
/**
|
||||
* The CREDENTIAL control frame. This first appeared in SPDY/3.
|
||||
*/
|
||||
SPDYLAY_CREDENTIAL = 10
|
||||
} spdylay_frame_type;
|
||||
|
||||
/**
|
||||
|
@ -635,6 +644,52 @@ typedef struct {
|
|||
int32_t delta_window_size;
|
||||
} spdylay_window_update;
|
||||
|
||||
/**
|
||||
* @struct
|
||||
*
|
||||
* The structure to hold chunk of memory.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* The pointer to the data.
|
||||
*/
|
||||
uint8_t *data;
|
||||
/**
|
||||
* The length of the data.
|
||||
*/
|
||||
size_t length;
|
||||
} spdylay_mem_chunk;
|
||||
|
||||
/**
|
||||
* @struct
|
||||
*
|
||||
* The CREDENTIAL control frame. This first appeared in SPDY/3. It has
|
||||
* the following members:
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* The control frame header.
|
||||
*/
|
||||
spdylay_ctrl_hd hd;
|
||||
/**
|
||||
* The index in the client certificate vector.
|
||||
*/
|
||||
uint16_t slot;
|
||||
/**
|
||||
* Cryptographic proof that the client has possession of the private
|
||||
* key associated with the certificate.
|
||||
*/
|
||||
spdylay_mem_chunk proof;
|
||||
/**
|
||||
* The certificate chain. The certs[0] is the leaf certificate.
|
||||
*/
|
||||
spdylay_mem_chunk *certs;
|
||||
/**
|
||||
* The number of certificates in |certs|.
|
||||
*/
|
||||
size_t ncerts;
|
||||
} spdylay_credential;
|
||||
|
||||
/**
|
||||
* @struct
|
||||
*
|
||||
|
@ -747,6 +802,10 @@ typedef union {
|
|||
* The WINDOW_UPDATE control frame.
|
||||
*/
|
||||
spdylay_window_update window_update;
|
||||
/**
|
||||
* The CREDENTIAL control frame.
|
||||
*/
|
||||
spdylay_credential credential;
|
||||
} spdylay_frame;
|
||||
|
||||
/**
|
||||
|
@ -895,54 +954,88 @@ typedef void (*spdylay_on_stream_close_callback)
|
|||
typedef void (*spdylay_on_request_recv_callback)
|
||||
(spdylay_session *session, int32_t stream_id, void *user_data);
|
||||
|
||||
#define SPDYLAY_MAX_SCHEME 255
|
||||
#define SPDYLAY_MAX_HOSTNAME 255
|
||||
|
||||
struct spdylay_origin;
|
||||
|
||||
/**
|
||||
* @struct
|
||||
*
|
||||
* The Web origin structure. The origin is the tuple (scheme, host,
|
||||
* port). The details of this structure is intentionally hidden. To
|
||||
* access these members, use accessor functions below.
|
||||
*/
|
||||
typedef struct spdylay_origin spdylay_origin;
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns the scheme member of the |origin|.
|
||||
*/
|
||||
const char* spdylay_origin_get_scheme(const spdylay_origin *origin);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns the host member of the |origin|.
|
||||
*/
|
||||
const char* spdylay_origin_get_host(const spdylay_origin *origin);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns the port member of the |origin|.
|
||||
*/
|
||||
uint16_t spdylay_origin_get_port(const spdylay_origin *origin);
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* Callback function invoked when the library wants to know whether
|
||||
* the client certificate is required for the given |origin| and if so
|
||||
* requests the cryptographic proof for the certificate. The |origin|
|
||||
* is the hostname and port number joined with ':' (e.g.,
|
||||
* example.org:8443). The implementation of this function must assign
|
||||
* the pointer to the buffer where proof is stored to the |*proof_ptr|
|
||||
* and its length to the |*prooflen_ptr|. Return 0 if the function
|
||||
* succeeds. If no client certificate is required for the |origin|,
|
||||
* the function must return SPDYLAY_ERR_CLIENT_CERT_NOT_NEEDED.
|
||||
* (TODO: add error code)
|
||||
* Callback function invoked when the library needs the cryptographic
|
||||
* proof that the client has possession of the private key associated
|
||||
* with the certificate for the given |origin|. If called with
|
||||
* |prooflen| == 0, the implementation of this function must return
|
||||
* the length of the proof in bytes. If called with |prooflen| > 0,
|
||||
* write proof into |proof| exactly |prooflen| bytes and return 0.
|
||||
*
|
||||
* The data stored in |*proof_ptr| will be copied just after the
|
||||
* function call. This copy lives until the CREDENTIAL frame is
|
||||
* sent. Because the client certificate vector has limited number of
|
||||
* slots, the application code may be required to pass the same proof
|
||||
* more than once.
|
||||
* Because the client certificate vector has limited number of slots,
|
||||
* the application code may be required to pass the same proof more
|
||||
* than once.
|
||||
*/
|
||||
typedef int (*spdylay_get_credential_proof)
|
||||
(spdylay_session *session, const char *origin,
|
||||
uint8_t **proof_ptr, size_t *prooflen_ptr, void *user_data);
|
||||
typedef ssize_t (*spdylay_get_credential_proof)
|
||||
(spdylay_session *session, const spdylay_origin *origin,
|
||||
uint8_t *proof, size_t prooflen, void *user_data);
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* Callback function invoked when the library needs the length of the
|
||||
* client certificate chain for the given |origin|. The
|
||||
* implementation of this function must return the length of the
|
||||
* client certificate chain. If no client certificate is required for
|
||||
* the given |origin|, return 0. If positive integer is returned,
|
||||
* :type:`spdylay_get_credential_proof` and
|
||||
* :type:`spdylay_get_credential_cert` callback functions will be used
|
||||
* to get the cryptographic proof and certificate respectively.
|
||||
*/
|
||||
typedef ssize_t (*spdylay_get_credential_ncerts)
|
||||
(spdylay_session *session, const spdylay_origin *origin, void *user_data);
|
||||
|
||||
/**
|
||||
* @functypedef
|
||||
*
|
||||
* Callback function invoked when the library needs the client
|
||||
* certificate for the given |origin|. The |origin| is the hostname
|
||||
* and port number joined with ':' (e.g., example.org:8443). The
|
||||
* implementation of this function must assign the pointer to the
|
||||
* buffer where certificate is stored to the |*cert_ptr| and its
|
||||
* length to the |*certlen_ptr|. Because the library requires the
|
||||
* certificate chain, this callback function will be called repeatedly
|
||||
* to get certificate chain starting with the leaf certificate.
|
||||
* Assign 0 to |*certlen_ptr| if there is no more
|
||||
* certificate. Currently, the library does not expect for this
|
||||
* function to fail. Therefore the function must return 0.
|
||||
*
|
||||
* The data stored in |*cert_ptr| will be copied just after the
|
||||
* function call. This copy lives until the CREDENTIAL frame is
|
||||
* sent. Because the client certificate vector has limited number of
|
||||
* slots, the application code may be required to pass the same
|
||||
* certificate more than once.
|
||||
* certificate for the given |origin|. The |index| is the index of the
|
||||
* certificate chain and 0 means the leaf certificate of the chain.
|
||||
* If called with |certlen| == 0, the implementation of this function
|
||||
* must return the length of the certificate in bytes. If called with
|
||||
* |certlen| > 0, write certificate into |cert| exactly |certlen|
|
||||
* bytes and return 0.
|
||||
*/
|
||||
typedef int (*spdylay_get_credential_cert)
|
||||
(spdylay_session *session, const char* origin,
|
||||
uint8_t **cert_ptr, size_t *certlen_ptr, void *user_data);
|
||||
typedef ssize_t (*spdylay_get_credential_cert)
|
||||
(spdylay_session *session, const spdylay_origin *origin, size_t index,
|
||||
uint8_t *cert, size_t certlen, void *user_data);
|
||||
|
||||
/**
|
||||
* @struct
|
||||
|
@ -1005,6 +1098,22 @@ typedef struct {
|
|||
* received.
|
||||
*/
|
||||
spdylay_on_request_recv_callback on_request_recv_callback;
|
||||
/**
|
||||
* Callback function invoked when the library needs the
|
||||
* cryptographic proof that the client has possession of the private
|
||||
* key associated with the certificate.
|
||||
*/
|
||||
spdylay_get_credential_proof get_credential_proof;
|
||||
/**
|
||||
* Callback function invoked when the library needs the length of the
|
||||
* client certificate chain.
|
||||
*/
|
||||
spdylay_get_credential_ncerts get_credential_ncerts;
|
||||
/**
|
||||
* Callback function invoked when the library needs the client
|
||||
* certificate.
|
||||
*/
|
||||
spdylay_get_credential_cert get_credential_cert;
|
||||
} spdylay_session_callbacks;
|
||||
|
||||
/**
|
||||
|
@ -1073,6 +1182,49 @@ int spdylay_session_server_new(spdylay_session **session_ptr,
|
|||
*/
|
||||
void spdylay_session_del(spdylay_session *session);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Sets the origin tuple (|scheme|, |host| and |port) that the
|
||||
* connection is made to and the client certificate is sent in the
|
||||
* first TLS handshake. This function must be called before any call
|
||||
* of `spdylay_session_send()` and `spdylay_session_recv()` and be
|
||||
* called only once per session. This function must not be called if
|
||||
* the |session| is initialized for server use. If the client did not
|
||||
* provide the client certificate in the first TLS handshake, this
|
||||
* function must not be called.
|
||||
*
|
||||
* This function stores the given origin at the slot 1 in the client
|
||||
* certificate vector.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* SPDYLAY_ERR_NOMEM
|
||||
* Out of memory
|
||||
* SPDYLAY_ERR_INVALID_STATE
|
||||
* The |session| is initialized for server use; or the client
|
||||
* certificate vector size is 0.
|
||||
*/
|
||||
int spdylay_session_set_initial_client_cert_origin(spdylay_session *session,
|
||||
const char *scheme,
|
||||
const char *host,
|
||||
uint16_t port);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Returns the origin at the index |slot| in the client certificate
|
||||
* vector. If there is no origin at the given |slot|, this function
|
||||
* returns ``NULL``.
|
||||
*
|
||||
* This function must not be called if the |session| is initialized
|
||||
* for server use.
|
||||
*/
|
||||
const spdylay_origin* spdylay_session_get_client_cert_origin
|
||||
(spdylay_session *session,
|
||||
size_t slot);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
|
@ -1094,6 +1246,14 @@ void spdylay_session_del(spdylay_session *session);
|
|||
* :member:`spdylay_session_callbacks.on_ctrl_not_send_callback` is
|
||||
* invoked. Abort the following steps.
|
||||
* 4. If the frame is SYN_STREAM, the stream is opened here.
|
||||
* If the |session| is initialized for client use and the protocol
|
||||
* version is :macro:`SPDYLAY_PROTO_SPDY3` and the library needs
|
||||
* the client certificate for the origin,
|
||||
* :member:`spdylay_session_callbacks.get_credential_ncerts` is
|
||||
* invoked. If the result is more than zero,
|
||||
* :member:`spdylay_session_callbacks.get_credential_proof` and
|
||||
* :member:`spdylay_session_callbacks.get_credential_cert` are also
|
||||
* invoked.
|
||||
* 5. :member:`spdylay_session_callbacks.before_ctrl_send_callback` is
|
||||
* invoked.
|
||||
* 6. :member:`spdylay_session_callbacks.send_callback` is invoked one
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Spdylay - SPDY Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "spdylay_client_cert_vector.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "spdylay_helper.h"
|
||||
|
||||
int spdylay_origin_equal(const spdylay_origin *lhs, const spdylay_origin *rhs)
|
||||
{
|
||||
return strcmp(lhs->scheme, rhs->scheme) == 0 &&
|
||||
strcmp(lhs->host, rhs->host) == 0 && lhs->port == rhs->port;
|
||||
}
|
||||
|
||||
int spdylay_origin_set(spdylay_origin *origin,
|
||||
const char *scheme, const char *host, uint16_t port)
|
||||
{
|
||||
if(strlen(scheme) > SPDYLAY_MAX_SCHEME ||
|
||||
strlen(host) > SPDYLAY_MAX_HOSTNAME) {
|
||||
return SPDYLAY_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
strcpy(origin->scheme, scheme);
|
||||
strcpy(origin->host, host);
|
||||
origin->port = port;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* spdylay_origin_get_scheme(const spdylay_origin *origin)
|
||||
{
|
||||
return origin->scheme;
|
||||
}
|
||||
|
||||
const char* spdylay_origin_get_host(const spdylay_origin *origin)
|
||||
{
|
||||
return origin->host;
|
||||
}
|
||||
|
||||
uint16_t spdylay_origin_get_port(const spdylay_origin *origin)
|
||||
{
|
||||
return origin->port;
|
||||
}
|
||||
|
||||
int spdylay_client_cert_vector_init(spdylay_client_cert_vector *certvec,
|
||||
size_t size)
|
||||
{
|
||||
certvec->size = certvec->capacity = size;
|
||||
certvec->last_slot = 0;
|
||||
if(certvec->capacity) {
|
||||
size_t size = sizeof(spdylay_origin*)*certvec->capacity;
|
||||
certvec->vector = malloc(size);
|
||||
if(certvec->vector == NULL) {
|
||||
return SPDYLAY_ERR_NOMEM;
|
||||
}
|
||||
memset(certvec->vector, 0, size);
|
||||
} else {
|
||||
certvec->vector = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spdylay_client_cert_vector_free(spdylay_client_cert_vector *certvec)
|
||||
{
|
||||
size_t i;
|
||||
for(i = 0; i < certvec->size; ++i) {
|
||||
free(certvec->vector[i]);
|
||||
}
|
||||
free(certvec->vector);
|
||||
}
|
||||
|
||||
int spdylay_client_cert_vector_resize(spdylay_client_cert_vector *certvec,
|
||||
size_t size)
|
||||
{
|
||||
if(certvec->capacity < size) {
|
||||
spdylay_origin **vector = realloc(certvec->vector,
|
||||
sizeof(spdylay_origin*)*size);
|
||||
if(vector == NULL) {
|
||||
return SPDYLAY_ERR_NOMEM;
|
||||
}
|
||||
memset(vector + certvec->capacity, 0,
|
||||
sizeof(spdylay_origin*)*(size - certvec->capacity));
|
||||
certvec->vector = vector;
|
||||
certvec->size = certvec->capacity = size;
|
||||
} else {
|
||||
size_t i;
|
||||
for(i = size; i < certvec->size; ++i) {
|
||||
free(certvec->vector[i]);
|
||||
certvec->vector[i] = NULL;
|
||||
}
|
||||
certvec->size = spdylay_min(certvec->size, size);
|
||||
if(certvec->last_slot > certvec->size) {
|
||||
certvec->last_slot = certvec->size;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t spdylay_client_cert_vector_find(spdylay_client_cert_vector *certvec,
|
||||
const spdylay_origin *origin)
|
||||
{
|
||||
size_t i;
|
||||
for(i = 0; i < certvec->size && certvec->vector[i]; ++i) {
|
||||
if(spdylay_origin_equal(origin, certvec->vector[i])) {
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t spdylay_client_cert_vector_put(spdylay_client_cert_vector *certvec,
|
||||
spdylay_origin *origin)
|
||||
{
|
||||
if(certvec->size == 0) {
|
||||
return 0;
|
||||
}
|
||||
if(certvec->last_slot == certvec->size) {
|
||||
certvec->last_slot = 1;
|
||||
} else {
|
||||
++certvec->last_slot;
|
||||
}
|
||||
free(certvec->vector[certvec->last_slot-1]);
|
||||
certvec->vector[certvec->last_slot-1] = origin;
|
||||
return certvec->last_slot;
|
||||
}
|
||||
|
||||
const spdylay_origin* spdylay_client_cert_vector_get_origin
|
||||
(spdylay_client_cert_vector *certvec,
|
||||
size_t slot)
|
||||
{
|
||||
if(slot == 0 || slot > certvec->size || !certvec->vector[slot-1]) {
|
||||
return NULL;
|
||||
} else {
|
||||
return certvec->vector[slot-1];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Spdylay - SPDY Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef SPDYLAY_CLIENT_CERT_VECTOR_H
|
||||
#define SPDYLAY_CLIENT_CERT_VECTOR_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <spdylay/spdylay.h>
|
||||
|
||||
struct spdylay_origin {
|
||||
char scheme[SPDYLAY_MAX_SCHEME+1];
|
||||
char host[SPDYLAY_MAX_HOSTNAME+1];
|
||||
uint16_t port;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
spdylay_origin **vector;
|
||||
/* The size of the vector. */
|
||||
size_t size;
|
||||
/* The real capacity of the vector. size <= capacity holds true. */
|
||||
size_t capacity;
|
||||
/* The last slot where origin is stored. The default value is 0. */
|
||||
size_t last_slot;
|
||||
} spdylay_client_cert_vector;
|
||||
|
||||
/*
|
||||
* Returns nonzero if |lhs| and |rhs| are equal. The equality is
|
||||
* defined such that each member is equal respectively.
|
||||
*/
|
||||
int spdylay_origin_equal(const spdylay_origin *lhs, const spdylay_origin *rhs);
|
||||
|
||||
/*
|
||||
* Convenient function to set members to the |origin|. The |origin|
|
||||
* must be allocated prior this call.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* SPDYLAY_ERR_INVALID_ARGUMENT
|
||||
* The |scheme| is longer than SPDYLAY_MAX_SCHEME; or the |host|
|
||||
* is longer than SPDYLAY_MAX_HOSTNAME.
|
||||
*/
|
||||
int spdylay_origin_set(spdylay_origin *origin,
|
||||
const char *scheme, const char *host, uint16_t port);
|
||||
|
||||
/*
|
||||
* Initializes the client certificate vector with the vector size
|
||||
* |size|.
|
||||
*/
|
||||
int spdylay_client_cert_vector_init(spdylay_client_cert_vector *certvec,
|
||||
size_t size);
|
||||
|
||||
void spdylay_client_cert_vector_free(spdylay_client_cert_vector *certvec);
|
||||
|
||||
/*
|
||||
* Returns the slot of the |origin| in the client certificate vector.
|
||||
* If it is not found, returns 0.
|
||||
*/
|
||||
size_t spdylay_client_cert_vector_find(spdylay_client_cert_vector *certvec,
|
||||
const spdylay_origin *origin);
|
||||
|
||||
/*
|
||||
* Puts the |origin| to the |certvec|. This function takes ownership
|
||||
* of the |origin| on success.
|
||||
*
|
||||
* This function returns the positive slot index of the certificate
|
||||
* vector where the |origin| is stored if it succeeds, or 0.
|
||||
*/
|
||||
size_t spdylay_client_cert_vector_put(spdylay_client_cert_vector *certvec,
|
||||
spdylay_origin *origin);
|
||||
|
||||
/*
|
||||
* Resizes client certificate vector to the size |size|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* SPDYLAY_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int spdylay_client_cert_vector_resize(spdylay_client_cert_vector *certvec,
|
||||
size_t size);
|
||||
|
||||
const spdylay_origin* spdylay_client_cert_vector_get_origin
|
||||
(spdylay_client_cert_vector *certvec,
|
||||
size_t slot);
|
||||
|
||||
#endif /* SPDYLAY_CLIENT_CERT_VECTOR_H */
|
|
@ -27,6 +27,7 @@
|
|||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "spdylay_helper.h"
|
||||
#include "spdylay_net.h"
|
||||
|
@ -500,6 +501,52 @@ void spdylay_frame_nv_2to3(char **nv)
|
|||
}
|
||||
}
|
||||
|
||||
#define SPDYLAY_HTTPS_PORT 443
|
||||
|
||||
int spdylay_frame_nv_set_origin(char **nv, spdylay_origin *origin)
|
||||
{
|
||||
int scheme_found, host_found;
|
||||
int i;
|
||||
scheme_found = host_found = 0;
|
||||
for(i = 0; nv[i]; i += 2) {
|
||||
if(scheme_found == 0 && strcmp(":scheme", nv[i]) == 0) {
|
||||
size_t len = strlen(nv[i+1]);
|
||||
if(len <= SPDYLAY_MAX_SCHEME) {
|
||||
strcpy(origin->scheme, nv[i+1]);
|
||||
scheme_found = 1;
|
||||
}
|
||||
} else if(host_found == 0 && strcmp(":host", nv[i]) == 0) {
|
||||
size_t len = strlen(nv[i+1]);
|
||||
char *sep = memchr(nv[i+1], ':', len);
|
||||
size_t hostlen;
|
||||
if(sep == NULL) {
|
||||
origin->port = SPDYLAY_HTTPS_PORT;
|
||||
sep = nv[i+1]+len;
|
||||
} else {
|
||||
unsigned long int port;
|
||||
errno = 0;
|
||||
port = strtoul(sep+1, NULL, 10);
|
||||
if(errno != 0 || port == 0 || port > UINT16_MAX) {
|
||||
continue;
|
||||
}
|
||||
origin->port = port;
|
||||
}
|
||||
hostlen = sep-nv[i+1];
|
||||
if(hostlen > SPDYLAY_MAX_HOSTNAME) {
|
||||
continue;
|
||||
}
|
||||
memcpy(origin->host, nv[i+1], hostlen);
|
||||
origin->host[hostlen] = '\0';
|
||||
host_found = 1;
|
||||
}
|
||||
}
|
||||
if(scheme_found && host_found) {
|
||||
return 0;
|
||||
} else {
|
||||
return SPDYLAY_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
}
|
||||
|
||||
void spdylay_frame_syn_stream_init(spdylay_syn_stream *frame,
|
||||
uint16_t version, uint8_t flags,
|
||||
int32_t stream_id, int32_t assoc_stream_id,
|
||||
|
@ -640,6 +687,36 @@ void spdylay_frame_settings_free(spdylay_settings *frame)
|
|||
free(frame->iv);
|
||||
}
|
||||
|
||||
void spdylay_frame_credential_init(spdylay_credential *frame,
|
||||
uint16_t version, uint16_t slot,
|
||||
spdylay_mem_chunk *proof,
|
||||
spdylay_mem_chunk *certs,
|
||||
size_t ncerts)
|
||||
{
|
||||
size_t i;
|
||||
memset(frame, 0, sizeof(spdylay_credential));
|
||||
frame->hd.version = version;
|
||||
frame->hd.type = SPDYLAY_CREDENTIAL;
|
||||
frame->slot = slot;
|
||||
frame->proof = *proof;
|
||||
frame->certs = certs;
|
||||
frame->ncerts = ncerts;
|
||||
frame->hd.length = 2+4+frame->proof.length;
|
||||
for(i = 0; i < ncerts; ++i) {
|
||||
frame->hd.length += 4+frame->certs[i].length;
|
||||
}
|
||||
}
|
||||
|
||||
void spdylay_frame_credential_free(spdylay_credential *frame)
|
||||
{
|
||||
size_t i;
|
||||
free(frame->proof.data);
|
||||
for(i = 0; i < frame->ncerts; ++i) {
|
||||
free(frame->certs[i].data);
|
||||
}
|
||||
free(frame->certs);
|
||||
}
|
||||
|
||||
void spdylay_frame_data_init(spdylay_data *frame, int32_t stream_id,
|
||||
uint8_t flags,
|
||||
const spdylay_data_provider *data_prd)
|
||||
|
@ -1076,6 +1153,158 @@ int spdylay_frame_unpack_settings(spdylay_settings *frame,
|
|||
return 0;
|
||||
}
|
||||
|
||||
ssize_t spdylay_frame_pack_credential(uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
spdylay_credential *frame)
|
||||
{
|
||||
ssize_t framelen;
|
||||
int r;
|
||||
size_t i, offset;
|
||||
framelen = SPDYLAY_FRAME_HEAD_LENGTH+2+4+frame->proof.length;
|
||||
for(i = 0; i < frame->ncerts; ++i) {
|
||||
framelen += 4+frame->certs[i].length;
|
||||
}
|
||||
r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen);
|
||||
if(r != 0) {
|
||||
return r;
|
||||
}
|
||||
memset(*buf_ptr, 0, framelen);
|
||||
spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);
|
||||
offset = SPDYLAY_FRAME_HEAD_LENGTH;
|
||||
spdylay_put_uint16be(&(*buf_ptr)[offset], frame->slot);
|
||||
offset += 2;
|
||||
spdylay_put_uint32be(&(*buf_ptr)[offset], frame->proof.length);
|
||||
offset += 4;
|
||||
memcpy(&(*buf_ptr)[offset], frame->proof.data, frame->proof.length);
|
||||
offset += frame->proof.length;
|
||||
for(i = 0; i < frame->ncerts; ++i) {
|
||||
spdylay_put_uint32be(&(*buf_ptr)[offset], frame->certs[i].length);
|
||||
offset += 4;
|
||||
memcpy(&(*buf_ptr)[offset], frame->certs[i].data, frame->certs[i].length);
|
||||
offset += frame->certs[i].length;
|
||||
}
|
||||
return framelen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Counts number of client certificate in CREDENTIAL frame payload
|
||||
* |payload| with length |payloadlen|. The |payload| points to the
|
||||
* length of the first certificate. This function also checks the
|
||||
* frame payload is properly composed.
|
||||
*
|
||||
* This function returns the number of certificates in |payload| if it
|
||||
* succeeds, or one of the following negative error codes:
|
||||
*
|
||||
* SPDYLAY_ERR_INVALID_FRAME
|
||||
* The frame payload is invalid.
|
||||
*/
|
||||
static int spdylay_frame_count_unpack_cert(const uint8_t *payload,
|
||||
size_t payloadlen)
|
||||
{
|
||||
size_t n, offset = 0;
|
||||
for(n = 1; 1; ++n) {
|
||||
size_t len;
|
||||
if(offset+4 > payloadlen) {
|
||||
return SPDYLAY_ERR_INVALID_FRAME;
|
||||
}
|
||||
len = spdylay_get_uint32(&payload[offset]);
|
||||
offset += 4;
|
||||
if(len > payloadlen || offset+len > payloadlen) {
|
||||
return SPDYLAY_ERR_INVALID_FRAME;
|
||||
} else {
|
||||
offset += len;
|
||||
if(offset == payloadlen) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unpacks client certificates in the |payload| with length
|
||||
* |payloadlen|. First allocates memory to store the |ncerts|
|
||||
* certificates. Stores certificates in the allocated space and set
|
||||
* its pointer to |*certs_ptr|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* SPDYLAY_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
static int spdylay_frame_unpack_cert(spdylay_mem_chunk **certs_ptr,
|
||||
size_t ncerts,
|
||||
const uint8_t *payload, size_t payloadlen)
|
||||
{
|
||||
size_t offset, i, j;
|
||||
spdylay_mem_chunk *certs;
|
||||
certs = malloc(sizeof(spdylay_mem_chunk)*ncerts);
|
||||
if(certs == NULL) {
|
||||
return SPDYLAY_ERR_NOMEM;
|
||||
}
|
||||
offset = 0;
|
||||
for(i = 0; i < ncerts; ++i) {
|
||||
certs[i].length = spdylay_get_uint32(&payload[offset]);
|
||||
offset += 4;
|
||||
certs[i].data = malloc(certs[i].length);
|
||||
if(certs[i].data == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
memcpy(certs[i].data, &payload[offset], certs[i].length);
|
||||
offset += certs[i].length;
|
||||
}
|
||||
*certs_ptr = certs;
|
||||
return 0;
|
||||
fail:
|
||||
for(j = 0; j < i; ++j) {
|
||||
free(certs[j].data);
|
||||
}
|
||||
free(certs);
|
||||
return SPDYLAY_ERR_NOMEM;
|
||||
}
|
||||
|
||||
int spdylay_frame_unpack_credential(spdylay_credential *frame,
|
||||
const uint8_t *head, size_t headlen,
|
||||
const uint8_t *payload,
|
||||
size_t payloadlen)
|
||||
{
|
||||
size_t offset;
|
||||
int rv;
|
||||
if(payloadlen < 10) {
|
||||
return SPDYLAY_ERR_INVALID_FRAME;
|
||||
}
|
||||
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
|
||||
offset = 0;
|
||||
frame->slot = spdylay_get_uint16(&payload[offset]);
|
||||
offset += 2;
|
||||
frame->proof.length = spdylay_get_uint32(&payload[offset]);
|
||||
offset += 4;
|
||||
if(frame->proof.length > payloadlen ||
|
||||
offset+frame->proof.length > payloadlen) {
|
||||
return SPDYLAY_ERR_INVALID_FRAME;
|
||||
}
|
||||
frame->proof.data = malloc(frame->proof.length);
|
||||
if(frame->proof.data == NULL) {
|
||||
return SPDYLAY_ERR_NOMEM;
|
||||
}
|
||||
memcpy(frame->proof.data, &payload[offset], frame->proof.length);
|
||||
offset += frame->proof.length;
|
||||
rv = spdylay_frame_count_unpack_cert(payload+offset, payloadlen-offset);
|
||||
if(rv < 0) {
|
||||
goto fail;
|
||||
}
|
||||
frame->ncerts = rv;
|
||||
rv = spdylay_frame_unpack_cert(&frame->certs, frame->ncerts,
|
||||
payload+offset, payloadlen-offset);
|
||||
if(rv != 0) {
|
||||
goto fail;
|
||||
}
|
||||
return 0;
|
||||
fail:
|
||||
free(frame->proof.data);
|
||||
return rv;
|
||||
}
|
||||
|
||||
spdylay_settings_entry* spdylay_frame_iv_copy(const spdylay_settings_entry *iv,
|
||||
size_t niv)
|
||||
{
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <spdylay/spdylay.h>
|
||||
#include "spdylay_zlib.h"
|
||||
#include "spdylay_buffer.h"
|
||||
#include "spdylay_client_cert_vector.h"
|
||||
|
||||
#define SPDYLAY_STREAM_ID_MASK 0x7fffffff
|
||||
#define SPDYLAY_LENGTH_MASK 0xffffff
|
||||
|
@ -443,6 +444,35 @@ int spdylay_frame_unpack_settings(spdylay_settings *frame,
|
|||
const uint8_t *head, size_t headlen,
|
||||
const uint8_t *payload, size_t payloadlen);
|
||||
|
||||
/*
|
||||
* Packs CREDENTIAL frame |frame| in wire format and store it in
|
||||
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|
|
||||
* length. This function expands |*buf_ptr| as necessary to store
|
||||
* given |frame|.
|
||||
*
|
||||
* This function returns the size of packed frame if it succeeds, or
|
||||
* returns one of the following negative error codes:
|
||||
*
|
||||
* SPDYLAY_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
ssize_t spdylay_frame_pack_credential(uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
spdylay_credential *frame);
|
||||
|
||||
/*
|
||||
* Unpacks CREDENTIAL wire format into |frame|.
|
||||
*
|
||||
* This function returns 0 if it succeeds or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* SPDYLAY_ERR_INVALID_FRAME
|
||||
* The input data are invalid.
|
||||
* SPDYLAY_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int spdylay_frame_unpack_credential(spdylay_credential *frame,
|
||||
const uint8_t *head, size_t headlen,
|
||||
const uint8_t *payload, size_t payloadlen);
|
||||
/*
|
||||
* Returns number of bytes to pack name/value pairs |nv|. This
|
||||
* function expects |nv| is sorted in ascending order of key.
|
||||
|
@ -661,6 +691,19 @@ void spdylay_frame_settings_init(spdylay_settings *frame,
|
|||
|
||||
void spdylay_frame_settings_free(spdylay_settings *frame);
|
||||
|
||||
/*
|
||||
* Initializes CREDENTIAL frame |frame| with given values. This
|
||||
* function takes ownership of |proof->data| and |certs| on success.
|
||||
* Note that the ownership of |proof| is not taken.
|
||||
*/
|
||||
void spdylay_frame_credential_init(spdylay_credential *frame,
|
||||
uint16_t version, uint16_t slot,
|
||||
spdylay_mem_chunk *proof,
|
||||
spdylay_mem_chunk *certs,
|
||||
size_t ncerts);
|
||||
|
||||
void spdylay_frame_credential_free(spdylay_credential *frame);
|
||||
|
||||
void spdylay_frame_data_init(spdylay_data *frame, int32_t stream_id,
|
||||
uint8_t flags,
|
||||
const spdylay_data_provider *data_prd);
|
||||
|
@ -716,6 +759,24 @@ void spdylay_frame_nv_3to2(char **nv);
|
|||
*/
|
||||
void spdylay_frame_nv_2to3(char **nv);
|
||||
|
||||
/*
|
||||
* Assigns the members of the |origin| using ":scheme" and ":host"
|
||||
* values in |nv|.
|
||||
*
|
||||
* If ":host" value contains ':', this function parses the chracters
|
||||
* after ':' as integer and uses it as port number.
|
||||
*
|
||||
* If ':' is missing in :host value, the default port number is used.
|
||||
* The only defined default port number is 443.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error code:
|
||||
*
|
||||
* SPDYLAY_ERR_INVALID_ARGUMENT
|
||||
* The |nv| lacks either :scheme or :host, or both.
|
||||
*/
|
||||
int spdylay_frame_nv_set_origin(char **nv, spdylay_origin *origin);
|
||||
|
||||
/*
|
||||
* Makes copy of |iv| and return the copy. The |niv| is the number of
|
||||
* entries in |iv|. This function returns the pointer to the copy if
|
||||
|
|
|
@ -35,4 +35,10 @@
|
|||
|
||||
typedef int (*spdylay_compar)(const void *lhs, const void *rhs);
|
||||
|
||||
/* Internal error code. They must be in the range [-499, -100],
|
||||
inclusive. */
|
||||
typedef enum {
|
||||
SPDYLAY_ERR_CREDENTIAL_PENDING = -100
|
||||
} spdylay_internal_error;
|
||||
|
||||
#endif /* SPDYLAY_INT_H */
|
||||
|
|
|
@ -66,6 +66,9 @@ void spdylay_outbound_item_free(spdylay_outbound_item *item)
|
|||
case SPDYLAY_WINDOW_UPDATE:
|
||||
spdylay_frame_window_update_free(&frame->window_update);
|
||||
break;
|
||||
case SPDYLAY_CREDENTIAL:
|
||||
spdylay_frame_credential_free(&frame->credential);
|
||||
break;
|
||||
}
|
||||
} else if(item->frame_cat == SPDYLAY_DATA) {
|
||||
spdylay_data *data_frame;
|
||||
|
|
|
@ -32,6 +32,13 @@
|
|||
#include <spdylay/spdylay.h>
|
||||
#include "spdylay_frame.h"
|
||||
|
||||
/* Priority for PING */
|
||||
#define SPDYLAY_OB_PRI_PING -10
|
||||
/* Priority for CREDENTIAL */
|
||||
#define SPDYLAY_OB_PRI_CREDENTIAL -2
|
||||
/* Priority for the frame which must be sent after CREDENTIAL */
|
||||
#define SPDYLAY_OB_PRI_AFTER_CREDENTIAL -1
|
||||
|
||||
typedef struct {
|
||||
spdylay_data_provider *data_prd;
|
||||
void *stream_user_data;
|
||||
|
|
|
@ -114,7 +114,8 @@ static void spdylay_inbound_frame_reset(spdylay_inbound_frame *iframe)
|
|||
static int spdylay_session_new(spdylay_session **session_ptr,
|
||||
uint16_t version,
|
||||
const spdylay_session_callbacks *callbacks,
|
||||
void *user_data)
|
||||
void *user_data,
|
||||
size_t cli_certvec_length)
|
||||
{
|
||||
int r;
|
||||
if(version != SPDYLAY_PROTO_SPDY2 && version != SPDYLAY_PROTO_SPDY3) {
|
||||
|
@ -205,8 +206,16 @@ static int spdylay_session_new(spdylay_session **session_ptr,
|
|||
(*session_ptr)->iframe.bufmax = SPDYLAY_INITIAL_INBOUND_FRAMEBUF_LENGTH;
|
||||
spdylay_inbound_frame_reset(&(*session_ptr)->iframe);
|
||||
|
||||
r = spdylay_client_cert_vector_init(&(*session_ptr)->cli_certvec,
|
||||
cli_certvec_length);
|
||||
if(r != 0) {
|
||||
goto fail_client_cert_vector;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_client_cert_vector:
|
||||
free((*session_ptr)->iframe.buf);
|
||||
fail_iframe_buf:
|
||||
free((*session_ptr)->nvbuf);
|
||||
fail_nvbuf:
|
||||
|
@ -232,7 +241,8 @@ int spdylay_session_client_new(spdylay_session **session_ptr,
|
|||
void *user_data)
|
||||
{
|
||||
int r;
|
||||
r = spdylay_session_new(session_ptr, version, callbacks, user_data);
|
||||
r = spdylay_session_new(session_ptr, version, callbacks, user_data,
|
||||
SPDYLAY_INITIAL_CLIENT_CERT_VECTOR_LENGTH);
|
||||
if(r == 0) {
|
||||
/* IDs for use in client */
|
||||
(*session_ptr)->next_stream_id = 1;
|
||||
|
@ -248,7 +258,8 @@ int spdylay_session_server_new(spdylay_session **session_ptr,
|
|||
void *user_data)
|
||||
{
|
||||
int r;
|
||||
r = spdylay_session_new(session_ptr, version, callbacks, user_data);
|
||||
r = spdylay_session_new(session_ptr, version, callbacks, user_data,
|
||||
0);
|
||||
if(r == 0) {
|
||||
(*session_ptr)->server = 1;
|
||||
/* IDs for use in client */
|
||||
|
@ -301,6 +312,7 @@ void spdylay_session_del(spdylay_session *session)
|
|||
free(session->nvbuf);
|
||||
spdylay_buffer_free(&session->inflatebuf);
|
||||
free(session->iframe.buf);
|
||||
spdylay_client_cert_vector_free(&session->cli_certvec);
|
||||
free(session);
|
||||
}
|
||||
|
||||
|
@ -354,8 +366,8 @@ int spdylay_session_add_frame(spdylay_session *session,
|
|||
unreachable. */
|
||||
assert(0);
|
||||
case SPDYLAY_PING:
|
||||
/* Ping has "height" priority. Give it -1. */
|
||||
item->pri = -1;
|
||||
/* Ping has highest priority. */
|
||||
item->pri = SPDYLAY_OB_PRI_PING;
|
||||
break;
|
||||
case SPDYLAY_GOAWAY:
|
||||
/* Should GOAWAY have higher priority? */
|
||||
|
@ -376,6 +388,9 @@ int spdylay_session_add_frame(spdylay_session *session,
|
|||
}
|
||||
break;
|
||||
}
|
||||
case SPDYLAY_CREDENTIAL:
|
||||
item->pri = SPDYLAY_OB_PRI_CREDENTIAL;
|
||||
break;
|
||||
}
|
||||
if(frame_type == SPDYLAY_SYN_STREAM) {
|
||||
r = spdylay_pq_push(&session->ob_ss_pq, item);
|
||||
|
@ -732,6 +747,146 @@ static int spdylay_session_predicate_data_send(spdylay_session *session,
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves cryptographic proof for the given |origin| using callback
|
||||
* function and store it in |proof|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* SPDYLAY_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
static int spdylay_session_get_credential_proof(spdylay_session *session,
|
||||
const spdylay_origin *origin,
|
||||
spdylay_mem_chunk *proof)
|
||||
{
|
||||
proof->length = session->callbacks.get_credential_proof(session,
|
||||
origin,
|
||||
NULL, 0,
|
||||
session->user_data);
|
||||
proof->data = malloc(proof->length);
|
||||
if(proof->data == NULL) {
|
||||
return SPDYLAY_ERR_NOMEM;
|
||||
}
|
||||
session->callbacks.get_credential_proof(session, origin,
|
||||
proof->data, proof->length,
|
||||
session->user_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves client certificate chain for the given |origin| using
|
||||
* callback functions and store the pointer to the allocated buffer
|
||||
* containing certificate chain to |*certs_ptr|. The length of
|
||||
* certificate chain is exactly |ncerts|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* SPDYLAY_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
static int spdylay_session_get_credential_cert(spdylay_session *session,
|
||||
const spdylay_origin *origin,
|
||||
spdylay_mem_chunk **certs_ptr,
|
||||
size_t ncerts)
|
||||
{
|
||||
spdylay_mem_chunk *certs;
|
||||
size_t i, j;
|
||||
certs = malloc(sizeof(spdylay_mem_chunk)*ncerts);
|
||||
if(certs == NULL) {
|
||||
return SPDYLAY_ERR_NOMEM;
|
||||
}
|
||||
for(i = 0; i < ncerts; ++i) {
|
||||
certs[i].length = session->callbacks.get_credential_cert
|
||||
(session,
|
||||
origin,
|
||||
i, NULL, 0,
|
||||
session->user_data);
|
||||
certs[i].data = malloc(certs[i].length);
|
||||
if(certs[i].data == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
session->callbacks.get_credential_cert(session, origin, i,
|
||||
certs[i].data, certs[i].length,
|
||||
session->user_data);
|
||||
}
|
||||
*certs_ptr = certs;
|
||||
return 0;
|
||||
fail:
|
||||
for(j = 0; j < i; ++j) {
|
||||
free(certs[i].data);
|
||||
}
|
||||
free(certs);
|
||||
return SPDYLAY_ERR_NOMEM;
|
||||
}
|
||||
|
||||
int spdylay_session_prep_credential(spdylay_session *session,
|
||||
spdylay_syn_stream *syn_stream)
|
||||
{
|
||||
int rv;
|
||||
spdylay_origin origin;
|
||||
spdylay_frame *frame;
|
||||
spdylay_mem_chunk proof;
|
||||
if(session->cli_certvec.size == 0) {
|
||||
return 0;
|
||||
}
|
||||
rv = spdylay_frame_nv_set_origin(syn_stream->nv, &origin);
|
||||
if(rv == 0) {
|
||||
size_t slot;
|
||||
slot = spdylay_client_cert_vector_find(&session->cli_certvec, &origin);
|
||||
if(slot == 0) {
|
||||
ssize_t ncerts;
|
||||
ncerts = session->callbacks.get_credential_ncerts(session, &origin,
|
||||
session->user_data);
|
||||
if(ncerts > 0) {
|
||||
spdylay_mem_chunk *certs;
|
||||
spdylay_origin *origin_copy;
|
||||
frame = malloc(sizeof(spdylay_frame));
|
||||
if(frame == NULL) {
|
||||
return SPDYLAY_ERR_NOMEM;
|
||||
}
|
||||
origin_copy = malloc(sizeof(spdylay_origin));
|
||||
if(origin_copy == NULL) {
|
||||
goto fail_after_frame;
|
||||
}
|
||||
*origin_copy = origin;
|
||||
slot = spdylay_client_cert_vector_put(&session->cli_certvec,
|
||||
origin_copy);
|
||||
|
||||
rv = spdylay_session_get_credential_proof(session, &origin, &proof);
|
||||
if(rv != 0) {
|
||||
goto fail_after_frame;
|
||||
}
|
||||
rv = spdylay_session_get_credential_cert(session, &origin,
|
||||
&certs, ncerts);
|
||||
if(rv != 0) {
|
||||
goto fail_after_proof;
|
||||
}
|
||||
spdylay_frame_credential_init(&frame->credential,
|
||||
session->version,
|
||||
slot, &proof, certs, ncerts);
|
||||
rv = spdylay_session_add_frame(session, SPDYLAY_CTRL, frame, NULL);
|
||||
if(rv != 0) {
|
||||
spdylay_frame_credential_free(&frame->credential);
|
||||
return rv;
|
||||
}
|
||||
return SPDYLAY_ERR_CREDENTIAL_PENDING;
|
||||
}
|
||||
} else {
|
||||
return slot;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
fail_after_proof:
|
||||
free(proof.data);
|
||||
fail_after_frame:
|
||||
free(frame);
|
||||
return rv;
|
||||
|
||||
}
|
||||
|
||||
static ssize_t spdylay_session_prep_frame(spdylay_session *session,
|
||||
spdylay_outbound_item *item)
|
||||
{
|
||||
|
@ -751,8 +906,30 @@ static ssize_t spdylay_session_prep_frame(spdylay_session *session,
|
|||
if(r != 0) {
|
||||
return r;
|
||||
}
|
||||
if(session->version == SPDYLAY_PROTO_SPDY3 &&
|
||||
session->callbacks.get_credential_proof &&
|
||||
session->callbacks.get_credential_cert &&
|
||||
!session->server) {
|
||||
int slot_index;
|
||||
slot_index = spdylay_session_prep_credential(session,
|
||||
&frame->syn_stream);
|
||||
if(slot_index == SPDYLAY_ERR_CREDENTIAL_PENDING) {
|
||||
/* CREDENTIAL frame has been queued. SYN_STREAM has to be
|
||||
sent after that. Change the priority of this item to
|
||||
achieve this. */
|
||||
item->pri = SPDYLAY_OB_PRI_AFTER_CREDENTIAL;
|
||||
r = spdylay_pq_push(&session->ob_ss_pq, item);
|
||||
if(r == 0) {
|
||||
return SPDYLAY_ERR_CREDENTIAL_PENDING;
|
||||
} else {
|
||||
return r;
|
||||
}
|
||||
} else if(r < 0) {
|
||||
return r;
|
||||
}
|
||||
frame->syn_stream.slot = slot_index;
|
||||
}
|
||||
stream_id = session->next_stream_id;
|
||||
|
||||
frame->syn_stream.stream_id = stream_id;
|
||||
session->next_stream_id += 2;
|
||||
if(session->version == SPDYLAY_PROTO_SPDY2) {
|
||||
|
@ -892,6 +1069,15 @@ static ssize_t spdylay_session_prep_frame(spdylay_session *session,
|
|||
return framebuflen;
|
||||
}
|
||||
break;
|
||||
case SPDYLAY_CREDENTIAL: {
|
||||
framebuflen = spdylay_frame_pack_credential(&session->aob.framebuf,
|
||||
&session->aob.framebufmax,
|
||||
&frame->credential);
|
||||
if(framebuflen < 0) {
|
||||
return framebuflen;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
framebuflen = SPDYLAY_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
@ -1134,6 +1320,8 @@ static int spdylay_session_after_frame_sent(spdylay_session *session)
|
|||
}
|
||||
case SPDYLAY_WINDOW_UPDATE:
|
||||
break;
|
||||
case SPDYLAY_CREDENTIAL:
|
||||
break;
|
||||
}
|
||||
spdylay_active_outbound_item_reset(&session->aob);
|
||||
} else if(item->frame_cat == SPDYLAY_DATA) {
|
||||
|
@ -1235,7 +1423,8 @@ int spdylay_session_send(spdylay_session *session)
|
|||
break;
|
||||
}
|
||||
framebuflen = spdylay_session_prep_frame(session, item);
|
||||
if(framebuflen == SPDYLAY_ERR_DEFERRED) {
|
||||
if(framebuflen == SPDYLAY_ERR_DEFERRED ||
|
||||
framebuflen == SPDYLAY_ERR_CREDENTIAL_PENDING) {
|
||||
continue;
|
||||
} else if(framebuflen < 0) {
|
||||
if(item->frame_cat == SPDYLAY_CTRL &&
|
||||
|
@ -1624,7 +1813,7 @@ int spdylay_session_on_settings_received(spdylay_session *session,
|
|||
/* Check ID/value pairs and persist them if necessary. */
|
||||
memset(check, 0, sizeof(check));
|
||||
for(i = 0; i < frame->settings.niv; ++i) {
|
||||
const spdylay_settings_entry *entry = &frame->settings.iv[i];
|
||||
spdylay_settings_entry *entry = &frame->settings.iv[i];
|
||||
/* SPDY/3 spec says if the multiple values for the same ID were
|
||||
found, use the first one and ignore the rest. */
|
||||
if(entry->settings_id > SPDYLAY_SETTINGS_MAX || entry->settings_id == 0 ||
|
||||
|
@ -1639,6 +1828,19 @@ int spdylay_session_on_settings_received(spdylay_session *session,
|
|||
if(entry->value < (1u << 31)) {
|
||||
spdylay_session_update_initial_window_size(session, entry->value);
|
||||
}
|
||||
} else if(entry->settings_id ==
|
||||
SPDYLAY_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE) {
|
||||
if(!session->server) {
|
||||
int rv;
|
||||
/* Limit certificate vector length in the reasonable size. */
|
||||
entry->value = spdylay_min(entry->value,
|
||||
SPDYLAY_MAX_CLIENT_CERT_VECTOR_LENGTH);
|
||||
rv = spdylay_client_cert_vector_resize(&session->cli_certvec,
|
||||
entry->value);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
session->remote_settings[entry->settings_id] = entry->value;
|
||||
}
|
||||
|
@ -1721,6 +1923,19 @@ int spdylay_session_on_window_update_received(spdylay_session *session,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int spdylay_session_on_credential_received(spdylay_session *session,
|
||||
spdylay_frame *frame)
|
||||
{
|
||||
if(!spdylay_session_check_version(session, frame->credential.hd.version)) {
|
||||
return 0;
|
||||
}
|
||||
/* We don't care about the body of the CREDENTIAL frame. It is left
|
||||
to the application code to decide it is invalid or not. */
|
||||
spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_CREDENTIAL,
|
||||
frame);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spdylay_session_on_headers_received(spdylay_session *session,
|
||||
spdylay_frame *frame)
|
||||
{
|
||||
|
@ -1930,6 +2145,19 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
|
|||
r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
|
||||
}
|
||||
break;
|
||||
case SPDYLAY_CREDENTIAL:
|
||||
r = spdylay_frame_unpack_credential(&frame.credential,
|
||||
session->iframe.headbuf,
|
||||
sizeof(session->iframe.headbuf),
|
||||
session->iframe.buf,
|
||||
session->iframe.len);
|
||||
if(r == 0) {
|
||||
r = spdylay_session_on_credential_received(session, &frame);
|
||||
spdylay_frame_credential_free(&frame.credential);
|
||||
} else if(spdylay_is_non_fatal(r)) {
|
||||
r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(spdylay_is_fatal(r)) {
|
||||
return r;
|
||||
|
@ -2360,3 +2588,38 @@ size_t spdylay_session_get_outbound_queue_size(spdylay_session *session)
|
|||
{
|
||||
return spdylay_pq_size(&session->ob_pq)+spdylay_pq_size(&session->ob_ss_pq);
|
||||
}
|
||||
|
||||
int spdylay_session_set_initial_client_cert_origin(spdylay_session *session,
|
||||
const char *scheme,
|
||||
const char *host,
|
||||
uint16_t port)
|
||||
{
|
||||
spdylay_origin *origin;
|
||||
size_t slot;
|
||||
if(strlen(scheme) > SPDYLAY_MAX_SCHEME ||
|
||||
strlen(host) > SPDYLAY_MAX_HOSTNAME) {
|
||||
return SPDYLAY_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
if(session->server ||
|
||||
(session->cli_certvec.size == 0 ||
|
||||
session->cli_certvec.last_slot != 0)) {
|
||||
return SPDYLAY_ERR_INVALID_STATE;
|
||||
}
|
||||
origin = malloc(sizeof(spdylay_origin));
|
||||
if(origin == NULL) {
|
||||
return SPDYLAY_ERR_NOMEM;
|
||||
}
|
||||
strcpy(origin->scheme, scheme);
|
||||
strcpy(origin->host, host);
|
||||
origin->port = port;
|
||||
slot = spdylay_client_cert_vector_put(&session->cli_certvec, origin);
|
||||
assert(slot == 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const spdylay_origin* spdylay_session_get_client_cert_origin
|
||||
(spdylay_session *session,
|
||||
size_t slot)
|
||||
{
|
||||
return spdylay_client_cert_vector_get_origin(&session->cli_certvec, slot);
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "spdylay_stream.h"
|
||||
#include "spdylay_buffer.h"
|
||||
#include "spdylay_outbound_item.h"
|
||||
#include "spdylay_client_cert_vector.h"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
|
@ -74,6 +75,11 @@ typedef struct {
|
|||
|
||||
#define SPDYLAY_INITIAL_WINDOW_SIZE 65536
|
||||
|
||||
/* Initial size of client certificate vector */
|
||||
#define SPDYLAY_INITIAL_CLIENT_CERT_VECTOR_LENGTH 8
|
||||
/* Maxmum size of client certificate vector */
|
||||
#define SPDYLAY_MAX_CLIENT_CERT_VECTOR_LENGTH 255
|
||||
|
||||
typedef enum {
|
||||
SPDYLAY_RECV_HEAD,
|
||||
SPDYLAY_RECV_PAYLOAD
|
||||
|
@ -167,6 +173,9 @@ struct spdylay_session {
|
|||
/* Settings value of the local endpoint. */
|
||||
uint32_t local_settings[SPDYLAY_SETTINGS_MAX+1];
|
||||
|
||||
/* Client certificate vector */
|
||||
spdylay_client_cert_vector cli_certvec;
|
||||
|
||||
spdylay_session_callbacks callbacks;
|
||||
void *user_data;
|
||||
};
|
||||
|
@ -411,6 +420,15 @@ int spdylay_session_on_headers_received(spdylay_session *session,
|
|||
int spdylay_session_on_window_update_received(spdylay_session *session,
|
||||
spdylay_frame *frame);
|
||||
|
||||
/*
|
||||
* Called when CREDENTIAL is received, assuming |frame.credential| is
|
||||
* properly initialized.
|
||||
*
|
||||
* Currently, this function always succeeds and returns 0.
|
||||
*/
|
||||
int spdylay_session_on_credential_received(spdylay_session *session,
|
||||
spdylay_frame *frame);
|
||||
|
||||
/*
|
||||
* Called when DATA is received.
|
||||
*
|
||||
|
@ -495,4 +513,21 @@ void spdylay_session_update_local_settings(spdylay_session *session,
|
|||
spdylay_settings_entry *iv,
|
||||
size_t niv);
|
||||
|
||||
/*
|
||||
* Returns the index in the client certificate vector for the
|
||||
* |syn_stream|. The origin is computed from |syn_stream->nv|. If no
|
||||
* client certificate is required, return 0. If CREDENTIAL frame needs
|
||||
* to be sent before the |syn_stream|, this function returns
|
||||
* :macro:`SPDYLAY_ERR_CREDENTIAL_PENDING`. In this case, CREDENTIAL
|
||||
* frame has been already queued. This function returns one of the
|
||||
* following negative error codes:
|
||||
*
|
||||
* SPDYLAY_ERR_NOMEM
|
||||
* Out of memory.
|
||||
* SPDYLAY_ERR_CREDENTIAL_PENDING
|
||||
* The CREDENTIAL frame must be sent before the |syn_stream|.
|
||||
*/
|
||||
int spdylay_session_prep_credential(spdylay_session *session,
|
||||
spdylay_syn_stream *syn_stream);
|
||||
|
||||
#endif /* SPDYLAY_SESSION_H */
|
||||
|
|
|
@ -27,11 +27,13 @@ check_PROGRAMS = main
|
|||
|
||||
OBJECTS = main.c spdylay_pq_test.c spdylay_map_test.c spdylay_queue_test.c \
|
||||
spdylay_buffer_test.c spdylay_zlib_test.c spdylay_session_test.c \
|
||||
spdylay_frame_test.c spdylay_stream_test.c spdylay_npn_test.c
|
||||
spdylay_frame_test.c spdylay_stream_test.c spdylay_npn_test.c \
|
||||
spdylay_client_cert_vector_test.c
|
||||
|
||||
HFILES = spdylay_pq_test.h spdylay_map_test.h spdylay_queue_test.h \
|
||||
spdylay_buffer_test.h spdylay_zlib_test.h spdylay_session_test.h \
|
||||
spdylay_frame_test.h spdylay_stream_test.h spdylay_npn_test.h
|
||||
spdylay_frame_test.h spdylay_stream_test.h spdylay_npn_test.h \
|
||||
spdylay_client_cert_vector_test.h
|
||||
|
||||
main_SOURCES = $(HFILES) $(OBJECTS)
|
||||
|
||||
|
|
19
tests/main.c
19
tests/main.c
|
@ -35,6 +35,7 @@
|
|||
#include "spdylay_frame_test.h"
|
||||
#include "spdylay_stream_test.h"
|
||||
#include "spdylay_npn_test.h"
|
||||
#include "spdylay_client_cert_vector_test.h"
|
||||
|
||||
static int init_suite1(void)
|
||||
{
|
||||
|
@ -144,6 +145,12 @@ int main(int argc, char* argv[])
|
|||
test_spdylay_submit_settings) ||
|
||||
!CU_add_test(pSuite, "session_get_outbound_queue_size",
|
||||
test_spdylay_session_get_outbound_queue_size) ||
|
||||
!CU_add_test(pSuite, "session_prep_credential",
|
||||
test_spdylay_session_prep_credential) ||
|
||||
!CU_add_test(pSuite, "session_submit_syn_stream_with_credential",
|
||||
test_spdylay_submit_syn_stream_with_credential) ||
|
||||
!CU_add_test(pSuite, "session_set_initial_client_cert_origin",
|
||||
test_spdylay_session_set_initial_client_cert_origin) ||
|
||||
!CU_add_test(pSuite, "frame_unpack_nv_spdy2",
|
||||
test_spdylay_frame_unpack_nv_spdy2) ||
|
||||
!CU_add_test(pSuite, "frame_unpack_nv_spdy3",
|
||||
|
@ -175,6 +182,8 @@ int main(int argc, char* argv[])
|
|||
test_spdylay_frame_pack_settings_spdy2) ||
|
||||
!CU_add_test(pSuite, "frame_pack_settings_spdy3",
|
||||
test_spdylay_frame_pack_settings_spdy3) ||
|
||||
!CU_add_test(pSuite, "frame_pack_credential",
|
||||
test_spdylay_frame_pack_credential) ||
|
||||
!CU_add_test(pSuite, "frame_nv_sort", test_spdylay_frame_nv_sort) ||
|
||||
!CU_add_test(pSuite, "frame_nv_downcase",
|
||||
test_spdylay_frame_nv_downcase) ||
|
||||
|
@ -186,8 +195,16 @@ int main(int argc, char* argv[])
|
|||
test_spdylay_frame_unpack_nv_check_name_spdy2) ||
|
||||
!CU_add_test(pSuite, "frame_unpack_nv_check_name_spdy3",
|
||||
test_spdylay_frame_unpack_nv_check_name_spdy3) ||
|
||||
!CU_add_test(pSuite, "frame_nv_set_origin",
|
||||
test_spdylay_frame_nv_set_origin) ||
|
||||
!CU_add_test(pSuite, "stream_add_pushed_stream",
|
||||
test_spdylay_stream_add_pushed_stream)) {
|
||||
test_spdylay_stream_add_pushed_stream) ||
|
||||
!CU_add_test(pSuite, "client_cert_vector_find",
|
||||
test_spdylay_client_cert_vector_find) ||
|
||||
!CU_add_test(pSuite, "client_cert_vector_resize",
|
||||
test_spdylay_client_cert_vector_resize) ||
|
||||
!CU_add_test(pSuite, "client_cert_vector_get_origin",
|
||||
test_spdylay_client_cert_vector_get_origin)) {
|
||||
CU_cleanup_registry();
|
||||
return CU_get_error();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Spdylay - SPDY Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "spdylay_client_cert_vector_test.h"
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
||||
#include "spdylay_client_cert_vector.h"
|
||||
|
||||
static spdylay_origin* create_origin(const char *scheme, const char *host,
|
||||
uint16_t port)
|
||||
{
|
||||
spdylay_origin *origin = malloc(sizeof(spdylay_origin));
|
||||
spdylay_origin_set(origin, scheme, host, port);
|
||||
return origin;
|
||||
}
|
||||
|
||||
void test_spdylay_client_cert_vector_find(void)
|
||||
{
|
||||
spdylay_client_cert_vector certvec;
|
||||
spdylay_origin *origin;
|
||||
const spdylay_origin *origin_get;
|
||||
size_t slot;
|
||||
spdylay_client_cert_vector_init(&certvec, 3);
|
||||
|
||||
origin = create_origin("https", "example.org", 443);
|
||||
CU_ASSERT(0 == spdylay_client_cert_vector_find(&certvec, origin));
|
||||
CU_ASSERT(1 == spdylay_client_cert_vector_put(&certvec, origin));
|
||||
slot = spdylay_client_cert_vector_find(&certvec, origin);
|
||||
CU_ASSERT(1 == slot);
|
||||
origin_get = spdylay_client_cert_vector_get_origin(&certvec, slot);
|
||||
CU_ASSERT(strcmp(origin->scheme, origin_get->scheme) == 0);
|
||||
CU_ASSERT(strcmp(origin->host, origin_get->host) == 0);
|
||||
CU_ASSERT(origin->port == origin_get->port);
|
||||
|
||||
origin = create_origin("https", "example.org", 8443);
|
||||
CU_ASSERT(0 == spdylay_client_cert_vector_find(&certvec, origin));
|
||||
CU_ASSERT(2 == spdylay_client_cert_vector_put(&certvec, origin));
|
||||
slot = spdylay_client_cert_vector_find(&certvec, origin);
|
||||
CU_ASSERT(2 == slot);
|
||||
|
||||
origin = create_origin("https", "example.com", 443);
|
||||
CU_ASSERT(0 == spdylay_client_cert_vector_find(&certvec, origin));
|
||||
CU_ASSERT(3 == spdylay_client_cert_vector_put(&certvec, origin));
|
||||
slot = spdylay_client_cert_vector_find(&certvec, origin);
|
||||
CU_ASSERT(3 == slot);
|
||||
|
||||
origin = create_origin("https", "example.com", 8443);
|
||||
CU_ASSERT(0 == spdylay_client_cert_vector_find(&certvec, origin));
|
||||
CU_ASSERT(1 == spdylay_client_cert_vector_put(&certvec, origin));
|
||||
slot = spdylay_client_cert_vector_find(&certvec, origin);
|
||||
CU_ASSERT(1 == slot);
|
||||
|
||||
origin = create_origin("https", "example.org", 443);
|
||||
CU_ASSERT(0 == spdylay_client_cert_vector_find(&certvec, origin));
|
||||
free(origin);
|
||||
|
||||
spdylay_client_cert_vector_free(&certvec);
|
||||
}
|
||||
|
||||
void test_spdylay_client_cert_vector_resize(void)
|
||||
{
|
||||
spdylay_client_cert_vector certvec;
|
||||
spdylay_origin *origin;
|
||||
size_t i;
|
||||
spdylay_client_cert_vector_init(&certvec, 3);
|
||||
|
||||
origin = create_origin("https", "example.org", 443);
|
||||
spdylay_client_cert_vector_put(&certvec, origin);
|
||||
origin = create_origin("https", "example.com", 443);
|
||||
spdylay_client_cert_vector_put(&certvec, origin);
|
||||
|
||||
CU_ASSERT(0 == spdylay_client_cert_vector_resize(&certvec, 1));
|
||||
CU_ASSERT(NULL != spdylay_client_cert_vector_get_origin(&certvec, 1));
|
||||
CU_ASSERT(1 == certvec.last_slot);
|
||||
|
||||
CU_ASSERT(0 == spdylay_client_cert_vector_resize(&certvec, 8));
|
||||
CU_ASSERT(NULL != spdylay_client_cert_vector_get_origin(&certvec, 1));
|
||||
CU_ASSERT(1 == certvec.last_slot);
|
||||
for(i = 2; i <= 8; ++i) {
|
||||
CU_ASSERT(NULL == spdylay_client_cert_vector_get_origin(&certvec, i));
|
||||
}
|
||||
|
||||
spdylay_client_cert_vector_free(&certvec);
|
||||
}
|
||||
|
||||
void test_spdylay_client_cert_vector_get_origin(void)
|
||||
{
|
||||
spdylay_client_cert_vector certvec;
|
||||
spdylay_origin *origin;
|
||||
spdylay_client_cert_vector_init(&certvec, 3);
|
||||
|
||||
origin = create_origin("https", "example.org", 443);
|
||||
CU_ASSERT(1 == spdylay_client_cert_vector_put(&certvec, origin));
|
||||
|
||||
CU_ASSERT(NULL == spdylay_client_cert_vector_get_origin(&certvec, 0));
|
||||
CU_ASSERT(NULL != spdylay_client_cert_vector_get_origin(&certvec, 1));
|
||||
CU_ASSERT(NULL == spdylay_client_cert_vector_get_origin(&certvec, 2));
|
||||
CU_ASSERT(NULL == spdylay_client_cert_vector_get_origin(&certvec, 3));
|
||||
CU_ASSERT(NULL == spdylay_client_cert_vector_get_origin(&certvec, 4));
|
||||
|
||||
spdylay_client_cert_vector_free(&certvec);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Spdylay - SPDY Library
|
||||
*
|
||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef SPDYLAY_CLIENT_CERT_VECTOR_TEST_H
|
||||
#define SPDYLAY_CLIENT_CERT_VECTOR_TEST_H
|
||||
|
||||
void test_spdylay_client_cert_vector_find(void);
|
||||
void test_spdylay_client_cert_vector_resize(void);
|
||||
void test_spdylay_client_cert_vector_get_origin(void);
|
||||
|
||||
#endif /* SPDYLAY_CLIENT_CERT_VECTOR_TEST_H */
|
|
@ -517,6 +517,66 @@ void test_spdylay_frame_pack_settings_spdy3(void)
|
|||
test_spdylay_frame_pack_settings_version(SPDYLAY_PROTO_SPDY3);
|
||||
}
|
||||
|
||||
static char* strcopy(const char* s)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
char *dest = malloc(len+1);
|
||||
memcpy(dest, s, len);
|
||||
dest[len] = '\0';
|
||||
return dest;
|
||||
}
|
||||
|
||||
void test_spdylay_frame_pack_credential(void)
|
||||
{
|
||||
spdylay_frame frame, oframe;
|
||||
uint8_t *buf = NULL;
|
||||
size_t buflen = 0;
|
||||
ssize_t framelen;
|
||||
spdylay_mem_chunk proof;
|
||||
spdylay_mem_chunk *certs;
|
||||
size_t ncerts;
|
||||
proof.data = (uint8_t*)strcopy("PROOF");
|
||||
proof.length = strlen("PROOF");
|
||||
ncerts = 2;
|
||||
certs = malloc(sizeof(spdylay_mem_chunk)*ncerts);
|
||||
certs[0].data = (uint8_t*)strcopy("CERT0");
|
||||
certs[0].length = strlen("CERT0");
|
||||
certs[1].data = (uint8_t*)strcopy("CERT1");
|
||||
certs[1].length = strlen("CERT1");
|
||||
spdylay_frame_credential_init(&frame.credential, SPDYLAY_PROTO_SPDY3,
|
||||
1, &proof, certs, ncerts);
|
||||
framelen = spdylay_frame_pack_credential(&buf, &buflen, &frame.credential);
|
||||
CU_ASSERT(0 == spdylay_frame_unpack_credential
|
||||
(&oframe.credential,
|
||||
&buf[0], SPDYLAY_FRAME_HEAD_LENGTH,
|
||||
&buf[SPDYLAY_FRAME_HEAD_LENGTH],
|
||||
framelen-SPDYLAY_FRAME_HEAD_LENGTH));
|
||||
CU_ASSERT(1 == oframe.credential.slot);
|
||||
CU_ASSERT(5 == oframe.credential.proof.length);
|
||||
CU_ASSERT(memcmp("PROOF", oframe.credential.proof.data, 5) == 0);
|
||||
CU_ASSERT(2 == oframe.credential.ncerts);
|
||||
CU_ASSERT(5 == oframe.credential.certs[0].length);
|
||||
CU_ASSERT(memcmp("CERT0", oframe.credential.certs[0].data, 5) == 0);
|
||||
CU_ASSERT(5 == oframe.credential.certs[1].length);
|
||||
CU_ASSERT(memcmp("CERT1", oframe.credential.certs[1].data, 5) == 0);
|
||||
CU_ASSERT(SPDYLAY_PROTO_SPDY3 == oframe.credential.hd.version);
|
||||
CU_ASSERT(SPDYLAY_CREDENTIAL == oframe.credential.hd.type);
|
||||
CU_ASSERT(SPDYLAY_CTRL_FLAG_NONE == oframe.credential.hd.flags);
|
||||
CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.credential.hd.length);
|
||||
spdylay_frame_credential_free(&oframe.credential);
|
||||
|
||||
/* Put large certificate length */
|
||||
spdylay_put_uint32be(&buf[8+2+4+5], INT32_MAX);
|
||||
CU_ASSERT(SPDYLAY_ERR_INVALID_FRAME == spdylay_frame_unpack_credential
|
||||
(&oframe.credential,
|
||||
&buf[0], SPDYLAY_FRAME_HEAD_LENGTH,
|
||||
&buf[SPDYLAY_FRAME_HEAD_LENGTH],
|
||||
framelen-SPDYLAY_FRAME_HEAD_LENGTH));
|
||||
|
||||
free(buf);
|
||||
spdylay_frame_credential_free(&frame.credential);
|
||||
}
|
||||
|
||||
void test_spdylay_frame_nv_sort(void)
|
||||
{
|
||||
char *nv[7];
|
||||
|
@ -670,3 +730,49 @@ void test_spdylay_frame_unpack_nv_check_name_spdy3(void)
|
|||
test_spdylay_frame_unpack_nv_check_name_with
|
||||
(spdylay_frame_get_len_size(SPDYLAY_PROTO_SPDY3));
|
||||
}
|
||||
|
||||
void test_spdylay_frame_nv_set_origin(void)
|
||||
{
|
||||
spdylay_origin origin;
|
||||
const char *nv1[] = {
|
||||
":host", "example.org",
|
||||
":scheme", "https",
|
||||
NULL
|
||||
};
|
||||
const char *nv2[] = {
|
||||
":host", "example.org:8443",
|
||||
":scheme", "https",
|
||||
NULL
|
||||
};
|
||||
const char *nv3[] = {
|
||||
":host", "example.org:0",
|
||||
":scheme", "https",
|
||||
NULL
|
||||
};
|
||||
const char *nv4[] = {
|
||||
":host", "example.org",
|
||||
NULL
|
||||
};
|
||||
const char *nv5[] = {
|
||||
":scheme", "https",
|
||||
NULL
|
||||
};
|
||||
CU_ASSERT(0 == spdylay_frame_nv_set_origin((char**)nv1, &origin));
|
||||
CU_ASSERT(strcmp("https", origin.scheme) == 0);
|
||||
CU_ASSERT(strcmp("example.org", origin.host) == 0);
|
||||
CU_ASSERT(443 == origin.port);
|
||||
|
||||
CU_ASSERT(0 == spdylay_frame_nv_set_origin((char**)nv2, &origin));
|
||||
CU_ASSERT(strcmp("https", origin.scheme) == 0);
|
||||
CU_ASSERT(strcmp("example.org", origin.host) == 0);
|
||||
CU_ASSERT(8443 == origin.port);
|
||||
|
||||
CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==
|
||||
spdylay_frame_nv_set_origin((char**)nv3, &origin));
|
||||
|
||||
CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==
|
||||
spdylay_frame_nv_set_origin((char**)nv4, &origin));
|
||||
|
||||
CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==
|
||||
spdylay_frame_nv_set_origin((char**)nv5, &origin));
|
||||
}
|
||||
|
|
|
@ -42,11 +42,13 @@ void test_spdylay_frame_pack_headers_spdy3(void);
|
|||
void test_spdylay_frame_pack_window_update(void);
|
||||
void test_spdylay_frame_pack_settings_spdy2(void);
|
||||
void test_spdylay_frame_pack_settings_spdy3(void);
|
||||
void test_spdylay_frame_pack_credential(void);
|
||||
void test_spdylay_frame_nv_sort(void);
|
||||
void test_spdylay_frame_nv_downcase(void);
|
||||
void test_spdylay_frame_nv_2to3(void);
|
||||
void test_spdylay_frame_nv_3to2(void);
|
||||
void test_spdylay_frame_unpack_nv_check_name_spdy2(void);
|
||||
void test_spdylay_frame_unpack_nv_check_name_spdy3(void);
|
||||
void test_spdylay_frame_nv_set_origin(void);
|
||||
|
||||
#endif /* SPDYLAY_FRAME_TEST_H */
|
||||
|
|
|
@ -1924,7 +1924,7 @@ void test_spdylay_session_on_settings_received(void)
|
|||
iv[2].value = 64*1024;
|
||||
iv[2].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
|
||||
|
||||
iv[3].settings_id = SPDYLAY_SETTINGS_CURRENT_CWND;
|
||||
iv[3].settings_id = SPDYLAY_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE;
|
||||
iv[3].value = 512;
|
||||
iv[3].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
|
||||
|
||||
|
@ -1933,7 +1933,7 @@ void test_spdylay_session_on_settings_received(void)
|
|||
iv[4].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
|
||||
|
||||
memset(&callbacks, 0, sizeof(spdylay_session_callbacks));
|
||||
spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks,
|
||||
spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks,
|
||||
&user_data);
|
||||
session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE] = 16*1024;
|
||||
|
||||
|
@ -1953,8 +1953,11 @@ void test_spdylay_session_on_settings_received(void)
|
|||
session->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS]);
|
||||
CU_ASSERT(64*1024 ==
|
||||
session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]);
|
||||
CU_ASSERT(512 ==
|
||||
session->remote_settings[SPDYLAY_SETTINGS_CURRENT_CWND]);
|
||||
/* We limit certificate vector in reasonable size. */
|
||||
CU_ASSERT(SPDYLAY_MAX_CLIENT_CERT_VECTOR_LENGTH ==
|
||||
session->remote_settings
|
||||
[SPDYLAY_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE]);
|
||||
CU_ASSERT(SPDYLAY_MAX_CLIENT_CERT_VECTOR_LENGTH == session->cli_certvec.size);
|
||||
CU_ASSERT(64*1024 == stream1->window_size);
|
||||
CU_ASSERT(0 == stream2->window_size);
|
||||
|
||||
|
@ -2064,3 +2067,157 @@ void test_spdylay_session_get_outbound_queue_size(void)
|
|||
|
||||
spdylay_session_del(session);
|
||||
}
|
||||
|
||||
static ssize_t get_credential_ncerts(spdylay_session *session,
|
||||
const spdylay_origin *origin,
|
||||
void *user_data)
|
||||
{
|
||||
if(strcmp("example.org", origin->host) == 0 &&
|
||||
strcmp("https", origin->scheme) == 0 &&
|
||||
443 == origin->port) {
|
||||
return 2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t get_credential_cert(spdylay_session *session,
|
||||
const spdylay_origin *origin,
|
||||
size_t index,
|
||||
uint8_t *cert, size_t certlen,
|
||||
void *user_data)
|
||||
{
|
||||
size_t len = strlen(origin->host);
|
||||
if(certlen == 0) {
|
||||
return len;
|
||||
} else {
|
||||
assert(certlen == len);
|
||||
memcpy(cert, origin->host, len);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t get_credential_proof(spdylay_session *session,
|
||||
const spdylay_origin *origin,
|
||||
uint8_t *proof, size_t prooflen,
|
||||
void *uer_data)
|
||||
{
|
||||
size_t len = strlen(origin->scheme);
|
||||
if(prooflen == 0) {
|
||||
return len;
|
||||
} else {
|
||||
assert(prooflen == len);
|
||||
memcpy(proof, origin->scheme, len);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void test_spdylay_session_prep_credential(void)
|
||||
{
|
||||
spdylay_session *session;
|
||||
spdylay_session_callbacks callbacks;
|
||||
const char *nv[] = { ":host", "example.org",
|
||||
":scheme", "https",
|
||||
NULL };
|
||||
const char *nv_nocert[] = { ":host", "nocert",
|
||||
":scheme", "https",
|
||||
NULL };
|
||||
spdylay_frame frame, *cred_frame;
|
||||
spdylay_outbound_item *item;
|
||||
size_t i;
|
||||
memset(&callbacks, 0, sizeof(spdylay_session_callbacks));
|
||||
callbacks.get_credential_ncerts = get_credential_ncerts;
|
||||
callbacks.get_credential_cert = get_credential_cert;
|
||||
callbacks.get_credential_proof = get_credential_proof;
|
||||
CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3,
|
||||
&callbacks, NULL));
|
||||
spdylay_frame_syn_stream_init(&frame.syn_stream, session->version,
|
||||
SPDYLAY_CTRL_FLAG_NONE, 1, 0, 0,
|
||||
dup_nv(nv));
|
||||
CU_ASSERT(SPDYLAY_ERR_CREDENTIAL_PENDING ==
|
||||
spdylay_session_prep_credential(session, &frame.syn_stream));
|
||||
item = spdylay_session_get_next_ob_item(session);
|
||||
CU_ASSERT(SPDYLAY_CREDENTIAL == OB_CTRL_TYPE(item));
|
||||
CU_ASSERT(SPDYLAY_OB_PRI_CREDENTIAL == item->pri);
|
||||
cred_frame = OB_CTRL(item);
|
||||
CU_ASSERT(strlen("https") == cred_frame->credential.proof.length);
|
||||
CU_ASSERT(memcmp("https", cred_frame->credential.proof.data,
|
||||
cred_frame->credential.proof.length) == 0);
|
||||
CU_ASSERT(2 == cred_frame->credential.ncerts);
|
||||
for(i = 0; i < cred_frame->credential.ncerts; ++i) {
|
||||
CU_ASSERT(strlen("example.org") == cred_frame->credential.certs[i].length);
|
||||
CU_ASSERT(memcmp("example.org", cred_frame->credential.certs[i].data,
|
||||
cred_frame->credential.certs[i].length) == 0);
|
||||
}
|
||||
/* Next spdylay_session_get_next_ob_item() call returns slot index */
|
||||
CU_ASSERT(1 == spdylay_session_prep_credential(session, &frame.syn_stream));
|
||||
|
||||
spdylay_frame_syn_stream_free(&frame.syn_stream);
|
||||
|
||||
spdylay_frame_syn_stream_init(&frame.syn_stream, session->version,
|
||||
SPDYLAY_CTRL_FLAG_NONE, 1, 0, 0,
|
||||
dup_nv(nv_nocert));
|
||||
CU_ASSERT(0 == spdylay_session_prep_credential(session, &frame.syn_stream));
|
||||
spdylay_frame_syn_stream_free(&frame.syn_stream);
|
||||
|
||||
spdylay_session_del(session);
|
||||
}
|
||||
|
||||
void test_spdylay_submit_syn_stream_with_credential(void)
|
||||
{
|
||||
spdylay_session *session;
|
||||
spdylay_session_callbacks callbacks;
|
||||
const char *nv[] = { ":host", "example.org",
|
||||
":scheme", "https",
|
||||
NULL };
|
||||
my_user_data ud;
|
||||
accumulator acc;
|
||||
|
||||
ud.acc = &acc;
|
||||
memset(&callbacks, 0, sizeof(spdylay_session_callbacks));
|
||||
callbacks.send_callback = block_count_send_callback;
|
||||
callbacks.on_ctrl_send_callback = on_ctrl_send_callback;
|
||||
callbacks.get_credential_ncerts = get_credential_ncerts;
|
||||
callbacks.get_credential_cert = get_credential_cert;
|
||||
callbacks.get_credential_proof = get_credential_proof;
|
||||
|
||||
CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3,
|
||||
&callbacks, &ud));
|
||||
|
||||
CU_ASSERT(0 == spdylay_submit_request(session, 0, nv, NULL, NULL));
|
||||
|
||||
ud.block_count = 1;
|
||||
ud.ctrl_send_cb_called = 0;
|
||||
CU_ASSERT(0 == spdylay_session_send(session));
|
||||
|
||||
CU_ASSERT(1 == ud.ctrl_send_cb_called);
|
||||
CU_ASSERT(SPDYLAY_CREDENTIAL == ud.sent_frame_type);
|
||||
|
||||
session->callbacks.send_callback = accumulator_send_callback;
|
||||
acc.length = 0;
|
||||
ud.ctrl_send_cb_called = 0;
|
||||
CU_ASSERT(0 == spdylay_session_send(session));
|
||||
CU_ASSERT(1 == ud.ctrl_send_cb_called);
|
||||
CU_ASSERT(SPDYLAY_SYN_STREAM == ud.sent_frame_type);
|
||||
/* Check slot */
|
||||
CU_ASSERT(1 == acc.buf[17]);
|
||||
|
||||
spdylay_session_del(session);
|
||||
}
|
||||
|
||||
void test_spdylay_session_set_initial_client_cert_origin(void)
|
||||
{
|
||||
spdylay_session *session;
|
||||
spdylay_session_callbacks callbacks;
|
||||
const spdylay_origin *origin;
|
||||
spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks, NULL);
|
||||
CU_ASSERT(0 == spdylay_session_set_initial_client_cert_origin
|
||||
(session, "https", "example.org", 443));
|
||||
origin = spdylay_session_get_client_cert_origin(session, 1);
|
||||
CU_ASSERT(NULL != origin);
|
||||
CU_ASSERT(strcmp("https", spdylay_origin_get_scheme(origin)) == 0);
|
||||
CU_ASSERT(strcmp("example.org", spdylay_origin_get_host(origin)) == 0);
|
||||
CU_ASSERT(443 == spdylay_origin_get_port(origin));
|
||||
|
||||
spdylay_session_del(session);
|
||||
}
|
||||
|
|
|
@ -64,5 +64,8 @@ void test_spdylay_session_on_ctrl_not_send(void);
|
|||
void test_spdylay_session_on_settings_received(void);
|
||||
void test_spdylay_submit_settings(void);
|
||||
void test_spdylay_session_get_outbound_queue_size(void);
|
||||
void test_spdylay_session_prep_credential(void);
|
||||
void test_spdylay_submit_syn_stream_with_credential(void);
|
||||
void test_spdylay_session_set_initial_client_cert_origin(void);
|
||||
|
||||
#endif /* SPDYLAY_SESSION_TEST_H */
|
||||
|
|
Loading…
Reference in New Issue