Version 0.3.0
- AUTH_INFO types and structures were moved to library internals
-- AUTH_FAILED is no longer returned in SRP (any fatal error in SRP means auth failed)
+- AUTH_FAILED is no longer returned in SRP authentication
+ (any fatal error in SRP means auth failed)
- Introduced GNUTLS_E_INTERRUPTED
-- Added support for Non blocking IO
+- Added support for non blocking IO
+- gnutls_recv() and gnutls_send() are now obsolete
Version 0.2.4 (12/10/2001)
- Better handling of X.509 certificate extensions
cover.tex.in
TEX_OBJECTS = gnutls.tex ../../lib/gnutls-api.tex serv1.tex ex1.tex ex2.tex ex3.tex fdl.tex \
- macros.tex cover.tex ciphersuites.tex resumedb.tex
+ macros.tex cover.tex ciphersuites.tex resumedb.tex translayer.tex
gnutls.ps: $(TEX_OBJECTS)
-$(LN_S) ../../lib/gnutls-api.tex .
transport layer}\footnote{TLS is mostly used over {\emph TCP/IP} although this is not restrictive, you may
use it over any reliable transport layer.}. \gnutls implements the
above protocols in reentrant way in order to be used in multiple threads of
-execution (without the need for Critical Sections).
+execution (without the need for Critical Sections and locks).
\par
Currently \gnutls implements:
\input{resumedb}
+\input{translayer}
+
\section{Client Examples}
This section contains examples of TLS and SSL clients, using \gnutls.
void gnutls_perror( int error);
const char* gnutls_strerror( int error);
-ssize_t gnutls_send(SOCKET cd, GNUTLS_STATE state, void *data, size_t sizeofdata, int flags);
-ssize_t gnutls_recv(SOCKET cd, GNUTLS_STATE state, void *data, size_t sizeofdata, int flags);
+#define gnutls_send gnutls_write
+#define gnutls_recv gnutls_read
ssize_t gnutls_write(SOCKET cd, GNUTLS_STATE state, void *data, size_t sizeofdata);
ssize_t gnutls_read(SOCKET cd, GNUTLS_STATE state, void *data, size_t sizeofdata);
int gnutls_dh_replace_params( gnutls_datum prime, gnutls_datum generator, int bits);
int gnutls_dh_generate_params( gnutls_datum* prime, gnutls_datum* generator, int bits);
-typedef ssize_t (*RECV_FUNC)(SOCKET, void*, size_t,int);
-typedef ssize_t (*SEND_FUNC)(SOCKET, const void*, size_t,int);
+typedef ssize_t (*PULL_FUNC)(SOCKET, void*, size_t);
+typedef ssize_t (*PUSH_FUNC)(SOCKET, const void*, size_t);
typedef void (*LOG_FUNC)( const char*);
-void gnutls_global_set_send_func( SEND_FUNC send_func);
-void gnutls_global_set_recv_func( RECV_FUNC recv_func);
+void gnutls_global_set_push_func( PUSH_FUNC push_func);
+void gnutls_global_set_pull_func( PULL_FUNC pull_func);
#include <gnutls_errors.h>
#include <gnutls_num.h>
+/* This is the only file that uses the berkeley sockets API.
+ */
+
#ifdef HAVE_ERRNO_H
# include <errno.h>
#endif
# define EAGAIN EWOULDBLOCK
#endif
-extern ssize_t (*_gnutls_recv_func)( SOCKET, void*, size_t, int);
-extern ssize_t (*_gnutls_send_func)( SOCKET,const void*, size_t, int);
+extern ssize_t (*_gnutls_pull_func)( SOCKET, void*, size_t);
+extern ssize_t (*_gnutls_push_func)( SOCKET,const void*, size_t);
/* Buffers received packets of type APPLICATION DATA and
* HANDSHAKE DATA.
/* This function is like read. But it does not return -1 on error.
* It does return gnutls_errno instead.
+ *
+ * Flags are only used if the default recv() function is being used.
*/
-static ssize_t _gnutls_Read(int fd, void *iptr, size_t sizeOfPtr, int flag)
+static ssize_t _gnutls_read(SOCKET fd, void *iptr, size_t sizeOfPtr, int flags)
{
size_t left;
ssize_t i=0;
left = sizeOfPtr;
while (left > 0) {
- i = _gnutls_recv_func(fd, &ptr[i], left, flag);
+
+ if (_gnutls_pull_func==NULL)
+ i = recv(fd, &ptr[i], left, flags);
+ else
+ i = _gnutls_pull_func(fd, &ptr[i], left);
+
if (i < 0) {
#ifdef READ_DEBUG
_gnutls_log( "READ: %d returned from %d, errno=%d\n", i, fd, errno);
_gnutls_log( "READ: returning %d bytes from %d\n", sizeOfPtr-left, fd);
#endif
goto finish;
- //return sizeOfPtr-left;
}
if (errno==EAGAIN) return GNUTLS_E_AGAIN;
else return GNUTLS_E_INTERRUPTED;
peekdata2 = gnutls_malloc( RCVLOWAT);
/* this was already read by using MSG_PEEK - so it shouldn't fail */
- _gnutls_Read( cd, peekdata2, RCVLOWAT, 0);
+ _gnutls_read( cd, peekdata2, RCVLOWAT, 0);
gnutls_free(peekdata2);
} else {
- _gnutls_Read( cd, &peekdata1, RCVLOWAT, 0);
+ _gnutls_read( cd, &peekdata1, RCVLOWAT, 0);
}
state->gnutls_internals.have_peeked_data=0;
* This is not a general purpose function. It returns EXACTLY the data requested.
*
*/
-ssize_t _gnutls_read_buffered( int fd, GNUTLS_STATE state, opaque **iptr, size_t sizeOfPtr, int flag, ContentType recv_type)
+ssize_t _gnutls_read_buffered( int fd, GNUTLS_STATE state, opaque **iptr, size_t sizeOfPtr, ContentType recv_type)
{
ssize_t ret=0, ret2=0;
int min;
/* read fresh data - but leave RCVLOWAT bytes in the kernel buffer.
*/
if ( recvdata - recvlowat > 0) {
- ret = _gnutls_Read( fd, &buf[min], recvdata - recvlowat, flag);
+ ret = _gnutls_read( fd, &buf[min], recvdata - recvlowat, 0);
/* return immediately if we got an interrupt or eagain
* error.
}
if (ret >= 0 && recvlowat > 0) {
- ret2 = _gnutls_Read( fd, &buf[min+ret], recvlowat, MSG_PEEK|flag);
+ ret2 = _gnutls_read( fd, &buf[min+ret], recvlowat, MSG_PEEK);
if (ret2 < 0 && gnutls_is_fatal_error(ret2)==0)
return ret2;
#endif
left = n;
while (left > 0) {
- i = _gnutls_send_func(fd, &ptr[i], left, flags);
+
+ if (_gnutls_push_func==NULL)
+ i = send(fd, &ptr[i], left, flags);
+ else
+ i = _gnutls_push_func(fd, &ptr[i], left);
+
if (i == -1) {
#if 0 /* currently this is not right, since the functions
* above this, cannot handle interrupt, and eagain errors.
left = n;
while (left > 0) {
- i = gnutls_send_int(fd, state, type, htype, &ptr[i], left, 0);
+ i = gnutls_send_int(fd, state, type, htype, &ptr[i], left);
if (i <= 0) {
return i;
}
left = sizeOfPtr;
while (left > 0) {
- i = gnutls_recv_int(fd, state, type, htype, &ptr[i], left, 0);
+ i = gnutls_recv_int(fd, state, type, htype, &ptr[i], left);
if (i < 0) {
return i;
} else {
int gnutls_insertDataBuffer(ContentType type, GNUTLS_STATE state, char *data, int length);
int gnutls_getDataBufferSize(ContentType type, GNUTLS_STATE state);
int gnutls_getDataFromBuffer(ContentType type, GNUTLS_STATE state, char *data, int length);
-ssize_t _gnutls_read_buffered(int fd, GNUTLS_STATE, opaque **iptr, size_t n, int, ContentType);
+ssize_t _gnutls_read_buffered(int fd, GNUTLS_STATE, opaque **iptr, size_t n, ContentType);
void _gnutls_read_clear_buffer( GNUTLS_STATE);
int _gnutls_clear_peeked_data( SOCKET cd, GNUTLS_STATE state);
extern const static_asn pkix_asn1_tab[];
-typedef ssize_t (*RECV_FUNC)(SOCKET, void*, size_t,int);
-typedef ssize_t (*SEND_FUNC)(SOCKET, const void*, size_t,int);
+typedef ssize_t (*PULL_FUNC)(SOCKET, void*, size_t);
+typedef ssize_t (*PUSH_FUNC)(SOCKET, const void*, size_t);
typedef void (*LOG_FUNC)( const char*);
-RECV_FUNC _gnutls_recv_func;
-SEND_FUNC _gnutls_send_func;
+PULL_FUNC _gnutls_pull_func;
+PUSH_FUNC _gnutls_push_func;
LOG_FUNC _gnutls_log_func;
static node_asn *PKIX1_ASN;
}
/**
- * gnutls_global_set_recv_func - This function sets the recv() function
- * @recv_func: it's a recv(2) like function
+ * gnutls_global_set_pull_func - This function sets a read like function
+ * @pull_func: it's a function like read
*
- * This is the function were you set the recv() function gnutls
- * is going to use. Normaly you may not use this function since
- * the default (recv(2)) will probably be ok, unless you use
- * some external library (like gnu pthreads), which provide
- * a front end to this function. This function should be
- * called once and after gnutls_global_init().
- * RECV_FUNC is of the form,
- * ssize_t (*RECV_FUNC)(SOCKET, void*, size_t,int);
+ * This is the function where you set a function for gnutls
+ * to receive data. Normaly, if you use berkeley style sockets,
+ * you may not use this function since the default (recv(2)) will
+ * probably be ok.
+ * This function should be called once and after gnutls_global_init().
+ * PULL_FUNC is of the form,
+ * ssize_t (*PULL_FUNC)(SOCKET, const void*, size_t);
**/
-void gnutls_global_set_recv_func( RECV_FUNC recv_func) {
- _gnutls_recv_func = recv_func;
+void gnutls_global_set_pull_func( PULL_FUNC pull_func) {
+ _gnutls_pull_func = pull_func;
}
/**
- * gnutls_global_set_send_func - This function sets the send() function
- * @send_func: it's a send(2) like function
+ * gnutls_global_set_push_func - This function sets the function to send data
+ * @push_func: it's a function like write
*
- * This is the function were you set the send() function gnutls
- * is going to use. Normaly you may not use this function since
- * the default (send(2)) will probably be ok, unless you use
- * some external library (like gnu pthreads), which provide
- * a front end to this function. This function should be
- * called once and after gnutls_global_init().
- * SEND_FUNC is of the form,
- * ssize_t (*SEND_FUNC)(SOCKET, const void*, size_t,int);
+ * This is the function where you set a push function for gnutls
+ * to use in order to send data. If you are going to use berkeley style
+ * sockets, you may not use this function since
+ * the default (send(2)) will probably be ok. Otherwise you should
+ * specify this function for gnutls to be able to send data.
+ *
+ * This function should be called once and after gnutls_global_init().
+ * PUSH_FUNC is of the form,
+ * ssize_t (*PUSH_FUNC)(SOCKET, const void*, size_t);
**/
-void gnutls_global_set_send_func( SEND_FUNC send_func) {
- _gnutls_send_func = send_func;
+void gnutls_global_set_push_func( PUSH_FUNC push_func) {
+ _gnutls_push_func = push_func;
}
/**
/* set default recv/send functions
*/
- _gnutls_recv_func = recv;
- _gnutls_send_func = send;
+ _gnutls_pull_func = NULL;
+ _gnutls_push_func = NULL;
gnutls_global_set_log_func( dlog);
/* initialize parser
case STATE30:
ret =
gnutls_recv_int(cd, state, GNUTLS_CHANGE_CIPHER_SPEC, -1,
- &ch, 1, 0);
+ &ch, 1);
STATE = STATE30;
if (ret <= 0) {
ERR("recv ChangeCipherSpec", ret);
int total_bytes);
void _gnutls_set_current_version(GNUTLS_STATE state, GNUTLS_Version version);
GNUTLS_Version gnutls_get_current_version(GNUTLS_STATE state);
-ssize_t gnutls_send_int(SOCKET cd, GNUTLS_STATE state, ContentType type, HandshakeType htype, const void* data, size_t sizeofdata, int flags);
-ssize_t gnutls_recv_int(SOCKET cd, GNUTLS_STATE state, ContentType type, HandshakeType, char* data, size_t sizeofdata, int flags);
+ssize_t gnutls_send_int(SOCKET cd, GNUTLS_STATE state, ContentType type, HandshakeType htype, const void* data, size_t sizeofdata);
+ssize_t gnutls_recv_int(SOCKET cd, GNUTLS_STATE state, ContentType type, HandshakeType, char* data, size_t sizeofdata);
ssize_t _gnutls_send_change_cipher_spec(SOCKET cd, GNUTLS_STATE state);
/* These macros return the advertized TLS version of
* if there are pending data to socket buffer. Used only
* if you have changed the default low water value (default is 1).
* Normally you will not need that function.
- * However, if you plan to use non standard recv() function you should set
- * this to zero.
+ * This function is only usefull if using berkeley style sockets.
+ * Otherwise it does nothing.
+ *
**/
int gnutls_set_lowat(GNUTLS_STATE state, int num) {
state->gnutls_internals.lowat = num;
return 0;
}
+extern ssize_t (*_gnutls_pull_func)( SOCKET, void*, size_t);
+
/**
* gnutls_init - This function initializes the state to null (null encryption etc...).
* @con_end: is used to indicate if this state is to be used for server or
(*state)->gnutls_internals.expire_time = DEFAULT_EXPIRE_TIME; /* one hour default */
- gnutls_set_lowat((*state), DEFAULT_LOWAT); /* the default for tcp */
+ if (_gnutls_pull_func==NULL)
+ gnutls_set_lowat((*state), DEFAULT_LOWAT); /* the default for tcp */
+ else
+ gnutls_set_lowat((*state), 0);
gnutls_set_max_handshake_data_buffer_size( (*state), MAX_HANDSHAKE_DATA_BUFFER_SIZE);
_gnutls_log( "Record: Sending Alert[%d|%d] - %s\n", data[0], data[1], _gnutls_alert2str((int)data[1]));
#endif
- if ( (ret = gnutls_send_int(cd, state, GNUTLS_ALERT, -1, data, 2, 0)) >= 0)
+ if ( (ret = gnutls_send_int(cd, state, GNUTLS_ALERT, -1, data, 2)) >= 0)
return 0;
else
return ret;
ret = gnutls_send_alert(cd, state, GNUTLS_WARNING, GNUTLS_CLOSE_NOTIFY);
if ( how == GNUTLS_SHUT_RDWR && ret == 0) {
- ret2 = gnutls_recv_int(cd, state, GNUTLS_ALERT, -1, NULL, 0, 0);
+ ret2 = gnutls_recv_int(cd, state, GNUTLS_ALERT, -1, NULL, 0);
state->gnutls_internals.may_read = 1;
}
state->gnutls_internals.may_write = 1;
* send (if called by the user the Content is specific)
* It is intended to transfer data, under the current state.
*/
-ssize_t gnutls_send_int(SOCKET cd, GNUTLS_STATE state, ContentType type, HandshakeType htype, const void *_data, size_t sizeofdata, int flags)
+ssize_t gnutls_send_int(SOCKET cd, GNUTLS_STATE state, ContentType type, HandshakeType htype, const void *_data, size_t sizeofdata)
{
uint8 *cipher;
int i, cipher_size;
return cipher_size; /* error */
}
- if (_gnutls_write(cd, cipher, cipher_size, flags) != cipher_size) {
+ if (_gnutls_write(cd, cipher, cipher_size, 0) != cipher_size) {
gnutls_free( cipher);
state->gnutls_internals.valid_connection = VALID_FALSE;
state->gnutls_internals.resumable = RESUME_FALSE;
return cipher_size;
}
- if (_gnutls_write(cd, cipher, cipher_size, flags) != cipher_size) {
+ if (_gnutls_write(cd, cipher, cipher_size, 0) != cipher_size) {
gnutls_free(cipher);
state->gnutls_internals.valid_connection = VALID_FALSE;
state->gnutls_internals.resumable = RESUME_FALSE;
_gnutls_log( "Record: Sent ChangeCipherSpec\n");
#endif
- return gnutls_send_int( cd, state, GNUTLS_CHANGE_CIPHER_SPEC, -1, data, 1, 0);
+ return gnutls_send_int( cd, state, GNUTLS_CHANGE_CIPHER_SPEC, -1, data, 1);
}
* that it accepts, the gnutls_state and the ContentType of data to
* send (if called by the user the Content is Userdata only)
* It is intended to receive data, under the current state.
- * flags is the sockets flags to use. Currently only MSG_DONTWAIT is
- * supported, and should be used together with MSG_WAITALL.
*/
-ssize_t gnutls_recv_int(SOCKET cd, GNUTLS_STATE state, ContentType type, HandshakeType htype, char *data, size_t sizeofdata, int flags)
+ssize_t gnutls_recv_int(SOCKET cd, GNUTLS_STATE state, ContentType type, HandshakeType htype, char *data, size_t sizeofdata)
{
uint8 *tmpdata;
int tmplen;
/* in order for GNUTLS_E_AGAIN to be returned the socket
* must be set to non blocking mode
*/
- if ( (ret = _gnutls_read_buffered(cd, state, &headers, header_size, flags, -1)) != header_size) {
+ if ( (ret = _gnutls_read_buffered(cd, state, &headers, header_size, -1)) != header_size) {
if (ret<0 && gnutls_is_fatal_error(ret)==0) return ret;
state->gnutls_internals.valid_connection = VALID_FALSE;
/* check if we have that data into buffer.
*/
- if ( (ret = _gnutls_read_buffered(cd, state, &recv_data, header_size+length, flags, recv_type)) != length+header_size) {
+ if ( (ret = _gnutls_read_buffered(cd, state, &recv_data, header_size+length, recv_type)) != length+header_size) {
if (ret<0 && gnutls_is_fatal_error(ret)==0) return ret;
state->gnutls_internals.valid_connection = VALID_FALSE;
}
/**
- * gnutls_send - sends to the peer the specified data
- * @cd: is a connection descriptor
- * @state: is a &GNUTLS_STATE structure.
- * @data: contains the data to send
- * @sizeofdata: is the length of the data
- * @flags: contains the flags to pass to send() function.
- *
- * This function has the same semantics as send() has. The only
- * difference is that is accepts a GNUTLS state. Currently flags cannot
- * be anything except 0.
- **/
-ssize_t gnutls_send(SOCKET cd, GNUTLS_STATE state, const void *data, size_t sizeofdata, int flags) {
- return gnutls_send_int( cd, state, GNUTLS_APPLICATION_DATA, -1, data, sizeofdata, flags);
-}
-
-/**
- * gnutls_recv - receives data from the TLS connection
+ * gnutls_write - sends to the peer the specified data
* @cd: is a connection descriptor
* @state: is a &GNUTLS_STATE structure.
* @data: contains the data to send
* @sizeofdata: is the length of the data
- * @flags: contains the flags to pass to recv() function.
*
- * This function has the same semantics as recv() has. The only
- * difference is that is accepts a GNUTLS state. Flags are the flags
- * passed to recv() and should be used with care in gnutls.
- * The only acceptable flag is currently MSG_DONTWAIT. In that case,
- * if the socket is set to non blocking IO it will return GNUTLS_E_AGAIN,
- * if there are no data in the socket.
+ * This function has the same semantics as write() has. The only
+ * difference is that is accepts a GNUTLS state.
*
- * If the recv() operation is interrupted then GNUTLS_E_INTERRUPTED, will be
- * returned.
+ * If the EINTR is returned by the internal push function (write())
+ * then GNUTLS_E_INTERRUPTED, will be returned.
*
* Returns the number of bytes received, zero on EOF, or
* a negative error code.
- **/
-ssize_t gnutls_recv(SOCKET cd, GNUTLS_STATE state, void *data, size_t sizeofdata, int flags) {
- return gnutls_recv_int( cd, state, GNUTLS_APPLICATION_DATA, -1, data, sizeofdata, flags);
-}
-
-/**
- * gnutls_write - sends to the peer the specified data
- * @cd: is a connection descriptor
- * @state: is a &GNUTLS_STATE structure.
- * @data: contains the data to send
- * @sizeofdata: is the length of the data
*
- * This function has the same semantics as write() has. The only
- * difference is that is accepts a GNUTLS state.
**/
ssize_t gnutls_write(SOCKET cd, GNUTLS_STATE state, const void *data, size_t sizeofdata) {
- return gnutls_send_int( cd, state, GNUTLS_APPLICATION_DATA, -1, data, sizeofdata, 0);
+ return gnutls_send_int( cd, state, GNUTLS_APPLICATION_DATA, -1, data, sizeofdata);
}
/**
* a negative error code.
**/
ssize_t gnutls_read(SOCKET cd, GNUTLS_STATE state, void *data, size_t sizeofdata) {
- return gnutls_recv_int( cd, state, GNUTLS_APPLICATION_DATA, -1, data, sizeofdata, 0);
+ return gnutls_recv_int( cd, state, GNUTLS_APPLICATION_DATA, -1, data, sizeofdata);
}