root/usr.bin/sdpquery/command.c
/*      $NetBSD: command.c,v 1.6 2023/12/17 14:38:49 andvar Exp $       */

/*-
 * Copyright (c) 2009 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Iain Hibbert.
 *
 * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
 */

#include <sys/cdefs.h>
__RCSID("$NetBSD: command.c,v 1.6 2023/12/17 14:38:49 andvar Exp $");

#include <bluetooth.h>
#include <err.h>
#include <sdp.h>
#include <stdlib.h>
#include <string.h>

#include "sdpquery.h"

static sdp_session_t open_session(void);
static void build_ssp(sdp_data_t *, int, const char **);

static struct alias {
        uint16_t        uuid;
        const char *    name;
        const char *    desc;
} aliases[] = {
        { SDP_SERVICE_CLASS_ADVANCED_AUDIO_DISTRIBUTION,
          "A2DP",       "Advanced Audio Distribution Profile"           },
        { SDP_UUID_PROTOCOL_BNEP,
          "BNEP",       "Bluetooth Network Encapsulation Protocol"      },
        { SDP_SERVICE_CLASS_COMMON_ISDN_ACCESS,
          "CIP",        "Common ISDN Access Service"                    },
        { SDP_SERVICE_CLASS_CORDLESS_TELEPHONY,
          "CTP",        "Cordless Telephony Service"                    },
        { SDP_SERVICE_CLASS_DIALUP_NETWORKING,
          "DUN",        "Dial Up Networking Service"                    },
        { SDP_SERVICE_CLASS_FAX,
          "FAX",        "Fax Service"                                   },
        { SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER,
          "FTRN",       "File Transfer Service"                         },
        { SDP_SERVICE_CLASS_GN,
          "GN",         "Group ad-hoc Network Service"                  },
        { SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE,
          "HID",        "Human Interface Device Service"                },
        { SDP_SERVICE_CLASS_HANDSFREE,
          "HF",         "Handsfree Service"                             },
        { SDP_SERVICE_CLASS_HEADSET,
          "HSET",       "Headset Service"                               },
        { SDP_UUID_PROTOCOL_L2CAP,
          "L2CAP",      "Logical Link Control and Adaptation Protocol"  },
        { SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
          "LAN",        "Lan access using PPP Service"                  },
        { SDP_SERVICE_CLASS_NAP,
          "NAP",        "Network Access Point Service"                  },
        { SDP_UUID_PROTOCOL_OBEX,
          "OBEX",       "Object Exchange Protocol"                      },
        { SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH,
          "OPUSH",      "Object Push Service"                           },
        { SDP_SERVICE_CLASS_PANU,
          "PANU",       "Personal Area Networking User Service"         },
        { SDP_SERVICE_CLASS_PNP_INFORMATION,
          "PNP",        "PNP Information Service"                       },
        { SDP_UUID_PROTOCOL_RFCOMM,
          "RFCOMM",     "RFCOMM Protocol"                               },
        { SDP_UUID_PROTOCOL_SDP,
          "SDP",        "Service Discovery Protocol"                    },
        { SDP_SERVICE_CLASS_SERIAL_PORT,
          "SP",         "Serial Port Service"                           },
        { SDP_SERVICE_CLASS_IR_MC_SYNC,
          "SYNC",       "IrMC Sync Client Service"                      },
};

int
do_sdp_browse(int argc, const char **argv)
{
        const char *av = ___STRING(SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);

        if (argc > 1)
                errx(EXIT_FAILURE, "Too many arguments");

        if (argc == 0) {
                argc = 1;
                argv = &av;
        }

        return do_sdp_search(argc, argv);
}

int
do_sdp_record(int argc, const char **argv)
{
        sdp_session_t   ss;
        sdp_data_t      rsp;
        char *          ep;
        unsigned long   handle;
        bool            rv;

        if (argc == 0)
                errx(EXIT_FAILURE, "Record handle required");

        ss = open_session();

        for (; argc-- > 0; argv++) {
                handle = strtoul(*argv, &ep, 0);
                if (*argv[0] == '\0' || *ep != '\0' || handle > UINT32_MAX)
                        errx(EXIT_FAILURE, "Invalid handle: %s", *argv);

                rv = sdp_service_attribute(ss, (uint32_t)handle, NULL, &rsp);
                if (!rv)
                        warn("%s", *argv);
                else
                        print_record(&rsp);

                if (argc > 0)
                        printf("\n\n");
        }

        sdp_close(ss);
        return EXIT_SUCCESS;
}

int
do_sdp_search(int argc, const char **argv)
{
        sdp_session_t   ss;
        sdp_data_t      ssp, rec, rsp;
        bool            rv;

        if (argc < 1)
                errx(EXIT_FAILURE, "UUID required");

        if (argc > 12)
                errx(EXIT_FAILURE, "Too many UUIDs");

        build_ssp(&ssp, argc, argv);

        ss = open_session();

        rv = sdp_service_search_attribute(ss, &ssp, NULL, &rsp);
        if (!rv)
                err(EXIT_FAILURE, "sdp_service_search_attribute");

        while (sdp_get_seq(&rsp, &rec)) {
                if (!rv)
                        printf("\n\n");
                else
                        rv = false;

                print_record(&rec);
        }

        if (rsp.next != rsp.end) {
                printf("\n\nAdditional Data:\n");
                sdp_data_print(&rsp, 4);
        }

        sdp_close(ss);

        return EXIT_SUCCESS;
}

static sdp_session_t
open_session(void)
{
        sdp_session_t ss;

        if (bdaddr_any(&remote_addr))
                ss = sdp_open_local(control_socket);
        else
                ss = sdp_open(&local_addr, &remote_addr);

        if (ss == NULL)
                err(EXIT_FAILURE, "sdp_open");

        return ss;
}

/*
 * build ServiceSearchPattern from arglist
 */
static void
build_ssp(sdp_data_t *ssp, int argc, const char **argv)
{
        static uint8_t  data[12 * sizeof(uuid_t)];
        char *          ep;
        uintmax_t       umax;
        uuid_t          uuid;
        uint32_t        status;
        int             i;

        ssp->next = data;
        ssp->end = data + sizeof(data);

        for (; argc-- > 0; argv++) {
                uuid_from_string(*argv, &uuid, &status);
                if (status != uuid_s_ok) {
                        umax = strtoumax(*argv, &ep, 0);
                        if (*argv[0] == '\0' || *ep != '\0') {
                                for (i = 0;; i++) {
                                        if (i == __arraycount(aliases))
                                                errx(EXIT_FAILURE,
                                                    "%s: Bad UUID", *argv);

                                        if (strcasecmp(aliases[i].name,
                                            *argv) == 0)
                                                break;
                                }

                                umax = aliases[i].uuid;
                        } else if (umax > UINT32_MAX)
                                errx(EXIT_FAILURE, "%s: Bad UUID", *argv);

                        uuid = BLUETOOTH_BASE_UUID;
                        uuid.time_low = (uint32_t)umax;
                }

                sdp_put_uuid(ssp, &uuid);
        }

        ssp->end = ssp->next;
        ssp->next = data;
}