1/*
2 * $Id: zip.c,v 1.15 2009-12-13 00:31:50 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 <sys/param.h>
16#include <sys/types.h>
17#include <atalk/logger.h>
18#include <sys/socket.h>
19#include <sys/ioctl.h>
20#include <sys/time.h>
21#ifdef TRU64
22#include <sys/mbuf.h>
23#include <net/route.h>
24#endif /* TRU64 */
25#include <net/if.h>
26#include <net/route.h>
27#include <netatalk/endian.h>
28#include <netatalk/at.h>
29
30#ifdef __svr4__
31#include <sys/sockio.h>
32#endif /* __svr4__ */
33
34#include <atalk/ddp.h>
35#include <atalk/zip.h>
36#include <atalk/atp.h>
37#include <atalk/util.h>
38
39#include "atserv.h"
40#include "interface.h"
41#include "gate.h"
42#include "zip.h"
43#include "rtmp.h"
44#include "list.h"
45#include "multicast.h"
46#include "main.h"
47
48struct ziptab	*ziptab = NULL, *ziplast = NULL;
49
50
51static int zonecheck(struct rtmptab *rtmp, struct interface *iface)
52{
53    struct list		*l;
54    struct ziptab	*czt, *zt;
55    int			cztcnt, ztcnt;
56
57    if (( iface->i_flags & IFACE_SEED ) == 0 ) {
58	return( 0 );
59    }
60
61    for ( cztcnt = 0, czt = iface->i_czt; czt; czt = czt->zt_next, cztcnt++ ) {
62	for ( l = rtmp->rt_zt; l; l = l->l_next ) {
63	    zt = (struct ziptab *)l->l_data;
64	    if ( czt->zt_len == zt->zt_len &&
65		    !strndiacasecmp( czt->zt_name, zt->zt_name, czt->zt_len )) {
66		break;
67	    }
68	}
69	if ( l == NULL ) {
70	    LOG(log_error, logtype_atalkd, "zonecheck: %.*s not in zone list", czt->zt_len,
71		    czt->zt_name );
72	    return( -1 );	/* configured zone not found in net zones */
73	}
74    }
75
76    for ( ztcnt = 0, l = rtmp->rt_zt; l; l = l->l_next, ztcnt++ )
77	;
78
79    if ( cztcnt != ztcnt ) {
80	LOG(log_error, logtype_atalkd, "zonecheck: %d configured zones, %d zones found",
81		cztcnt, ztcnt );
82	return( -1 );		/* more net zones than configured zones */
83    }
84
85    return( 0 );
86}
87
88
89int zip_packet(struct atport *ap,struct sockaddr_at *from, char *data, int len)
90{
91    struct ziphdr	zh;
92    struct atphdr	ah;
93    struct interface	*iface;
94    struct gate		*gate;
95    struct rtmptab	*rtmp = NULL;
96    struct list		*l;
97    struct ziptab	*zt;
98    u_short		firstnet, lastnet, index, nz;
99    char		*end, zname[ 32 ], packet[ ATP_BUFSIZ ], *nzones, *lastflag;
100    char		*reply, *rend, *ziphdr;
101    int			zlen, n, zipop, rcnt, qcnt, zcnt, zsz;
102    extern int		stabletimer;
103
104    end = data + len;
105
106    if ( data >= end ) {
107	LOG(log_info, logtype_atalkd, "zip_packet malformed packet" );
108	return 1;
109    }
110
111    /* get interface */
112    iface = ap->ap_iface;
113
114    switch( *data++ ) {
115    case DDPTYPE_ZIP :
116	if ( data + sizeof( struct ziphdr ) > end ) {
117	    LOG(log_info, logtype_atalkd, "zip_packet malformed packet" );
118	    return 1;
119	}
120	memcpy( &zh, data, sizeof( struct ziphdr ));
121	data += sizeof( struct ziphdr );
122
123	switch ( zh.zh_op ) {
124	case ZIPOP_QUERY :
125	    /* set up reply */
126	    reply = packet;
127	    rend = packet + sizeof( packet );
128	    *reply++ = DDPTYPE_ZIP;
129	    ziphdr = reply;
130	    reply += 2;
131	    rcnt = 0;
132
133	    qcnt = zh.zh_count;
134
135	    while ( data + sizeof( u_short ) <= end && qcnt-- > 0 ) {
136		memcpy( &firstnet, data, sizeof( u_short ));
137		data += sizeof( u_short );
138
139		/*
140		 * Look for the given network number (firstnet).
141		 * Perhaps we could do better than brute force?
142		 */
143		for ( iface = interfaces; iface; iface = iface->i_next ) {
144		    for ( rtmp = iface->i_rt; rtmp; rtmp = rtmp->rt_inext ) {
145			if ( firstnet == rtmp->rt_firstnet ) {
146			    break;
147			}
148		    }
149		    if ( rtmp ) {
150			break;
151		    }
152		}
153		if ( rtmp == NULL ) {
154		    continue;
155		}
156
157		/*
158		 * Count the number of zones in this list, and the
159		 * number of byte it will consume in a reply.
160		 */
161		for ( zsz = 0, zcnt = 0, l = rtmp->rt_zt; l; l = l->l_next ) {
162		    zcnt++;
163		    zt = (struct ziptab *)l->l_data;
164		    zsz += sizeof( u_short ) + 1 + zt->zt_len;
165		}
166
167		/*
168		 * We might send this list in the current reply, as the
169		 * first thing in the next reply, or as an extended packet.
170		 */
171		if ( reply + zsz > rend ) {
172		    if ( rcnt > 0 ) {
173			zh.zh_op = ZIPOP_REPLY;
174			zh.zh_cnt = rcnt;
175			memcpy( ziphdr, &zh, sizeof( struct ziphdr ));
176			if ( sendto( ap->ap_fd, packet, reply - packet, 0,
177				(struct sockaddr *)from,
178				sizeof( struct sockaddr_at )) < 0 ) {
179			    LOG(log_error, logtype_atalkd, "zip reply sendto: %s",
180				    strerror(errno) );
181			}
182
183			reply = packet + 3;
184			rcnt = 0;
185		    }
186
187		    if ( reply + zsz > rend ) {
188			/* ereply */
189			for ( l = rtmp->rt_zt; l; l = l->l_next, rcnt++ ) {
190			    zt = (struct ziptab *)l->l_data;
191			    if ( reply + sizeof( u_short ) + 1 + zt->zt_len >
192				    rend ) {
193				zh.zh_op = ZIPOP_EREPLY;
194				zh.zh_cnt = zcnt;
195				memcpy( ziphdr, &zh, sizeof( struct ziphdr ));
196				if ( sendto( ap->ap_fd, packet, reply - packet,
197					0, (struct sockaddr *)from,
198					sizeof( struct sockaddr_at )) < 0 ) {
199				    LOG(log_error, logtype_atalkd, "zip reply sendto: %s",
200					    strerror(errno) );
201				}
202
203				reply = packet + 3;
204				rcnt = 0;
205			    }
206
207			    memcpy( reply, &firstnet, sizeof( u_short ));
208			    reply += sizeof( u_short );
209			    *reply++ = zt->zt_len;
210			    memcpy( reply, zt->zt_name, zt->zt_len );
211			    reply += zt->zt_len;
212			}
213
214			if ( rcnt > 0 ) {
215			    zh.zh_op = ZIPOP_EREPLY;
216			    zh.zh_cnt = zcnt;
217			    memcpy( ziphdr, &zh, sizeof( struct ziphdr ));
218			    if ( sendto( ap->ap_fd, packet, reply - packet, 0,
219				    (struct sockaddr *)from,
220				    sizeof( struct sockaddr_at )) < 0 ) {
221				LOG(log_error, logtype_atalkd, "zip reply sendto: %s",
222					strerror(errno) );
223			    }
224
225			    reply = packet + 3;
226			    rcnt = 0;
227			}
228			continue;
229		    }
230		}
231
232		for ( l = rtmp->rt_zt; l; l = l->l_next, rcnt++ ) {
233		    zt = (struct ziptab *)l->l_data;
234		    memcpy( reply, &firstnet, sizeof( u_short ));
235		    reply += sizeof( u_short );
236		    *reply++ = zt->zt_len;
237		    memcpy( reply, zt->zt_name, zt->zt_len );
238		    reply += zt->zt_len;
239		}
240	    }
241
242	    if ( rcnt > 0 ) {
243		zh.zh_op = ZIPOP_REPLY;
244		zh.zh_cnt = rcnt;
245		memcpy( ziphdr, &zh, sizeof( struct ziphdr ));
246		if ( sendto( ap->ap_fd, packet, reply - packet, 0,
247			(struct sockaddr *)from,
248			sizeof( struct sockaddr_at )) < 0 ) {
249		    LOG(log_error, logtype_atalkd, "zip reply sendto: %s",
250			    strerror(errno) );
251		}
252	    }
253	    break;
254
255	case ZIPOP_REPLY :
256	    for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
257		if (( from->sat_addr.s_net == 0 ||
258			gate->g_sat.sat_addr.s_net == from->sat_addr.s_net ) &&
259			gate->g_sat.sat_addr.s_node == from->sat_addr.s_node ) {
260		    break;
261		}
262	    }
263	    if ( gate == NULL ) {
264		LOG(log_info, logtype_atalkd, "zip reply from non-gateway %u.%u",
265		    ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
266		return 1;
267	    }
268
269	    rtmp = NULL;
270
271	    do {
272		if ( data + sizeof( u_short ) + 1 > end ) {	/* + strlen */
273		    LOG(log_info, logtype_atalkd, "zip reply short (%d)", len );
274		    return 1;
275		}
276		memcpy( &firstnet, data, sizeof( u_short ));
277		data += sizeof( u_short );
278
279		if ( rtmp && rtmp->rt_firstnet != firstnet ) {
280		    /* XXX */
281		    if ( rtmp->rt_gate == NULL &&
282			    zonecheck( rtmp, gate->g_iface ) != 0 ) {
283			LOG(log_error, logtype_atalkd, "zip_packet seed zonelist mismatch" );
284			return -1;
285		    }
286		    rtmp->rt_flags &= ~RTMPTAB_ZIPQUERY;
287		}
288
289		/* Check if this is the interface's route. */
290		if ( firstnet == gate->g_iface->i_rt->rt_firstnet ) {
291		    rtmp = gate->g_iface->i_rt;
292		} else {
293		    for ( rtmp = gate->g_rt; rtmp; rtmp = rtmp->rt_next ) {
294			if ( rtmp->rt_firstnet == firstnet ) {
295			    break;
296			}
297		    }
298
299		    /*
300		     * Update head to this rtmp entry.
301		     */
302		    if ( rtmp != NULL && gate->g_rt != rtmp ) {
303			gate->g_rt->rt_prev->rt_next = gate->g_rt;
304			gate->g_rt = rtmp;
305			rtmp->rt_prev->rt_next = NULL;
306		    }
307		}
308
309		zlen = *data++;
310		if ( zlen > 32 || zlen <= 0 ) {
311		    LOG(log_info, logtype_atalkd, "zip reply bad packet" );
312		    return 1;
313		}
314		if ( data + zlen > end ) {
315		    LOG(log_info, logtype_atalkd, "zip reply short (%d)", len );
316		    return 1;
317		}
318		memcpy( zname, data, zlen );
319		data += zlen;
320
321		/*
322		 * We won't find any rtmp entry if the gateway is no longer
323		 * telling us about the entry.
324		 */
325		if ( rtmp == NULL ) {
326		    LOG(log_info, logtype_atalkd, "zip skip reply %u from %u.%u (no rtmp)",
327			    ntohs( firstnet ), ntohs( from->sat_addr.s_net ),
328			    from->sat_addr.s_node );
329		/*
330		 * Check if the route is still in use (the iprev check is
331		 * no good if rtmp is the interface's route).
332		 */
333		} else if ( rtmp->rt_iprev == NULL && rtmp->rt_prev != NULL ) {
334		    LOG(log_info, logtype_atalkd,
335			    "zip skip reply %u-%u from %u.%u (rtmp not in use)",
336			    ntohs( rtmp->rt_firstnet ),
337			    ntohs( rtmp->rt_lastnet ),
338			    ntohs( from->sat_addr.s_net ),
339			    from->sat_addr.s_node );
340		/*
341		 * Check if we've got an outstanding query for this route.
342		 * We will often get this, since we ask every router on a
343		 * net to verify our interface's zone(s).
344		 */
345		} else if (( rtmp->rt_flags & RTMPTAB_ZIPQUERY ) == 0 ) {
346		    LOG(log_info, logtype_atalkd,
347			    "zip skip reply %u-%u from %u.%u (no query)",
348			    ntohs( rtmp->rt_firstnet ),
349			    ntohs( rtmp->rt_lastnet ),
350			    ntohs( from->sat_addr.s_net ),
351			    from->sat_addr.s_node );
352		} else {
353		    if (addzone( rtmp, zlen, zname ) < 0) {
354		        LOG(log_error, logtype_atalkd, "zip_packet: addzone");
355			return -1;
356		    }
357		    rtmp->rt_flags |= RTMPTAB_HASZONES;
358		}
359	    } while ( data < end );
360
361	    if ( rtmp && rtmp->rt_flags & RTMPTAB_HASZONES ) {
362		/* XXX */
363		if ( rtmp->rt_gate == NULL &&
364			zonecheck( rtmp, gate->g_iface ) != 0 ) {
365		    LOG(log_error, logtype_atalkd, "zip_packet seed zonelist mismatch" );
366		    return -1;
367		}
368		rtmp->rt_flags &= ~RTMPTAB_ZIPQUERY;
369	    }
370	    break;
371
372	case ZIPOP_EREPLY :
373	    for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
374		if (( from->sat_addr.s_net == 0 ||
375			gate->g_sat.sat_addr.s_net == from->sat_addr.s_net ) &&
376			gate->g_sat.sat_addr.s_node == from->sat_addr.s_node ) {
377		    break;
378		}
379	    }
380	    if ( gate == NULL ) {
381		LOG(log_info, logtype_atalkd, "zip ereply from non-gateway %u.%u",
382		    ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
383		return 1;
384	    }
385
386	    /*
387	     * Note that we're not advancing "data" here.  We do that
388	     * at the top of the do-while loop, below.
389	     */
390	    if ( data + sizeof( u_short ) + 1 > end ) {	/* + strlen */
391		LOG(log_info, logtype_atalkd, "zip ereply short (%d)", len );
392		return 1;
393	    }
394	    memcpy( &firstnet, data, sizeof( u_short ));
395
396	    /* Check if this is the interface's route. */
397	    if ( firstnet == gate->g_iface->i_rt->rt_firstnet ) {
398		rtmp = gate->g_iface->i_rt;
399	    } else {
400		for ( rtmp = gate->g_rt; rtmp; rtmp = rtmp->rt_next ) {
401		    if ( rtmp->rt_firstnet == firstnet ) {
402			break;
403		    }
404		}
405		if ( rtmp == NULL ) {
406		    LOG(log_info, logtype_atalkd, "zip ereply %u from %u.%u (no rtmp)",
407			    ntohs( firstnet ), ntohs( from->sat_addr.s_net ),
408			    from->sat_addr.s_node );
409		    return 1;
410		}
411		if ( rtmp->rt_iprev == NULL ) {
412		    LOG(log_info, logtype_atalkd,
413			    "zip ereply %u-%u from %u.%u (rtmp not in use)",
414			    ntohs( rtmp->rt_firstnet ),
415			    ntohs( rtmp->rt_lastnet ),
416			    ntohs( from->sat_addr.s_net ),
417			    from->sat_addr.s_node );
418		}
419
420		/* update head to *next* rtmp entry */
421		if ( rtmp->rt_next != NULL ) {
422		    gate->g_rt->rt_prev->rt_next = gate->g_rt;
423		    gate->g_rt = rtmp->rt_next;
424		    rtmp->rt_next = NULL;
425		}
426	    }
427
428	    if (( rtmp->rt_flags & RTMPTAB_ZIPQUERY ) == 0 ) {
429		LOG(log_info, logtype_atalkd, "zip ereply %u-%u from %u.%u (no query)",
430			ntohs( rtmp->rt_firstnet ),
431			ntohs( rtmp->rt_lastnet ),
432			ntohs( from->sat_addr.s_net ),
433			from->sat_addr.s_node );
434		return 0;
435	    }
436
437	    do {
438		/*
439		 * We copy out firstnet, twice (see above).  Not
440		 * a big deal, and it makes the end condition cleaner.
441		 */
442		if ( data + sizeof( u_short ) + 1 > end ) {	/* + strlen */
443		    LOG(log_info, logtype_atalkd, "zip ereply short (%d)", len );
444		    return 1;
445		}
446		memcpy( &firstnet, data, sizeof( u_short ));
447		data += sizeof( u_short );
448
449		/* check route */
450		if ( firstnet != rtmp->rt_firstnet ) {
451		    LOG(log_info, logtype_atalkd, "zip ereply with multiple nets" );
452		    return 1;
453		}
454
455		zlen = *data++;
456		if ( zlen > 32 || zlen <= 0 ) {
457		    LOG(log_info, logtype_atalkd, "zip ereply bad zone length (%d)", zlen );
458		    return 1;
459		}
460		if ( data + zlen > end ) {
461		    LOG(log_info, logtype_atalkd, "zip ereply short (%d)", len );
462		    return 1;
463		}
464		memcpy( zname, data, zlen );
465		data += zlen;
466		if (addzone( rtmp, zlen, zname ) < 0) {
467		    LOG(log_error, logtype_atalkd, "zip_packet: addzone");
468		    return -1;
469		}
470	    } while ( data < end );
471
472	    if ( rtmp ) {
473		/*
474		 * Count zones for rtmptab entry.
475		 */
476		for ( n = 0, l = rtmp->rt_zt; l; l = l->l_next, n++ )
477		    ;
478		if ( n == zh.zh_count ) {
479		    rtmp->rt_flags |= RTMPTAB_HASZONES;
480		    /* XXX */
481		    if ( rtmp->rt_gate == NULL &&
482			    zonecheck( rtmp, gate->g_iface ) != 0 ) {
483			LOG(log_error, logtype_atalkd, "zip_packet seed zonelist mismatch" );
484			return -1;
485		    }
486		    rtmp->rt_flags &= ~RTMPTAB_ZIPQUERY;
487		}
488	    }
489	    break;
490
491	case ZIPOP_GNI :
492	    /*
493	     * Don't answer with bogus information.
494	     */
495	    if (((iface->i_flags & IFACE_ISROUTER) == 0) ||
496		iface->i_rt->rt_zt == NULL ||
497		( iface->i_flags & IFACE_CONFIG ) == 0 ) {
498		return 0;
499	    }
500
501	    if ( zh.zh_zero != 0 || data + 2 * sizeof( u_short ) > end ) {
502		LOG(log_info, logtype_atalkd, "zip_packet malformed packet" );
503		return 1;
504	    }
505
506	    memcpy( &firstnet, data, sizeof( u_short ));
507	    data += sizeof( u_short );
508	    memcpy( &lastnet, data, sizeof( u_short ));
509	    data += sizeof( u_short );
510	    if ( firstnet != 0 || lastnet != 0 || data >= end ) {
511		LOG(log_info, logtype_atalkd, "zip_packet malformed packet" );
512		return 1;
513	    }
514
515	    zlen = *data++;
516	    if ( zlen < 0 || zlen > 32 ) {
517		LOG(log_info, logtype_atalkd, "zip_packet malformed packet" );
518		return 1;
519	    }
520	    memcpy( zname, data, zlen );
521
522	    data = packet;
523	    end = data + sizeof( packet );
524	    zh.zh_op = ZIPOP_GNIREPLY;
525	    zh.zh_flags = 0;
526
527	    /*
528	     * Skip to the nets.  Fill in header when we're done.
529	     */
530	    data += 1 + sizeof( struct ziphdr );
531	    memcpy( data, &iface->i_rt->rt_firstnet, sizeof( u_short ));
532	    data += sizeof( u_short );
533	    memcpy( data, &iface->i_rt->rt_lastnet, sizeof( u_short ));
534	    data += sizeof( u_short );
535
536	    *data++ = zlen;
537	    memcpy( data, zname, zlen );
538	    data += zlen;
539
540	    /*
541	     * Check if the given zone is valid.  If it's valid, just fill in
542	     * the multicast address.  If it's not, fill the multicast address
543	     * in with the default zone and return the default zone.
544	     */
545	    for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
546		zt = (struct ziptab *)l->l_data;
547		if ( zt->zt_len == zlen &&
548			strndiacasecmp( zname, zt->zt_name, zlen ) == 0 ) {
549		    break;
550		}
551	    }
552	    if ( l == NULL ) {
553		zt = (struct ziptab *)iface->i_rt->rt_zt->l_data;
554		zh.zh_flags |= ZIPGNI_INVALID;
555	    }
556
557	    for ( n = 0, l = iface->i_rt->rt_zt; l; l = l->l_next, n++ )
558		;
559	    if ( n == 1 ) {
560		zh.zh_flags |= ZIPGNI_ONEZONE;
561	    }
562
563	    /* multicast */
564	    *data++ = 6;	/* sizeof ??? */
565	    if (zone_bcast(zt) < 0) {
566	      LOG(log_error, logtype_atalkd, "zip_packet: zone_bcast");
567	      return -1;
568	    }
569	    memcpy(data, zt->zt_bcast, 6);
570	    data += 6;
571
572	    /*
573	     * Add default zone.
574	     */
575	    if ( zh.zh_flags & ZIPGNI_INVALID ) {
576		*data++ = zt->zt_len;
577		memcpy( data, zt->zt_name, zt->zt_len );
578		data += zt->zt_len;
579	    }
580
581	    /* fill in header */
582	    *packet = DDPTYPE_ZIP;
583	    memcpy( packet + 1, &zh, sizeof( struct ziphdr ));
584
585	    /*
586	     * If the address we received this request from isn't correct
587	     * for the net we received it on, send a broadcast.
588	     */
589	    if ( ntohs( from->sat_addr.s_net ) <
590		    ntohs( iface->i_rt->rt_firstnet ) ||
591		    ntohs( from->sat_addr.s_net ) >
592		    ntohs( iface->i_rt->rt_lastnet )) {
593		from->sat_addr.s_net = 0;
594		from->sat_addr.s_node = ATADDR_BCAST;
595	    }
596
597	    if ( sendto( ap->ap_fd, packet, data - packet, 0,
598		    (struct sockaddr *)from,
599		    sizeof( struct sockaddr_at )) < 0 ) {
600		LOG(log_error, logtype_atalkd, "zip gni sendto %u.%u: %s",
601			ntohs( from->sat_addr.s_net ), from->sat_addr.s_node,
602			strerror(errno) );
603		return 1;
604	    }
605	    break;
606
607	case ZIPOP_GNIREPLY :
608	    /*
609	     * Ignore ZIP GNIReplys which are either late or unsolicited.
610	     */
611	    LOG(log_debug, logtype_atalkd, "zip gnireply from %u.%u (%s %x)",
612		    ntohs( from->sat_addr.s_net ), from->sat_addr.s_node,
613		    iface->i_name, iface->i_flags );
614
615	    if (( iface->i_flags & ( IFACE_CONFIG|IFACE_PHASE1 )) ||
616		    ( iface->i_flags & IFACE_ADDR ) == 0 ) {
617		LOG(log_debug, logtype_atalkd, "zip ignoring gnireply" );
618		return 1;
619	    }
620
621	    if ( data + 2 * sizeof( u_short ) > end ) {
622		LOG(log_info, logtype_atalkd, "zip_packet malformed packet" );
623		return 1;
624	    }
625	    memcpy( &firstnet, data, sizeof( u_short ));
626	    data += sizeof( u_short );
627	    memcpy( &lastnet, data, sizeof( u_short ));
628	    data += sizeof( u_short );
629
630	    /*
631	     * We never ask for a zone, so we can get back what the
632	     * default zone is.
633	     */
634	    if ( data >= end || data + *data > end ) {
635		LOG(log_info, logtype_atalkd, "zip_packet malformed packet" );
636		return 1;
637	    }
638	    if ( *data++ != 0 ) {
639		LOG(log_info, logtype_atalkd, "zip_packet unsolicited zone" );
640		return 1;
641	    }
642
643	    /* skip multicast (should really check it) */
644	    if ( data >= end || data + *data > end ) {
645		LOG(log_info, logtype_atalkd, "zip_packet malformed packet" );
646		return 1;
647	    }
648	    data += *data + 1;
649
650	    if ( data >= end || data + *data > end ) {
651		LOG(log_info, logtype_atalkd, "zip_packet malformed packet" );
652		return 1;
653	    }
654
655	    /*
656	     * First, if we're not seed, we always get our zone information
657	     * from the net -- we don't even save what was in the file.
658	     * Second, if we are seed, we keep our zone list in the
659	     * interface structure, not in the zone table.  This allows us
660	     * to check that the net is giving us good zones.
661	     */
662	    if ( (iface->i_flags & IFACE_SEED) && iface->i_czt) {
663		if ( iface->i_czt->zt_len != *data ||
664			strndiacasecmp( iface->i_czt->zt_name,
665			data + 1, *data ) != 0 ) {
666		    LOG(log_error, logtype_atalkd, "default zone mismatch on %s",
667			    iface->i_name );
668		    LOG(log_error, logtype_atalkd, "%.*s != %.*s",
669			    iface->i_czt->zt_len, iface->i_czt->zt_name,
670			    *data, data + 1 );
671		    LOG(log_error, logtype_atalkd, "Seed error! Exiting!" );
672		    return -1;
673		}
674	    }
675
676	    if (addzone( iface->i_rt, *data, data + 1 ) < 0) {
677	        LOG(log_error, logtype_atalkd, "zip_packet: addzone");
678	        return -1;
679	    }
680
681	    /*
682	     * The netrange we received from the router doesn't match the
683	     * range we have locally. This is not a problem, unless we
684	     * have seed information.
685	     */
686	    if ( firstnet != iface->i_rt->rt_firstnet ||
687		    lastnet != iface->i_rt->rt_lastnet ) {
688		if ( iface->i_flags & IFACE_SEED ) {
689		    LOG(log_error, logtype_atalkd, "netrange mismatch on %s",
690			    iface->i_name );
691		    LOG(log_error, logtype_atalkd, "%u-%u != %u-%u",
692			    ntohs( firstnet ), ntohs( lastnet ),
693			    ntohs( iface->i_rt->rt_firstnet ),
694			    ntohs( iface->i_rt->rt_lastnet ));
695		    LOG(log_error, logtype_atalkd, "Seed error! Exiting!" );
696		    return -1;
697		}
698
699
700		/*
701		 * It is possible that we will corrupt our route database
702		 * by just forcing this change.  A better solution would
703		 * be to search all of our current routes, looking for
704		 * this new route, and delete any old versions.  Also, we
705		 * would call rtmp_delete() on the old net range, in case
706		 * there is some other net which actually had that range.  XXX
707		 */
708		iface->i_rt->rt_firstnet = firstnet;
709		iface->i_rt->rt_lastnet = lastnet;
710
711		if ( ntohs( iface->i_addr.sat_addr.s_net ) <
712			ntohs( firstnet ) ||
713			ntohs( iface->i_addr.sat_addr.s_net ) >
714			ntohs( lastnet )) {
715		    iface->i_addr.sat_addr.s_net = 0;	/* ATADDR_ANYNET? */
716		}
717		setaddr( iface, IFACE_PHASE2, iface->i_addr.sat_addr.s_net,
718			iface->i_addr.sat_addr.s_node, firstnet, lastnet );
719		stabletimer = UNSTABLE;
720	    }
721
722	    /* add addr to loopback route */
723	    if ( looproute( iface, RTMP_ADD )) { /* -1 or 1 */
724		LOG(log_error, logtype_atalkd,
725			"zip_packet: can't route %u.%u to loopback: %s",
726			ntohs( iface->i_addr.sat_addr.s_net ),
727			iface->i_addr.sat_addr.s_node,
728			strerror(errno) );
729		return -1;
730	    }
731
732	    LOG(log_info, logtype_atalkd, "zip_packet configured %s from %u.%u",
733		    iface->i_name, ntohs( from->sat_addr.s_net ),
734		    from->sat_addr.s_node );
735	    iface->i_flags |= IFACE_CONFIG;
736	    if ( iface == ciface ) {
737		ciface = ciface->i_next;
738		bootaddr( ciface );
739	    }
740	    break;
741
742	case ZIPOP_NOTIFY :
743#ifdef DEBUG
744	    printf( "zip notify from %u.%u\n", ntohs( from->sat_addr.s_net ),
745		    from->sat_addr.s_node );
746#endif /* DEBUG */
747	    break;
748
749	default :
750	    LOG(log_info, logtype_atalkd, "zip_packet bad zip op from %u.%u",
751		    ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
752	}
753	break;
754
755    case DDPTYPE_ATP :
756	if ( data + sizeof( struct atphdr ) > end ) {
757	    LOG(log_info, logtype_atalkd, "zip atp malformed packet" );
758	    return 1;
759	}
760	memcpy( &ah, data, sizeof( struct atphdr ));
761	data += sizeof( struct atphdr );
762	if ( ah.atphd_ctrlinfo != ATP_TREQ ) {
763	    LOG(log_info, logtype_atalkd, "zip atp bad control" );
764	    return 1;
765	}
766	ah.atphd_ctrlinfo = ATP_TRESP | ATP_EOM;
767	if ( ah.atphd_bitmap != 1 ) {
768	    LOG(log_error, logtype_atalkd, "zip atp bad bitmap" );
769	    return 1;
770	}
771	ah.atphd_bitmap = 0;
772
773	zipop = *data++;
774	data++;
775	memcpy( &index, data, sizeof( u_short ));
776	data += sizeof( u_short );
777	index = ntohs( index );
778	if ( data != end ) {
779	    LOG(log_info, logtype_atalkd, "zip atp malformed packet" );
780	    return 1;
781	}
782
783	data = packet;
784	end = data + sizeof( packet );
785	*data++ = DDPTYPE_ATP;
786	memcpy( data, &ah, sizeof( struct atphdr ));
787	data += sizeof( struct atphdr );
788	lastflag = data++;		/* mark and space for last flag */
789	*data++ = 0;
790	nzones = data;			/* mark and space for zone count */
791	data += sizeof( u_short );
792
793	switch ( zipop ) {
794	case ZIPOP_GETMYZONE :
795	    if ( index != 0 ) {
796		LOG(log_info, logtype_atalkd, "zip atp gmz bad index" );
797		return 1;
798	    }
799
800	    if ( iface->i_flags & IFACE_LOOPBACK ) {
801		iface = interfaces->i_next;	/* first interface */
802	    } else if ( ntohs( iface->i_rt->rt_firstnet ) >
803		    ntohs( from->sat_addr.s_net ) ||
804		    ntohs( iface->i_rt->rt_lastnet ) <
805		    ntohs( from->sat_addr.s_net )) {
806		return 0;
807	    }
808
809	    if ( iface->i_rt->rt_zt == NULL ) {
810		return 0;
811	    }
812	    zt = (struct ziptab *)iface->i_rt->rt_zt->l_data;
813	    if ( data + 1 + zt->zt_len > end ) {
814		LOG(log_info, logtype_atalkd, "zip atp gmz reply too long" );
815		return 1;
816	    }
817	    *data++ = zt->zt_len;
818	    memcpy( data, zt->zt_name, zt->zt_len );
819	    data += zt->zt_len;
820
821	    *lastflag = 0;
822	    nz = 1;
823	    break;
824
825	case ZIPOP_GETZONELIST :
826	    for ( zt = ziptab; zt && ( index > 1 ); zt = zt->zt_next, index-- )
827		;
828	    for ( nz = 0; zt; zt = zt->zt_next, nz++ ) {
829		if ( data + 1 + zt->zt_len > end ) {
830		    break;
831		}
832		*data++ = zt->zt_len;
833		memcpy( data, zt->zt_name, zt->zt_len );
834		data += zt->zt_len;
835	    }
836
837	    *lastflag = ( zt == NULL );		/* Too clever? */
838	    break;
839
840	case ZIPOP_GETLOCALZONES :
841	    if ( iface->i_flags & IFACE_LOOPBACK ) {
842		iface = interfaces->i_next;	/* first interface */
843	    } else if ( ntohs( iface->i_rt->rt_firstnet ) >
844		    ntohs( from->sat_addr.s_net ) ||
845		    ntohs( iface->i_rt->rt_lastnet ) <
846		    ntohs( from->sat_addr.s_net )) {
847		return 0;
848	    }
849
850	    for ( l = iface->i_rt->rt_zt; l && ( index > 1 );
851		    l = l->l_next, index-- )
852		;
853	    for ( nz = 0; l; l = l->l_next, nz++ ) {
854		zt = (struct ziptab *)l->l_data;
855		if ( data + 1 + zt->zt_len > end ) {
856		    break;
857		}
858		*data++ = zt->zt_len;
859		memcpy( data, zt->zt_name, zt->zt_len );
860		data += zt->zt_len;
861	    }
862
863	    *lastflag = ( l == NULL );
864	    break;
865
866	default :
867	    LOG(log_info, logtype_atalkd, "zip atp bad option" );
868	    return 1;
869	}
870
871	/* send reply */
872	if ( nz > 0 ) {
873	    nz = htons( nz );
874	    memcpy( nzones, &nz, sizeof( u_short ));
875	    if ( sendto( ap->ap_fd, packet, data - packet, 0,
876		    (struct sockaddr *)from,
877		    sizeof( struct sockaddr_at )) < 0 ) {
878		LOG(log_error, logtype_atalkd, "zip atp sendto %u.%u: %s",
879			ntohs( from->sat_addr.s_net ), from->sat_addr.s_node,
880			strerror(errno) );
881		return 1;
882	    }
883	}
884	break;
885
886    default :
887	LOG(log_info, logtype_atalkd, "zip_packet bad ddp type from %u.%u",
888		ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
889	return 1;
890    }
891
892    return 0;
893}
894
895int zip_getnetinfo(struct interface *iface)
896{
897    struct atport	*ap;
898    struct ziphdr	zh;
899    struct sockaddr_at	sat;
900    char		*data, packet[ 40 ];
901    u_short		net;
902
903    LOG(log_info, logtype_atalkd, "zip_getnetinfo for %s", iface->i_name );
904
905    for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
906	if ( ap->ap_packet == zip_packet ) {
907	    break;
908	}
909    }
910    if ( ap == NULL ) {
911	LOG(log_error, logtype_atalkd, "zip_getnetinfo can't find zip socket!" );
912	return -1;
913    }
914
915    data = packet;
916
917    *data++ = DDPTYPE_ZIP;
918
919    zh.zh_op = ZIPOP_GNI;
920    zh.zh_zero = 0;
921    memcpy( data, &zh, sizeof( struct ziphdr ));
922    data += sizeof( struct ziphdr );
923    net = 0;
924    memcpy( data, &net, sizeof( u_short ));
925    data += sizeof( u_short );
926    memcpy( data, &net, sizeof( u_short ));
927    data += sizeof( u_short );
928
929    /*
930     * Set our requesting zone to NULL, so the response will contain
931     * the default zone.
932     */
933    *data++ = 0;
934
935#ifdef BSD4_4
936    sat.sat_len = sizeof( struct sockaddr_at );
937#endif /* BSD4_4 */
938    sat.sat_family = AF_APPLETALK;
939    sat.sat_addr.s_net = 0;
940    sat.sat_addr.s_node = ATADDR_BCAST;
941    sat.sat_port = ap->ap_port;
942
943    if ( sendto( ap->ap_fd, packet, data - packet, 0, (struct sockaddr *)&sat,
944	    sizeof( struct sockaddr_at )) < 0 ) {
945	LOG(log_error, logtype_atalkd, "zip_getnetinfo sendto: %s", strerror(errno) );
946	return -1;
947    }
948    return 0;
949}
950
951struct ziptab *newzt(const int len, const char *name)
952{
953    struct ziptab	*zt;
954
955    if (( zt = (struct ziptab *)calloc(1, sizeof( struct ziptab ))) == NULL ) {
956	return( NULL );
957    }
958
959    zt->zt_len = len;
960    if (( zt->zt_name = (char *)malloc( len )) == NULL ) {
961	free(zt);
962	return( NULL );
963    }
964
965    memcpy( zt->zt_name, name, len );
966    return( zt );
967}
968
969
970/*
971 * Insert at the end.  Return 1 if a mapping already exists, 0 otherwise.
972 * -1 on error.
973 */
974static int add_list(struct list **head, void *data)
975{
976    struct list	*l, *l2;
977
978    for ( l = *head; l; l = l->l_next ) {
979	if ( l->l_data == data ) {
980	    return( 1 );
981	}
982    }
983    if (( l = (struct list *)malloc( sizeof( struct list ))) == NULL ) {
984	LOG(log_error, logtype_atalkd, "add_list malloc: %s", strerror(errno) );
985	return -1;
986    }
987
988    l->l_data = data;
989    l->l_next = NULL;
990    if ( *head == NULL ) {
991	l->l_prev = NULL;
992	*head = l;
993    } else {
994	/* find end of list */
995	for ( l2 = *head; l2->l_next; l2 = l2->l_next )
996	    ;
997	l->l_prev = l2;
998	l2->l_next = l;
999    }
1000    return( 0 );
1001}
1002
1003int addzone(struct rtmptab *rt, int len, char *zone)
1004{
1005    struct ziptab	*zt;
1006    int			cc, exists = 0;
1007
1008    for ( zt = ziptab; zt; zt = zt->zt_next ) {
1009	if ( zt->zt_len == len &&
1010		strndiacasecmp( zt->zt_name, zone, len ) == 0 ) {
1011	    break;
1012	}
1013    }
1014    if ( zt == NULL ) {
1015	if (( zt = newzt( len, zone )) == NULL ) {
1016	    LOG(log_error, logtype_atalkd, "addzone newzt: %s", strerror(errno) );
1017	    return -1;
1018	}
1019	if ( ziptab == NULL ) {
1020	    zt->zt_prev = NULL;
1021	    ziptab = zt;
1022	} else {
1023	    zt->zt_prev = ziplast;
1024	    ziplast->zt_next = zt;
1025	}
1026	ziplast = zt;
1027    }
1028
1029    if ((cc = add_list( &zt->zt_rt, rt )) < 0)
1030      return -1;
1031
1032    if (cc)
1033      exists++;
1034
1035    if ((cc = add_list( &rt->rt_zt, zt )) < 0 )
1036      return -1;
1037
1038    if (cc) {
1039        if ( !exists ) {
1040	    LOG(log_error, logtype_atalkd, "addzone corrupted route/zone mapping" );
1041	    return -1;
1042	}
1043	/*
1044	 * We get the repeat for local nets which have zone information
1045	 * already: we ask anyway, just to make sure.
1046	 */
1047
1048	return 0;
1049    }
1050    if ( exists ) {
1051	LOG(log_error, logtype_atalkd, "addzone corrupted zone/route mapping" );
1052	return -1;
1053    }
1054    return 0;
1055}
1056