1335640Shselasky/*
2335640Shselasky * Copyright (c) 2012 Jakub Zawadzki
3335640Shselasky * All rights reserved.
4335640Shselasky *
5335640Shselasky * Redistribution and use in source and binary forms, with or without
6335640Shselasky * modification, are permitted provided that the following conditions
7335640Shselasky * are met:
8335640Shselasky *
9335640Shselasky * 1. Redistributions of source code must retain the above copyright
10335640Shselasky * notice, this list of conditions and the following disclaimer.
11335640Shselasky * 2. Redistributions in binary form must reproduce the above copyright
12335640Shselasky * notice, this list of conditions and the following disclaimer in the
13335640Shselasky * documentation and/or other materials provided with the distribution.
14335640Shselasky * 3. The name of the author may not be used to endorse or promote
15335640Shselasky * products derived from this software without specific prior written
16335640Shselasky * permission.
17335640Shselasky *
18335640Shselasky * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19335640Shselasky * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20335640Shselasky * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21335640Shselasky * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22335640Shselasky * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23335640Shselasky * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24335640Shselasky * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25335640Shselasky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26335640Shselasky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27335640Shselasky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28335640Shselasky * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29335640Shselasky */
30335640Shselasky
31335640Shselasky#ifdef HAVE_CONFIG_H
32335640Shselasky#include <config.h>
33335640Shselasky#endif
34335640Shselasky
35335640Shselasky#include <string.h>
36335640Shselasky
37335640Shselasky#include <time.h>
38335640Shselasky#include <sys/time.h>
39335640Shselasky
40335640Shselasky#include <dbus/dbus.h>
41335640Shselasky
42335640Shselasky#include "pcap-int.h"
43335640Shselasky#include "pcap-dbus.h"
44335640Shselasky
45335640Shselasky/*
46335640Shselasky * Private data for capturing on D-Bus.
47335640Shselasky */
48335640Shselaskystruct pcap_dbus {
49335640Shselasky	DBusConnection *conn;
50335640Shselasky	u_int	packets_read;	/* count of packets read */
51335640Shselasky};
52335640Shselasky
53335640Shselaskystatic int
54335640Shselaskydbus_read(pcap_t *handle, int max_packets _U_, pcap_handler callback, u_char *user)
55335640Shselasky{
56335640Shselasky	struct pcap_dbus *handlep = handle->priv;
57335640Shselasky
58335640Shselasky	struct pcap_pkthdr pkth;
59335640Shselasky	DBusMessage *message;
60335640Shselasky
61335640Shselasky	char *raw_msg;
62335640Shselasky	int raw_msg_len;
63335640Shselasky
64335640Shselasky	int count = 0;
65335640Shselasky
66335640Shselasky	message = dbus_connection_pop_message(handlep->conn);
67335640Shselasky
68335640Shselasky	while (!message) {
69335640Shselasky		/* XXX handle->opt.timeout = timeout_ms; */
70335640Shselasky		if (!dbus_connection_read_write(handlep->conn, 100)) {
71335640Shselasky			pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Connection closed");
72335640Shselasky			return -1;
73335640Shselasky		}
74335640Shselasky
75335640Shselasky		if (handle->break_loop) {
76335640Shselasky			handle->break_loop = 0;
77335640Shselasky			return -2;
78335640Shselasky		}
79335640Shselasky
80335640Shselasky		message = dbus_connection_pop_message(handlep->conn);
81335640Shselasky	}
82335640Shselasky
83335640Shselasky	if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
84335640Shselasky		pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Disconnected");
85335640Shselasky		return -1;
86335640Shselasky	}
87335640Shselasky
88335640Shselasky	if (dbus_message_marshal(message, &raw_msg, &raw_msg_len)) {
89335640Shselasky		pkth.caplen = pkth.len = raw_msg_len;
90335640Shselasky		/* pkth.caplen = min (payload_len, handle->snapshot); */
91335640Shselasky
92335640Shselasky		gettimeofday(&pkth.ts, NULL);
93335640Shselasky		if (handle->fcode.bf_insns == NULL ||
94335640Shselasky		    bpf_filter(handle->fcode.bf_insns, (u_char *)raw_msg, pkth.len, pkth.caplen)) {
95335640Shselasky			handlep->packets_read++;
96335640Shselasky			callback(user, &pkth, (u_char *)raw_msg);
97335640Shselasky			count++;
98335640Shselasky		}
99335640Shselasky
100335640Shselasky		dbus_free(raw_msg);
101335640Shselasky	}
102335640Shselasky	return count;
103335640Shselasky}
104335640Shselasky
105335640Shselaskystatic int
106335640Shselaskydbus_write(pcap_t *handle, const void *buf, size_t size)
107335640Shselasky{
108335640Shselasky	/* XXX, not tested */
109335640Shselasky	struct pcap_dbus *handlep = handle->priv;
110335640Shselasky
111335640Shselasky	DBusError error = DBUS_ERROR_INIT;
112335640Shselasky	DBusMessage *msg;
113335640Shselasky
114335640Shselasky	if (!(msg = dbus_message_demarshal(buf, size, &error))) {
115335640Shselasky		pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "dbus_message_demarshal() failed: %s", error.message);
116335640Shselasky		dbus_error_free(&error);
117335640Shselasky		return -1;
118335640Shselasky	}
119335640Shselasky
120335640Shselasky	dbus_connection_send(handlep->conn, msg, NULL);
121335640Shselasky	dbus_connection_flush(handlep->conn);
122335640Shselasky
123335640Shselasky	dbus_message_unref(msg);
124335640Shselasky	return 0;
125335640Shselasky}
126335640Shselasky
127335640Shselaskystatic int
128335640Shselaskydbus_stats(pcap_t *handle, struct pcap_stat *stats)
129335640Shselasky{
130335640Shselasky	struct pcap_dbus *handlep = handle->priv;
131335640Shselasky
132335640Shselasky	stats->ps_recv = handlep->packets_read;
133335640Shselasky	stats->ps_drop = 0;
134335640Shselasky	stats->ps_ifdrop = 0;
135335640Shselasky	return 0;
136335640Shselasky}
137335640Shselasky
138335640Shselaskystatic void
139335640Shselaskydbus_cleanup(pcap_t *handle)
140335640Shselasky{
141335640Shselasky	struct pcap_dbus *handlep = handle->priv;
142335640Shselasky
143335640Shselasky	dbus_connection_unref(handlep->conn);
144335640Shselasky
145335640Shselasky	pcap_cleanup_live_common(handle);
146335640Shselasky}
147335640Shselasky
148335640Shselasky/*
149335640Shselasky * We don't support non-blocking mode.  I'm not sure what we'd
150335640Shselasky * do to support it and, given that we don't support select()/
151335640Shselasky * poll()/epoll_wait()/kevent() etc., it probably doesn't
152335640Shselasky * matter.
153335640Shselasky */
154335640Shselaskystatic int
155335640Shselaskydbus_getnonblock(pcap_t *p)
156335640Shselasky{
157335640Shselasky	pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
158335640Shselasky	    "Non-blocking mode isn't supported for capturing on D-Bus");
159335640Shselasky	return (-1);
160335640Shselasky}
161335640Shselasky
162335640Shselaskystatic int
163335640Shselaskydbus_setnonblock(pcap_t *p, int nonblock _U_)
164335640Shselasky{
165335640Shselasky	pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
166335640Shselasky	    "Non-blocking mode isn't supported for capturing on D-Bus");
167335640Shselasky	return (-1);
168335640Shselasky}
169335640Shselasky
170335640Shselaskystatic int
171335640Shselaskydbus_activate(pcap_t *handle)
172335640Shselasky{
173335640Shselasky#define EAVESDROPPING_RULE "eavesdrop=true,"
174335640Shselasky
175335640Shselasky	static const char *rules[] = {
176335640Shselasky		EAVESDROPPING_RULE "type='signal'",
177335640Shselasky		EAVESDROPPING_RULE "type='method_call'",
178335640Shselasky		EAVESDROPPING_RULE "type='method_return'",
179335640Shselasky		EAVESDROPPING_RULE "type='error'",
180335640Shselasky	};
181335640Shselasky
182335640Shselasky	#define N_RULES sizeof(rules)/sizeof(rules[0])
183335640Shselasky
184335640Shselasky	struct pcap_dbus *handlep = handle->priv;
185335640Shselasky	const char *dev = handle->opt.device;
186335640Shselasky
187335640Shselasky	DBusError error = DBUS_ERROR_INIT;
188335640Shselasky	u_int i;
189335640Shselasky
190335640Shselasky	if (strcmp(dev, "dbus-system") == 0) {
191335640Shselasky		if (!(handlep->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) {
192335640Shselasky			pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to get system bus: %s", error.message);
193335640Shselasky			dbus_error_free(&error);
194335640Shselasky			return PCAP_ERROR;
195335640Shselasky		}
196335640Shselasky
197335640Shselasky	} else if (strcmp(dev, "dbus-session") == 0) {
198335640Shselasky		if (!(handlep->conn = dbus_bus_get(DBUS_BUS_SESSION, &error))) {
199335640Shselasky			pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to get session bus: %s", error.message);
200335640Shselasky			dbus_error_free(&error);
201335640Shselasky			return PCAP_ERROR;
202335640Shselasky		}
203335640Shselasky
204335640Shselasky	} else if (strncmp(dev, "dbus://", 7) == 0) {
205335640Shselasky		const char *addr = dev + 7;
206335640Shselasky
207335640Shselasky		if (!(handlep->conn = dbus_connection_open(addr, &error))) {
208335640Shselasky			pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to open connection to: %s: %s", addr, error.message);
209335640Shselasky			dbus_error_free(&error);
210335640Shselasky			return PCAP_ERROR;
211335640Shselasky		}
212335640Shselasky
213335640Shselasky		if (!dbus_bus_register(handlep->conn, &error)) {
214335640Shselasky			pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to register bus %s: %s\n", addr, error.message);
215335640Shselasky			dbus_error_free(&error);
216335640Shselasky			return PCAP_ERROR;
217335640Shselasky		}
218335640Shselasky
219335640Shselasky	} else {
220335640Shselasky		pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't get bus address from %s", handle->opt.device);
221335640Shselasky		return PCAP_ERROR;
222335640Shselasky	}
223335640Shselasky
224335640Shselasky	/* Initialize some components of the pcap structure. */
225335640Shselasky	handle->bufsize = 0;
226335640Shselasky	handle->offset = 0;
227335640Shselasky	handle->linktype = DLT_DBUS;
228335640Shselasky	handle->read_op = dbus_read;
229335640Shselasky	handle->inject_op = dbus_write;
230335640Shselasky	handle->setfilter_op = install_bpf_program; /* XXX, later add support for dbus_bus_add_match() */
231335640Shselasky	handle->setdirection_op = NULL;
232335640Shselasky	handle->set_datalink_op = NULL;      /* can't change data link type */
233335640Shselasky	handle->getnonblock_op = dbus_getnonblock;
234335640Shselasky	handle->setnonblock_op = dbus_setnonblock;
235335640Shselasky	handle->stats_op = dbus_stats;
236335640Shselasky	handle->cleanup_op = dbus_cleanup;
237335640Shselasky
238335640Shselasky#ifndef _WIN32
239335640Shselasky	/*
240335640Shselasky	 * Unfortunately, trying to do a select()/poll()/epoll_wait()/
241335640Shselasky	 * kevent()/etc. on a D-Bus connection isn't a simple
242335640Shselasky	 * case of "give me an FD on which to wait".
243335640Shselasky	 *
244335640Shselasky	 * Apparently, you have to register "add watch", "remove watch",
245335640Shselasky	 * and "toggle watch" functions with
246335640Shselasky	 * dbus_connection_set_watch_functions(),
247335640Shselasky	 * keep a *set* of FDs, add to that set in the "add watch"
248335640Shselasky	 * function, subtract from it in the "remove watch" function,
249335640Shselasky	 * and either add to or subtract from that set in the "toggle
250335640Shselasky	 * watch" function, and do the wait on *all* of the FDs in the
251335640Shselasky	 * set.  (Yes, you need the "toggle watch" function, so that
252335640Shselasky	 * the main loop doesn't itself need to check for whether
253335640Shselasky	 * a given watch is enabled or disabled - most libpcap programs
254335640Shselasky	 * know nothing about D-Bus and shouldn't *have* to know anything
255335640Shselasky	 * about D-Bus other than how to decode D-Bus messages.)
256335640Shselasky	 *
257335640Shselasky	 * Implementing that would require considerable changes in
258335640Shselasky	 * the way libpcap exports "selectable FDs" to its client.
259335640Shselasky	 * Until that's done, we just say "you can't do that".
260335640Shselasky	 */
261335640Shselasky	handle->selectable_fd = handle->fd = -1;
262335640Shselasky#endif
263335640Shselasky
264335640Shselasky	if (handle->opt.rfmon) {
265335640Shselasky		/*
266335640Shselasky		 * Monitor mode doesn't apply to dbus connections.
267335640Shselasky		 */
268335640Shselasky		dbus_cleanup(handle);
269335640Shselasky		return PCAP_ERROR_RFMON_NOTSUP;
270335640Shselasky	}
271335640Shselasky
272335640Shselasky	/*
273335640Shselasky	 * Turn a negative snapshot value (invalid), a snapshot value of
274335640Shselasky	 * 0 (unspecified), or a value bigger than the normal maximum
275335640Shselasky	 * value, into the maximum message length for D-Bus (128MB).
276335640Shselasky	 */
277335640Shselasky	if (handle->snapshot <= 0 || handle->snapshot > 134217728)
278335640Shselasky		handle->snapshot = 134217728;
279335640Shselasky
280335640Shselasky	/* dbus_connection_set_max_message_size(handlep->conn, handle->snapshot); */
281335640Shselasky	if (handle->opt.buffer_size != 0)
282335640Shselasky		dbus_connection_set_max_received_size(handlep->conn, handle->opt.buffer_size);
283335640Shselasky
284335640Shselasky	for (i = 0; i < N_RULES; i++) {
285335640Shselasky		dbus_bus_add_match(handlep->conn, rules[i], &error);
286335640Shselasky		if (dbus_error_is_set(&error)) {
287335640Shselasky			dbus_error_free(&error);
288335640Shselasky
289335640Shselasky			/* try without eavesdrop */
290335640Shselasky			dbus_bus_add_match(handlep->conn, rules[i] + strlen(EAVESDROPPING_RULE), &error);
291335640Shselasky			if (dbus_error_is_set(&error)) {
292335640Shselasky				pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to add bus match: %s\n", error.message);
293335640Shselasky				dbus_error_free(&error);
294335640Shselasky				dbus_cleanup(handle);
295335640Shselasky				return PCAP_ERROR;
296335640Shselasky			}
297335640Shselasky		}
298335640Shselasky	}
299335640Shselasky
300335640Shselasky	return 0;
301335640Shselasky}
302335640Shselasky
303335640Shselaskypcap_t *
304335640Shselaskydbus_create(const char *device, char *ebuf, int *is_ours)
305335640Shselasky{
306335640Shselasky	pcap_t *p;
307335640Shselasky
308335640Shselasky	if (strcmp(device, "dbus-system") &&
309335640Shselasky		strcmp(device, "dbus-session") &&
310335640Shselasky		strncmp(device, "dbus://", 7))
311335640Shselasky	{
312335640Shselasky		*is_ours = 0;
313335640Shselasky		return NULL;
314335640Shselasky	}
315335640Shselasky
316335640Shselasky	*is_ours = 1;
317335640Shselasky	p = pcap_create_common(ebuf, sizeof (struct pcap_dbus));
318335640Shselasky	if (p == NULL)
319335640Shselasky		return (NULL);
320335640Shselasky
321335640Shselasky	p->activate_op = dbus_activate;
322335640Shselasky	/*
323335640Shselasky	 * Set these up front, so that, even if our client tries
324335640Shselasky	 * to set non-blocking mode before we're activated, or
325335640Shselasky	 * query the state of non-blocking mode, they get an error,
326335640Shselasky	 * rather than having the non-blocking mode option set
327335640Shselasky	 * for use later.
328335640Shselasky	 */
329335640Shselasky	p->getnonblock_op = dbus_getnonblock;
330335640Shselasky	p->setnonblock_op = dbus_setnonblock;
331335640Shselasky	return (p);
332335640Shselasky}
333335640Shselasky
334335640Shselaskyint
335335640Shselaskydbus_findalldevs(pcap_if_list_t *devlistp, char *err_str)
336335640Shselasky{
337335640Shselasky	/*
338335640Shselasky	 * The notion of "connected" vs. "disconnected" doesn't apply.
339335640Shselasky	 * XXX - what about the notions of "up" and "running"?
340335640Shselasky	 */
341335640Shselasky	if (add_dev(devlistp, "dbus-system",
342335640Shselasky	    PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE, "D-Bus system bus",
343335640Shselasky	    err_str) == NULL)
344335640Shselasky		return -1;
345335640Shselasky	if (add_dev(devlistp, "dbus-session",
346335640Shselasky	    PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE, "D-Bus session bus",
347335640Shselasky	    err_str) == NULL)
348335640Shselasky		return -1;
349335640Shselasky	return 0;
350335640Shselasky}
351335640Shselasky
352