summaryrefslogtreecommitdiff
path: root/modules/pcp/listener.c
blob: 774767229aac7cc6d54aab87a65d976e48b95538 (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
/**
 * @file listener.c Port Control Protocol module -- multicast listener
 *
 * Copyright (C) 2010 - 2016 Creytiv.com
 */

#include <re.h>
#include <rew.h>
#include <baresip.h>
#include "pcp.h"


/*
 * Listen for incoming notifications on unicast/multicast port 5350
 */


struct pcp_listener {
	struct udp_sock *us;
	struct sa srv;
	struct sa group;
	pcp_msg_h *msgh;
	void *arg;
};


static void destructor(void *arg)
{
	struct pcp_listener *pl = arg;

	if (sa_isset(&pl->group, SA_ADDR))
		(void)udp_multicast_leave(pl->us, &pl->group);

	mem_deref(pl->us);
}


static void udp_recv(const struct sa *src, struct mbuf *mb, void *arg)
{
	struct pcp_listener *pl = arg;
	struct pcp_msg *msg;
	int err;

#if 0
	if (!sa_cmp(src, &pl->srv, SA_ADDR)) {
		debug("pcp: listener: ignore %zu bytes from non-server %J\n",
		      mb->end, src);
		return;
	}
#endif

	err = pcp_msg_decode(&msg, mb);
	if (err)
		return;

	/* Validate PCP request */
	if (!msg->hdr.resp) {
		info("pcp: listener: ignore request from %J\n", src);
		goto out;
	}

	if (pl->msgh)
		pl->msgh(msg, pl->arg);

 out:
	mem_deref(msg);
}


int pcp_listen(struct pcp_listener **plp, const struct sa *srv,
	       pcp_msg_h *msgh, void *arg)
{
	struct pcp_listener *pl;
	struct sa laddr;
	int err;

	if (!plp || !srv || !msgh)
		return EINVAL;

	pl = mem_zalloc(sizeof(*pl), destructor);
	if (!pl)
		return ENOMEM;

	pl->srv  = *srv;
	pl->msgh = msgh;
	pl->arg  = arg;

	/* note: must listen on ANY to get multicast working */
	sa_init(&laddr, sa_af(srv));
	sa_set_port(&laddr, PCP_PORT_CLI);

	err = udp_listen(&pl->us, &laddr, udp_recv, pl);
	if (err)
		goto out;

	switch (sa_af(&laddr)) {

	case AF_INET:
		err = sa_set_str(&pl->group, "224.0.0.1", 0);
		break;

	case AF_INET6:
		err = sa_set_str(&pl->group, "ff02::1", 0);
		break;

	default:
		err = EAFNOSUPPORT;
		break;
	}
	if (err)
		goto out;

	err = udp_multicast_join(pl->us, &pl->group);
	if (err)
		goto out;

 out:
	if (err)
		mem_deref(pl);
	else
		*plp = pl;

	return err;
}