summaryrefslogtreecommitdiff
path: root/lib/send.c
blob: 3161bbe60a696cfd0ada40b320c1f88896c88b60 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* Copyright 2011,2013 NORDUnet A/S. All rights reserved.
   See LICENSE 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 "conn.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 (tcp_init_connect_timer (conn))
	return -1;
      if (event_init_bufferevent (conn, conn->active_peer))
	return -1;
    }
  else
    {
      if (udp_init (conn, pkt))
	return -1;
      if (udp_init_retransmit_timer (conn))
	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->flags |= RS_PACKET_SENT;
  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 */
    {
      event_assign (conn->wev, conn->evb, event_get_fd (conn->wev),
		    EV_WRITE, event_get_callback (conn->wev), pkt);
      err = event_add (conn->wev, NULL);
      if (err < 0)
	return rs_err_conn_push_fl (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_p (conn))
    {
      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 (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->flags & RS_PACKET_SENT) == 0)
	{
	  assert (rs_err_conn_peek_code (conn));
	  return rs_err_conn_peek_code (conn);
	}
    }

  return RSE_OK;
}