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