/*
 * Asnychrounous NBP lookup
 *
 * (c) 1997 Stefan Bethke. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Copyright (c) 1990,1996 Regents of The University of Michigan.
 * All Rights Reserved.
 *
 *     Permission to use, copy, modify, and distribute this software and
 *     its documentation for any purpose and without fee is hereby granted,
 *     provided that the above copyright notice appears in all copies and
 *     that both that copyright notice and this permission notice appear
 *     in supporting documentation, and that the name of The University
 *     of Michigan not be used in advertising or publicity pertaining to
 *     distribution of the software without specific, written prior
 *     permission. This software is supplied as is without expressed or
 *     implied warranties of any kind.
 */


#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <netatalk/at.h>
#include <atalk/ddp.h>
#include <netdb.h>

#include "nbp_lkup_async.h"

extern int gDebug;
extern int nbp_parse(char *, struct nbpnve *, int);

static char *nbp_addstring(char *p, char *s)
{
    int i;

    if (s) {
        if ((i = strlen(s)) > NBPSTRLEN) {
            i = 31;
        }

        *p++ = i;
        bcopy(s, p, i);
        p += i;
    } else {
        *p++ = 0;
    }

    return p;
}

static char *nbp_addtuple(char *p, struct sockaddr_at addr,
                          char *name, char *type, char *zone)
{
    struct nbptuple nt;
    nt.nt_net = addr.sat_addr.s_net;
    nt.nt_node = addr.sat_addr.s_node;
    nt.nt_port = addr.sat_port;
    bcopy(&nt, p, SZ_NBPTUPLE);
    p += SZ_NBPTUPLE;
    p = nbp_addstring(p, name);
    p = nbp_addstring(p, type);
    p = nbp_addstring(p, zone);
    return p;
}

int nbp_lookup_req(int s, char *name, char *type, char *zone)
{
    static int rqid = 1;
    static int nbp_port = 0;
    struct sockaddr_at addr;
    socklen_t i;
    char buffer[500];
    char *p = buffer;
    struct nbphdr nh;
    struct servent *se;
    i = sizeof(struct sockaddr_at);

    if (getsockname(s, (struct sockaddr *) &addr, &i) < 0) {
        return -1;
    }

    *p++ = DDPTYPE_NBP;
    nh.nh_op = NBPOP_BRRQ;
    nh.nh_cnt = 1;
    nh.nh_id = rqid++;
    bcopy(&nh, p, SZ_NBPHDR);
    p += SZ_NBPHDR;
    p = nbp_addtuple(p, addr, name, type, zone);
    bzero(&addr, sizeof(struct sockaddr_at));
#ifdef BSD4_4
    addr.sat_len = sizeof(struct sockaddr_at);
#endif
    addr.sat_family = AF_APPLETALK;
    addr.sat_addr.s_net = ATADDR_ANYNET;
    addr.sat_addr.s_node = ATADDR_ANYNODE;

    if (nbp_port == 0) {
        if ((se = getservbyname("nbp", "ddp")) == NULL) {
            nbp_port = 2;
        } else {
            nbp_port = ntohs(se->s_port);
        }
    }

    addr.sat_port = nbp_port;

    if (gDebug) {
        printf("looking up '%s:%s@%s'\n", name, type, zone);
    }

    if (sendto(s, buffer, p - buffer, 0, (struct sockaddr *) &addr,
               sizeof(struct sockaddr_at)) < 0) {
        return -1;
    }

    return 0;
}

struct nbpnve *nbp_parse_lkup_repl(char *buffer, int len)
{
    static char *lastbuffer = NULL;
    static char *nexttuple = NULL;
    static struct nbpnve nve;
    struct nbphdr *nh;
    int i;

    if (lastbuffer != buffer) {	/* new packet */
        lastbuffer = buffer;
        nexttuple = 0;

        if (*buffer++ != DDPTYPE_NBP) {
            return 0;
        }

        len--;
        nh = (struct nbphdr *) buffer;
        buffer += SZ_NBPHDR;

        if (nh->nh_op != NBPOP_LKUPREPLY) {
            return 0;
        }

        len -= SZ_NBPHDR;
        nexttuple = buffer;
    } else {
        len -= nexttuple - buffer;
    }

    i = nbp_parse(nexttuple, &nve, len);

    if (i >= 0) {
        nexttuple += len - i;

        if (gDebug)
            printf("received nbp entry '%.*s:%.*s@%.*s'\n",
                   nve.nn_objlen, nve.nn_obj,
                   nve.nn_typelen, nve.nn_type,
                   nve.nn_zonelen, nve.nn_zone);

        return &nve;
    }

    lastbuffer = 0;
    nexttuple = 0;
    return 0;
}
