diff options
| -rw-r--r-- | lib/COPYING | 2 | ||||
| -rw-r--r-- | lib/Makefile.am | 6 | ||||
| -rw-r--r-- | lib/attr.c | 3 | ||||
| -rw-r--r-- | lib/compat.c | 7 | ||||
| -rw-r--r-- | lib/compat.h | 3 | ||||
| -rw-r--r-- | lib/conf.c | 3 | ||||
| -rw-r--r-- | lib/conn.c | 93 | ||||
| -rw-r--r-- | lib/debug.c | 3 | ||||
| -rw-r--r-- | lib/debug.h | 3 | ||||
| -rw-r--r-- | lib/err.c | 3 | ||||
| -rw-r--r-- | lib/event.c | 252 | ||||
| -rw-r--r-- | lib/event.h | 11 | ||||
| -rw-r--r-- | lib/packet.c | 782 | ||||
| -rw-r--r-- | lib/packet.h | 4 | ||||
| -rw-r--r-- | lib/peer.c | 19 | ||||
| -rw-r--r-- | lib/peer.h | 4 | ||||
| -rw-r--r-- | lib/radsec.c | 5 | ||||
| -rw-r--r-- | lib/radsec.h | 9 | ||||
| -rw-r--r-- | lib/request.c | 3 | ||||
| -rw-r--r-- | lib/send.c | 128 | ||||
| -rw-r--r-- | lib/tcp.c | 276 | ||||
| -rw-r--r-- | lib/tcp.h | 6 | ||||
| -rw-r--r-- | lib/tls.c | 3 | ||||
| -rw-r--r-- | lib/tls.h | 3 | ||||
| -rw-r--r-- | lib/udp.c | 85 | ||||
| -rw-r--r-- | lib/udp.h | 4 | 
26 files changed, 931 insertions, 789 deletions
| diff --git a/lib/COPYING b/lib/COPYING index 0a1858c..7256aa4 100644 --- a/lib/COPYING +++ b/lib/COPYING @@ -1,5 +1,3 @@ -Copyright 2010, 2011 NORDUnet A/S. All rights reserved. -  Redistribution and use in source and binary forms, with or without  modification, are permitted provided that the following conditions are  met: diff --git a/lib/Makefile.am b/lib/Makefile.am index 69c1c27..fdaf1ec 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -15,10 +15,14 @@ libradsec_la_SOURCES = \  	conn.c \  	debug.c \  	err.c \ +	event.c \  	packet.c \  	peer.c \  	radsec.c \ -	request.c +	request.c \ +	send.c \ +	tcp.c \ +	udp.c  libradsec_la_SOURCES += \  	rsp_debug.c \ @@ -1,4 +1,5 @@ -/* See the file COPYING for licensing information.  */ +/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */  #if defined HAVE_CONFIG_H  #include <config.h> diff --git a/lib/compat.c b/lib/compat.c index b00bea1..fa075a1 100644 --- a/lib/compat.c +++ b/lib/compat.c @@ -1,3 +1,10 @@ +/* Copyright 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif +  #include <sys/types.h>  #include <sys/socket.h>  #include "compat.h" diff --git a/lib/compat.h b/lib/compat.h index 202d6dd..50ae22e 100644 --- a/lib/compat.h +++ b/lib/compat.h @@ -1 +1,4 @@ +/* Copyright 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */ +  ssize_t compat_send (int sockfd, const void *buf, size_t len, int flags); @@ -1,4 +1,5 @@ -/* See the file COPYING for licensing information.  */ +/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */  #if defined HAVE_CONFIG_H  #include <config.h> @@ -1,4 +1,5 @@ -/* See the file COPYING for licensing information.  */ +/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */  #if defined HAVE_CONFIG_H  #include <config.h> @@ -11,6 +12,8 @@  #include <event2/bufferevent.h>  #include <radsec/radsec.h>  #include <radsec/radsec-impl.h> +#include "event.h" +#include "tcp.h"  int  rs_conn_create (struct rs_context *ctx, struct rs_connection **conn, @@ -193,3 +196,91 @@ int rs_conn_fd (struct rs_connection *conn)    assert (conn->active_peer);    return conn->fd;  } + +static void +_rcb (struct rs_packet *packet, void *user_data) +{ +  struct rs_packet *pkt = (struct rs_packet *) user_data; +  assert (pkt); +  pkt->valid_flag = 1; +  if (pkt->conn->bev) +    bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ); +  else +    event_del (pkt->conn->rev); +} + +/* Special function used in libradsec blocking dispatching mode, +   i.e. with socket set to block on read/write and with no libradsec +   callbacks registered. + +   For any other use of libradsec, a the received_cb callback should +   be registered in the callbacks member of struct rs_connection. + +   On successful reception, verification and decoding of a RADIUS +   message, PKT_OUT will upon return point at a pointer to a struct +   rs_packet containing the message. + +   If anything goes wrong or if the read times out (TODO: explain), +   PKT_OUT will point at the NULL pointer and one or more errors are +   pushed on the connection (available through rs_err_conn_pop()).  */ + +int +rs_conn_receive_packet (struct rs_connection *conn, +		        struct rs_packet *request, +		        struct rs_packet **pkt_out) +{ +  int err = 0; +  struct rs_packet *pkt = NULL; + +  assert (conn); +  assert (conn->realm); +  assert (!conn->user_dispatch_flag); /* Dispatching mode only.  */ + +  if (rs_packet_create (conn, pkt_out)) +    return -1; +  pkt = *pkt_out; +  pkt->conn = conn; +  pkt->original = request; + +  assert (conn->evb); +  assert (conn->bev); +  assert (conn->active_peer); +  assert (conn->fd >= 0); + +  conn->callbacks.received_cb = _rcb; +  conn->user_data = pkt; +  if (conn->bev) +    { +      bufferevent_setwatermark (conn->bev, EV_READ, RS_HEADER_LEN, 0); +      bufferevent_setcb (conn->bev, tcp_read_cb, NULL, tcp_event_cb, pkt); +      bufferevent_enable (conn->bev, EV_READ); +    } +  else +    { +      err = event_add (conn->rev, NULL); +      if (err < 0) +	return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__, +				    "event_add: %s", +				    evutil_gai_strerror (err)); +    } + +  /* Dispatch.  */ +  rs_debug (("%s: entering event loop\n", __func__)); +  err = event_base_dispatch (conn->evb); +  conn->callbacks.received_cb = NULL; +  if (err < 0) +    return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__, +				"event_base_dispatch: %s", +				evutil_gai_strerror (err)); +  rs_debug (("%s: event loop done\n", __func__)); + +  if (!pkt->valid_flag) +    return -1; + +#if defined (DEBUG) +      rs_dump_packet (pkt); +#endif + +  pkt->original = NULL;		/* FIXME: Why?  */ +  return RSE_OK; +} diff --git a/lib/debug.c b/lib/debug.c index 4544f3c..59f25c1 100644 --- a/lib/debug.c +++ b/lib/debug.c @@ -1,4 +1,5 @@ -/* See the file COPYING for licensing information.  */ +/* Copyright 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */  #if defined HAVE_CONFIG_H  #include <config.h> diff --git a/lib/debug.h b/lib/debug.h index 4a899b2..a8d8632 100644 --- a/lib/debug.h +++ b/lib/debug.h @@ -1,4 +1,5 @@ -/* See the file COPYING for licensing information.  */ +/* Copyright 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */  #define hd(p, l) { int i;		\      for (i = 1; i <= l; i++) {		\ @@ -1,4 +1,5 @@ -/* See the file COPYING for licensing information.  */ +/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */  #if defined HAVE_CONFIG_H  #include <config.h> diff --git a/lib/event.c b/lib/event.c new file mode 100644 index 0000000..97a08c8 --- /dev/null +++ b/lib/event.c @@ -0,0 +1,252 @@ +/* Copyright 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <event2/event.h> +#include <event2/bufferevent.h> +#if defined (RS_ENABLE_TLS) +#include <event2/bufferevent_ssl.h> +#include <openssl/err.h> +#endif +#include <radsec/radsec.h> +#include <radsec/radsec-impl.h> +#if defined (RS_ENABLE_TLS) +#include "tls.h" +#endif +#include "udp.h" +#include "event.h" +#include "packet.h" +#include "debug.h" + +static void +_evlog_cb (int severity, const char *msg) +{ +  const char *sevstr; +  switch (severity) +    { +    case _EVENT_LOG_DEBUG: +#if !defined (DEBUG_LEVENT) +      return; +#endif +      sevstr = "debug"; +      break; +    case _EVENT_LOG_MSG: +      sevstr = "msg"; +      break; +    case _EVENT_LOG_WARN: +      sevstr = "warn"; +      break; +    case _EVENT_LOG_ERR: +      sevstr = "err"; +      break; +    default: +      sevstr = "???"; +      break; +    } +  fprintf (stderr, "libevent: [%s] %s\n", sevstr, msg); /* FIXME: stderr?  */ +} + +int +event_init_socket (struct rs_connection *conn, struct rs_peer *p) +{ +  if (conn->fd != -1) +    return RSE_OK; + +  assert (p->addr); +  conn->fd = socket (p->addr->ai_family, p->addr->ai_socktype, +		     p->addr->ai_protocol); +  if (conn->fd < 0) +    return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__, +				"socket: %d (%s)", +				errno, strerror (errno)); +  if (evutil_make_socket_nonblocking (conn->fd) < 0) +    { +      evutil_closesocket (conn->fd); +      conn->fd = -1; +      return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__, +				  "evutil_make_socket_nonblocking: %d (%s)", +				  errno, strerror (errno)); +    } +  return RSE_OK; +} + +static void +_conn_timeout_cb (int fd, short event, void *data) +{ +  struct rs_connection *conn; + +  assert (data); +  conn = (struct rs_connection *) data; + +  if (event & EV_TIMEOUT) +    { +      rs_debug (("%s: connection timeout on %p (fd %d) connecting to %p\n", +		 __func__, conn, conn->fd, conn->active_peer)); +      conn->is_connecting = 0; +      rs_err_conn_push_fl (conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL); +      event_loopbreak (conn); +    } +} + +int +event_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer) +{ +  if (conn->bev) +    return RSE_OK; + +  if (conn->realm->type == RS_CONN_TYPE_TCP) +    { +      conn->bev = bufferevent_socket_new (conn->evb, conn->fd, 0); +      if (!conn->bev) +	return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, +				    "bufferevent_socket_new"); +    } +#if defined (RS_ENABLE_TLS) +  else if (conn->realm->type == RS_CONN_TYPE_TLS) +    { +      if (rs_tls_init (conn)) +	return -1; +      /* Would be convenient to pass BEV_OPT_CLOSE_ON_FREE but things +	 seem to break when be_openssl_ctrl() (in libevent) calls +	 SSL_set_bio() after BIO_new_socket() with flag=1.  */ +      conn->bev = +	bufferevent_openssl_socket_new (conn->evb, conn->fd, conn->tls_ssl, +					BUFFEREVENT_SSL_CONNECTING, 0); +      if (!conn->bev) +	return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, +				    "bufferevent_openssl_socket_new"); +    } +#endif	/* RS_ENABLE_TLS */ +  else +    { +      return rs_err_conn_push_fl (conn, RSE_INTERNAL, __FILE__, __LINE__, +				  "%s: unknown connection type: %d", __func__, +				  conn->realm->type); +    } + +  return RSE_OK; +} + +void +event_do_connect (struct rs_connection *conn) +{ +  struct rs_peer *p; +  int err, sockerr; + +  assert (conn); +  assert (conn->active_peer); +  p = conn->active_peer; + +#if defined (DEBUG) +  { +    char host[80], serv[80]; + +    getnameinfo (p->addr->ai_addr, +		 p->addr->ai_addrlen, +		 host, sizeof(host), serv, sizeof(serv), +		 0 /* NI_NUMERICHOST|NI_NUMERICSERV*/); +    rs_debug (("%s: connecting to %s:%s\n", __func__, host, serv)); +  } +#endif + +  if (p->conn->bev)		/* TCP */ +    { +      event_set_timeout (conn); +      err = bufferevent_socket_connect (p->conn->bev, p->addr->ai_addr, +					p->addr->ai_addrlen); +      if (err < 0) +	rs_err_conn_push_fl (p->conn, RSE_EVENT, __FILE__, __LINE__, +			     "bufferevent_socket_connect: %s", +			     evutil_gai_strerror (err)); +      else +	p->conn->is_connecting = 1; +    } +  else				/* UDP */ +    { +      err = connect (p->conn->fd, p->addr->ai_addr, p->addr->ai_addrlen); +      if (err < 0) +	{ +	  sockerr = evutil_socket_geterror (p->conn->fd); +	  rs_debug (("%s: %d: connect: %d (%s)\n", __func__, p->conn->fd, +		     sockerr, evutil_socket_error_to_string (sockerr))); +	  rs_err_conn_push_fl (p->conn, RSE_SOCKERR, __FILE__, __LINE__, +			       "%d: connect: %d (%s)", p->conn->fd, sockerr, +			       evutil_socket_error_to_string (sockerr)); +	} +    } +} + +int +event_loopbreak (struct rs_connection *conn) +{ +  int err = event_base_loopbreak (conn->evb); +  if (err < 0) +    rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, +			 "event_base_loopbreak: %s", +			 evutil_gai_strerror (err)); +  return err; +} + + +int +event_set_timeout (struct rs_connection *conn) +{ +  struct timeval tv; + +  if (!conn->tev) +    conn->tev = evtimer_new (conn->evb, _conn_timeout_cb, conn); +  if (!conn->tev) +    return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, +				"evtimer_new"); +  tv.tv_sec = conn->realm->timeout; +  tv.tv_usec = 0; +  evtimer_add (conn->tev, &tv); + +  return RSE_OK; +} + +void +event_on_disconnect (struct rs_connection *conn) +{ +  conn->is_connecting = 0; +  conn->is_connected = 0; +  rs_debug (("%s: %p disconnected\n", __func__, conn->active_peer)); +  if (conn->callbacks.disconnected_cb) +    conn->callbacks.disconnected_cb (conn->user_data); +} + +void +event_on_connect (struct rs_connection *conn, struct rs_packet *pkt) +{ +  assert (!conn->is_connecting); +  conn->is_connected = 1; +  rs_debug (("%s: %p connected\n", __func__, conn->active_peer)); +  if (conn->tev) +    evtimer_del (conn->tev); +  if (conn->callbacks.connected_cb) +    conn->callbacks.connected_cb (conn->user_data); +  if (pkt) +    packet_do_send (pkt); +} + +int +event_init_eventbase (struct rs_connection *conn) +{ +  if (conn->evb) +    return RSE_OK; + +#if defined (DEBUG) +  event_enable_debug_mode (); +#endif +  event_set_log_callback (_evlog_cb); +  conn->evb = event_base_new (); +  if (!conn->evb) +    return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, +				"event_base_new"); + +  return RSE_OK; +} diff --git a/lib/event.h b/lib/event.h new file mode 100644 index 0000000..5395f58 --- /dev/null +++ b/lib/event.h @@ -0,0 +1,11 @@ +/* Copyright 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */ + +int event_set_timeout (struct rs_connection *conn); +void event_on_disconnect (struct rs_connection *conn); +void event_on_connect (struct rs_connection *conn, struct rs_packet *pkt); +int event_loopbreak (struct rs_connection *conn); +int event_init_eventbase (struct rs_connection *conn); +int event_init_socket (struct rs_connection *conn, struct rs_peer *p); +int event_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer); +void event_do_connect (struct rs_connection *conn); diff --git a/lib/packet.c b/lib/packet.c index afe2725..6ba9fd3 100644 --- a/lib/packet.c +++ b/lib/packet.c @@ -1,60 +1,27 @@ -/* See the file COPYING for licensing information.  */ +/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */  #if defined HAVE_CONFIG_H  #include <config.h>  #endif -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <sys/time.h> -#include <sys/types.h>  #include <assert.h> -#include <freeradius/libradius.h> -#include <event2/event.h>  #include <event2/bufferevent.h>  #include <radsec/radsec.h>  #include <radsec/radsec-impl.h> -#include "compat.h"  #include "debug.h" -#if defined (RS_ENABLE_TLS) -#include <event2/bufferevent_ssl.h> -#include <openssl/err.h> -#include "tls.h" -#endif +#include "packet.h" +  #if defined (DEBUG)  #include <netdb.h>  #include <sys/socket.h>  #include <event2/buffer.h>  #endif -static int -_close_conn (struct rs_connection **connp) -{ -  int r; -  assert (connp); -  assert (*connp); -  r = rs_conn_destroy (*connp); -  if (!r) -    *connp = NULL; -  return r; -} - -static int -_loopbreak (struct rs_connection *conn) -{ -  int err = event_base_loopbreak (conn->evb); -  if (err < 0) -    rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, -			 "event_base_loopbreak: %s", -			 evutil_gai_strerror (err)); -  return err; -} -  /* Badly named helper function for preparing a RADIUS message and     queue it.  FIXME: Rename.  */ -static int -_do_send (struct rs_packet *pkt) +int +packet_do_send (struct rs_packet *pkt)  {    VALUE_PAIR *vp = NULL; @@ -109,583 +76,6 @@ _do_send (struct rs_packet *pkt)    return RSE_OK;  } -static void -_on_connect (struct rs_connection *conn, struct rs_packet *pkt) -{ -  assert (!conn->is_connecting); -  conn->is_connected = 1; -  rs_debug (("%s: %p connected\n", __func__, conn->active_peer)); -  if (conn->tev) -    evtimer_del (conn->tev); -  if (conn->callbacks.connected_cb) -    conn->callbacks.connected_cb (conn->user_data); -  if (pkt) -    _do_send (pkt); -} - -static void -_on_disconnect (struct rs_connection *conn) -{ -  conn->is_connecting = 0; -  conn->is_connected = 0; -  rs_debug (("%s: %p disconnected\n", __func__, conn->active_peer)); -  if (conn->callbacks.disconnected_cb) -    conn->callbacks.disconnected_cb (conn->user_data); -} - -static void -_event_cb (struct bufferevent *bev, short events, void *user_data) -{ -  struct rs_packet *pkt = (struct rs_packet *) user_data; -  struct rs_connection *conn = NULL; -  struct rs_peer *p = NULL; -  int sockerr = 0; -#if defined (RS_ENABLE_TLS) -  unsigned long tlserr = 0; -#endif - -  assert (pkt); -  assert (pkt->conn); -  assert (pkt->conn->active_peer); -  conn = pkt->conn; -  p = conn->active_peer; - -  conn->is_connecting = 0; -  if (events & BEV_EVENT_CONNECTED) -    { -      _on_connect (conn, pkt); -    } -  else if (events & BEV_EVENT_EOF) -    { -      _on_disconnect (conn); -    } -  else if (events & BEV_EVENT_TIMEOUT) -    { -      rs_debug (("%s: %p times out on %s\n", __func__, p, -		 (events & BEV_EVENT_READING) ? "read" : "write")); -      rs_err_conn_push_fl (pkt->conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL); -    } -  else if (events & BEV_EVENT_ERROR) -    { -      sockerr = evutil_socket_geterror (conn->active_peer->fd); -      if (sockerr == 0)	/* FIXME: True that errno == 0 means closed? */ -	{ -	  _on_disconnect (conn); -	} -      else -	{ -	  rs_debug (("%s: %d: %d (%s)\n", __func__, conn->fd, sockerr, -		     evutil_socket_error_to_string (sockerr))); -	  rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__, -			       "%d: %d (%s)", conn->fd, sockerr, -			       evutil_socket_error_to_string (sockerr)); -	} -#if defined (RS_ENABLE_TLS) -      if (conn->tls_ssl)	/* FIXME: correct check?  */ -	{ -	  for (tlserr = bufferevent_get_openssl_error (conn->bev); -	       tlserr; -	       tlserr = bufferevent_get_openssl_error (conn->bev)) -	    { -	      rs_debug (("%s: openssl error: %s\n", __func__, -			 ERR_error_string (tlserr, NULL))); -	      rs_err_conn_push_fl (pkt->conn, RSE_SSLERR, __FILE__, __LINE__, -				   ERR_error_string (tlserr, NULL)); -	    } -	} -#endif	/* RS_ENABLE_TLS */ -      _loopbreak (conn); -    } - -#if defined (DEBUG) -  if (events & BEV_EVENT_ERROR && events != BEV_EVENT_ERROR) -    rs_debug (("%s: BEV_EVENT_ERROR and more: 0x%x\n", __func__, events)); -#endif -} - -static void -_write_cb (struct bufferevent *bev, void *ctx) -{ -  struct rs_packet *pkt = (struct rs_packet *) ctx; - -  assert (pkt); -  assert (pkt->conn); - -  if (pkt->conn->callbacks.sent_cb) -    pkt->conn->callbacks.sent_cb (pkt->conn->user_data); -} - -/* Read one RADIUS packet header.  Return !0 on error.  A return value -   of 0 means that we need more data.  */ -static int -_read_header (struct rs_packet *pkt) -{ -  size_t n = 0; - -  n = bufferevent_read (pkt->conn->bev, pkt->hdr, RS_HEADER_LEN); -  if (n == RS_HEADER_LEN) -    { -      pkt->hdr_read_flag = 1; -      pkt->rpkt->data_len = (pkt->hdr[2] << 8) + pkt->hdr[3]; -      if (pkt->rpkt->data_len < 20 || pkt->rpkt->data_len > 4096) -	{ -	  _close_conn (&pkt->conn); -	  return rs_err_conn_push (pkt->conn, RSE_INVALID_PKT, -				   "invalid packet length: %d", -				   pkt->rpkt->data_len); -	} -      pkt->rpkt->data = rs_malloc (pkt->conn->ctx, pkt->rpkt->data_len); -      if (!pkt->rpkt->data) -	{ -	  _close_conn (&pkt->conn); -	  return rs_err_conn_push_fl (pkt->conn, RSE_NOMEM, __FILE__, __LINE__, -				      NULL); -	} -      memcpy (pkt->rpkt->data, pkt->hdr, RS_HEADER_LEN); -      bufferevent_setwatermark (pkt->conn->bev, EV_READ, -				pkt->rpkt->data_len - RS_HEADER_LEN, 0); -      rs_debug (("%s: packet header read, total pkt len=%d\n", -		 __func__, pkt->rpkt->data_len)); -    } -  else if (n < 0) -    { -      rs_debug (("%s: buffer frozen while reading header\n", __func__)); -    } -  else	    /* Error: libevent gave us less than the low watermark. */ -    { -      _close_conn (&pkt->conn); -      return rs_err_conn_push_fl (pkt->conn, RSE_INTERNAL, __FILE__, __LINE__, -				  "got %d octets reading header", n); -    } - -  return 0; -} - -static int -_read_packet (struct rs_packet *pkt) -{ -  size_t n = 0; - -  rs_debug (("%s: trying to read %d octets of packet data\n", __func__, -	     pkt->rpkt->data_len - RS_HEADER_LEN)); - -  n = bufferevent_read (pkt->conn->bev, -			pkt->rpkt->data + RS_HEADER_LEN, -			pkt->rpkt->data_len - RS_HEADER_LEN); - -  rs_debug (("%s: read %ld octets of packet data\n", __func__, n)); - -  if (n == pkt->rpkt->data_len - RS_HEADER_LEN) -    { -      bufferevent_disable (pkt->conn->bev, EV_READ); -      rs_debug (("%s: complete packet read\n", __func__)); -      pkt->hdr_read_flag = 0; -      memset (pkt->hdr, 0, sizeof(*pkt->hdr)); - -      /* Checks done by rad_packet_ok: -	 - lenghts (FIXME: checks really ok for tcp?) -	 - invalid code field -	 - attribute lengths >= 2 -	 - attribute sizes adding up correctly  */ -      if (!rad_packet_ok (pkt->rpkt, 0) != 0) -	{ -	  _close_conn (&pkt->conn); -	  return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__, -				      "invalid packet: %s", fr_strerror ()); -	} - -      /* TODO: Verify that reception of an unsolicited response packet -	 results in connection being closed.  */ - -      /* If we have a request to match this response against, verify -	 and decode the response.  */ -      if (pkt->original) -	{ -	  /* Verify header and message authenticator.  */ -	  if (rad_verify (pkt->rpkt, pkt->original->rpkt, -			  pkt->conn->active_peer->secret)) -	    { -	      _close_conn (&pkt->conn); -	      return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__, -					  "rad_verify: %s", fr_strerror ()); -	    } - -	  /* Decode and decrypt.  */ -	  if (rad_decode (pkt->rpkt, pkt->original->rpkt, -			  pkt->conn->active_peer->secret)) -	    { -	      _close_conn (&pkt->conn); -	      return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__, -					  "rad_decode: %s", fr_strerror ()); -	    } -	} - -#if defined (DEBUG) -      /* Find out what happens if there's data left in the buffer.  */ -      { -	size_t rest = 0; -	rest = evbuffer_get_length (bufferevent_get_input (pkt->conn->bev)); -	if (rest) -	  rs_debug (("%s: returning with %d octets left in buffer\n", __func__, -		     rest)); -      } -#endif - -      /* Hand over message to user, changes ownership of pkt.  Don't -	 touch it afterwards -- it might have been freed.  */ -      if (pkt->conn->callbacks.received_cb) -	pkt->conn->callbacks.received_cb (pkt, pkt->conn->user_data); -    } -  else if (n < 0)		/* Buffer frozen.  */ -    rs_debug (("%s: buffer frozen when reading packet\n", __func__)); -  else				/* Short packet.  */ -    rs_debug (("%s: waiting for another %d octets\n", __func__, -	       pkt->rpkt->data_len - RS_HEADER_LEN - n)); - -  return 0; -} - -/* Read callback for TCP. - -   Read exactly one RADIUS message from BEV and store it in struct -   rs_packet passed in CTX (hereby called 'pkt'). - -   Verify the received packet against pkt->original, if !NULL. - -   Inform upper layer about successful reception of valid RADIUS -   message by invoking conn->callbacks.recevied_cb(), if !NULL.  */ -static void -_read_cb (struct bufferevent *bev, void *user_data) -{ -  struct rs_packet *pkt = (struct rs_packet *) user_data; - -  assert (pkt); -  assert (pkt->conn); -  assert (pkt->rpkt); - -  pkt->rpkt->sockfd = pkt->conn->fd; -  pkt->rpkt->vps = NULL; - -  if (!pkt->hdr_read_flag) -    if (_read_header (pkt)) -      return; -  _read_packet (pkt); -} - -static void -_evlog_cb (int severity, const char *msg) -{ -  const char *sevstr; -  switch (severity) -    { -    case _EVENT_LOG_DEBUG: -#if !defined (DEBUG_LEVENT) -      return; -#endif -      sevstr = "debug"; -      break; -    case _EVENT_LOG_MSG: -      sevstr = "msg"; -      break; -    case _EVENT_LOG_WARN: -      sevstr = "warn"; -      break; -    case _EVENT_LOG_ERR: -      sevstr = "err"; -      break; -    default: -      sevstr = "???"; -      break; -    } -  fprintf (stderr, "libevent: [%s] %s\n", sevstr, msg); /* FIXME: stderr?  */ -} - -static int -_init_eventbase (struct rs_connection *conn) -{ -  if (conn->evb) -    return RSE_OK; - -#if defined (DEBUG) -  event_enable_debug_mode (); -#endif -  event_set_log_callback (_evlog_cb); -  conn->evb = event_base_new (); -  if (!conn->evb) -    return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, -				"event_base_new"); - -  return RSE_OK; -} - -static int -_init_socket (struct rs_connection *conn, struct rs_peer *p) -{ -  if (conn->fd != -1) -    return RSE_OK; - -  assert (p->addr); -  conn->fd = socket (p->addr->ai_family, p->addr->ai_socktype, -		     p->addr->ai_protocol); -  if (conn->fd < 0) -    return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__, -				"socket: %d (%s)", -				errno, strerror (errno)); -  if (evutil_make_socket_nonblocking (conn->fd) < 0) -    { -      evutil_closesocket (conn->fd); -      conn->fd = -1; -      return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__, -				  "evutil_make_socket_nonblocking: %d (%s)", -				  errno, strerror (errno)); -    } -  return RSE_OK; -} - -static struct rs_peer * -_pick_peer (struct rs_connection *conn) -{ -  assert (conn); - -  if (conn->active_peer) -    conn->active_peer = conn->active_peer->next; /* Next.  */ -  if (!conn->active_peer) -    conn->active_peer = conn->peers; /* From the top.  */ - -  return conn->active_peer; -} - -static void -_conn_timeout_cb (int fd, short event, void *data) -{ -  struct rs_connection *conn; - -  assert (data); -  conn = (struct rs_connection *) data; - -  if (event & EV_TIMEOUT) -    { -      rs_debug (("%s: connection timeout on %p (fd %d) connecting to %p\n", -		 __func__, conn, conn->fd, conn->active_peer)); -      conn->is_connecting = 0; -      rs_err_conn_push_fl (conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL); -      _loopbreak (conn); -    } -} -static int -_set_timeout (struct rs_connection *conn) -{ -  struct timeval tv; - -  if (!conn->tev) -    conn->tev = evtimer_new (conn->evb, _conn_timeout_cb, conn); -  if (!conn->tev) -    return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, -				"evtimer_new"); -  tv.tv_sec = conn->realm->timeout; -  tv.tv_usec = 0; -  evtimer_add (conn->tev, &tv); - -  return RSE_OK; -} - -static int -_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer) -{ -  if (conn->bev) -    return RSE_OK; - -  if (conn->realm->type == RS_CONN_TYPE_TCP) -    { -      conn->bev = bufferevent_socket_new (conn->evb, conn->fd, 0); -      if (!conn->bev) -	return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, -				    "bufferevent_socket_new"); -    } -#if defined (RS_ENABLE_TLS) -  else if (conn->realm->type == RS_CONN_TYPE_TLS) -    { -      if (rs_tls_init (conn)) -	return -1; -      /* Would be convenient to pass BEV_OPT_CLOSE_ON_FREE but things -	 seem to break when be_openssl_ctrl() (in libevent) calls -	 SSL_set_bio() after BIO_new_socket() with flag=1.  */ -      conn->bev = -	bufferevent_openssl_socket_new (conn->evb, conn->fd, conn->tls_ssl, -					BUFFEREVENT_SSL_CONNECTING, 0); -      if (!conn->bev) -	return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, -				    "bufferevent_openssl_socket_new"); -    } -#endif	/* RS_ENABLE_TLS */ -  else -    { -      return rs_err_conn_push_fl (conn, RSE_INTERNAL, __FILE__, __LINE__, -				  "%s: unknown connection type: %d", __func__, -				  conn->realm->type); -    } - -  return RSE_OK; -} - -/* Callback for conn->wev and conn->rev.  FIXME: Rename.  */ -static void -_evcb (evutil_socket_t fd, short what, void *user_data) -{ -  //rs_debug (("%s: fd=%d what=0x%x\n", __func__, fd, what)); -  if (what & EV_TIMEOUT) -    { -      struct rs_connection *conn = (struct rs_connection *) user_data; -      assert (conn); -      conn->is_connecting = 0; -      rs_debug (("%s: UDP timeout NYI", __func__)); -    } -  else if (what & EV_READ) -    { -      struct rs_connection *conn = (struct rs_connection *) user_data; -      assert (conn); -      /* read a single UDP packet and stick it in a new struct -	 rs_packet */ - -      rs_debug (("%s: UDP read NYI", __func__)); -    } -  else if (what & EV_WRITE) -    { -      struct rs_packet *pkt = (struct rs_packet *) user_data; -      assert (pkt); -      /* Socket ready for writing, possibly as a result of a -	 successful connect.  */ -      if (!pkt->conn->is_connected) -	_on_connect (pkt->conn, pkt); -      if (pkt->conn->out_queue) -	{ -	  /* Send one packet, the first.  */ -	  ssize_t r = 0; -	  struct rs_packet *p = pkt->conn->out_queue; - -	  assert (p->rpkt); -	  assert (p->rpkt->data); -	  r = compat_send (fd, p->rpkt->data, p->rpkt->data_len, 0); -	  if (r == -1) -	    { -	      int sockerr = evutil_socket_geterror (p->conn->fd); -	      if (sockerr != EAGAIN) -		rs_err_conn_push_fl (p->conn, RSE_SOCKERR, __FILE__, __LINE__, -				     "%d: send: %d (%s)", fd, sockerr, -				     evutil_socket_error_to_string (sockerr)); -	      return;		/* Don't unlink packet. */ -	    } -	  pkt->conn->out_queue = p->next; -	} -    } -} - -static int -_init_udp (struct rs_connection *conn, struct rs_packet *pkt) -{ -  assert (!conn->bev); - -  conn->rev = event_new (conn->evb, conn->fd, EV_READ|EV_PERSIST, _evcb, conn); -  conn->wev = event_new (conn->evb, conn->fd, EV_WRITE|EV_PERSIST, _evcb, pkt); -  if (!conn->rev || !conn->wev) -    { -      if (conn->rev) -	event_free (conn->rev); -      /* ENOMEM _or_ EINVAL but EINVAL only if we use EV_SIGNAL, at -	 least for now (libevent-2.0.5).  */ -      return rs_err_conn_push_fl (conn, RSE_NOMEM, __FILE__, __LINE__, NULL); -    } -  return RSE_OK; -} - -static void -_do_connect (struct rs_connection *conn) -{ -  struct rs_peer *p; -  int err, sockerr; - -  assert (conn); -  assert (conn->active_peer); -  p = conn->active_peer; - -#if defined (DEBUG) -  { -    char host[80], serv[80]; - -    getnameinfo (p->addr->ai_addr, -		 p->addr->ai_addrlen, -		 host, sizeof(host), serv, sizeof(serv), -		 0 /* NI_NUMERICHOST|NI_NUMERICSERV*/); -    rs_debug (("%s: connecting to %s:%s\n", __func__, host, serv)); -  } -#endif - -  if (p->conn->bev)		/* TCP */ -    { -      _set_timeout (conn); -      err = bufferevent_socket_connect (p->conn->bev, p->addr->ai_addr, -					p->addr->ai_addrlen); -      if (err < 0) -	rs_err_conn_push_fl (p->conn, RSE_EVENT, __FILE__, __LINE__, -			     "bufferevent_socket_connect: %s", -			     evutil_gai_strerror (err)); -      else -	p->conn->is_connecting = 1; -    } -  else				/* UDP */ -    { -      err = connect (p->conn->fd, p->addr->ai_addr, p->addr->ai_addrlen); -      if (err < 0) -	{ -	  sockerr = evutil_socket_geterror (p->conn->fd); -	  rs_debug (("%s: %d: connect: %d (%s)\n", __func__, p->conn->fd, -		     sockerr, evutil_socket_error_to_string (sockerr))); -	  rs_err_conn_push_fl (p->conn, RSE_SOCKERR, __FILE__, __LINE__, -			       "%d: connect: %d (%s)", p->conn->fd, sockerr, -			       evutil_socket_error_to_string (sockerr)); -	} -    } -} - -static int -_conn_open (struct rs_connection *conn, struct rs_packet *pkt) -{ -  if (_init_eventbase (conn)) -    return -1; - -  if (!conn->active_peer) -    _pick_peer (conn); -  if (!conn->active_peer) -    return rs_err_conn_push_fl (conn, RSE_NOPEER, __FILE__, __LINE__, NULL); - -  if (_init_socket (conn, conn->active_peer)) -    return -1; - -  if (conn->realm->type == RS_CONN_TYPE_TCP -      || conn->realm->type == RS_CONN_TYPE_TLS) -    { -      if (_init_bufferevent (conn, conn->active_peer)) -	return -1; -    } -  else -    { -      if (_init_udp (conn, pkt)) -	return -1; -    } - -  if (!conn->is_connected) -    if (!conn->is_connecting) -      _do_connect (conn); - -  return RSE_OK; -} - -static int -_conn_is_open_p (struct rs_connection *conn) -{ -  return conn->active_peer && conn->is_connected; -} -  /* Public functions.  */  int  rs_packet_create (struct rs_connection *conn, struct rs_packet **pkt_out) @@ -744,165 +134,6 @@ rs_packet_create_authn_request (struct rs_connection *conn,    return RSE_OK;  } -/* User callback used when we're dispatching for user.  */ -static void -_wcb (void *user_data) -{ -  struct rs_packet *pkt = (struct rs_packet *) user_data; -  assert (pkt); -  pkt->written_flag = 1; -  if (pkt->conn->bev) -    bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ); -  else -    event_del (pkt->conn->wev); -} - -int -rs_packet_send (struct rs_packet *pkt, void *user_data) -{ -  struct rs_connection *conn = NULL; -  int err = 0; - -  assert (pkt); -  assert (pkt->conn); -  conn = pkt->conn; - -  if (_conn_is_open_p (conn)) -    _do_send (pkt); -  else -    if (_conn_open (conn, pkt)) -      return -1; - -  assert (conn->evb); -  assert (conn->active_peer); -  assert (conn->fd >= 0); - -  conn->user_data = user_data; - -  if (conn->bev)		/* TCP */ -    { -      bufferevent_setcb (conn->bev, NULL, _write_cb, _event_cb, pkt); -      bufferevent_enable (conn->bev, EV_WRITE); -    } -  else			/* UDP */ -    { -      err = event_add (conn->wev, NULL); -      if (err < 0) -	return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__, -				    "event_add: %s", -				    evutil_gai_strerror (err)); -    } - -  /* Do dispatch, unless the user wants to do it herself.  */ -  if (!conn->user_dispatch_flag) -    { -      conn->callbacks.sent_cb = _wcb; -      conn->user_data = pkt; -      rs_debug (("%s: entering event loop\n", __func__)); -      err = event_base_dispatch (conn->evb); -      if (err < 0) -	return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__, -				    "event_base_dispatch: %s", -				    evutil_gai_strerror (err)); -      rs_debug (("%s: event loop done\n", __func__)); -      conn->callbacks.sent_cb = NULL; -      conn->user_data = NULL; - -      if (!pkt->written_flag) -	return -1; -    } - -  return RSE_OK; -} - -static void -_rcb (struct rs_packet *packet, void *user_data) -{ -  struct rs_packet *pkt = (struct rs_packet *) user_data; -  assert (pkt); -  pkt->valid_flag = 1; -  if (pkt->conn->bev) -    bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ); -  else -    event_del (pkt->conn->rev); -} - -/* Special function used in libradsec blocking dispatching mode, -   i.e. with socket set to block on read/write and with no libradsec -   callbacks registered. - -   For any other use of libradsec, a the received_cb callback should -   be registered in the callbacks member of struct rs_connection. - -   On successful reception, verification and decoding of a RADIUS -   message, PKT_OUT will upon return point at a pointer to a struct -   rs_packet containing the message. - -   If anything goes wrong or if the read times out (TODO: explain), -   PKT_OUT will point at the NULL pointer and one or more errors are -   pushed on the connection (available through rs_err_conn_pop()).  */ - -int -rs_conn_receive_packet (struct rs_connection *conn, -		        struct rs_packet *request, -		        struct rs_packet **pkt_out) -{ -  int err = 0; -  struct rs_packet *pkt = NULL; - -  assert (conn); -  assert (conn->realm); -  assert (!conn->user_dispatch_flag); /* Dispatching mode only.  */ - -  if (rs_packet_create (conn, pkt_out)) -    return -1; -  pkt = *pkt_out; -  pkt->conn = conn; -  pkt->original = request; - -  assert (conn->evb); -  assert (conn->bev); -  assert (conn->active_peer); -  assert (conn->fd >= 0); - -  conn->callbacks.received_cb = _rcb; -  conn->user_data = pkt; -  if (conn->bev) -    { -      bufferevent_setwatermark (conn->bev, EV_READ, RS_HEADER_LEN, 0); -      bufferevent_setcb (conn->bev, _read_cb, NULL, _event_cb, pkt); -      bufferevent_enable (conn->bev, EV_READ); -    } -  else -    { -      err = event_add (conn->rev, NULL); -      if (err < 0) -	return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__, -				    "event_add: %s", -				    evutil_gai_strerror (err)); -    } - -  /* Dispatch.  */ -  rs_debug (("%s: entering event loop\n", __func__)); -  err = event_base_dispatch (conn->evb); -  conn->callbacks.received_cb = NULL; -  if (err < 0) -    return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__, -				"event_base_dispatch: %s", -				evutil_gai_strerror (err)); -  rs_debug (("%s: event loop done\n", __func__)); - -  if (!pkt->valid_flag) -    return -1; - -#if defined (DEBUG) -      rs_dump_packet (pkt); -#endif - -  pkt->original = NULL;		/* FIXME: Why?  */ -  return RSE_OK; -} -  void  rs_packet_add_attr (struct rs_packet *pkt, struct rs_attr *attr)  { @@ -927,3 +158,4 @@ rs_packet_destroy (struct rs_packet *pkt)        rs_free (pkt->conn->ctx, pkt);      }  } + diff --git a/lib/packet.h b/lib/packet.h new file mode 100644 index 0000000..7053329 --- /dev/null +++ b/lib/packet.h @@ -0,0 +1,4 @@ +/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */ + +int packet_do_send (struct rs_packet *pkt); @@ -1,4 +1,6 @@ -/* See the file COPYING for licensing information.  */ +/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */ +  #if defined HAVE_CONFIG_H  #include <config.h>  #endif @@ -6,7 +8,22 @@  #include <assert.h>  #include <radsec/radsec.h>  #include <radsec/radsec-impl.h> +#include "peer.h" + +struct rs_peer * +peer_pick_peer (struct rs_connection *conn) +{ +  assert (conn); + +  if (conn->active_peer) +    conn->active_peer = conn->active_peer->next; /* Next.  */ +  if (!conn->active_peer) +    conn->active_peer = conn->peers; /* From the top.  */ + +  return conn->active_peer; +} +/* Public functions.  */  int  rs_peer_create (struct rs_connection *conn, struct rs_peer **peer_out)  { diff --git a/lib/peer.h b/lib/peer.h new file mode 100644 index 0000000..f430bb2 --- /dev/null +++ b/lib/peer.h @@ -0,0 +1,4 @@ +/* Copyright 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */ + +struct rs_peer *peer_pick_peer (struct rs_connection *conn); diff --git a/lib/radsec.c b/lib/radsec.c index f191e73..6eb7c39 100644 --- a/lib/radsec.c +++ b/lib/radsec.c @@ -1,4 +1,5 @@ -/* See the file COPYING for licensing information.  */ +/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */  #if defined HAVE_CONFIG_H  #include <config.h> @@ -24,6 +25,8 @@  #endif  #include "rsp_debug.h" + +/* Public functions.  */  int  rs_context_create(struct rs_context **ctx, const char *dict)  { diff --git a/lib/radsec.h b/lib/radsec.h new file mode 100644 index 0000000..c88be66 --- /dev/null +++ b/lib/radsec.h @@ -0,0 +1,9 @@ +/* Copyright 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */ + +/* RFC 5080 2.2.1.  Retransmission Behavior */ +#define IRT 2 +#define MRC 5 +#define MRT 16 +#define MRD 30 + diff --git a/lib/request.c b/lib/request.c index 9aa29cb..1d0b4ac 100644 --- a/lib/request.c +++ b/lib/request.c @@ -1,4 +1,5 @@ -/* See the file COPYING for licensing information.  */ +/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */  #if defined HAVE_CONFIG_H  #include <config.h> diff --git a/lib/send.c b/lib/send.c new file mode 100644 index 0000000..c49eaa9 --- /dev/null +++ b/lib/send.c @@ -0,0 +1,128 @@ +/* Copyright 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <event2/event.h> +#include <event2/bufferevent.h> +#include <radsec/radsec.h> +#include <radsec/radsec-impl.h> +#include "debug.h" +#include "packet.h" +#include "event.h" +#include "peer.h" +#include "tcp.h" +#include "udp.h" + +static int +_conn_open (struct rs_connection *conn, struct rs_packet *pkt) +{ +  if (event_init_eventbase (conn)) +    return -1; + +  if (!conn->active_peer) +    peer_pick_peer (conn); +  if (!conn->active_peer) +    return rs_err_conn_push_fl (conn, RSE_NOPEER, __FILE__, __LINE__, NULL); + +  if (event_init_socket (conn, conn->active_peer)) +    return -1; + +  if (conn->realm->type == RS_CONN_TYPE_TCP +      || conn->realm->type == RS_CONN_TYPE_TLS) +    { +      if (event_init_bufferevent (conn, conn->active_peer)) +	return -1; +    } +  else +    { +      if (udp_init (conn, pkt)) +	return -1; +    } + +  if (!conn->is_connected) +    if (!conn->is_connecting) +      event_do_connect (conn); + +  return RSE_OK; +} + +static int +_conn_is_open_p (struct rs_connection *conn) +{ +  return conn->active_peer && conn->is_connected; +} + +/* User callback used when we're dispatching for user.  */ +static void +_wcb (void *user_data) +{ +  struct rs_packet *pkt = (struct rs_packet *) user_data; +  assert (pkt); +  pkt->written_flag = 1; +  if (pkt->conn->bev) +    bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ); +  else +    event_del (pkt->conn->wev); +} + +int +rs_packet_send (struct rs_packet *pkt, void *user_data) +{ +  struct rs_connection *conn = NULL; +  int err = 0; + +  assert (pkt); +  assert (pkt->conn); +  conn = pkt->conn; + +  if (_conn_is_open_p (conn)) +    packet_do_send (pkt); +  else +    if (_conn_open (conn, pkt)) +      return -1; + +  assert (conn->evb); +  assert (conn->active_peer); +  assert (conn->fd >= 0); + +  conn->user_data = user_data; + +  if (conn->bev)		/* TCP */ +    { +      bufferevent_setcb (conn->bev, NULL, tcp_write_cb, tcp_event_cb, pkt); +      bufferevent_enable (conn->bev, EV_WRITE); +    } +  else			/* UDP */ +    { +      err = event_add (conn->wev, NULL); +      if (err < 0) +	return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__, +				    "event_add: %s", +				    evutil_gai_strerror (err)); +    } + +  /* Do dispatch, unless the user wants to do it herself.  */ +  if (!conn->user_dispatch_flag) +    { +      conn->callbacks.sent_cb = _wcb; +      conn->user_data = pkt; +      rs_debug (("%s: entering event loop\n", __func__)); +      err = event_base_dispatch (conn->evb); +      if (err < 0) +	return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__, +				    "event_base_dispatch: %s", +				    evutil_gai_strerror (err)); +      rs_debug (("%s: event loop done\n", __func__)); +      conn->callbacks.sent_cb = NULL; +      conn->user_data = NULL; + +      if (!pkt->written_flag) +	return -1; +    } + +  return RSE_OK; +} diff --git a/lib/tcp.c b/lib/tcp.c new file mode 100644 index 0000000..2e641f6 --- /dev/null +++ b/lib/tcp.c @@ -0,0 +1,276 @@ +/* Copyright 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <event2/event.h> +#include <event2/bufferevent.h> +#if defined (RS_ENABLE_TLS) +#include <event2/bufferevent_ssl.h> +#include <openssl/err.h> +#endif +#include <radsec/radsec.h> +#include <radsec/radsec-impl.h> +#include "tcp.h" +#include "packet.h" +#include "debug.h" +#include "event.h" + +#if defined (DEBUG) +#include <event2/buffer.h> +#endif + +static int +_close_conn (struct rs_connection **connp) +{ +  int r; +  assert (connp); +  assert (*connp); +  r = rs_conn_destroy (*connp); +  if (!r) +    *connp = NULL; +  return r; +} + +/* Read one RADIUS packet header.  Return !0 on error.  A return value +   of 0 means that we need more data.  */ +static int +_read_header (struct rs_packet *pkt) +{ +  size_t n = 0; + +  n = bufferevent_read (pkt->conn->bev, pkt->hdr, RS_HEADER_LEN); +  if (n == RS_HEADER_LEN) +    { +      pkt->hdr_read_flag = 1; +      pkt->rpkt->data_len = (pkt->hdr[2] << 8) + pkt->hdr[3]; +      if (pkt->rpkt->data_len < 20 || pkt->rpkt->data_len > 4096) +	{ +	  _close_conn (&pkt->conn); +	  return rs_err_conn_push (pkt->conn, RSE_INVALID_PKT, +				   "invalid packet length: %d", +				   pkt->rpkt->data_len); +	} +      pkt->rpkt->data = rs_malloc (pkt->conn->ctx, pkt->rpkt->data_len); +      if (!pkt->rpkt->data) +	{ +	  _close_conn (&pkt->conn); +	  return rs_err_conn_push_fl (pkt->conn, RSE_NOMEM, __FILE__, __LINE__, +				      NULL); +	} +      memcpy (pkt->rpkt->data, pkt->hdr, RS_HEADER_LEN); +      bufferevent_setwatermark (pkt->conn->bev, EV_READ, +				pkt->rpkt->data_len - RS_HEADER_LEN, 0); +      rs_debug (("%s: packet header read, total pkt len=%d\n", +		 __func__, pkt->rpkt->data_len)); +    } +  else if (n < 0) +    { +      rs_debug (("%s: buffer frozen while reading header\n", __func__)); +    } +  else	    /* Error: libevent gave us less than the low watermark. */ +    { +      _close_conn (&pkt->conn); +      return rs_err_conn_push_fl (pkt->conn, RSE_INTERNAL, __FILE__, __LINE__, +				  "got %d octets reading header", n); +    } + +  return 0; +} + +static int +_read_packet (struct rs_packet *pkt) +{ +  size_t n = 0; + +  rs_debug (("%s: trying to read %d octets of packet data\n", __func__, +	     pkt->rpkt->data_len - RS_HEADER_LEN)); + +  n = bufferevent_read (pkt->conn->bev, +			pkt->rpkt->data + RS_HEADER_LEN, +			pkt->rpkt->data_len - RS_HEADER_LEN); + +  rs_debug (("%s: read %ld octets of packet data\n", __func__, n)); + +  if (n == pkt->rpkt->data_len - RS_HEADER_LEN) +    { +      bufferevent_disable (pkt->conn->bev, EV_READ); +      rs_debug (("%s: complete packet read\n", __func__)); +      pkt->hdr_read_flag = 0; +      memset (pkt->hdr, 0, sizeof(*pkt->hdr)); + +      /* Checks done by rad_packet_ok: +	 - lenghts (FIXME: checks really ok for tcp?) +	 - invalid code field +	 - attribute lengths >= 2 +	 - attribute sizes adding up correctly  */ +      if (!rad_packet_ok (pkt->rpkt, 0) != 0) +	{ +	  _close_conn (&pkt->conn); +	  return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__, +				      "invalid packet: %s", fr_strerror ()); +	} + +      /* TODO: Verify that reception of an unsolicited response packet +	 results in connection being closed.  */ + +      /* If we have a request to match this response against, verify +	 and decode the response.  */ +      if (pkt->original) +	{ +	  /* Verify header and message authenticator.  */ +	  if (rad_verify (pkt->rpkt, pkt->original->rpkt, +			  pkt->conn->active_peer->secret)) +	    { +	      _close_conn (&pkt->conn); +	      return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__, +					  "rad_verify: %s", fr_strerror ()); +	    } + +	  /* Decode and decrypt.  */ +	  if (rad_decode (pkt->rpkt, pkt->original->rpkt, +			  pkt->conn->active_peer->secret)) +	    { +	      _close_conn (&pkt->conn); +	      return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__, +					  "rad_decode: %s", fr_strerror ()); +	    } +	} + +#if defined (DEBUG) +      /* Find out what happens if there's data left in the buffer.  */ +      { +	size_t rest = 0; +	rest = evbuffer_get_length (bufferevent_get_input (pkt->conn->bev)); +	if (rest) +	  rs_debug (("%s: returning with %d octets left in buffer\n", __func__, +		     rest)); +      } +#endif + +      /* Hand over message to user, changes ownership of pkt.  Don't +	 touch it afterwards -- it might have been freed.  */ +      if (pkt->conn->callbacks.received_cb) +	pkt->conn->callbacks.received_cb (pkt, pkt->conn->user_data); +    } +  else if (n < 0)		/* Buffer frozen.  */ +    rs_debug (("%s: buffer frozen when reading packet\n", __func__)); +  else				/* Short packet.  */ +    rs_debug (("%s: waiting for another %d octets\n", __func__, +	       pkt->rpkt->data_len - RS_HEADER_LEN - n)); + +  return 0; +} + +/* Read callback for TCP. + +   Read exactly one RADIUS message from BEV and store it in struct +   rs_packet passed in CTX (hereby called 'pkt'). + +   Verify the received packet against pkt->original, if !NULL. + +   Inform upper layer about successful reception of valid RADIUS +   message by invoking conn->callbacks.recevied_cb(), if !NULL.  */ +void +tcp_read_cb (struct bufferevent *bev, void *user_data) +{ +  struct rs_packet *pkt = (struct rs_packet *) user_data; + +  assert (pkt); +  assert (pkt->conn); +  assert (pkt->rpkt); + +  pkt->rpkt->sockfd = pkt->conn->fd; +  pkt->rpkt->vps = NULL; + +  if (!pkt->hdr_read_flag) +    if (_read_header (pkt)) +      return; +  _read_packet (pkt); +} + +void +tcp_event_cb (struct bufferevent *bev, short events, void *user_data) +{ +  struct rs_packet *pkt = (struct rs_packet *) user_data; +  struct rs_connection *conn = NULL; +  struct rs_peer *p = NULL; +  int sockerr = 0; +#if defined (RS_ENABLE_TLS) +  unsigned long tlserr = 0; +#endif + +  assert (pkt); +  assert (pkt->conn); +  assert (pkt->conn->active_peer); +  conn = pkt->conn; +  p = conn->active_peer; + +  conn->is_connecting = 0; +  if (events & BEV_EVENT_CONNECTED) +    { +      event_on_connect (conn, pkt); +    } +  else if (events & BEV_EVENT_EOF) +    { +      event_on_disconnect (conn); +    } +  else if (events & BEV_EVENT_TIMEOUT) +    { +      rs_debug (("%s: %p times out on %s\n", __func__, p, +		 (events & BEV_EVENT_READING) ? "read" : "write")); +      rs_err_conn_push_fl (pkt->conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL); +    } +  else if (events & BEV_EVENT_ERROR) +    { +      sockerr = evutil_socket_geterror (conn->active_peer->fd); +      if (sockerr == 0)	/* FIXME: True that errno == 0 means closed? */ +	{ +	  event_on_disconnect (conn); +	} +      else +	{ +	  rs_debug (("%s: %d: %d (%s)\n", __func__, conn->fd, sockerr, +		     evutil_socket_error_to_string (sockerr))); +	  rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__, +			       "%d: %d (%s)", conn->fd, sockerr, +			       evutil_socket_error_to_string (sockerr)); +	} +#if defined (RS_ENABLE_TLS) +      if (conn->tls_ssl)	/* FIXME: correct check?  */ +	{ +	  for (tlserr = bufferevent_get_openssl_error (conn->bev); +	       tlserr; +	       tlserr = bufferevent_get_openssl_error (conn->bev)) +	    { +	      rs_debug (("%s: openssl error: %s\n", __func__, +			 ERR_error_string (tlserr, NULL))); +	      rs_err_conn_push_fl (pkt->conn, RSE_SSLERR, __FILE__, __LINE__, +				   ERR_error_string (tlserr, NULL)); +	    } +	} +#endif	/* RS_ENABLE_TLS */ +      event_loopbreak (conn); +    } + +#if defined (DEBUG) +  if (events & BEV_EVENT_ERROR && events != BEV_EVENT_ERROR) +    rs_debug (("%s: BEV_EVENT_ERROR and more: 0x%x\n", __func__, events)); +#endif +} + +void +tcp_write_cb (struct bufferevent *bev, void *ctx) +{ +  struct rs_packet *pkt = (struct rs_packet *) ctx; + +  assert (pkt); +  assert (pkt->conn); + +  if (pkt->conn->callbacks.sent_cb) +    pkt->conn->callbacks.sent_cb (pkt->conn->user_data); +} + diff --git a/lib/tcp.h b/lib/tcp.h new file mode 100644 index 0000000..eae3e7b --- /dev/null +++ b/lib/tcp.h @@ -0,0 +1,6 @@ +/* Copyright 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */ + +void tcp_event_cb (struct bufferevent *bev, short events, void *user_data); +void tcp_read_cb (struct bufferevent *bev, void *user_data); +void tcp_write_cb (struct bufferevent *bev, void *ctx); @@ -1,4 +1,5 @@ -/* See the file COPYING for licensing information.  */ +/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */  #if defined HAVE_CONFIG_H  #include <config.h> @@ -1,4 +1,5 @@ -/* See the file COPYING for licensing information.  */ +/* Copyright 2010 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */  #if defined (__cplusplus)  extern "C" { diff --git a/lib/udp.c b/lib/udp.c new file mode 100644 index 0000000..3573033 --- /dev/null +++ b/lib/udp.c @@ -0,0 +1,85 @@ +/* Copyright 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <event2/event.h> +#include <radsec/radsec.h> +#include <radsec/radsec-impl.h> +#include "debug.h" +#include "event.h" +#include "compat.h" +#include "udp.h" + +/* Callback for conn->wev and conn->rev.  FIXME: Rename.  */ +static void +_evcb (evutil_socket_t fd, short what, void *user_data) +{ +  //rs_debug (("%s: fd=%d what=0x%x\n", __func__, fd, what)); +  if (what & EV_TIMEOUT) +    { +      struct rs_connection *conn = (struct rs_connection *) user_data; +      assert (conn); +      conn->is_connecting = 0; +      rs_debug (("%s: UDP timeout NYI", __func__)); +    } +  else if (what & EV_READ) +    { +      struct rs_connection *conn = (struct rs_connection *) user_data; +      assert (conn); +      /* read a single UDP packet and stick it in a new struct +	 rs_packet */ + +      rs_debug (("%s: UDP read NYI", __func__)); +    } +  else if (what & EV_WRITE) +    { +      struct rs_packet *pkt = (struct rs_packet *) user_data; +      assert (pkt); +      /* Socket ready for writing, possibly as a result of a +	 successful connect.  */ +      if (!pkt->conn->is_connected) +	event_on_connect (pkt->conn, pkt); +      if (pkt->conn->out_queue) +	{ +	  /* Send one packet, the first.  */ +	  ssize_t r = 0; +	  struct rs_packet *p = pkt->conn->out_queue; + +	  assert (p->rpkt); +	  assert (p->rpkt->data); +	  r = compat_send (fd, p->rpkt->data, p->rpkt->data_len, 0); +	  if (r == -1) +	    { +	      int sockerr = evutil_socket_geterror (p->conn->fd); +	      if (sockerr != EAGAIN) +		rs_err_conn_push_fl (p->conn, RSE_SOCKERR, __FILE__, __LINE__, +				     "%d: send: %d (%s)", fd, sockerr, +				     evutil_socket_error_to_string (sockerr)); +	      return;		/* Don't unlink packet. */ +	    } +	  pkt->conn->out_queue = p->next; +	} +    } +} + +int +udp_init (struct rs_connection *conn, struct rs_packet *pkt) +{ +  assert (!conn->bev); + +  conn->rev = event_new (conn->evb, conn->fd, EV_READ|EV_PERSIST, _evcb, conn); +  conn->wev = event_new (conn->evb, conn->fd, EV_WRITE|EV_PERSIST, _evcb, pkt); +  if (!conn->rev || !conn->wev) +    { +      if (conn->rev) +	event_free (conn->rev); +      /* ENOMEM _or_ EINVAL but EINVAL only if we use EV_SIGNAL, at +	 least for now (libevent-2.0.5).  */ +      return rs_err_conn_push_fl (conn, RSE_NOMEM, __FILE__, __LINE__, NULL); +    } +  return RSE_OK; +} diff --git a/lib/udp.h b/lib/udp.h new file mode 100644 index 0000000..a2a7228 --- /dev/null +++ b/lib/udp.h @@ -0,0 +1,4 @@ +/* Copyright 2011 NORDUnet A/S. All rights reserved. +   See the file COPYING for licensing information.  */ + +int udp_init (struct rs_connection *conn, struct rs_packet *pkt); | 
