/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1988, 1989, 1998 Apple Computer, Inc. * * The information contained herein is subject to change without * notice and should not be construed as a commitment by Apple * Computer, Inc. Apple Computer, Inc. assumes no responsibility * for any errors that may appear. * * Confidential and Proprietary to Apple Computer, Inc. */ /* "@(#)nbp_send.c: 2.0, 1.12; 9/28/89; Copyright 1988-89, Apple Computer, Inc." */ /* * Title: nbp_send.c * * Facility: AppleTalk Name Binding Protocol Library Interface * * Author: Gregory Burns, Creation Date: Jun-24-1988 * * History: * X01-001 Gregory Burns 24-Jun-1988 * Initial Creation. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SET_ERRNO(e) errno = e #define ZONEOK TRUE #define NOZONE FALSE #define METAOK TRUE #define NOMETA FALSE static int nbpId = 0; static int doNbpReply(at_nbp_t *nbpIn, u_char *reply, u_char **replyPtr, int got, int max, int buf_len); static int nbp_len(at_nbptuple_t *tuple); static void nbp_pack_tuple(at_entity_t *entity, at_nbptuple_t *tuple); static int nbp_unpack_tuple(at_nbptuple_t *tuple, at_entity_t *entity); static void time_diff(struct timeval *time1, struct timeval *time2, struct timeval *diff); int _nbp_send_ (func, addr, name, reply, max, retry) u_char func; at_inet_t *addr; /* used only for nbp_confirm; checked there */ at_entity_t *name; u_char *reply; int max; at_retry_t *retry; { at_ddp_t ddpIn; at_nbp_t *nbpIn = (at_nbp_t *) ddpIn.data; struct sockaddr_at local, remote; int len, flags = 0; at_nbp_t nbpOutBuf, *nbpOut = &nbpOutBuf; at_retry_t retrybuf; int fd; int olderrno; int n, got = 0; /* Number of tuples received */ fd_set selmask; struct timeval timeout; char expectedControl; u_char *replyPtr; struct timeval start_time, stop_time, time_spent, time_remaining; struct timezone tzp; int result, size, ddp_addr_size; at_if_cfg_t cfg; ddp_addr_t ddp_addr; SET_ERRNO(0); if (max <= 0) { fprintf(stderr, "nbp_send: bad max %d\n", max); SET_ERRNO(EINVAL); return(-1); } if ((fd = socket(AF_APPLETALK, SOCK_RAW, DDP_NBP)) < 0) { fprintf(stderr, "nbp_send: socket failed\n"); return(-1); } size = sizeof(struct sockaddr_at); bzero((caddr_t)&remote, size); remote.sat_len = size; remote.sat_family = AF_APPLETALK; remote.sat_port = NBP_SOCKET; bzero((caddr_t)&local, size); local.sat_len = size; local.sat_family = AF_APPLETALK; if (bind(fd, (struct sockaddr *)&local, size) < 0) { fprintf(stderr, "nbp_send: bind failed\n"); return(-1); } cfg.ifr_name[0] = '\0'; /* use the default interface */ if (ioctl(fd, AIOCGETIFCFG, (caddr_t)&cfg) < 0) { fprintf(stderr, "nbp_send: AIOCGETIFCFG failed (%s)\n", sys_errlist[errno]); (void)close(fd); return(-1); } /* get the "lport" part of the local address of this connection to use in the "return address" part of the tuple */ ddp_addr_size = sizeof(ddp_addr); if (getsockopt(fd, 0, DDP_GETSOCKNAME, &ddp_addr, &ddp_addr_size) < 0) { fprintf(stderr, "nbp_send: getsockopt failed\n"); return(-1); } nbpOut->tuple[0].enu_addr = ddp_addr.inet; /* fill in the default address, since fd was not bound to a specific local address */ nbpOut->tuple[0].enu_addr.node = cfg.node.s_node; nbpOut->tuple[0].enu_addr.net = cfg.node.s_net; if (!retry) { retrybuf.retries = NBP_RETRY_COUNT; retrybuf.interval = NBP_RETRY_INTERVAL; retrybuf.backoff = 1; } else { if ((retry->retries <= 0) || (retry->interval <= 0)) { fprintf(stderr, "nbp_send: bad retry %d %d\n", retry->retries, retry->interval); SET_ERRNO(EINVAL); goto out; } retrybuf.retries = retry->retries; retrybuf.interval = retry->interval; retrybuf.backoff = retry->backoff; } retry = &retrybuf; /* Set up common NBP header fields */ nbpOut->control = func; nbpOut->tuple_count = 1; nbpOut->at_nbp_id = ++nbpId; nbpOut->tuple[0].enu_enum = 0; if (!_nbp_validate_entity_(name, METAOK, ZONEOK)) { fprintf(stderr, "nbp_send: nbp_validate_entity failed\n"); SET_ERRNO(EINVAL); goto out; } switch (func) { case NBP_LKUP: if (cfg.router.s_node) { remote.sat_addr = cfg.router; nbpOut->control = NBP_BRRQ; } else { /* Check for attempt to lookup non-local zone when no bridge exists. Inside AppleTalk, pp VI-5. */ if (name->zone.str[0] != '*' ) { SET_ERRNO(ENETUNREACH); goto out; } remote.sat_addr.s_net = 0; remote.sat_addr.s_node = 0xff; } expectedControl = NBP_LKUP_REPLY; SET_ERRNO(0); break; case NBP_CONFIRM: nbpOut->control = NBP_LKUP; remote.sat_addr.s_net = addr->net; remote.sat_addr.s_node = addr->node; expectedControl = NBP_LKUP_REPLY; SET_ERRNO(0); break; default: SET_ERRNO(EINVAL); fprintf(stderr, "nbp_send: bad func %d\n", func); goto out; } (void)nbp_pack_tuple(name, &nbpOut->tuple[0]); len = NBP_HDR_SIZE + nbp_len(&nbpOut->tuple[0]); replyPtr = reply; while (got < max) { if ((result = sendto(fd, nbpOut, len, flags, (struct sockaddr *)&remote, size)) < 0) { fprintf(stderr, "nbp_send: sendto failed\n"); goto out; } time_remaining.tv_sec = retry->interval; time_remaining.tv_usec = 0; timeout = time_remaining; gettimeofday(&start_time, &tzp); poll: FD_ZERO(&selmask); FD_SET(fd, &selmask); switch (select(fd+1, &selmask, 0, 0, &timeout)) { default: if ((result = recvfrom(fd, &ddpIn, DDP_DATAGRAM_SIZE, flags, NULL, &size)) < 0) { fprintf(stderr, "nbp_send: recvfrom failed\n"); goto out; } /* If we re-poll, return immediately */ timeout.tv_sec = timeout.tv_usec = 0; /* Match NBP-ID and must be reply to request func */ if (nbpIn->at_nbp_id != nbpOut->at_nbp_id) goto poll; if (nbpIn->control != expectedControl) goto poll; n = doNbpReply(nbpIn, reply, &replyPtr, got, max, result); got += n; SET_ERRNO(0); if (got >= max) break; goto poll; SET_ERRNO(0); break; case -1: /* An error occurred */ if (errno != EINTR) { fprintf(stderr, "nbp_send: select failed\n"); goto out; } /* fall through */ SET_ERRNO(0); case 0: /* Nothing found, we timed out */ gettimeofday (&stop_time, &tzp); time_diff(&stop_time, &start_time, &time_spent); time_diff(&time_remaining, &time_spent, &timeout); time_remaining = timeout; if (timeout.tv_sec > 0 || timeout.tv_usec > 0) { gettimeofday(&start_time, &tzp); goto poll; } SET_ERRNO(0); break; } /* Are we finished yet ? */ if (retry->retries-- == 0) { SET_ERRNO(0); break; } } out: olderrno = errno; (void)close(fd); SET_ERRNO(olderrno); if (errno) return(-1); if ((got > 0) && (func == NBP_CONFIRM)) addr->socket = nbpIn->tuple[0].enu_addr.socket; return (got); } /* _nbp_send_ */ static int doNbpReply(nbpIn, reply, replyPtr, got, max, buf_len) at_nbp_t *nbpIn; u_char *reply, **replyPtr; int got, max, buf_len; { int i, wegot = 0, tot_len; at_nbptuple_t *tuple, *tupleIn, *tupleNext; tupleIn = &nbpIn->tuple[0]; tupleNext = * (at_nbptuple_t **) replyPtr; tot_len = nbp_len(tupleIn); for (i = 0; i < (int) nbpIn->tuple_count; i++) { /* check that the actual size of the current tuple doesn't exceed the buffer size */ if ((buf_len - tot_len) < 0) { /*buffer is corrupt - set the reply back (wegot-1) entries and return 0 */ tupleNext -= (wegot - 1); return 0; } else { for (tuple = (at_nbptuple_t *) reply; tuple < tupleNext; tuple++) { if (tuple->enu_enum == tupleIn->enu_enum && tuple->enu_addr.net == tupleIn->enu_addr.net && tuple->enu_addr.node == tupleIn->enu_addr.node && tuple->enu_addr.socket == tupleIn->enu_addr.socket){ goto skip; } } if (got + wegot >= max) break; wegot++; tupleNext->enu_addr = tupleIn->enu_addr; tupleNext->enu_enum = tupleIn->enu_enum; /* Here, if the tuple is bad, nbp_unpack_tuple will now return an error. We then set the reply back (wegot-1) entries and return 0 */ if ((nbp_unpack_tuple(tupleIn, &tupleNext->enu_entity)) != 0) { tupleNext -= (wegot - 1); return 0; } tupleNext++; skip: /* skip over current tuple to next in buffer */ tupleIn = (at_nbptuple_t *) (((u_char *) tupleIn) + nbp_len(tupleIn)); /* add length of next tuple to tot_len */ tot_len += nbp_len(tupleIn); } } *replyPtr = (u_char *) tupleNext; return (wegot); } /* tuple is in the compressed (no "filler") format */ static int nbp_len(tuple) at_nbptuple_t *tuple; { register u_char *p; int len; len = sizeof(at_inet_t) + 1; p = (u_char *)&tuple->enu_entity; len += *p + 1; p += *p + 1; len += *p + 1; p += *p + 1; len += *p + 1; return (len); } /* tuple is in the compressed (no "filler") format */ static void nbp_pack_tuple(entity, tuple) at_entity_t *entity; at_nbptuple_t *tuple; { register u_char *p; p = (u_char *)&tuple->enu_entity; memcpy (p, &entity->object, entity->object.len + 1); p += entity->object.len + 1; memcpy (p, &entity->type, entity->type.len + 1); p += entity->type.len + 1; memcpy (p, &entity->zone, entity->zone.len + 1); } /* tuple is in the compressed (no "filler") format */ static int nbp_unpack_tuple(tuple, entity) at_nbptuple_t *tuple; at_entity_t *entity; { register u_char *p; p = (u_char *)&tuple->enu_entity; if (*p > NBP_NVE_STR_SIZE) { return -1; } memcpy (&entity->object, p, *p + 1); p += *p + 1; if (*p > NBP_NVE_STR_SIZE) { return -1; } memcpy (&entity->type, p, *p + 1); p += *p + 1; if (*p > NBP_NVE_STR_SIZE) { return -1; } memcpy (&entity->zone, p, *p + 1); return 0; } /* returns (time1 - time2) in diff. * diff may be the same as either time1 or time2. * Will return 0's in diff if the time difference is negative. */ static void time_diff (time1, time2, diff) struct timeval *time1, *time2, *diff; { int carry = 0; if (time1->tv_usec >= time2->tv_usec) { diff->tv_usec = time1->tv_usec - time2->tv_usec; } else { diff->tv_usec = time1->tv_usec+1000000-time2->tv_usec; carry = 1; } if (time1->tv_sec < (time2->tv_sec + carry)) diff->tv_sec = diff->tv_usec = 0; else diff->tv_sec = time1->tv_sec - (time2->tv_sec + carry); return; }