]> arthur.barton.de Git - netatalk.git/blob - etc/atalkd/nbp.c
Remove bdb env on exit
[netatalk.git] / etc / atalkd / nbp.c
1 /*
2  * $Id: nbp.c,v 1.13 2009-10-14 02:24:05 didg Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * All Rights Reserved. See COPYRIGHT.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #include <stdlib.h>
13 #include <string.h>
14 #include <errno.h>
15 #include <atalk/logger.h>
16 #include <sys/types.h>
17 #include <sys/param.h>
18 #include <sys/socket.h>
19 #include <sys/ioctl.h>
20 #ifdef TRU64
21 #include <sys/mbuf.h>
22 #include <net/route.h>
23 #endif /* TRU64 */
24 #include <net/if.h>
25 #include <netatalk/at.h>
26 #include <atalk/ddp.h>
27 #include <atalk/atp.h>
28 #include <atalk/nbp.h>
29 #include <atalk/util.h>
30
31 #ifdef __svr4__
32 #include <sys/sockio.h>
33 #endif /* __svr4__ */
34
35 #include "atserv.h"
36 #include "interface.h"
37 #include "list.h"
38 #include "rtmp.h"
39 #include "gate.h"
40 #include "zip.h"
41 #include "nbp.h"
42 #include "multicast.h"
43
44 extern int  transition;
45
46 struct nbptab   *nbptab = NULL;
47
48 static
49 void nbp_ack( int fd, int nh_op, int nh_id, struct sockaddr_at *to)
50 {
51     struct nbphdr   nh;
52     char        *data, packet[ SZ_NBPHDR + 1 ];
53
54     nh.nh_op = nh_op;
55     nh.nh_cnt = 0;
56     nh.nh_id = nh_id;
57     data = packet;
58     *data++ = DDPTYPE_NBP;
59     memcpy( data, &nh, SZ_NBPHDR );
60     data += SZ_NBPHDR;
61     if ( sendto( fd, packet, data - packet, 0, (struct sockaddr *)to,
62                  sizeof( struct sockaddr_at )) < 0 ) {
63         LOG(log_error, logtype_atalkd, "sendto: %s", strerror(errno) );
64     }
65 }
66
67 int nbp_packet(struct atport *ap, struct sockaddr_at *from, char *data, int len)
68 {
69     struct nbphdr   nh;
70     struct nbptuple nt;
71     struct nbpnve   nn;
72     struct sockaddr_at  sat;
73     struct nbptab   *ntab;
74     struct ziptab   *zt=NULL;
75     struct interface    *iface;
76     struct list     *l;
77     struct rtmptab  *rtmp;
78     char        *end, *nbpop, *zonep, packet[ ATP_BUFSIZ ];
79     int         n, i, cc, locallkup;
80     u_char      tmplen;
81
82     end = data + len;
83     if ( data >= end ) {
84         LOG(log_info, logtype_atalkd, "nbp_packet malformed packet" );
85         return 1;
86     }
87     if ( *data++ != DDPTYPE_NBP ) {
88         LOG(log_info, logtype_atalkd, "nbp_packet bad ddp type" );
89         return 1;
90     }
91
92     if ( data + SZ_NBPHDR + SZ_NBPTUPLE > end ) {
93         LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
94         return 1;
95     }
96     memcpy( &nh, data, SZ_NBPHDR );
97     nbpop = data;           /* remember for fwd and brrq */
98     data += SZ_NBPHDR;
99     if ( nh.nh_cnt != 1 ) {
100         LOG(log_info, logtype_atalkd, "nbp_packet: bad tuple count (%d/%d)", nh.nh_cnt,
101             nh.nh_op );
102         return 1;
103     }
104
105     memcpy( &nt, data, SZ_NBPTUPLE );
106     data += SZ_NBPTUPLE;
107
108     memset( &nn.nn_sat, 0, sizeof( struct sockaddr_at ));
109 #ifdef BSD4_4
110     nn.nn_sat.sat_len = sizeof( struct sockaddr_at );
111 #endif /* BSD4_4 */
112     nn.nn_sat.sat_family = AF_APPLETALK;
113     nn.nn_sat.sat_addr.s_net = nt.nt_net;
114     nn.nn_sat.sat_addr.s_node = nt.nt_node;
115     nn.nn_sat.sat_port = nt.nt_port;
116
117     /* object */
118     tmplen = (u_char) *data;
119     if ( data >= end || tmplen > 32 || data + tmplen > end ) {
120         LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
121         return 1;
122     }
123     nn.nn_objlen = tmplen;
124     data++;
125     memcpy( nn.nn_obj, data, nn.nn_objlen );
126     data += nn.nn_objlen;
127
128     /* type */
129     tmplen = (u_char) *data;
130     if ( data >= end || tmplen > 32 || data + tmplen > end ) {
131         LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
132         return 1;
133     }
134     nn.nn_typelen = tmplen;
135     data++;
136     memcpy( nn.nn_type, data, nn.nn_typelen );
137     data += nn.nn_typelen;
138
139     /* zone */
140     tmplen = (u_char) *data;
141     if ( data >= end || tmplen > 32 || data + tmplen > end ) {
142         LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
143         return 1;
144     }
145     zonep = data;           /* remember for fwd */
146     nn.nn_zonelen = tmplen;
147     data++;
148     memcpy( nn.nn_zone, data, nn.nn_zonelen );
149     data += nn.nn_zonelen;
150
151     if ( data != end ) {
152         LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
153         return 1;
154     }
155
156     locallkup = 0;
157     switch ( nh.nh_op ) {
158
159     case NBPOP_RGSTR :
160         /*
161          * Find the ziptab entry for the zone we're trying to register in.
162          */
163         if ( nn.nn_zonelen == 0 ||
164              ( nn.nn_zonelen == 1 && *nn.nn_zone == '*' )) {
165             if ( interfaces->i_next->i_rt->rt_zt ) {
166                 zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
167             } else {
168                 zt = NULL;
169             }
170         } else {
171             for ( zt = ziptab; zt; zt = zt->zt_next ) {
172                 if ( zt->zt_len == nn.nn_zonelen && strndiacasecmp( zt->zt_name,
173                                                                     nn.nn_zone, zt->zt_len ) == 0 ) {
174                     break;
175                 }
176             }
177             if ( zt == NULL ) {
178                 nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
179                 return 0;
180             }
181         }
182
183         /*
184          * Observe that we don't have to do any local-zone verification
185          * if the zone aleady has a multicast address set.
186          */
187         if ( zt != NULL && zt->zt_bcast == NULL ) {
188             /*
189              * Check if zone is associated with any of our local interfaces.
190              */
191             for ( iface = interfaces; iface; iface = iface->i_next ) {
192                 for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
193                     if ( zt == (struct ziptab *)l->l_data ) {
194                         break;
195                     }
196                 }
197                 if ( l != NULL ) {
198                     break;
199                 }
200             }
201             if ( iface == NULL ) {
202                 nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
203                 return 0;
204             }
205
206             /* calculate and save multicast address */
207             if (zone_bcast(zt) < 0) {
208                 LOG(log_error, logtype_atalkd, "nbp_packet: zone_bcast");
209                 return -1;
210             }
211
212             for ( iface = interfaces; iface; iface = iface->i_next ) {
213                 if (( iface->i_flags & IFACE_PHASE2 ) == 0 ) {
214                     continue;
215                 }
216                 for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
217                     if ( zt == (struct ziptab *)l->l_data ) {
218                         /* add multicast */
219                         if (addmulti(iface->i_name, zt->zt_bcast) < 0) {
220                             LOG(log_error, logtype_atalkd, "nbp_packet: addmulti: %s",
221                                 strerror(errno) );
222                             return -1;
223                         }
224                     }
225                 }
226             }
227         }
228
229         if (( ntab = (struct nbptab *)malloc( sizeof( struct nbptab )))
230             == NULL ) {
231             LOG(log_error, logtype_atalkd, "nbp_packet: malloc: %s", strerror(errno) );
232             return -1;
233         }
234         memcpy( &ntab->nt_nve, &nn, sizeof( struct nbpnve ));
235         ntab->nt_iface = ap->ap_iface;
236         ntab->nt_next = nbptab;
237         ntab->nt_prev = NULL;
238         if ( nbptab ) {
239             nbptab->nt_prev = ntab;
240         }
241         nbptab = ntab;
242
243         nbp_ack( ap->ap_fd, NBPOP_OK, (int)nh.nh_id, from );
244         break;
245
246     case NBPOP_UNRGSTR :
247         /* deal with local zone info */
248         if (( nn.nn_zonelen == 1 && *nn.nn_zone == '*' ) ||
249             ( nn.nn_zonelen == 0 )) {
250             locallkup = 1;
251             if ( interfaces->i_next->i_rt->rt_zt ) {
252                 zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
253             } else {
254                 zt = NULL;
255             }
256         }
257
258         /* remove from our data, perhaps removing a multicast address */
259         for ( ntab = nbptab; ntab; ntab = ntab->nt_next ) {
260             if ( ntab->nt_nve.nn_objlen != nn.nn_objlen ||
261                  strndiacasecmp( ntab->nt_nve.nn_obj, nn.nn_obj,
262                                  nn.nn_objlen )) {
263                 continue;
264             }
265             if ( ntab->nt_nve.nn_typelen != nn.nn_typelen ||
266                  strndiacasecmp( ntab->nt_nve.nn_type, nn.nn_type,
267                                  nn.nn_typelen )) {
268                 continue;
269             }
270             /*
271              * I *think* we really do check the zone, here.
272              *
273              * i changed it to better handle local zone cases as well.
274              * -- asun
275              */
276
277             /* match local zones */
278             if (locallkup) {
279                 /* ntab is also local zone */
280                 if (( ntab->nt_nve.nn_zonelen == 1 &&
281                       *ntab->nt_nve.nn_zone == '*' ) ||
282                     (ntab->nt_nve.nn_zonelen == 0))
283                     break;
284
285                 /* ntab is default zone */
286                 if (zt && (zt->zt_len == ntab->nt_nve.nn_zonelen) &&
287                     (strndiacasecmp(ntab->nt_nve.nn_zone, zt->zt_name,
288                                     zt->zt_len) == 0)) {
289                     break;
290                 }
291             }
292
293             /* match particular zone */
294             if ((ntab->nt_nve.nn_zonelen == nn.nn_zonelen) &&
295                 (strndiacasecmp( ntab->nt_nve.nn_zone, nn.nn_zone,
296                                  nn.nn_zonelen ) == 0)) {
297                 break;
298             }
299         }
300         if ( ntab == NULL ) {
301             nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
302             return 0;
303         }
304
305         if ( ntab->nt_next != NULL ) {
306             ntab->nt_next->nt_prev = ntab->nt_prev;
307         }
308         if ( ntab->nt_prev != NULL ) {
309             ntab->nt_prev->nt_next = ntab->nt_next;
310         }
311         if ( ntab == nbptab ) {
312             nbptab = ntab->nt_next;
313         }
314
315         /*
316          * Check for another nbptab entry with the same zone.  If
317          * there isn't one, find the ziptab entry for the zone and
318          * remove the multicast address from the appropriate interfaces.
319          * XXX
320          */
321
322         nbp_ack( ap->ap_fd, NBPOP_OK, (int)nh.nh_id, from );
323         break;
324
325     case NBPOP_BRRQ :
326         /*
327          * Couple of things:  1. Unless we have the -t flag (which is sort
328          * of a misnomer, since you need it if you're doing any phase 1
329          * work), always send NBPOP_FWD.  2. If we get a zone of '*',
330          * and we know what the sender meant by '*', we copy the real
331          * zone into the packet.
332          */
333         if ( nn.nn_zonelen == 0 ||
334              ( nn.nn_zonelen == 1 && *nn.nn_zone == '*' )) {
335             iface = ap->ap_iface;
336             if ( iface && iface->i_rt->rt_zt ) {
337                 zt = (struct ziptab *)iface->i_rt->rt_zt->l_data;
338             } else if ( interfaces->i_next->i_rt->rt_zt ) {
339                 zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
340             } else {
341                 zt = NULL;
342             }
343
344             /*
345              * Copy zone into packet.  Note that we're changing len, data, and
346              * nbpop.  Later, we'll use ( data - len ) to mean the beginning
347              * of this packet.
348              */
349             if ( zt ) {
350                 memcpy( packet, data - len, len );
351                 nbpop = packet + ( len - ( data - nbpop ));
352                 data = packet + ( len - ( data - zonep ));
353                 *data++ = zt->zt_len;
354                 memcpy( data, zt->zt_name, zt->zt_len );
355                 data += zt->zt_len;
356                 len = data - packet;
357             }
358         } else {
359             for ( zt = ziptab; zt; zt = zt->zt_next ) {
360                 if ( zt->zt_len == nn.nn_zonelen && strndiacasecmp( zt->zt_name,
361                                                                     nn.nn_zone, zt->zt_len ) == 0 ) {
362                     break;
363                 }
364             }
365             if ( zt == NULL ) {
366                 nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
367                 return 0;
368             }
369         }
370
371         /*
372          * If we've got no zones, send out LKUP on the local net.
373          * Otherwise, look through the zone table.
374          */
375         if ( zt == NULL ) {
376 #ifdef BSD4_4
377             sat.sat_len = sizeof( struct sockaddr_at );
378 #endif /* BSD4_4 */
379             sat.sat_family = AF_APPLETALK;
380             sat.sat_port = ap->ap_port;
381
382             nh.nh_op = NBPOP_LKUP;
383             memcpy( nbpop, &nh, SZ_NBPHDR );
384             sat.sat_addr.s_net = 0;         /* XXX */
385             sat.sat_addr.s_node = ATADDR_BCAST;
386
387             /* Find the first non-loopback ap */
388             for ( iface = interfaces; iface; iface = iface->i_next ) {
389                 if ((( iface->i_flags & IFACE_LOOPBACK ) == 0) &&
390                     (iface == ap->ap_iface ||
391                      (iface->i_flags & IFACE_ISROUTER))) {
392                     break;
393                 }
394             }
395             if ( iface == NULL ) {
396                 return 0;
397             }
398             for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
399                 if ( ap->ap_packet == nbp_packet ) {
400                     break;
401                 }
402             }
403             if (ap == NULL)
404                 return 0;
405
406             if ( sendto( ap->ap_fd, data - len, len, 0, (struct sockaddr *)&sat,
407                          sizeof( struct sockaddr_at )) < 0 ) {
408                 LOG(log_error, logtype_atalkd, "nbp brrq sendto: %s", strerror(errno) );
409             }
410
411             locallkup = 1;
412         } else {
413 #ifdef BSD4_4
414             sat.sat_len = sizeof( struct sockaddr_at );
415 #endif /* BSD4_4 */
416             sat.sat_family = AF_APPLETALK;
417             sat.sat_port = ap->ap_port;
418             for ( l = zt->zt_rt; l; l = l->l_next ) {
419                 rtmp = (struct rtmptab *)l->l_data;
420
421                 if ( rtmp->rt_gate == NULL ) {
422                     for ( iface = interfaces; iface;
423                           iface = iface->i_next ) {
424                         if ( iface->i_rt == rtmp ) {
425                             break;
426                         }
427                     }
428                     if ( !iface ) {
429                         LOG(log_error, logtype_atalkd, "nbp_packet: \
430 Can't find route's interface!" );
431                         return -1;
432                     }
433                     ap = iface->i_ports;
434                 } else {
435                     ap = rtmp->rt_gate->g_iface->i_ports;
436                 }
437                 for ( ; ap; ap = ap->ap_next ) {
438                     if ( ap->ap_packet == nbp_packet ) {
439                         break;
440                     }
441                 }
442                 if ( !ap ) {
443                     LOG(log_error, logtype_atalkd, "nbp_packet: Can't find port!" );
444                     return -1;
445                 }
446
447                 if ( transition &&
448                      ( rtmp->rt_flags & RTMPTAB_EXTENDED ) == 0 ) {
449                     if ( rtmp->rt_gate == NULL ) {
450                         locallkup = 1;
451                     }
452                     nh.nh_op = NBPOP_LKUP;
453                     memcpy( nbpop, &nh, SZ_NBPHDR );
454                     sat.sat_addr.s_net = rtmp->rt_firstnet;
455                     sat.sat_addr.s_node = ATADDR_BCAST;
456                 } else {
457                     if ( rtmp->rt_gate == NULL ) {
458                         nh.nh_op = NBPOP_LKUP;
459                         memcpy( nbpop, &nh, SZ_NBPHDR );
460                         sat.sat_addr.s_net = 0;
461                         sat.sat_addr.s_node = ATADDR_BCAST;
462                         locallkup = 1;
463                     } else {
464                         nh.nh_op = NBPOP_FWD;
465                         memcpy( nbpop, &nh, SZ_NBPHDR );
466                         sat.sat_addr.s_net = rtmp->rt_firstnet;
467                         sat.sat_addr.s_node = 0;
468                     }
469                 }
470
471                 if ( sendto( ap->ap_fd, data - len, len, 0,
472                              (struct sockaddr *)&sat,
473                              sizeof( struct sockaddr_at )) < 0 ) {
474                     LOG(log_error, logtype_atalkd, "nbp brrq sendto %u.%u: %s",
475                         ntohs( sat.sat_addr.s_net ), sat.sat_addr.s_node,
476                         strerror(errno) );
477                     continue;
478                 }
479             }
480         }
481
482         if ( !locallkup ) {
483             break;
484         }
485         /*FALL THROUGH*/
486
487     case NBPOP_FWD :
488         /* send lkup on net. we need to make sure we're a router. */
489         if ( !locallkup && (ap->ap_iface->i_flags & IFACE_ISROUTER)) {
490             nh.nh_op = NBPOP_LKUP;
491             memcpy( nbpop, &nh, SZ_NBPHDR );
492             from->sat_addr.s_net = 0;
493             from->sat_addr.s_node = ATADDR_BCAST;
494             if ( sendto( ap->ap_fd, data - len, len, 0, (struct sockaddr *)from,
495                          sizeof( struct sockaddr_at )) < 0 ) {
496                 LOG(log_error, logtype_atalkd, "nbp fwd sendto %u.%u: %s",
497                     ntohs( from->sat_addr.s_net ), from->sat_addr.s_node,
498                     strerror(errno) );
499                 return 0;
500             }
501         }
502         /*FALL THROUGH*/
503
504     case NBPOP_LKUP :
505         /* search our data */
506         n = i = 0;
507         data = packet + 1 + SZ_NBPHDR;
508         end = packet + sizeof( packet );
509
510         for ( ntab = nbptab; ntab; ntab = ntab->nt_next ) {
511             /* don't send out entries if we don't want to route. */
512             if ((ap->ap_iface != ntab->nt_iface) &&
513                 (ntab->nt_iface->i_flags & IFACE_ISROUTER) == 0) {
514                 continue;
515             }
516
517             if ( nn.nn_objlen != 1 || *nn.nn_obj != '=' ) {
518                 if ( ntab->nt_nve.nn_objlen != nn.nn_objlen ||
519                      strndiacasecmp( ntab->nt_nve.nn_obj, nn.nn_obj,
520                                      nn.nn_objlen )) {
521                     continue;
522                 }
523             }
524
525             if ( nn.nn_typelen != 1 || *nn.nn_type != '=' ) {
526                 if ( ntab->nt_nve.nn_typelen != nn.nn_typelen ||
527                      strndiacasecmp( ntab->nt_nve.nn_type, nn.nn_type,
528                                      nn.nn_typelen )) {
529                     continue;
530                 }
531             }
532
533             if ( nn.nn_zonelen != 0 &&
534                  ( nn.nn_zonelen != 1 || *nn.nn_zone != '*' )) {
535                 if ( ntab->nt_nve.nn_zonelen == 0 ||
536                      ( ntab->nt_nve.nn_zonelen == 1 &&
537                        *ntab->nt_nve.nn_zone == '*' )) {
538                     if ( interfaces->i_next->i_rt->rt_zt ) {
539                         zt = (struct ziptab *)interfaces->i_next->i_rt->
540                             rt_zt->l_data;
541                         if ( zt->zt_len != nn.nn_zonelen ||
542                              strndiacasecmp( zt->zt_name, nn.nn_zone,
543                                              zt->zt_len )) {
544                             continue;
545                         }
546                     }
547                 } else {
548                     if ( ntab->nt_nve.nn_zonelen != nn.nn_zonelen ||
549                          strndiacasecmp( ntab->nt_nve.nn_zone, nn.nn_zone,
550                                          nn.nn_zonelen )) {
551                         continue;
552                     }
553                 }
554             }
555
556             /*
557              * Another tuple won't fit. Send what we've already
558              * got, and start the next packet.
559              */
560             if ( n > 14 || data + SZ_NBPTUPLE + 3 + ntab->nt_nve.nn_objlen +
561                  ntab->nt_nve.nn_typelen + ntab->nt_nve.nn_zonelen > end ) {
562                 nh.nh_op = NBPOP_LKUPREPLY;
563                 nh.nh_cnt = n;
564                 cc = data - packet;
565                 data = packet;
566                 *data++ = DDPTYPE_NBP;
567                 memcpy( data, &nh, SZ_NBPHDR );
568
569                 if ( sendto( ap->ap_fd, packet, cc, 0,
570                              (struct sockaddr *)&nn.nn_sat,
571                              sizeof( struct sockaddr_at )) < 0 ) {
572                     LOG(log_error, logtype_atalkd, "nbp lkup sendto %u.%u: %s",
573                         ntohs( nn.nn_sat.sat_addr.s_net ),
574                         nn.nn_sat.sat_addr.s_node,
575                         strerror(errno) );
576                     return 0;
577                 }
578
579                 n = 0;
580                 data = packet + 1 + SZ_NBPHDR;
581                 end = packet + sizeof( packet );
582             }
583
584             nt.nt_net = ntab->nt_nve.nn_sat.sat_addr.s_net;
585             nt.nt_node = ntab->nt_nve.nn_sat.sat_addr.s_node;
586             nt.nt_port = ntab->nt_nve.nn_sat.sat_port;
587             /*
588              * Right now, we'll just give each name a unique enum.  In
589              * the future, we might need to actually assign and save
590              * an enum, based on the associated address.  For the moment,
591              * the enums will be unique and constant, since the order
592              * is fixed.
593              */
594             nt.nt_enum = i++;
595
596             memcpy( data, &nt, SZ_NBPTUPLE );
597             data += SZ_NBPTUPLE;
598
599             *data++ = ntab->nt_nve.nn_objlen;
600             memcpy( data, ntab->nt_nve.nn_obj, ntab->nt_nve.nn_objlen );
601             data += ntab->nt_nve.nn_objlen;
602
603             *data++ = ntab->nt_nve.nn_typelen;
604             memcpy(data, ntab->nt_nve.nn_type, ntab->nt_nve.nn_typelen );
605             data += ntab->nt_nve.nn_typelen;
606
607             /*
608              * Macs won't see something with a zone of 0 length.  We
609              * will always return '*' instead.  Perhaps we should
610              * unconditionally return the real zone?
611              */
612             if ( ntab->nt_nve.nn_zonelen ) {
613                 *data++ = ntab->nt_nve.nn_zonelen;
614                 memcpy( data, ntab->nt_nve.nn_zone, ntab->nt_nve.nn_zonelen );
615                 data += ntab->nt_nve.nn_zonelen;
616             } else {
617                 *data++ = 1;
618                 *data++ = '*';
619             }
620
621             n++;
622         }
623
624         if ( n != 0 ) {
625             nh.nh_op = NBPOP_LKUPREPLY;
626             nh.nh_cnt = n;
627             cc = data - packet;
628             data = packet;
629             *data++ = DDPTYPE_NBP;
630             memcpy( data, &nh, SZ_NBPHDR );
631
632             if ( sendto( ap->ap_fd, packet, cc, 0,
633                          (struct sockaddr *)&nn.nn_sat,
634                          sizeof( struct sockaddr_at )) < 0 ) {
635                 LOG(log_error, logtype_atalkd, "nbp lkup sendto %u.%u: %s",
636                     ntohs( nn.nn_sat.sat_addr.s_net ),
637                     nn.nn_sat.sat_addr.s_node,
638                     strerror(errno) );
639                 return 0;
640             }
641         }
642         break;
643
644     default :
645         LOG(log_info, logtype_atalkd, "nbp_packet: bad op (%d)", nh.nh_op );
646         return 1;
647     }
648
649     return 0;
650 }