/*- * SPDX-License-Identifier: BSD-2-Clause * * btsockstat.c * * Copyright (c) 2001-2002 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: btsockstat.c,v 1.8 2003/05/21 22:40:25 max Exp $ */ #include #include #include #include #include #include #define _WANT_SOCKET #include #include #define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void hcirawpr (kvm_t *kvmd, u_long addr); static void l2caprawpr (kvm_t *kvmd, u_long addr); static void l2cappr (kvm_t *kvmd, u_long addr); static void l2caprtpr (kvm_t *kvmd, u_long addr); static void rfcommpr (kvm_t *kvmd, u_long addr); static void rfcommpr_s (kvm_t *kvmd, u_long addr); static char * bdaddrpr (bdaddr_p const ba, char *str, int len); static kvm_t * kopen (char const *memf); static int kread (kvm_t *kvmd, u_long addr, char *buffer, int size); static void usage (void); /* * List of symbols */ static struct nlist nl[] = { #define N_HCI_RAW 0 { "_ng_btsocket_hci_raw_sockets" }, #define N_L2CAP_RAW 1 { "_ng_btsocket_l2cap_raw_sockets" }, #define N_L2CAP 2 { "_ng_btsocket_l2cap_sockets" }, #define N_L2CAP_RAW_RT 3 { "_ng_btsocket_l2cap_raw_rt" }, #define N_L2CAP_RT 4 { "_ng_btsocket_l2cap_rt" }, #define N_RFCOMM 5 { "_ng_btsocket_rfcomm_sockets" }, #define N_RFCOMM_S 6 { "_ng_btsocket_rfcomm_sessions" }, { "" }, }; #define state2str(x) \ (((x) >= sizeof(states)/sizeof(states[0]))? "UNKNOWN" : states[(x)]) /* * Main */ static int numeric_bdaddr = 0; int main(int argc, char *argv[]) { int opt, proto = -1, route = 0; kvm_t *kvmd = NULL; char *memf = NULL; while ((opt = getopt(argc, argv, "hnM:p:r")) != -1) { switch (opt) { case 'n': numeric_bdaddr = 1; break; case 'M': memf = optarg; break; case 'p': if (strcasecmp(optarg, "hci_raw") == 0) proto = N_HCI_RAW; else if (strcasecmp(optarg, "l2cap_raw") == 0) proto = N_L2CAP_RAW; else if (strcasecmp(optarg, "l2cap") == 0) proto = N_L2CAP; else if (strcasecmp(optarg, "rfcomm") == 0) proto = N_RFCOMM; else if (strcasecmp(optarg, "rfcomm_s") == 0) proto = N_RFCOMM_S; else usage(); /* NOT REACHED */ break; case 'r': route = 1; break; case 'h': default: usage(); /* NOT REACHED */ } } if ((proto == N_HCI_RAW || proto == N_RFCOMM || proto == N_RFCOMM_S) && route) usage(); /* NOT REACHED */ /* * Discard setgid privileges if not the running kernel so that * bad guys can't print interesting stuff from kernel memory. */ if (memf != NULL) if (setgid(getgid()) != 0) err(1, "setgid"); kvmd = kopen(memf); if (kvmd == NULL) return (1); switch (proto) { case N_HCI_RAW: hcirawpr(kvmd, nl[N_HCI_RAW].n_value); break; case N_L2CAP_RAW: if (route) l2caprtpr(kvmd, nl[N_L2CAP_RAW_RT].n_value); else l2caprawpr(kvmd, nl[N_L2CAP_RAW].n_value); break; case N_L2CAP: if (route) l2caprtpr(kvmd, nl[N_L2CAP_RT].n_value); else l2cappr(kvmd, nl[N_L2CAP].n_value); break; case N_RFCOMM: rfcommpr(kvmd, nl[N_RFCOMM].n_value); break; case N_RFCOMM_S: rfcommpr_s(kvmd, nl[N_RFCOMM_S].n_value); break; default: if (route) { l2caprtpr(kvmd, nl[N_L2CAP_RAW_RT].n_value); l2caprtpr(kvmd, nl[N_L2CAP_RT].n_value); } else { hcirawpr(kvmd, nl[N_HCI_RAW].n_value); l2caprawpr(kvmd, nl[N_L2CAP_RAW].n_value); l2cappr(kvmd, nl[N_L2CAP].n_value); rfcommpr_s(kvmd, nl[N_RFCOMM_S].n_value); rfcommpr(kvmd, nl[N_RFCOMM].n_value); } break; } return (kvm_close(kvmd)); } /* main */ /* * Print raw HCI sockets */ static void hcirawpr(kvm_t *kvmd, u_long addr) { ng_btsocket_hci_raw_pcb_p this = NULL, next = NULL; ng_btsocket_hci_raw_pcb_t pcb; struct socket so; int first = 1; if (addr == 0) return; if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0) return; for ( ; this != NULL; this = next) { if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0) return; if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0) return; next = LIST_NEXT(&pcb, next); if (first) { first = 0; fprintf(stdout, "Active raw HCI sockets\n" \ "%-8.8s %-8.8s %-6.6s %-6.6s %-6.6s %-16.16s\n", "Socket", "PCB", "Flags", "Recv-Q", "Send-Q", "Local address"); } if (pcb.addr.hci_node[0] == 0) { pcb.addr.hci_node[0] = '*'; pcb.addr.hci_node[1] = 0; } fprintf(stdout, "%-8lx %-8lx %-6.6x %6d %6d %-16.16s\n", (unsigned long) pcb.so, (unsigned long) this, pcb.flags, so.so_rcv.sb_ccc, so.so_snd.sb_ccc, pcb.addr.hci_node); } } /* hcirawpr */ /* * Print raw L2CAP sockets */ static void l2caprawpr(kvm_t *kvmd, u_long addr) { ng_btsocket_l2cap_raw_pcb_p this = NULL, next = NULL; ng_btsocket_l2cap_raw_pcb_t pcb; struct socket so; int first = 1; if (addr == 0) return; if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0) return; for ( ; this != NULL; this = next) { if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0) return; if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0) return; next = LIST_NEXT(&pcb, next); if (first) { first = 0; fprintf(stdout, "Active raw L2CAP sockets\n" \ "%-8.8s %-8.8s %-6.6s %-6.6s %-17.17s\n", "Socket", "PCB", "Recv-Q", "Send-Q", "Local address"); } fprintf(stdout, "%-8lx %-8lx %6d %6d %-17.17s\n", (unsigned long) pcb.so, (unsigned long) this, so.so_rcv.sb_ccc, so.so_snd.sb_ccc, bdaddrpr(&pcb.src, NULL, 0)); } } /* l2caprawpr */ /* * Print L2CAP sockets */ static void l2cappr(kvm_t *kvmd, u_long addr) { static char const * const states[] = { /* NG_BTSOCKET_L2CAP_CLOSED */ "CLOSED", /* NG_BTSOCKET_L2CAP_CONNECTING */ "CON", /* NG_BTSOCKET_L2CAP_CONFIGURING */ "CONFIG", /* NG_BTSOCKET_L2CAP_OPEN */ "OPEN", /* NG_BTSOCKET_L2CAP_DISCONNECTING */ "DISCON" }; ng_btsocket_l2cap_pcb_p this = NULL, next = NULL; ng_btsocket_l2cap_pcb_t pcb; struct socket so; int first = 1; char local[24], remote[24]; if (addr == 0) return; if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0) return; for ( ; this != NULL; this = next) { if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0) return; if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0) return; next = LIST_NEXT(&pcb, next); if (first) { first = 0; fprintf(stdout, "Active L2CAP sockets\n" \ "%-8.8s %-6.6s %-6.6s %-23.23s %-17.17s %-5.5s %s\n", "PCB", "Recv-Q", "Send-Q", "Local address/PSM", "Foreign address", "CID", "State"); } fprintf(stdout, "%-8lx %6d %6d %-17.17s/%-5d %-17.17s %-5d %s\n", (unsigned long) this, so.so_rcv.sb_ccc, so.so_snd.sb_ccc, bdaddrpr(&pcb.src, local, sizeof(local)), pcb.psm, bdaddrpr(&pcb.dst, remote, sizeof(remote)), pcb.cid, (so.so_options & SO_ACCEPTCONN)? "LISTEN" : state2str(pcb.state)); } } /* l2cappr */ /* * Print L2CAP routing table */ static void l2caprtpr(kvm_t *kvmd, u_long addr) { ng_btsocket_l2cap_rtentry_p this = NULL, next = NULL; ng_btsocket_l2cap_rtentry_t rt; int first = 1; if (addr == 0) return; if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0) return; for ( ; this != NULL; this = next) { if (kread(kvmd, (u_long) this, (char *) &rt, sizeof(rt)) < 0) return; next = LIST_NEXT(&rt, next); if (first) { first = 0; fprintf(stdout, "Known %sL2CAP routes\n", (addr == nl[N_L2CAP_RAW_RT].n_value)? "raw " : ""); fprintf(stdout, "%-8.8s %-8.8s %-17.17s\n", "RTentry", "Hook", "BD_ADDR"); } fprintf(stdout, "%-8lx %-8lx %-17.17s\n", (unsigned long) this, (unsigned long) rt.hook, bdaddrpr(&rt.src, NULL, 0)); } } /* l2caprtpr */ /* * Print RFCOMM sockets */ static void rfcommpr(kvm_t *kvmd, u_long addr) { static char const * const states[] = { /* NG_BTSOCKET_RFCOMM_DLC_CLOSED */ "CLOSED", /* NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT */ "W4CON", /* NG_BTSOCKET_RFCOMM_DLC_CONFIGURING */ "CONFIG", /* NG_BTSOCKET_RFCOMM_DLC_CONNECTING */ "CONN", /* NG_BTSOCKET_RFCOMM_DLC_CONNECTED */ "OPEN", /* NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING */ "DISCON" }; ng_btsocket_rfcomm_pcb_p this = NULL, next = NULL; ng_btsocket_rfcomm_pcb_t pcb; struct socket so; int first = 1; char local[24], remote[24]; if (addr == 0) return; if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0) return; for ( ; this != NULL; this = next) { if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0) return; if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0) return; next = LIST_NEXT(&pcb, next); if (first) { first = 0; fprintf(stdout, "Active RFCOMM sockets\n" \ "%-8.8s %-6.6s %-6.6s %-17.17s %-17.17s %-4.4s %-4.4s %s\n", "PCB", "Recv-Q", "Send-Q", "Local address", "Foreign address", "Chan", "DLCI", "State"); } fprintf(stdout, "%-8lx %6d %6d %-17.17s %-17.17s %-4d %-4d %s\n", (unsigned long) this, so.so_rcv.sb_ccc, so.so_snd.sb_ccc, bdaddrpr(&pcb.src, local, sizeof(local)), bdaddrpr(&pcb.dst, remote, sizeof(remote)), pcb.channel, pcb.dlci, (so.so_options & SO_ACCEPTCONN)? "LISTEN" : state2str(pcb.state)); } } /* rfcommpr */ /* * Print RFCOMM sessions */ static void rfcommpr_s(kvm_t *kvmd, u_long addr) { static char const * const states[] = { /* NG_BTSOCKET_RFCOMM_SESSION_CLOSED */ "CLOSED", /* NG_BTSOCKET_RFCOMM_SESSION_LISTENING */ "LISTEN", /* NG_BTSOCKET_RFCOMM_SESSION_CONNECTING */ "CONNECTING", /* NG_BTSOCKET_RFCOMM_SESSION_CONNECTED */ "CONNECTED", /* NG_BTSOCKET_RFCOMM_SESSION_OPEN */ "OPEN", /* NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING */ "DISCONNECTING" }; ng_btsocket_rfcomm_session_p this = NULL, next = NULL; ng_btsocket_rfcomm_session_t s; struct socket so; int first = 1; if (addr == 0) return; if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0) return; for ( ; this != NULL; this = next) { if (kread(kvmd, (u_long) this, (char *) &s, sizeof(s)) < 0) return; if (kread(kvmd, (u_long) s.l2so, (char *) &so, sizeof(so)) < 0) return; next = LIST_NEXT(&s, next); if (first) { first = 0; fprintf(stdout, "Active RFCOMM sessions\n" \ "%-8.8s %-8.8s %-4.4s %-5.5s %-5.5s %-4.4s %s\n", "L2PCB", "PCB", "Flags", "MTU", "Out-Q", "DLCs", "State"); } fprintf(stdout, "%-8lx %-8lx %-4x %-5d %-5d %-4s %s\n", (unsigned long) so.so_pcb, (unsigned long) this, s.flags, s.mtu, s.outq.len, LIST_EMPTY(&s.dlcs)? "No" : "Yes", state2str(s.state)); } } /* rfcommpr_s */ /* * Return BD_ADDR as string */ static char * bdaddrpr(bdaddr_p const ba, char *str, int len) { static char buffer[MAXHOSTNAMELEN]; struct hostent *he = NULL; if (str == NULL) { str = buffer; len = sizeof(buffer); } if (memcmp(ba, NG_HCI_BDADDR_ANY, sizeof(*ba)) == 0) { str[0] = '*'; str[1] = 0; return (str); } if (!numeric_bdaddr && (he = bt_gethostbyaddr((char *)ba, sizeof(*ba), AF_BLUETOOTH)) != NULL) { strlcpy(str, he->h_name, len); return (str); } bt_ntoa(ba, str); return (str); } /* bdaddrpr */ /* * Open kvm */ static kvm_t * kopen(char const *memf) { kvm_t *kvmd = NULL; char errbuf[_POSIX2_LINE_MAX]; kvmd = kvm_openfiles(NULL, memf, NULL, O_RDONLY, errbuf); if (setgid(getgid()) != 0) err(1, "setgid"); if (kvmd == NULL) { warnx("kvm_openfiles: %s", errbuf); return (NULL); } if (kvm_nlist(kvmd, nl) < 0) { warnx("kvm_nlist: %s", kvm_geterr(kvmd)); goto fail; } if (nl[0].n_type == 0) { warnx("kvm_nlist: no namelist"); goto fail; } return (kvmd); fail: kvm_close(kvmd); return (NULL); } /* kopen */ /* * Read kvm */ static int kread(kvm_t *kvmd, u_long addr, char *buffer, int size) { if (kvmd == NULL || buffer == NULL) return (-1); if (kvm_read(kvmd, addr, buffer, size) != size) { warnx("kvm_read: %s", kvm_geterr(kvmd)); return (-1); } return (0); } /* kread */ /* * Print usage and exit */ static void usage(void) { fprintf(stdout, "Usage: btsockstat [-M core ] [-n] [-p proto] [-r]\n"); exit(255); } /* usage */