--- /dev/null
+Please use this format when reporting bugs to netatalk@umich.edu.
+Assuming that you've not supplied a fix, our goal will be to reproduce
+your problem, here. If we are missing information necessary to do
+that, we may need to ask you to provide it, thus lengthening the
+turn-around time for a fix.
+
+Subject: Short summary of the problem (please make this meaningful!)
+
+Environment:
+ The version of netatalk you're running, any changes you've made
+ to the configuration, e.g. AFS, what machine you're running
+ netatalk on, and the version of the operating system. If Macs
+ are involved, the types of Mac, how they are connected and what
+ system software they are running. If printers are involved,
+ the type of printer and how they are connected. Any other
+ pertinent information.
+
+Description:
+ Detailed description of the problem, suggestion, or complaint.
+
+Repeat-By:
+ The sequence of events that causes the problem to occur.
+
+Fix:
+ Description of how to fix the problem. If you don't know a fix
+ for the problem, don't include this section.
--- /dev/null
+Changes from the 1.4b1 release:
+
+ Fixed the maximum free/total volume size in afpd.
+
+ Made ~ the default volume in afpd.
+
+ Fixed pid file handling and changed setpgrp() to setpgid() in afpd,
+ papd, and atalkd.
+
+ Added code to afpd to set the Unix file time stamps with utime().
+
+ Fixed a bug in papd's printcap code which limited it to 15 or so
+ printers.
+
+ Fixed papd's handling of piped printers.
+
+ Fixed papd's handling of bad job names.
+
+ Fixed atalkd to send NBP LKUP packets from NBP port.
+
+ Added "sync;sync;sync" to Solaris kinstall to help with streams
+ file corruption.
+
+ Added nlocalrts to streams ddp.conf. Thanks Thomas Tornblom.
+
+ Fixed signed extension infinite loop in Solaris module.
+
+ Moved all the config files to .../config.
+
+Changes from the 1.3.3 release:
+
+ Added code from Sun Microsystems, Inc (OPCOM) for Solaris support.
+ See COPYRIGHT.
+
+ Added support for FreeBSD, mostly changes by Mark Dawson and Julian
+ Elischer.
+
+ All sorts of other stuff.
+
+Changes from the 1.3.1 release:
+
+ Added options to psf's filter names to support accounting on HPs.
+ !!! NOTE: The location of the filters has changed, see the man
+ page for where.
+
+ Added code from Alan Cox to support Linux.
+
+ Rewrote papd. Now handles dropped connections better.
+ Configuration has been modernized. !!! NOTE: The format of the
+ configuration file has changed, but NOT THE NAME.
+
+ Added Kerberos support to papd.
+
+ atalkd now removes routes on a SIGTERM. Still can't just restart
+ it, but it's closer.
+
+ Changed atalkd and the kernel driver to remove a hack added to
+ support sending to 0.255. Now the kernel will allow multiple open
+ sockets with the same port, so long as the addess associated with
+ the port is different. atalkd now opens a socket for each port on
+ each interface.
+
+ atalkd now rewrites its configuration file. If no configuration
+ file is given, one will be generated. Permissions on the new
+ configuration file will be inherited from the old one. If there is
+ no old one, permissions default to 644. Won't rewrite the file if
+ the owner doesn't have write permission.
+
+ Removed support for the "AFS Kerberos UAM", in favor of the
+ "AuthMan UAM". Kerberos support should now be much more
+ straight-forward.
+
+ Fixed a bug in afpd which would cause incorrect group calculations
+ on ultrix machines.
+
+ Fixed a bug in afpd which causes SimpleText and some other
+ applications to silently fail to write. There's also a bug in
+ MacOS, but we can't fix that.
+
+ Fixed a bad interaction with afpd and AFS which would cause file
+ writes to not propogate between AFS clients.
+
+ !!! CHANGED the name(s) of afpd's config files. The new files are
+ AppleVolumes.system and AppleVolumes.default. If AppleVolumes.system
+ exists, it is always read, AppleVolumes.default is only read if the
+ user has no AppleVolumes file. Included a flag "-u" to indicate
+ which file has precedence. "-u" user wins, otherwise ".system"
+ wins.
+
+ Rewrote the AppleVolumes parsing code. Now works.
+
+ Added a filename extension mapping to afpd. User always takes
+ precedence, regardless of the "-u" flag. Code to change the type
+ of all Unix files contributed by Kee Hinckley <nazgul@utopia.com>.
+
+ afpd now supports both UFS and AFS volumes simultaneously. It also
+ uses access() to attempt to calculate reasonable Mac permissions
+ for AFS directories.
+
+ Changed reporting of file times. Files that are written from Unix
+ now update the Mac's idea of the files modification time. Unix
+ mtime is now reported instead of ctime.
+
+ Added support for a new UAM to afpd. This requires that client
+ Macs have MacTCP and AuthMan installed. Should make running afpd
+ for AFS easier.
+
+ Removed code so that otherwise valid volumes for which the mounting
+ user has no permission will appear in the volume selection dialog
+ on the Mac gray-ed out.
+
+ Added code from Chris Metcalf of MIT to the AppleDouble library
+ which improves permission inheritance.
+
+ Added code from G. Paul Ziemba of Alantec, Inc to better report
+ errors in psf. Also changed psf to use syslog for errors that
+ users aren't interested in.
+
+ Added information to psf's man page to better explain the
+ interaction between psf, pap, and lpd.
+
+ Make psf/pap/psa do accounting when it's turnes on in
+ /etc/printcap.
+
+ Changed pap's error message when there is no printer specified on
+ the command line and no .paprc is found. Also heavily modified
+ pap's man page to reflect changes in the "new" version of pap,
+ including moving it from section 8 to section 1.
+
+ Fixed a byte-order bug in pap's sequence numbers. Doubt if pap has
+ ever worked right on little endian machines!
+
+ Added a flag to pap to optionally close before receiving EOF from
+ the printer. Off by default. psf calls pap with this option on.
+
+ Added timeouts to the nbp library calls. This means that processes
+ won't hang when atalkd dies during boot, thus hanging your
+ machine.
+
+Changes from the 1.3 release:
+
+ Fixed a bug in afpd which would cause APPL mappings to contain both
+ mac and unix path names. The fixed code will handle the old
+ (corrupted) database.
+
+ Fixed a *very* serious bug which would cause files to be corrupted
+ when copying to afpd.
+
+ Fixed a bug in afpd which would cause replies to icon writes to
+ contain the written icon.
+
+ Filled in the function code switch in afpd. Previously, a hacker
+ could probably have used afpd to get unauthorized access to a
+ machine running afpd.
+
+ Fixed a bug in the asp portion of libatalk.a which could cause the
+ malloc()/free() database to be corrupted.
+
+ Fixed a bug in atalkd's zip query code. With this bug, only the
+ first N % 255 nets get queried. However, since nets bigger than
+ 255 are usually pretty unstable, the unqueried for nets will
+ eventually get done, when N drops by one.
+
+ Suppressed a spurious error ("route: No such process") in atalkd.
+
+Changes from the 1.2.1 release:
+
+ atalkd is completely rewritten for phase 2 support. atalkd.conf
+ from previous version will not work!
+
+ afpd now has better AFS support. In particular, the configuration
+ for AFS was made much easier; a number of Kerberos-related
+ byte-ordering and time problems were found; clear-text passwords
+ were added (thanks to geeb@umich.edu).
+
+ afpd now handles Unix permissions much better (thanks to
+ metcalf@mit.edu).
+
+ There are many, many more changes, but most are small bug fixes.
+
+Changes from the 1.2 release:
+
+ The Sun support now uses loadable kernel modules (a la VDDRV)
+ instead of binary patches. As such, it should work on any sunos
+ greater than 4.1, and is confirmed to work under 4.1.1 and 4.1.2.
+
+ The DEC support no longer requires source. It also runs under
+ ultrix 4.1 and 4.2. It still requires patching your kernel, but the
+ patches are limited to those files available to binary-only sites
+ -- primarily hooks for things like netatalk.
+
+ The etc.rc script now uses changes made to nbprgstr (see below).
+
+ aecho now takes machine names on the command line.
+
+ nbplkup now takes a command line argument specifying the number of
+ responses to accept. It also takes its defaults from the NBPLKUP
+ environment variable.
+
+ nbprgstr may be used to register a name at any requested port.
+
+ afpd now logs if an illegal shell is used during login, instead of
+ silently denying service.
+
+ A bug in afpd which caused position information for the directory
+ children of the root of a volume to be ignored has been fixed.
+
+ Several typos in afpd which would cause include files necessary to
+ ultrix to be skipped have been fixed.
+
+ atalkd will no long propagate routes to networks whose zone
+ it doesn't know.
+
+ atalkd no longer dumps core if it receives a ZIP GetMyZone request
+ from a network whose zone it doesn't know. (Since this currently
+ can only happen from off net, it's not precisely a legal request.)
+
+ pap and papd (optionally) no longer check the connection id in PAP
+ DATA responses. Both also maintain the function code in non-first-packet
+ PAP DATA responses. These changes are work-arounds to deal with
+ certain AppleTalk printer cards, notably the BridgePort LocalTalk
+ card for HP LJIIISIs.
+
+ pap no longer sends an EOF response to each PAP SENDDATA request,
+ only the first.
+
+ A bug in papd which would cause it to return a random value when
+ printing the procset to a piped printer has been fixed.
+
+ A bug relating to NBP on reverse-endian machines has been fixed.
+
+ atp_rsel() from libatalk now returns a correct value even if it
+ hasn't recieved anything yet.
+
+ atalk_addr() from libatalk no longer accepts addresses in octal
+ format, since AppleTalk addresses can have leading zeros. Also it
+ checks that the separator character is a '.'.
+
+ Pseudo man pages for nbplkup, nbprgstr, and nbpunrgstr, have been
+ added.
+
+ The example in the psf(8) man page is now correct.
+
+ The man pages for changed commands have been updated.
+
+ The README files for various machine have been updated
+ appropriately.
--- /dev/null
+GENERAL:
+-------
+Name: Wesley Craig
+Email: wesley.craig@umich.edu
+
+Name: Adrian Sun
+Email: asun@cobaltnet.com
+
+
+MISCELLANEOUS PATCHES:
+---------------
+Name: Steve Hirsch
+What: ProDOS II support/initial Randnum support
+
+Name: Robert Marinchick
+What: initial rquota patch
+
+Name: Frank Morton
+What: megatron information patch
+
+TESTING:
+-------
+Name: Tim Carlson
+What: Solaris
+
+
+DISTRIBUTIONS:
+-------------
+Name: iNOUE Koichi
+What: RPM for RedHat systems
+
+Name: Stefan Bethke
+What: FreeBSD
+
+ADVICE:
+------
+Name: Leland Wallace
--- /dev/null
+Copyright (c) 1990,1996 Regents of The University of Michigan.
+All Rights Reserved.
+
+ Permission to use, copy, modify, and distribute this software and
+ its documentation for any purpose and without fee is hereby granted,
+ provided that the above copyright notice appears in all copies and
+ that both that copyright notice and this permission notice appear
+ in supporting documentation, and that the name of The University
+ of Michigan not be used in advertising or publicity pertaining to
+ distribution of the software without specific, written prior
+ permission. This software is supplied as is without expressed or
+ implied warranties of any kind.
+
+This product includes software developed by the University of
+California, Berkeley and its contributors.
+
+Solaris code is encumbered by the following:
+ Copyright (C) 1996 by Sun Microsystems Computer Co.
+
+ Permission to use, copy, modify, and distribute this software and
+ its documentation for any purpose and without fee is hereby
+ granted, provided that the above copyright notice appear in all
+ copies and that both that copyright notice and this permission
+ notice appear in supporting documentation. This software is
+ provided "as is" without express or implied warranty.
+
+Modifications for Appleshare IP and other files copyrighted by Adrian
+Sun are under the following copyright:
+
+ Copyright (c) 1997,1998,1999,2000 Adrian Sun (asun@cobalt.com)
+ All Rights Reserved.
+
+ Permission to use, copy, modify, and distribute this software and
+ its documentation for any purpose and without fee is hereby granted,
+ provided that the above copyright notice appears in all copies and
+ that both that copyright notice and this permission notice appear
+ in supporting documentation. This software is supplied as is
+ without expressed or implied warranties of any kind.
+
+Research Systems Unix Group
+The University of Michigan
+c/o Wesley Craig
+535 W. William Street
+Ann Arbor, Michigan
++1-313-764-2278
+netatalk@umich.edu
--- /dev/null
+2000-02-28 a sun <asun@asun.cobalt.com>
+
+ * etc/afpd/directory.h (CNID_INODE): xor the inode a little
+ differently.
+
+2000-02-23 a sun <asun@asun.cobalt.com>
+
+ * etc/afpd/volume.c (creatvol): / is a special case. you can't
+ share it unless you give it a name.
+
+2000-02-21 a sun <asun@asun.cobalt.com>
+
+ * distrib/initscripts/rc.atalk.redhat/cobalt: added changes to
+ make redhat 6.x happier.
+
+2000-02-17 a sun <asun@asun.cobalt.com>
+
+ * libatalk/adouble/ad_lock.c (adf_unlock): off-by-one error with
+ lock removal. this + the log right below fix ragtime.
+
+2000-02-16 a sun <asun@asun.cobalt.com>
+
+ * etc/afpd/fork.c (afp_bytelock): only error on bytelocks
+ positioned at 0x7FFFFFFF if there's no resource fork.
+
+2000-02-14 a sun <asun@asun.cobalt.com>
+
+ * libatalk/adouble/ad_lock.c: re-wrote locking bits so that
+ allocations happen in blocks. added missing case that omnis
+ database triggers.
+
+2000-02-07 a sun <asun@asun.cobalt.com>
+
+ * bin/nbp/Makefile (install): make nbprgstr/nbpunrgstr with 700
+ permissions.
+
+ * include/atalk/adouble.h (sendfile): change to deal with
+ <sys/sendfile.h>
+
+2000-01-25 a sun <asun@asun.cobalt.com>
+
+ * etc/afpd/ofork.c: keep track of oforks being used for each
+ directory so that we can update them if the directory tree gets
+ modified.
+
+ * etc/afpd/directory.c (deletecurdir): remove dangling symlinks on
+ delete.
+
+2000-01-24 a sun <asun@asun.cobalt.com>
+
+ * etc/afpd/directory.h (CNID): moved cnid assignment here along
+ with helpful macros.
+
+ * etc/afpd/directory.c: changed directory search to use red-black
+ trees to improve balance. parent-child tree changed to circular
+ doubly-linked list to speed up insert/remove times. there's still
+ one obstacle to actually freeing red-black tree entries. i need to
+ add an ofork list to struct dir to minimize search times.
+
+2000-01-18 a sun <asun@asun.cobalt.com>
+
+ * etc/afpd/directory.c (dirinsert): detect attempts to add
+ pre-existing entries as just symbolic links.
+
+ * etc/afpd/filedir.h (CNID): moved inode-cnid assignment here and
+ extended to directories.
+
+2000-01-03 a sun <asun@asun.cobalt.com>
+
+ * etc/uams/uams_pam.c (PAM_conv): surround PAM_BINARY_PROMPT with
+ an #ifdef.
+
+ * etc/afpd/status.c (status_init): fixed a bunch of problems here
+ that manifested under solaris 7.
+
+ * etc/afpd/main.c (main): use FD_SETSIZE instead of FD_SETSIZE +
+ 1.
+
+
+1999-12-27 a sun <asun@asun.cobalt.com>
+
+ * libatalk/util/getiface.c: moved interface detection code to here
+ so that i can use if_nameindex() or getifconf() depending upon
+ what's available.
+
+1999-12-13 a sun <asun@asun.cobalt.com>
+
+ * libatalk/dsi/dsi_tcp.c (dsi_tcp_init): added if_nameindex()
+ based interface code.
+
+ * etc/afpd/afp_options.c (afp_options_parseline): added
+ -server_quantum as an option. using hex would be a good idea.
+
+ * libatalk/dsi/dsi_opensess.c (dsi_opensession): added bits to set
+ the server quantum. by default, the server quantum is limited to
+ 1MB due to a bug in the os 9 appleshare client.
+
+ * distrib/initscripts/rc.atalk.{cobalt,redhat}: surround nbp stuff
+ with double quotes.
+
+ * etc/uams/uams_dhx_pam.c (pam_changepw): added dhx-based password
+ changing for pam.
+
+1999-12-06 a sun <asun@asun.cobalt.com>
+
+ * etc/afpd/directory.c (setdirparams): don't error if we can't set
+ the desktop owner/permisssions.
+
+1999-11-04 a sun <asun@asun.cobaltnet.com>
+
+ * etc/afpd/fork.c (afp_openfork): had the ordering wrong on an
+ openfork.
+
+1999-11-02 a sun <asun@asun.cobaltnet.com>
+
+ * etc/afpd/afp_dsi.c (afp_over_dsi): flush data for unknown dsi
+ commands.
+
+1999-10-28 a sun <asun@asun.cobaltnet.com>
+
+ * etc/uams/*.c: return FPError_PARAM if the user is unknown.
+
+1999-10-27 a sun <asun@asun.cobaltnet.com>
+
+ * etc/afpd/fork.c (afp_read): if sendfile isn't supported, use the
+ old looping method.
+
+1999-10-25 a sun <asun@asun.cobaltnet.com>
+
+ * libatalk/nbp/nbp_unrgstr.c (nbp_unrgstr): fix nbp unregisters.
+
+1999-10-21 a sun <asun@asun.cobaltnet.com>
+
+ * etc/afpd/Makefile (install): moved install of afpd earlier per
+ suggestion by steven michaud.
+
+1999-10-05 a sun <asun@asun.cobaltnet.com>
+
+ * etc/uams/uams_randnum.c (afppasswd): for ~/.passwd's, turn
+ ourselves into the user so that nfs is happy.
+
+1999-09-19 a sun <asun@adrian5>
+
+ * libatalk/netddp/netddp_open.c, nbp/*.c: only use the bcast stuff
+ if it's on an os x server machine.
+
+1999-09-15 a sun <asun@adrian5>
+
+ * libatalk/nbp/nbp_unrgstr.c,nbp_lkup.c,nbp_rgstr.c: os x server
+ wants ATADDR_BCAST. that probably means that i need to do
+ multihoming appletalk a little differently. bleah.
+
+1999-09-09 <asun@asun.cobaltnet.com>
+
+ * etc/afpd/directory.c (getdirparams), libatalk/adouble/ad_open.c
+ (ad_open): mondo lameness. i forgot that directory lookups can be
+ done with "." as the directory name. that was auto-hiding
+ them. bleah. i also figured out which bit was the invisible bit
+ for finderinfo information.
+
+1999-09-06 Adrian Sun <asun@glazed.cobaltnet.com>
+
+ * etc/afpd/desktop.c (mtoupath): fixed a bug in codepage support
+ that accidentally crept in.
+
+1999-08-31 Adrian Sun <asun@glazed.cobaltnet.com>
+
+ * etc/afpd/quota.c (getfsquota): use group quotas in quota
+ calculations if the user belongs to a single group. just use the
+ user quotas if the user belongs to multiple groups.
+
+ * etc/afpd/volume.c (getvolspace): added an options:limitsize to
+ restrict the available space to 2GB. this is for macs running
+ older versions of the operating system with newer versions of the
+ appleshare client. weird huh?
+
+ * etc/afpd/quota.c (uquota_getvolspace): bleah. 64-bit shifts
+ don't get promoted in the same way as arithmetic operations. added
+ some more casts to deal with that issue.
+
+1999-08-24 Adrian Sun <asun@glazed.cobaltnet.com>
+
+ * man/man?/Makefile: don't re-build .tmp files if they already
+ exist. this gets the install phase to work correctly.
+
+1999-08-13 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/directory.c, file.c, filedir.c: illegal characters get
+ AFPERR_PARAM. also, reject names with /'s in them if the nohex
+ option is used.
+
+1999-08-12 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/filedir.c,file.c,directory.c: changed error for
+ illegal filenames to AFPERR_EXIST.
+
+1999-08-11 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/desktop.h (validupath): if usedots is set, .Apple* and
+ .Parent files are no longer valid file names.
+
+ * etc/afpd/volume.c (volset): added usedots and nohex as
+ options. usedots stops :hex translation of . files while nohex
+ stops :hex translation of everything but . files. in addition,
+ . files created on the unix side are by default hidden.
+
+ * libatalk/adouble/ad_open.c: initialize more bits.
+
+1999-08-10 a sun <asun@hecate.darksunrising.blah>
+
+ * distrib/initscripts/rc.atalk.redhat (WORKSTATION): use the
+ actual name for nbp registration rather than ATALK_NAME.
+
+ * sys/solaris/Makefile (kernel): make sure osdefs and machinedefs
+ get used when building the kernel module.
+
+ * sys/solaris: changed strings.h to string.h
+
+1999-08-08 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/volume.c (readvolfile): changed volume options into an
+ array of structs to ease maintenance.
+
+1999-08-05 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/status.c (status_init): change the default icon
+ depending upon whether or not it's an ASIP or an AppleTalk
+ connection.
+
+1999-08-04 Adrian Sun <asun@glazed.cobaltnet.com>
+
+ * etc/atalkd/main.c (setaddr): made a failure with setaddr a
+ little more informative.
+
+1999-08-03 Adrian Sun <asun@glazed.cobaltnet.com>
+
+ * yippee. someone figured what was happening with the installation
+ of the man pages. i got rid of a duplicate entry.
+
+1999-08-02 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/volume.c (readvolfile): added a per-file way of setting
+ default options. it keys in on a :DEFAULT: label.
+
+1999-07-30 a sun <asun@hecate.darksunrising.blah>
+
+ * moved rc.atalk.* scripts to distrib/initscripts.
+
+1999-07-27 a sun <asun@hecate.darksunrising.blah>
+
+ * contrib/printing: added patch from job@uchicago.edu
+
+ * etc/afpd/file.c: forgot to initialize struct ad in
+ some places.
+
+ * etc/afpd/nls/makecode.c: added an empty mapping.
+
+ * etc/psf/Makefile (install): well cp -d didn't work either. just
+ use tar.
+
+1999-07-26 a sun <asun@hecate.darksunrising.blah>
+
+ * sys/solaris/tpi.c (tpi_attach): changed DDI_NT_NET to DDI_PSEUDO
+ (from denny@geekworld.com).
+
+ * distrib/rpm/netatalk-asun.spec (Summary): incorporated new spec
+ and patch files from inoue.
+
+ * sys/linux/Makefile (install-sysv): fixed up a bit.
+
+ * etc/psf/Makefile (install): use cp -d instead of cp -a to make
+ *bsd happier.
+
+ * etc/afpd/afp_options.c (afp_options_parseline): reversed meaning
+ of -icon. now it means to use the yucky bitmap instead of the
+ apple icon.
+
+ * bin/afppasswd/Makefile (all): add -Iinclude/openssl for
+ afppasswd as well.
+
+1999-07-18 a sun <asun@hecate.darksunrising.blah>
+
+ * create links/mangle files in the compile rather than the install
+ phase so that rpm will be happier.
+
+1999-07-17 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/file.c (afp_createfile), directory (afp_createdir),
+ filedir.c (afp_rename, afp_moveandrename): don't allow the
+ creation/renaming of names with certain characters if mswindows
+ compatibility is enabled.
+
+1999-07-16 a sun <asun@hecate.darksunrising.blah>
+
+ * rc.atalk.redhat: incorporated chkconfig from inoue.
+
+1999-07-15 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/atalkd/config.c (getifconf): wrap check against
+ IFF_MULTICAST behind an #ifdef IFF_MULTICAST.
+
+ * sys/netbsd/Makefile (LDSHAREDFLAGS): key in on machine type.
+
+1999-07-11 a sun <asun@hecate.darksunrising.blah>
+
+ * contrib/ICDumpSuffixMap: added internet config perl script from
+ inoue.
+
+ * contrib/printing: added contributed solaris printing scripts
+ from job@uchicago.edu.
+
+1999-07-10 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/atalkd/interface.h, rtmp.h: prototyped functions.
+
+ * etc/atalkd/zip.c: converted bcopy's to memcpy's.
+
+ * etc/atalkd/nbp.c,rtmp.c: added checks for the interface for
+ dontroute cases.
+
+ * etc/atalkd/main.c: converted bzero/bcopy to memset/memcpy.
+
+1999-07-08 a sun <asun@hecate.darksunrising.blah>
+
+ * libatalk/nbp/nbp_rgstr.c (nbp_rgstr): return EADDRINUSE if the
+ address already exists.
+
+1999-07-06 a sun <asun@hecate.darksunrising.blah>
+
+ * rc.atalk.redhat: changed netatalk.config to netatalk.conf
+
+1999-07-05 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/atalkd/nbp.c (nbp_packet): add interface to nbp struct. this
+ is so that we can filter by interface in the future. however, it
+ doesn't seem to work that well right now. bleah.
+
+ * etc/atalkd/main.c: fixed up dontroute option so that it doesn't
+ screw up atalkd.conf. also, we need to do a bootaddr if dontroute
+ is set.
+
+ * libatalk/atp,nbp,netddp; bin/aecho,nbp,getzones,pap;
+ etc/papd,afpd: accept -A <ddp address> as an option so that you
+ can specify the address to be used on a multihomed server. for
+ papd, you use the 'pa' option in papd.conf.
+
+1999-07-04 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/atalkd/config.c (parseline): initialize parseline properly
+ so that we don't get extraneous junk.
+
+ * etc/afpd/afp_options.c (afp_options_parseline): do
+ gethostbyaddr/gethostbyname's for -ipaddr and -fqdn.
+
+ * etc/atalkd/config.c (getifconf/readconf): check to see if the
+ supported device can support appletalk addresses. either continue
+ or exit depending upon whether or not it's auto-configed.
+
+1999-07-03 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/afp_options.c (afp_options_parse): -I (-[no]icon) will
+ toggle the volume icon so that it uses the apple icon instead.
+
+ * etc/afpd/config.c (AFPConfigInit): added more logic for the
+ -proxy option. here are the rules: -proxy will always try to
+ create a DDP server instance. by default, the proxy server will
+ still allow you to login with an appletalk connection. to prevent
+ that, just set the uamlist to an empty string.
+
+1999-07-02 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/status.c (status_netaddress): added support for fqdn
+ (not available in the appleshare client yet).
+
+1999-07-01 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/config.c (DSIConfigInit): application code for proxy
+ setup. it's the -proxy option.
+
+ * libatalk/dsi/dsi_init/tcp.c (dsi_init/dsi_tcp_init): added
+ support for proxy setup.
+
+1999-06-30 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/filedir.c (afp_rename): fixed up some error
+ codes. quark express should be happier.
+
+ * etc/afpd/uam.c (uam_afpserver_option): added
+ UAM_OPTION_HOSTNAME. use this to set PAM_RHOST. i just got a
+ report that setting that fixes pam on solaris machines.
+
+1999-06-28 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/ofork.c (of_alloc): report out of forks in syslog..
+
+ * etc/afpd/enumerate.c (afp_enumerate): close an opendir leak.
+
+ * include/atalk/{dsi,asp}.h: make cmdlen and datalen ints.
+
+ * etc/afpd/fork.c (afp_write): fixed up error condition.
+
+1999-06-26 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/uams/Makefile (install): changed install location of uams.
+
+ * sys/linux/Makefile (install-sysv): always install redhat
+ script. netatalk.config script only gets installed if it's not
+ there already.
+
+1999-06-23 a sun <asun@hecate.darksunrising.blah>
+
+ * rc.atalk.redhat: merged in redhat contrib rpm rc.atalk script.
+
+ * etc/afpd/afp_options.c (afp_options_init): changed default
+ maxusers to 20.
+
+1999-06-22 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/config.c (DSIConfigInit): truncate options->server to
+ just the server name here.
+
+ * etc/afpd/volume.c (volxlate): made $s return something
+ meaningful no matter what.
+
+ * libatalk/adouble/ad_sendfile.c (ad_readfile): freebsd sendfile
+ wants an off_t.
+
+1999-06-20 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/volume.c (volxlate): added variable substitution. if it
+ doesn't understand the variable, it just spits it back out.
+
+ (creatvol): display truncated volume name if it's too long.
+
+ * sys/{generic,solaris}/Makefile: added NO_CRYPTLIB option to deal
+ with oses that have -lcrypt but shouldn't use it.
+
+1999-06-11 a sun <asun@hecate.darksunrising.blah>
+
+ * include/atalk/afp.h: added comments to FPErrors.
+
+ * etc/afpd/enumerate.c (afp_enumerate): make FPEnumerate do some
+ more error checking.
+
+ * include/atalk/util.h: server_lock() returns pid_t.
+
+1999-06-10 a sun <asun@hecate.darksunrising.blah>
+
+ * README.ASUN: added location for both ssleay and openssl.
+
+ * etc/uams: moved install to LIBDIR/uams. "uams_*" now means "uam
+ server." in the future, there will be "uamc_*." changed the shared
+ library names to match.
+
+ * include/atalk/atp.h,nbp.h: forgot to include <sys/cdefs.h>
+
+ * etc/uams/Makefile: openssl-0.9.3c uses <openssl/*.h> so add that
+ to the include path.
+
+ * sys/{solaris,ultrix}/Makefile: just use -I../sys/generic instead
+ of doing a link.
+
+ * include/atalk/uam.h, etc/uams/uam_*.c, etc/afpd/uam.c: added uam
+ type field. do type check.
+
+ * etc/uams/uam_*pam.c: added a couple more error codes.
+
+1999-06-08 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/nls/Makefile (codepage.h): make sure that a link to
+ codepage.h gets made.
+
+ * libatalk/*/Makefile: make sure that the profiled directory gets
+ created.
+
+ * etc/afpd/directory.c (afp_mapname): removed an extraneous line
+ that was causing mapname to fail.
+
+1999-06-07 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/atalkd/main.c (main): added a note to check the syslog if
+ atalkd can't be setup.
+
+ * sys/linux/Makefile: added -DNEED_QUOTACTL_WRAPPER to the list of
+ auto-detected #defines.
+
+1999-06-06 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/fork.c (afp_write): argh. i moved things around a
+ little too much and ended up with an uninitialized eid. strangely,
+ the compiler didn't complain. simplified bits a little as
+ well. also, FPWrite was returning the wrong error messages. on
+ 64-bit filesystems, the offset can wraparound. so, report a disk
+ full error if that's going to happen. egcs-19990602 gets one
+ memcpy right and another wrong on my udb. bleah.
+
+ (afp_read): fixed the error messages here as well.
+
+1999-06-05 a sun <asun@hecate.darksunrising.blah>
+
+ * Makefile, sys/generic, sys/{ultrix,solaris}/Makefile: create
+ some links on the fly if they're missing.
+
+ * etc/afpd/directory.c (copydir): fixed a leaking opendir and
+ re-arranged a little.
+
+1999-06-04 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd: prototyped everything here and moved the FP functions
+ into include files.
+
+ * libatalk/util/bprint.c: moved all of the bprints to here.
+
+ * libatalk/asp, include/atalk/asp.h: prototyped asp functions.
+
+ * include/atalk/atp.h, libatalk/atp: prototyped atp functions.
+
+ * libatalk/nbp, include/atalk/nbp.h: added prototypes for nbp
+ functions.
+
+ * bin/afppasswd/Makefile (afppasswd): fixed a misspelling in the
+ install phase.
+
+ * bin/afppasswd/afppasswd.c: added -a option so that root can add
+ new users. turned all of the options into bits. added newlines to
+ each entry.
+
+1999-06-03 a sun <asun@hecate.darksunrising.blah>
+
+ * sys/freebsd/Makefile: turn on sendfile support if running on a
+ FreeBSD 3+ machine.
+
+1999-06-02 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/uams/uam_dhx_pam.c: fixed memory freeing part of pam
+ conversation function.
+
+ * sys/*/Makefile: check at make time to see if -lrpcsvc and
+ -lcrypt should be included in the appropriate places.
+
+1999-05-28 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/file.c (deletefile): added more error checking here as
+ well.
+
+ * etc/afpd/directory.c (renamedir): added a couple a few more
+ error bits.
+
+ * sys/sunos/Makefile: sunos should really work now.
+
+1999-05-27 a sun <asun@hecate.darksunrising.blah>
+
+ * include/atalk/afp.h: added in a couple new error codes (one
+ deals with password setting policy, the other with maximum logins
+ by any user).
+
+ * etc/afpd/fork.c (afp_openfork): try to re-open files on
+ read-only volumes as read-only.
+
+1999-05-26 a sun <asun@hecate.darksunrising.blah>
+
+ * sys/solaris/Makefile: fixed a few bobbles here. solaris uses
+ uname -p. other oses seem to use uname -m for the same information.
+
+ * etc/uams/uam_pam.c (pam_changepw): added check for same
+ password.
+
+ * etc/uams/uam_randnum.c (randnum_changepw): added in cracklib and
+ same password checks.
+
+ * sys/osx/Makefile: moved the os x server stuff into its own build
+ directory.
+
+ * sys/linux/Makefile, sys/solaris/Makefile: key in on OSVERSION
+ and MACHINETYPE for some stuff.
+
+1999-05-25 a sun <asun@hecate.darksunrising.blah>
+
+ * sys/sunos/Makefile: various bits to make stuff work with sunos
+ again.
+
+1999-05-25 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/file.c (copyfile): only copy the header file if newname
+ is specified.
+
+ * etc/afpd/directory.c (copydir): make sure to balk if the
+ directory already exists. in addition, make sure to preserve the
+ timestamps.
+
+1999-05-24 a sun <asun@hecate.darksunrising.blah>
+
+ * bin/afppasswd/afppasswd.c: global password updating utility for
+ the randnum authentication method.
+
+1999-05-22 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/uams/uam_randnum.c (afppasswd): added in global password
+ file for the randnum authentication method. it looks for a .key
+ file as well to handle encryption.
+
+ * etc/afpd/afp_options.c (afp_options_parseline): added
+ -passwdfile as an option so that you can specify a global randnum
+ password file if desired.
+
+ * etc/afpd/volume.c (readvolfile): we now have rwlist and rolist
+ as an AppleVolumes.* option. if the user is in the rolist, the
+ volume gets set as readonly. if there's a rwlist, and the user
+ isn't in it, the volume also gets set as readonly.
+
+1999-05-21 a sun <asun@hecate.darksunrising.blah>
+
+ * libatalk/adouble/ad_lock.c (ad_fcntl_lock): plug a leak if we
+ can't allocate the reference counting variable.
+
+ * etc/uams/uam_*.c: make sure that uam_setup returns an error
+ code.
+
+1999-05-19 a sun <asun@hecate.darksunrising.blah>
+
+ * include/atalk/paths.h (_PATH_LOCKDIR): added os x server's
+ /var/run as the lock file directory.
+
+ * etc/afpd/fork.c (afp_write): kanehara@tpk.toppan.co.jp reported
+ a problem with FPWrite getting a request count of 0. that's
+ fixed.
+
+ * etc/afpd/Makefile: bleah. for some reason, pam doesn't like to
+ load itself from a shared library. i've compensated by linking it
+ into afpd again.
+
+ * etc/uams/uam_dhx_passwd.c: okay. DHX now works. something's
+ still screwy with the dhx_pam stuff though.
+
+1999-05-18 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/uam.c (uam_getname): i forgot that getname modified the
+ username to fit what's in pw->pw_name if necessary.
+
+1999-05-16 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/uams/uam_dhx_passwd/pam.c: almost ready versions of the DHX
+ authentication method. i'm still missing a little info to get it
+ all right.
+
+ * bin/megatron/nad.c (nad_header_read): if there isn't a mac name,
+ create it from the unix name.
+
+ * bin/megatron/megatron.c (megatron): oops. need to turn fdCreator
+ and fdType into strings.
+
+1999-05-16 a sun <asun@pelvetia>
+
+ * etc/afpd/uam.c (uam_afpserver_option): changed the interface a
+ little. now, you pass in an int * if you want to either get/set
+ the size of the option. added in UAM_OPTION_RANDNUM for generic
+ (4-byte granularity) random number generation.
+
+ * etc/afpd/switch.c: added afp_logout to preauth_switch.
+
+1999-05-15 a sun <asun@hecate.darksunrising.blah>
+
+ * bin/megatron/macbin.c (bin_open): make error message for
+ macbinary files more informative.
+
+ (test_header): added more macbinary tests. it now has a workaround
+ for apple's incorrectly generated macbinary files.
+
+1999-05-14 a sun <asun@hecate.darksunrising.blah>
+
+ * sys/solaris/Makefile: added shared library generation bits.
+
+ * etc/uams: moved server-side uams here.
+
+ * include/netatalk/endian.h: fixed some solaris bits.
+
+ * etc/afpd/config.c (configfree): don't do an asp_close. instead,
+ do an atp_close and free the asp object. oh yeah, as afpd needs
+ to export symbols to its modules, make sure you don't do anything
+ more exciting than strip --strip-debug with it.
+
+1999-05-12 a sun <asun@hecate.darksunrising.blah>
+
+ * various places that use sigaction: zero out struct sigaction so
+ that we don't send something confusing. also make sure that we
+ don't set a timer unless we already have a sigaction set.
+
+ * etc/afpd/fork.c (afp_openfork): don't error on trying to open an
+ empty resource fork read-only. also, added back in the bit of code
+ that prevented locks from being attempted on non-existent resource
+ forks.
+
+ * etc/afpd/afp_options.c (getoption): added a uamlist commandline
+ option (-U list).
+
+ * libatalk/netddp/netddp_open.c: don't bind if nothing was passed
+ in.
+
+ * libatalk/nbp/nbp_unrgstr.c (nbp_unrgstr): oops. forgot to
+ convert this over to use by the netddp interface.
+
+1999-05-12 a sun <asun@pelvetia>
+
+ * etc/afpd/uam.c: os x server's runtime library loader is
+ braindead. as a result, i've switched to using an exported struct
+ with the uam's name.
+
+ * bin/aecho,getzones: changed these to use the netddp interface.
+
+ * libatalk/nbp/nbp_rgstr.c,unrgstr.c: fixed more leaky bits.
+
+ * libatalk/netddp: abstracted the ddp interface to netddp. besides
+ the prior socket-driven interface, there's now an os x server
+ interface. so, instead of calling socket/sendto/recvfrom, you call
+ netddp_open/netddp_sendto/netddp_recvfrom.
+
+1999-05-11 a sun <asun@pelvetia>
+
+ * libatalk/nbp/nbp_lkup.c: oh my. nbp_lookup was fd leaky if there
+ was a problem.
+
+ * etc/atalkd/main.c (main): make sure that if -dontroute is
+ selected for all but one interface, that interface also gets
+ -dontroute set.
+
+1999-05-10 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/auth.c: re-wrote to deal with plug-in uams. it's much
+ smaller than it used to be.
+
+1999-05-09 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/uams/uam_guest.c, uam_pam.c, uam_passwd.c,
+ uam_randnum.c: uam modules. these should probably be moved out of
+ afpd (and into something like etc/uam_server) when the printing
+ stuff gets uam support.
+
+1999-05-08 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/uam.c: interface to user authentication modules.
+ it should eventually be moved to libatalk, but that's not
+ necessary until the printing uam stuff is done. everything is from
+ the server-side perspective, but that's only because there aren't
+ any client-side uses right now.
+
+ * libatalk/util/module.c: generic interface to run-time library
+ loading functions. right now, the dlfcn family and os x server's
+ NS-style way of doing things are the ones understood. in addition,
+ there's a DLSYM_PREPEND_UNDERSCORE for those systems that need it.
+
+ * libatalk/asp/asp_write.c (asp_wrtcont): log both the read and
+ write part of write continuations.
+
+1999-05-07 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/atalkd: added the ability to turn off routing for particular
+ interfaces. specify -dontroute for each interface that you don't
+ want to route.
+
+1999-05-06 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/auth.c: got rid of global clrtxtname and switched to
+ using obj->username.
+
+1999-05-04 a sun <asun@hecate.darksunrising.blah>
+
+ * libatalk/dsi/dsi_write.c (dsi_write): dsi_write could loop
+ forever if there's a problem while it's being used. that's fixed.
+
+1999-05-01 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/directory.c (renamedir,copydir,deletedir): added bits
+ so that renaming a directory works across filesystems.
+
+1999-04-27 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/fork.c (getforkparams): report mtime if it's > than
+ what's stored in the header file.
+
+ * config/afpd.conf: incorporated a patch by Stefan Bethke to make
+ afpd.conf more understandable.
+
+ * sys/solaris/if.c: many of the firstnet/lastnet bits weren't
+ endian converted. that's fixed.
+
+ * libatalk/adouble/ad_lock.c (adf_find(x)lock): F_RD/WRLCK aren't
+ necessarily ORable, so use ADLOCK_RD/WR instead.
+
+ (ad_fcntl_unlock): erk. fixed a typo that had the resource fork
+ unlock accidentally getting the data fork locks.
+
+1999-04-24 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/fork.c (afp_openfork): always try to create a resource
+ fork if asked.
+
+1999-04-21 a sun <asun@hecate.darksunrising.blah>
+
+ * libatalk/adouble/ad_open.c, ad_read.c/ad_write.c, ad_flush.c:
+ turned the mmapped header stuff into and #ifdef
+ USE_MMAPPED_HEADERS option.
+
+ * libatalk/adouble/ad_open.c (ad_header_read): darn. i forgot that
+ the hfs fs doesn't currently have mmappable header files. rather
+ than implement that, i just reverted back to a modified version
+ of the old way of reading headers.
+
+1999-04-15 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/fork.c (afp_bytelock): byte locks become read locks on
+ read-only files.
+
+ (afp_openfork): deal with read-only data forks that don't have
+ corresponding .AppleDouble files. we can't really do anything with
+ deny locks in this case. just make sure that read locks are set.
+
+ * etc/afpd/file.c (getfilparams): oops. got the parentheses wrong
+ around FILPBIT_FINFO.
+
+ * etc/afpd/fork.c (afp_read): as we share open files now, check
+ for fork type against of_flags instead of just checking to see if
+ the file is open. this fixes a bug that caused resource forks to
+ get filled with data fork information.
+
+1999-04-09 a sun <asun@porifera.zoology.washington.edu>
+
+ * sys/generic/Makefile: AFP/tcp now compiles on irix with quota
+ support.
+
+1999-04-09 a sun <asun@mead1.u.washington.edu>
+
+ * sys/generic/Makefile: AFP/tcp now compiles on aix with quota
+ support.
+
+1999-04-09 a sun <asun@saul6.u.washington.edu>
+
+ * sys/generic/Makefile: AFP/tcp part now compiles on digital unix
+ with quota support enabled.
+
+1999-04-08 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/volume.c, fork.c, file.c, directory.c, filedir.c,
+ config/AppleVolumes.default: added read-only volume option.
+
+ * etc/afpd/quota.c (uquota_getvolspace): modified for os x
+ server.
+
+1999-04-03 a sun <asun@hecate.darksunrising.blah>
+
+ * bin/megatron/macbin.c (bin_write): only pad if we need to do so
+ (from <jk@espy.org>).
+ (bin_header_write/read): fixed up screwed up file date
+ generation/reading with macbinary files.
+
+ * bin/megatron: changed all of the bcopy/bzero/bcmp's to
+ memcpy/memset/memcmp's. added macbinary III support.
+
+ * bin/megatron/macbin.c (bin_open): added --stdout as an option so
+ that we can stream macbinary file creation to stdout.
+
+ * bin/megatron/megatron.c: incorporated information patch (--header
+ and --macheader) from <fmorton@base2inc.com>.
+
+
+1999-04-02 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd: whee! there are no more bcopy/bcmp's in this
+ directory.
+
+ * libatalk: changed the bcopy/bzero's to memcpy/memset's. added in
+ dummy ints for some of the files that can get compiled to empty
+ objects. check for the type of msync() available as well.
+
+1999-03-31 a sun <asun@hecate.darksunrising.blah>
+
+ * INSTALL/README.GENERIC: added information for a generic
+ architecture. It includes the information needed to get netatalk
+ to compile on a random unix platform.
+
+ * etc/afpd/quota.c: moved the quota stuff here so that we can
+ #ifdef it out on a machine without quota support.
+
+1999-03-30 a sun <asun@hecate.darksunrising.blah>
+
+ * libatalk/adouble/ad_lock.c: reference count the locked ranges as
+ well. this prevents multiple read locks on the same byte range
+ from disappearing if one user disappears.
+
+ (ad_fcntl_lock): here are the current rules for file
+ synchronization:
+ 1) if there's a appledouble header, we use the beginning
+ of that for both data and resource forks.
+ 2) if there isn't, we use the end of the data fork (or past the
+ end on 64-bit machines)
+
+1999-03-28 a sun <asun@hecate.darksunrising.blah>
+
+ * libatalk/adouble/ad_open.c (ad_refresh): okay. mmapping the
+ appledouble entry parts is done.
+
+ * libatalk/cnid/cnid_add.c (cnid_add): prevent anyone from adding
+ in an illegal cnid.
+
+1999-03-27 a sun <asun@hecate.darksunrising.blah>
+
+ * libatalk/adouble/ad_open.c (ad_refresh): started making the
+ appledouble header parsing more generic so that we can read in
+ arbitrary appledouble header files. i just mmap the parts that we
+ need.
+
+1999-03-22 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/file.c (afp_copyfile): return the correct error
+ response on a failed copy. also, error if the file is already open
+ to prevent problems with locks. we really need to ad_lock
+ this during the copy
+
+1999-03-21 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/volume.c (readvolfile): switched volume options to
+ using ':' as a delimiter as that's one of the characters that's
+ not allowed as part of a mac filename.
+ (volset): changed access to allow/deny
+
+ * etc/afpd/auth.c (noauth_login): make sure that the username gets
+ set.
+
+1999-03-17 a sun <asun@hecate.darksunrising.blah>
+
+ * NOTE to myself: jeremy allison said that samba uses refcounts to
+ prevent close() from killing all the byte locks. so, i've started
+ converting afpd to using refcounting as well. luckily, we already
+ have of_findname, so we know when files are open. in cases where
+ files are already open, this will replace an ad_open with a lookup
+ into a hash table.
+
+ * etc/afpd/directory.c (getdirparams/getfilparams): check for
+ NULL names when getting directory/file names.
+
+ * etc/afpd/directory.{c,h} (DIRDID_ROOT/DIRDID_ROOT_PARENT): make
+ sure these are always in network byte order.
+
+1999-03-15 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/fork.c (afp_openfork): okay, fixed the file
+ synchronization bits. now, we use two bytes to do the read/write
+ stuff. when access is needed, a read lock is attempted. if a deny
+ lock is needed, a write lock is attempted. we even handle the
+ access None mode now by saving the access modes.
+
+ * etc/afpd/fork.h (AFPFORK_ACCMASK): started adding bits so that
+ we can obey all of the file synchronization rules.
+
+ * etc/afpd/fork.c (afp_bytelock): got the meaning of the clearbit
+ reversed. with helios lantest's lock/unlock 4000 times test, it
+ looks like i get <1 second overhead on my machine when using byte
+ locks. NOTE: this will get a little worse when serialization gets
+ added. in addition, 0x80000000 only works for 64-bit machines. i
+ reserve the last allowable bit for 32-bit machines.
+
+ actually, both 64-bit machines and 32-bit machines use 0x7FFFFFFF
+ now as i'm able to trigger a kernel oops in linux with the 64-bit
+ code.
+
+ (afp_read/afp_write): make sure to use the same offset when doing
+ a tmplock.
+
+1999-03-14 a sun <asun@hecate.darksunrising.blah>
+
+ * libatalk/adouble/ad_lock.c: i went and implemented a bunch of
+ stuff to get byte locks to work correctly (except for the
+ serialization) only to discover that files that use byte locks
+ also request a deny write mode. luckily, byte locks only cover up
+ to 0x7FFFFFFF. so, i'll just use 0x80000000 for the
+ synchronization locks.
+
+1999-03-08 a sun <asun@hecate.darksunrising.blah>
+
+ * sys/{*bsd,ultrix,solaris,linux}/Makefile (depend): surround
+ DEPS with double quotes so that multiple defines work.
+
+1999-03-06 a sun <asun@hecate.darksunrising.blah>
+
+ * libatalk/adouble/ad_write.c, ad_read.c: make off off_t in size.
+
+ * libatalk/adouble/ad_flush.c (adf_fcntl_relock), ad_lock.c
+ (adf_fcntl_locked): okay. fcntl locks now check against multiple
+ programs on the same machine opening the same file. current
+ problems with the mechanism that i don't want to fix unless
+ necessary:
+ 1) there's a race during the relock phase. serialization
+ would solve that.
+ 2) it assumes that each fd only locks a single contiguous
+ range at a time. keeping a list of locked ranges would
+ solve that.
+
+ also, i changed the default to using fcntl locks. if the above two
+ are really necessary, i'll probably switch to something a little
+ more featureful like the berkeley db's lock manager.
+
+ (note to myself: stuff new from asun2.1.3 from 1999-03-03)
+
+1999-03-05 a sun <asun@hecate.darksunrising.blah>
+
+ * libatalk/adouble/ad_lock.c: got rid of the endflag checks to
+ reduce system calls a little.
+
+ * etc/afpd/auth.c (getname): do a case-insensitive compare on the
+ login name as well.
+
+ * sys/solaris/Makefile: added 64-bit solaris patch from
+ <jason@pattosoft.com.au>.
+
+1999-03-03 a sun <asun@hecate.darksunrising.blah>
+
+ * include/netatalk/endian.h: make solaris 2.5 complain less.
+
+ * bin/adv1tov2/adv1tov2.c, libatalk/adouble/ad_open.c (ad_v1tov2):
+ fixed a couple problems with the adv1tov2 stuff.
+
+1999-02-26 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/volume.c (afp_openvol): erk. the volume password gets
+ aligned along an even boundary.
+
+1999-02-23 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/volume.c (readvolfile): added volume password support.
+
+1999-02-14 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/atalkd/multicast.c (addmulti): added FreeBSD's data-link
+ layer multicast setting bits.
+
+ * libatalk/adouble/ad_open.c (ad_v1tov2): make sure to stick in
+ prodos field info when converting.
+
+ * rc.atalk.redhat: added pidof checking in case the machine
+ crashes. also added rc.atalk.wrapper to the redhat rc script
+ installation.
+
+1999-02-07 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/fork.c (afp_setforkparams): make sure to do better
+ error detection here and more fully report error conditions.
+
+ (flushfork): make sure to flush the header if necessary (rfork
+ length changed or modification date needs to be set).
+
+ (afp_write): ugh. this wasn't returning the right values for the
+ last byte written if the endflag was set. in addition, it was
+ setting the modification date. that should be left to FPCloseFork
+ and FPFlush(Fork). this fixes a problem that shows up with
+ QuarkXPress.
+
+ NOTE: as of now, changes to the rfork info are the only things
+ that aren't flushed immediately when altered.
+
+ * etc/afpd/fork.c (get/setforkparams), ofork.c: what ugliness. we
+ need to report bitmap errors if we try to fiddle with the wrong
+ fork. i added an of_flags field to keep things sorted.
+
+ * libatalk/adouble/ad_open.c (ad_v1tov2): oops. in all of the
+ movement, i forgot to make sure that the pre-asun2.2.0 features
+ still compile.
+
+1999-02-06 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/filedir.c (afp_moveandrename): make sure to save the
+ old name even when it's a directory.
+
+ * globals.h: added oldtmp and newtmp to AFPObj to reduce the
+ number of buffers used. use these when needed in afp_* calls.
+
+ * etc/afpd/directory.c (deletecurdir): delete stray .AppleDouble
+ files when deleting a directory.
+
+1999-02-05 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/file.c (afp_createfile): fixed a hard create error
+ check bug.
+
+ * fixed up a few bobbles in the netatalk-990130 merge.
+
+ * the noadouble option should be pretty much implemented. here's
+ how it goes:
+ when a directory is created, the corresponding
+ .AppleDouble directory is not.
+
+ comments, dates, and other file attributes will get
+ silently ignored and not result in the creation of a new
+ .AppleDouble directory.
+
+ however, if anything possessing a resource fork is copied
+ into that directory, the corresponding .AppleDouble
+ directory will be created. once this happens, any
+ other file in the directory can acquire an AppleDouble
+ header file in the future.
+
+1999-02-03 a sun <asun@hecate.darksunrising.blah>
+
+ * merged in the rest of netatalk-990130.
+
+ * sys/solaris: merged in netatalk-990130 changes.
+
+ * etc/afpd/file.c,fork.c,desktop.c libatalk/adouble/ad_sendfile.c:
+ tested and fixed the sendfile bits on linux. it looks like linux
+ doesn't permit socket -> file sendfiles yet.
+
+ * etc/afpd/fork.c (afp_read): we can't stream FPRead's with
+ newline character checking on.
+
+1999-02-02 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/fork.c (afp_flush), ofork.c (of_flush): FPFlush
+ operates on a per-volume basis.
+
+1999-01-31 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/file.c (copyfile): sendfile bits added here also.
+
+ * etc/afpd/desktop.c (afp_geticon): added sendfile bits here as
+ well.
+
+ * libatalk/adouble/ad_sendfile.c (ad_writefile/ad_readfile):
+ implemented sendfile bits. currently, there's support for linux
+ and freebsd. unfortunately, freebsd's implementation doesn't allow
+ file->file or socket->file copies. bleah.
+
+1999-01-30 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/file.c (setfilparams), directory (setdirparams),
+ volume.c (volset): added in the beginnings of a NOADOUBLE option
+ for those that don't want AppleDouble headers to be created by
+ default. it doesn't really work that well right now.
+
+1999-01-29 a sun <asun@hecate.darksunrising.blah>
+
+ * libatalk/adouble/ad_open.c (ad_v1tov2): separated v1tov2 bits
+ from ad_refresh. made broken v1 header checking the default. fixed
+ broken v1 date checking. now, it just checks to see if the v1
+ MDATE is > than st_mtime by 5 years.
+
+ * etc/afpd/directory.c: make date setting alter directory dates as
+ well.
+
+1999-01-24 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/afp_dsi.c (alarm_handler,afp_over_dsi): added a
+ CHILD_RUNNING flag to prevent afpd from timing out on long copies.
+
+1999-01-21 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/volume.c (afp_openvol), libatalk/cnid/cnid_nextid.c:
+ shift the beginning of the fake did's if necessary.
+
+ * libatalk/adouble/ad_open.c (ad_refresh): fixed screwed-up date
+ detection code.
+
+ * libatalk/cnid/cnid_add.c,cnid_open.c,cnid_close.c: made some
+ changes so that the CNIDs will still work even when the .AppleDB
+ directory is read-only. if you're still allowed to create files on
+ these volumes, you'll get a temporary id for those.
+
+1999-01-20 a sun <asun@hecate.darksunrising.blah>
+
+ * libatalk/cnid/{cnid_close.c,cnid_open.c}: added bits so that log
+ files get cleared out on cnid_close() if it's the last user for a
+ volume.
+
+1999-01-18 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/volume.c (afp_setvolparams): added FPSetVolParms. this
+ allows us to set the backup date on the server.
+
+ * etc/afpd/file.c (afp_exchangefiles): whee! we now have
+ FPExchangeFiles. we also have FPDeleteID, so that only leaves us
+ with FPCatSearch to do.
+
+1999-01-16 a sun <asun@hecate.darksunrising.blah>
+
+ * sys/solaris/ddp.c (ddp_rput): added a couple htons()'s for the
+ net addresses.
+
+1999-01-11 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/volume.c (volset, afp_openvol): you can now specify a
+ dbpath= in AppleVolumes.* for the CNID database.
+
+ * libatalk/adouble/ad_open.c (ad_refresh): if we build in an
+ appledouble v1 environment, turn on v1compat by default.
+
+1999-01-10 a sun <asun@hecate.darksunrising.blah>
+
+ * libatalk/adouble/ad_open.c (ad_refresh): added v1compat option
+ to handle broken ad headers.
+
+ * etc/afpd/file.c (setfilparams): we need to make sure that we
+ flush the file if we've created it even if there's an error. the
+ magic number/version don't get saved if we don't.
+
+ * etc/afpd/appl.c, etc/afpd/directory.c, etc/afpd/desktop.c: added
+ DIRBITS to mkdirs.
+
+1998-12-30 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/volume.c (afp_openvol): got rid of unnecessary v_did.
+
+ * etc/afpd/file.c (afp_resolveid, afp_createid): added these two
+ in.
+
+ * well, what do you know? the cnid stuff compiles in.
+
+1998-12-29 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/fork.c, directory.c, file.c, filedir.c, volume.c,
+ enumerate.c: added in stubs for CNID database conditional on
+ AD_VERSION > AD_VERSION1.
+
+ * etc/afpd/nls/makecode.c: added iso8859-1 mapping.
+
+1998-12-27 a sun <asun@hecate.darksunrising.blah>
+
+ * bin/adv1tov2/adv1tov2.c: turn non-printable ascii characters
+ into hex code as well.
+
+1998-12-21 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/auth.c: fixed FPChangePW for 2-way randnums.
+
+1998-12-15 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/fork.c (read_file/write_file): do crlf translation in
+ both directions so that we can recover from problems if
+ necessary.
+
+1998-12-14 a sun <asun@hecate.darksunrising.blah>
+
+ * bin/adv1tov2/adv1tov2.c: small utility program that recursively
+ descends a directory and converts everything it sees into
+ AppleDouble v2.
+
+1998-12-13 a sun <asun@hecate.darksunrising.blah>
+
+ * libatalk/adouble/ad_flush.c (ad_rebuild_header): moved the
+ header rebuilding here so that ad_refresh can use it.
+
+ * libatalk/adouble/ad_open.c (ad_refresh): added locking to v1->v2
+ conversion.
+
+ * bin/megatron/asingle.c: yuk. removed all of
+ the duplicate stuff here and had it use the #defines in adouble.h.
+
+ * libatalk/adouble/ad_open.c (ad_refresh): finished v1 -> v2
+ conversion routine. we still need a shortname creator and a cnid
+ database for the v2 features to be useful.
+
+1998-12-11 a sun <asun@hecate.darksunrising.blah>
+
+ * libatalk/adouble/ad_open.c (ad_refresh): punt if we get a file
+ that we don't understand.
+
+1998-12-10 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/desktop.c (utompath,mtoupath): simplified the codepage
+ stuff. also made sure to lower/upper the 8-bit characters as
+ well.
+
+ * libatalk/util/strdicasecmp.c: the casemapping had a few wrong
+ characters.
+
+ * etc/afpd/fork.c (getforkparams): make sure that the ROpen/DOpen
+ attribute bits are in the correct byte ordering.
+
+1998-12-09 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/volume.c (volset): made prodos an option to options=
+ flag. also added crlf as an option.
+
+ * libatalk/adouble/ad_open.c (ad_refresh): fix up times if
+ necessary.
+ (ad_open): deal correctly with 0-length files by treating them as
+ newly created.
+
+ * etc/afpd/volume.c (getvolparams), file.c (get/setfilparams),
+ fork.c (getforkparams), directory.c (get/setdirparams): finished
+ adding appledouble version 1 and 2 date conversion. also added
+ attribute setting.
+
+ * etc/afpd/volume.c (getvolparams): make sure to flush the header
+ file if we needed to fiddle with it.
+
+ * libatalk/adouble/ad_date.c, ad_attr.c: date/attribute
+ setting/retrieval code.
+
+ * libatalk/adouble/ad_open.c (ad_open): initialize date
+ structures here instead of elsewhere.
+
+1998-12-07 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/directory.c, fork.c, volume.c, file.c: added unix<->afp
+ time conversion code.
+
+1998-12-05 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/volume.c (volset): changed prodos setting to
+ prodos=true.
+
+1998-12-04 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/volume.c (volset): okay. you now have the following
+ options to casefold: lowercase,uppercase,xlatelower,xlateupper
+ tolower -> lowercases everything in both directions
+ toupper -> uppercases everything in both directions
+ xlatelower -> client sees lowercase, server sees uppercase
+ xlateupper -> client sees uppercase, server sees lowercase
+
+ NOTE: you only should use this if you really need to do so. this
+ and the codepage option can cause mass confusion if applied
+ blindly to pre-existing directories.
+
+1998-12-03 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/desktop.c (utompath,mtoupath), etc/afpd/volume.h: added
+ multiple options to casefold. bits 0 and 1 deal with MTOU, and
+ bits 2 and 3 deal with UTOM. i did it that way so that you can
+ casefold in one direction only or in both directions if
+ desired. needless to say, setting both bits for UTOM or MTOU
+ doesn't make any sense. right now, toupper takes precedence in
+ MTOU, and tolower takes precedence in UTOM.
+
+1998-12-02 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/afp_options.c (afp_options_*): added -uampath and
+ -codepagepath to the list of available options. they specify the
+ directories to look for uam modules and codepages,
+ respectively. currently, -uampath doesn't do anything.
+
+ * etc/afpd/volume.c (readvolfile): spruced up options to
+ AppleVolumes files. now you can have mtoufile=<codepage.x>,
+ utomfile=<codepage.y>, casefold=<num> for volumes.
+
+ * etc/afpd/desktop.c (utompath,mtoupath): added
+ codepage/casefolding support. casefold is currently an int that
+ could have multiple actions. right now, i just lowercase in
+ mtoupath and uppercase in utompath.
+
+ * etc/afpd/ofork.c (of_alloc, of_findname, of_rename): added vol
+ as an additional specifier so that we don't have problems with
+ the same path names on multiple volumes.
+
+1998-11-29 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/volume.c (getvolparams): added AFP2.1 volume attribute
+ bits.
+
+1998-11-24 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/atalkd/config.c (readconf, getifconf): added IFF_SLAVE to
+ prevent atalkd from trying to use a slave channel.
+
+1998-11-23 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/volume.c (getvolparams): we shouldn't set the custom
+ icon bit by default on the root directory. that screws up pre-OS 8
+ systems.
+
+1998-11-19 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * libatalk/dsi/dsi_getsess.c (dsi_getsession): ignore SIGPIPE's
+ so that we can gracefully shut down the server side.
+
+ * etc/afpd/afp_dsi.c (afp_over_dsi), etc/afpd/afp_options.c,
+ libatalk/dsi/dsi_getsess.c (dsi_getsession),
+ libatalk/asp/asp_getsess.c (asp_getsession): made the tickle timer
+ interval an option (-tickleval <sec>).
+
+ * etc/afpd/afp_dsi.c (afp_dsi_timedown): added child.die so that
+ we don't stomp on a shutdown timer. libatalk/dsi/dsi_read.c,
+ dsi_write.c both save/restore the old timer, so they don't really
+ care what the actual value is.
+
+1998-11-18 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * due to the recent obsession with bug fixing and low-level dsi
+ cleanups, i've decided that this should really be asun2.1.1
+ instead of asun2.1.0a.
+
+1998-11-17 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * libatalk/dsi/dsi_tcp.c (dsi_tcp_open): moved the afpd connection
+ announcement here from etc/afpd/afp_dsi.c.
+
+ * libatalk/dsi/dsi_stream.c: moved all of the read/write functions
+ into here as they're pretty generic. now, the protocol specific
+ stuff only handles open and close.
+
+ * etc/afpd/fork.c (afp_read/write), desktop.c (afp_geticon),
+ file.c (copyfile), include/atalk/dsi.h (dsi_writefile/readfile):
+ added initial stubs for sendfile support. i still need to go
+ through and calculate the appropriate offsets to use.
+
+ * libatalk/dsi/dsi_read.c, dsi_write.c: disable the interval timer
+ instead of just ignoring it.
+
+ * etc/afpd/desktop.c (afp_geticon), etc/afpd/fork.c (afp_read),
+ libatalk/dsi/dsi_read.c (dsi_readinit, dsi_readinit): modified the
+ dsi_read interface to return errors so that i can kill things
+ gracefully.
+
+1998-11-16 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * libatalk/dsi/dsi_tcp.c (dsi_tcp_send/dsi_tcp_write): erk. read()
+ and write() treat a return of 0 differently.
+
+1998-11-16 a sun <asun@hecate.darksunrising.blah>
+
+ * libatalk/dsi/dsi_read.c (dsi_readinit): make sure to stick in
+ the error code.
+
+1998-11-15 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/fork.c (afp_read): re-ordered some of the checks here
+ to return earlier on 0-sized files.
+
+1998-11-13 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/afp_dsi.c (afp_over_dsi): moved the dsi->noreply toggle
+ check to here from dsi_cmdreply.
+
+1998-11-11 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/atalkd/zip.c (zip_packet): make sure to copy multicast zone
+ back out. (reported by Michael Zuelsdorff <micha@dolbyco.han.de>)
+
+1998-11-09 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/directory.c (getdirparams): changed unknown bit reply
+ code to AFPERR_BITMAP instead of AFPERR_PARAM.
+
+1998-11-06 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/enumerate.c (afp_enumerate), directory.c (renamedir):
+ fixed a couple of failed realloc leaks.
+
+ * etc/afpd/filedir.c (afp_moveandrename, afp_rename): added bits
+ to deal with case-insensitive, case-preserving filesystems.
+
+1998-10-30 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/auth.c: fixed randnum password changing check.
+
+1998-10-27 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/atalkd/main.c: add a check for SIOCATALKDIFADDR if
+ SIOCDIFADDR fails.
+
+ * etc/afpd/volume.c (getvolparams): ad_open had the wrong
+ parameters.
+
+ * etc/afpd/unix.c (setdeskowner): added a little extra space to
+ prevent buffer overflows here.
+
+1998-10-26 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * sys/linux/Makefile: fixed PAM message.
+
+ * sys/solaris/Makefile: make failure to ln -s a non-fatal error.
+
+ * etc/papd/session.c, bin/pap/pap.c: changed sequence count to
+ wrap from 0 -> 1 instead of from 0xFFFF -> 1.
+
+ * etc/afpd/filedir.c (afp_rename, afp_moveandrename): actually, we
+ should check against the entire unix name.
+
+1998-10-21 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/filedir.c (afp_rename, afp_moveandrename): make sure
+ to check against mac name.
+
+1998-10-19 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/auth.c (afp_changepw): make password changing deal
+ correctly with "real" user names. also, moved seteuid() to before
+ the pam_authenticate() bit as shadow passwords need that.
+
+ * etc/afpd/enumerate.c (afp_enumerate): make sure to check the mac
+ name against MACFILELEN.
+
+1998-10-16 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/file.c (renamefile), filedir.c (afp_rename),
+ directory.c (renamedir): use strndiacasecmp() for AFPERR_SAMEOBJ
+ checks. also make sure test occurs before checking to see if the
+ destination exists.
+
+1998-10-15 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/auth.c (afp_changepw): fixed a bit of brain damage. i
+ forgot that password changing needs root privileges to work.
+
+ * etc/afpd/auth.c (PAM_conversation): the supplied code was
+ incorrect. i cleaned it up a bit.
+
+ * sys/linux/Makefile: fixed the installation bits.
+
+1998-10-14 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/auth.c (afp_changepw): don't kill the connection here
+ if there's a problem.
+
+1998-10-10 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/enumerate.c, fork.c, ofork.c, file.c,
+ globals.h, directory.c, auth.c: #defined MACFILELEN and used
+ that. also made sure that files > MACFILELEN never show up.
+
+1998-09-25 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/{afpd,papd,atalkd}/bprint.c (bprint): got rid of the
+ spurious pointer dereference.
+
+ * etc/afpd/ofork.c (of_alloc/of_rename): allocate the max mac file
+ length so that we don't need to realloc.
+
+ * etc/afpd/filedir.c (afp_rename, afp_moveandrename): make sure to
+ return AFPERR_BUSY if the dest has an ofork open.
+
+ * etc/afpd/file.c (renamefile), directory.c (renamedir), filedir.c
+ (afp_rename): return AFPERR_SAMEOBJ if source == dest
+
+1998-09-21 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd: went through and looked for places that needed to use
+ curdir instead of dir. i think i have them all right now.
+
+ * etc/afpd/filedir.c (afp_moveandrename): wasn't keeping track of
+ curdir correctly. what this really means is that cname should be
+ fixed to return everything it changes as opposed to changing a
+ global variable.
+
+1998-09-19 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/config.c (configinit): do the right thing if
+ AFPConfigInit fails.
+
+1998-09-18 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/config.c (ASP/DSIConfigInit, configfree): how
+ embarrassing. i wasn't doing refcounts correctly.
+
+1998-09-17 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/nfsquota.c (getnfsquota): ultrix uses dqb_bwarn instead
+ of dqb_btimelimit.
+
+ * sys/ultrix/Makefile: ultrix understands the old rquota format.
+
+ * etc/afpd/ofork.c (of_findname): erk. forgot to only search in
+ the current directory.
+ (of_rename): erk. changed it to handle renaming a file that has
+ been opened multiple times.
+
+ * etc/atalkd: made sure that if you don't specify -router, things
+ are as they were before.
+
+1998-09-13 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/status.c (status_flags): forgot to turn on password
+ setting if randnum passwords are being used.
+
+1998-09-11 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/unix.c (setdirmode): erk. make sure only to setgid on
+ directories.
+
+ * bin/aecho/aecho.c (main): incorporated -c <num> (ala ping) patch
+ from "Fred Lindberg" <lindberg@id.wustl.edu>.
+
+1998-09-03 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/directory.c (afp_closedir, afp_opendir): added these in
+ for more AFP 2.0 compliance. unfortunately, apple's appleshare
+ client doesn't like non-fixed directory ids.
+
+1998-08-31 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/volume.c (accessvol): the accessible volume list can
+ now be controlled by groups and usernames. just use something of
+ the following form: @group,name,name2,@group2,name3
+
+ NOTE: you can't have any spaces, and the parser forces you to
+ include all options. in this case, there are some apple II options
+ that need to be entered. they need to go away soon anyway.
+
+ * etc/afpd/auth.c (getname): oops. i forgot to copy the gecos
+ field into a temporary buffer before calling strtok.
+
+1998-08-29 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/atalkd/main.c (as_timer), rtmp.c (rtmp_delzones): when the
+ last router on an interface goes down, we need to delete the
+ interface's zone table.
+
+1998-08-28 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/status.c (afp_getsrvrinfo): although it's never used,
+ i've added this in to increase AFP compliance.
+
+ * etc/afpd/auth.c (afp_getuserinfo): added FPGetUserInfo -- this
+ should make afpd compatible with OS 8.5's Nav Services.
+
+ * etc/atalkd/config.c,main.c: -router now specifies router mode
+ with any number of interfaces.
+
+1998-08-27 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/atalkd/main.c (as_timer): well, i figured out how to set up
+ atalkd as a single-interface router. now, you can get zones with
+ only single interfaces! i'm only waiting on wes for the approved
+ configuration toggle.
+
+1998-08-26 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * libatalk/adouble/ad_lock.c, include/atalk/adouble.h: turned the
+ ADLOCK_* into real #defines and added translations in the
+ lock-type specific functions. this should make it easier to
+ recompile things without screwing up.
+
+1998-08-26 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/atalkd/nbp.c (nbp_packet): forgot to handle another local
+ zone case.
+
+1998-08-25 a sun <asun@hecate.darksunrising.blah>
+
+ * etc/afpd/status.c (status_server): changed status_server to
+ use only the obj part of obj:type@zone-style names.
+
+ * etc/atalkd/nbp.c (nbp_packet): unregistering wasn't handling
+ zones properly. it was matching on anything but the actual zone.
+
+1998-08-18 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/auth.c (clrtxt_login): added pam_set_time(PAM_TTY) so
+ that solaris' pam session setup doesn't crap out.
+
+1998-08-17 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/atalkd/multicast.c (zone_bcast): fixed to do the right thing
+ with zip multicast info.
+
+1998-08-15 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/nfsquota.c: made the old-style rquota fields dependent
+ upon -DUSE_OLD_RQUOTA and defined that for sunos. also included
+ <sys/time.h> for ultrix breakage.
+
+1998-08-13 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/filedir.c (afp_rename), etc/afpd/ofork.c (of_rename): i
+ knew that speeding up of_findname would be useful. in any case, i
+ discovered the source of yet another small AFP non-compliance that
+ was confusing WordPerfect. on an afp_rename, we also need to
+ rename the corresponding ofork. i've added an of_rename() to do
+ this.
+
+1998-08-13 a sun <asun@hecate>
+
+ * etc/afpd/ofork.c (of_dealloc,of_alloc): sped up dealloc by
+ sticking refnum in ofork.
+
+1998-08-12 a sun <asun@hecate>
+
+ * etc/afpd/fork.c (afp_openfork): added already open attribute
+ bits.
+
+ * etc/afpd/ofork.c: added a hashed of_findname.
+
+1998-08-06 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/fork.c (afp_openfork): fixed a problem with opening
+ forks from read-only non-appledouble media.
+
+1998-07-23 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/afs.c (afs_getvolspace), etc/afpd/volume.c
+ (getvolspace): modified them to treak afs like the other
+ getvolspaces w.r.t. VolSpace.
+
+1998-07-21 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/unix.c (mountp): erk. i forgot that symlinks are often
+ used for filesystems. nfs quotas sometimes failed as a
+ result. that's fixed now.
+
+1998-07-20 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/auth.c (login): added a -DRUN_AS_USER #define so that
+ it's simple to run the server process as a non-root user.
+
+1998-07-17 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/nfsquota.c (callaurpc, getnfsquota), volume.h: it turns
+ out that i was opening lots of sockets with callaurpc. now, the
+ socket gets saved and reused.
+
+ NOTE: quota-1.55-9 from redhat 5.x doesn't return the correct size
+ for rquota's bsize. unless fixed, rquota will report incorrect
+ values.
+
+1998-07-08 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/uam/README: added some preliminary ideas on a
+ plug-in uam architecture. in addition, this should allow arbitrary
+ afp function replacement. eventually, auth.c should get a
+ bit cleaner.
+
+1998-07-07 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/nfsquota.c: added headers and redefined a couple
+ structure fields so that sunos4 compiles.
+
+ * libatalk/compat/rquota_xdr.c: compile if we're using glibc <
+ 2. this should get redhat 4.2 machines. NOTE: they're still
+ missing librpcsvc.a, so they'll need to remove that from the
+ etc/afpd/Makefile.
+
+1998-07-06 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * libatalk/compat/rquota_xdr.c: it turns out that solaris is
+ missing a couple functions needed for rquota support. here they
+ are.
+
+ * etc/afpd/unix.c (mountp): fixed the nfs detection for
+ solaris. we still need bsd and ultrix.
+
+1998-07-05 a sun <asun@hecate>
+
+ * include/atalk/adouble.h: marked out space for appledouble v2.
+
+1998-07-04 a sun <asun@hecate>
+
+ * etc/afpd: plugged up some ad_open leaks. made sure that we don't
+ get negative numbers for length fields and such.
+
+1998-07-04 a sun <asun@hecate>
+
+ * etc/afpd/nfsquota.c (getnfsquota): added nfs rquota
+ support. Robert J. Marinchick <rjm8m@majink1.itc.virginia.edu>
+ provided the initial bits from the bsd quota program.
+
+ * etc/afpd/unix.c (getquota): made getquota call getfsquota or
+ getnfsquota depending upon the type of fs.
+
+ * etc/afpd/unix.c (mountp/special): munged mountp and
+ special to return either the nfs mount point or the fs
+ device. set the vol->v_nfs flag if it's nfs.
+
+ * etc/afpd/volume.c (getvolspace): xbfree and xbtotal will now
+ honor results returned from uquota_getvolspace.
+
+1998-06-29 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * etc/afpd/file.c (copyfile): mmapping the file copy only helps on
+ multiple copies. as that's not the case here, i've reverted to
+ just doing read + write.
+
+1998-06-28 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * sys/linux/Makefile: fixed the redhat-style atalk
+ installation. also, it doesn't over-write an existing
+ /etc/rc.d/init.d/atalk file.
+
+ * etc/afpd, libatalk/adouble/ad_write.c: removed <sys/file.h> and
+ </usr/ucbinclude/sys/file.h> so that we rely upon adouble.h.
+
+1998-06-19 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * changed sys/linux/Makefile to install a redhat-style sysv atalk
+ script instead of the bsd one.
+
+ * include/atalk/adouble.h: moved same-name list stub to struct
+ ad_adf.
+
+Thu Jun 18 18:20:28 1998 a sun <asun@purgatorius>
+
+ * changed to asunx.y.z notation as i was getting tired of
+ increasing numbers. as this version is undergoing a fairly
+ substantial overhaul, i bumped it to 2.1.0. don't ask why asun1.0
+ never existed. i just started at 2.0.
+
+ * ofork (etc/afpd/{ofork.c,ofork.h,fork.c}: put in skeleton code
+ for hashed-by-name oforks and oforks which group by name to help
+ with byte locks and of_findname.
+
+ * adouble (include/atalk/adouble.h): started implementing
+ appledouble v2. mostly, i added bits to headers. v2 has wonderful
+ bits that should make prodos support much less clunky, allow
+ persistent dids, and allow msdos support.
+
+ * finder info: added bits to directory.c and file.c describing the
+ actual contents of finder info (from IM: Toolbox
+ Essentials). also, set default directory view to an allowed value
+ thanks to a suggestion from the appledouble v2 specs. that should
+ help with read-only media.
+
+ * etc/afpd/{directory.c,volume.c,afs.c,directory.h}: added
+ DIRDID_ROOT and DIRDID_ROOT_PARENT so people know that these did's
+ are reserved.
+
+Wed Jun 17 11:54:49 1998 a sun <asun@purgatorius>
+
+ * well, i'm starting a changelog as i keep forgetting what i've
+ done.
+
+ * locks: revamped them to be more in line with what should
+ happen. currently, i've reverted back to flock-style locks as i'll
+ need to stick in more code to get fcntl-style locks to work
+ properly. specifically, i think modifying of_alloc/of_dealloc to
+ keep track of locks, fds, and names should solve the problem with
+ fcntl locks being process-specific and not fd specific.
+
--- /dev/null
+This is the AFS README file for netatalk.
+
+NOTE: You'll need to have the AFS server libraries and include files.
+These are different than the client libraries and include files. Also,
+you will need the Kerberos v4 (patch level 10) libraries and include
+files.
+
+1. CONFIGURE FOR AFS. Edit the root Makefile to include the pathname
+ to both the AFS and Kerberos libraries and include files. There
+ are brief instructions at the top of the Makefile.
+
+2. ADD AFP PRINCIPAL TO YOUR SRVTAB. afpd requires an entry in your
+ /etc/srvtab to do Kerberos authentication. This entry is of the
+ form
+
+ afpserver.NBPNAME@REALM
+
+ So, if you want your afp server to be called "bob" and your realm
+ is "camelot", you would need a principal
+
+ afpserver.bob@camelot
+
+ If you don't have an incorrect /etc/srvtab, Macs attempting to
+ authenticate with "Kerberos IV UAM"/"AuthMan UAM" will get a
+ "remote configuration" error and afpd will log "krb4_logincont:
+ krb_rd_req: Can't decode authenticator". NOTE: nbpname MUST match
+ the kerberos principal name in order for authentication to
+ succeed. If afpd's default nbpname (see the afpd man page) does
+ not match your site's method for generating principal names, then
+ you must use the "-n" option when starting afpd.
+
+3. CLIENT SOFTWARE. There are several pieces of Mac software that you
+ will need. The first, "AuthMan UAM", is an alternate UAM which
+ does authentication with afpd for AFS. AuthMan UAM requires the
+ second, AuthMan. AuthMan requires MacTCP. The third, AFS
+ Privileges, allows the Mac user to display the AFS acls on afp
+ mounted directories. "AuthMan UAM" and "AFS Privileges" are
+ available via anonymous ftp from terminator.rs.itd.umich.edu in
+ ~ftp/unix/netatalk/kerberos-AFS.sit.hqx. AuthMan is available via
+ anonymous ftp from monet.ccs.itd.umich.edu in ~ftp/pub. The
+ revision as of this writing is 1.0.9A.
+
+4. AFS HELP. For more information about AFS, you should contact
+ afs-sales@transarc.com.
+
+Research Systems Unix Group
+The University of Michigan netatalk@umich.edu
+c/o Wesley Craig +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
--- /dev/null
+This is the FreeBSD README file for netatalk.
+
+Note that kernel support for netatalk appears in FreeBSD 2.2-current
+dated after 12 September 1996.
+
+1. KERNEL. Add the line
+
+ options NETATALK
+
+ to the config file for your kernel. Rebuild and install your
+ kernel. Reboot.
+
+Research Systems Unix Group
+The University of Michigan netatalk@umich.edu
+c/o Wes Craig +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
--- /dev/null
+The generic system profile is for use on systems that don't have native
+appletalk support. For those systems, it should still be possible to get
+the AFP/tcp portion of netatalk to still work.
+
+To do that, you will need the following information:
+ 1) Endian order: If your machine does not specify the
+ byte-order in netinet/in.h, you may need to modify
+ netatalk/endian.h.
+
+ 2) Integer sizes: If your machine does not define intX_t and
+ u_intX_t, you will need to define them in
+ netatalk/endian.h. To ease matters, you can specify
+ _ISOC9X_SOURCE if you have inttypes.h, HAVE_64BIT_LONGS for
+ 64 bit machines, or HAVE_32BIT_LONGS for 32 bit
+ machines. NOTE: you should only use HAVE_32/64BIT_LONGS on
+ machines that don't have a header file somewhere with the
+ integer sizes. If you have a file with all the relevant
+ bits, modify netatalk/endian.h to include it.
+
+ 3) Quota/statfs information: You may be able to get away with
+ either BSD4_4 or __svr4__, but that's unlikely if your os
+ is some bizarre hybrid. If you don't have quota support,
+ just specify NO_QUOTA_SUPPORT. In addition, if you'll need
+ to specify the include file that gets statfs() (usually
+ either USE_VFS_H or USE_STATFS_H although BSD4_4 and
+ __svr4__ bring in a set of include files for that). Look at
+ etc/afpd/quota.c, unix.c, and unix.h for more information.
+ Finally, if you have a really old version of rquota, you
+ can define USE_OLD_RQUOTA as well.
+
+ 4) path information for lock/spool/printer files. you'll need
+ to specify -D_PATH_LOCKDIR if include/atalk/paths.h doesn't
+ have the correct paths specified for printer info and lock
+ files.
+
+Beyond that, you should make sure that your operating system looks and
+smells like a Un*x POSIXy operating system. The only operating systems
+that netatalk supports that don't quite fit that description are
+sunos 4 and ultrix. If your operating system is peculiar, you may need
+to add in compatibility routines (libatalk/compat,
+include/atalk/compat.h) to make it look more like the others.
+
+If you would like native AppleTalk support, you will need kernel support
+for your operating system. Look at the Solaris STREAMS module if your
+operating system supports that framework. Otherwise, look at the ddp
+code in FreeBSD, NetBSD, or OpenBSD if your operating system is BSDish
+in nature. If your operating system looks different than these two
+cases, you'll have to roll your own implementation.
+
--- /dev/null
+This is the Linux README file for netatalk.
+
+We no longer include linux kernel code with netatalk, since Linux now
+includes AppleTalk support.
+
+1. UIO.H. On older versions of Linux, you may need to make a link from
+ /usr/include/sys/uio.h to ../linux/uio.h. In particular, this file
+ changed from linux 1.2.x to 1.3.x, so if, for instance, you've
+ installed Slackware 3.0.0, and upgraded your kernel to 1.3.x,
+ you'll need to fix this.
+
+2. MAKE CONFIG. Configure your kernel with "make config". Answer yes
+ to "AppleTalk DDP" support.
+
+3. INSTALL KERNEL. Make and install your kernel. Be sure to update
+ your boot blocks!
+
+4. If you are using libc.so.5, you will need to comment out the
+ -lcrypt and the -lrpcsvc in sys/linux/Makefile. If you're using PAM,
+ make sure you declare -DUSE_PAM and have -lpam -ldl.
+
+5. Quota support should work under linux now. If you're using glibc
+ 2.x or libc > 5.4.34, you can comment out the
+ -DNEED_QUOTACTL_WRAPPER in sys/linux/Makefile.
+
+6. Linux 2.2.x provides the sendfile() call. This reduces overhead
+ when sending/copying files. This option will be autoconfigured on
+ compile. NOTE: you might run into problems if you have this option
+ compiled in and you switch to a machine running an os < 2.2.x.
+
+
+Research Systems Unix Group
+The University of Michigan netatalk@umich.edu
+c/o Wes Craig +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
--- /dev/null
+This is the NetBSD README file for netatalk.
+
+Note that kernel support for netatalk appears in NetBSD 1.3, and in
+NetBSD 1.2D dated after April 5, 1997.
+
+1. KERNEL. If not already present, add the line
+
+ options NETATALK
+
+ to the config file for your kernel. Rebuild and install your
+ kernel. Reboot.
+
+Research Systems Unix Group
+The University of Michigan netatalk@umich.edu
+c/o Wes Craig +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
--- /dev/null
+This is the OpenBSD README file for netatalk.
+
+Note that kernel support for netatalk appears in OpenBSD 2.2, or
+openbsd-current dated after Aug 1, 1997.
+
+1. KERNEL. Add the line
+
+ options NETATALK
+
+ to the config file for your kernel. Rebuild and install your
+ kernel. Reboot.
+
+Research Systems Unix Group
+The University of Michigan netatalk@umich.edu
+c/o Wes Craig +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
--- /dev/null
+This is the Solaris README file for netatalk.
+
+1. SELECT COMPILER. This distribution is configured to build with
+ gcc. It should also work with cc. At the present time only cc
+ v5.0 and above can build the 64-bit kernel module.
+
+1a. SELECT KERNEL TYPE. Edit sys/solaris/Makefile and set KCFLAGS to
+ include sparcv9 support if you run a 64-bit kernel, or leave it
+ commented out for a 32-bit kernel. Only UltraSPARC systems
+ running Solaris 7 and above support a 64-bit kernel. If you're
+ not sure what kernel you use, run "isainfo -v". You're running a
+ 64-bit kernel if the result includes 64-bit (sparcv9), otherwize
+ it's 32-bit.
+
+ NOTE: If you want both the 32-bit and 64-bit kernel modules to be
+ installed, first compile and install the version appropriate to
+ the kernel that you're currently running, then make clean,
+ compile and install the other version.
+
+2. EDIT NETCONFIG. Add the following line to /etc/netconfig:
+
+ ddp tpi_clts - appletalk ddp /dev/ddp -
+
+ This makes the socket library aware of the AppleTalk protocol
+ family.
+
+3. INSTALL DRIVER. Since the STREAMS ddp driver must be installed as
+ root, we've separated that portion of the build. Type
+
+ make kinstall
+
+ to install the driver. This copies the driver and it's config file
+ into /usr/kernel/drv and /usr/kernel/strmod, runs "add_drv ddp" to
+ make the kernel aware of the new driver, and adds an rc file to
+ /etc/rc?.d.
+
+4. ATALKD.CONF. Under Solaris, you must create atalkd.conf, since
+ Solaris provides no method for determining the names of the
+ available interfaces. It is sufficent to name the available
+ interfaces in atalkd.conf, one per line. E.g.
+
+ le0
+
+ on a line by itself, on many Suns. See atalkd(8).
+
+5. PRINTING. To quote my Solaris documentation, "Setting up printing
+ services using the LP print service command-line interface is
+ complicated and error-prone." See your Solaris documentation for
+ similarly helpful statements.
+
+ Presuming that the Solaris print filters are installed (see your
+ helpful Solaris documentation), the following command adds the
+ printer named "bob" your to system and enables printing:
+
+ lpadmin -p bob -i /usr/local/atalk/etc/lp2pap.sh \
+ -I postscript -v /dev/null -T PS
+ enable bob
+
+ This creates the directory /etc/lp/bob, in which you should create
+ a file called ".paprc" containing the NBP name of the printer. See
+ the "pap" man page for more information.
+
+Research Systems Unix Group
+The University of Michigan netatalk@umich.edu
+c/o Wes Craig +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
--- /dev/null
+This is the SunOS README file for netatalk.
+
+1. KERNEL MODULE. This version of netatalk requires that your kernel
+ be configured with
+
+ options VDDRV
+
+ The loadable kernel module is made and installed during the normal
+ make and make install. NOTE: Unloading the kernel module may not
+ work correctly and may cause your kernel to panic, hang, or do
+ other nasty things.
+
+Research Systems Unix Group
+The University of Michigan netatalk@umich.edu
+c/o Wesley Craig +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
--- /dev/null
+This is the Ultrix README file for netatalk.
+
+1. REMOVE PREVIOUS NETATALK. If this is the first release of netatalk
+ you've used, skip to step (2). If you've installed netatalk
+ before, you will need to remove the old patches.
+
+ There are two easy ways to remove the old patches. The simplist,
+ is to apply the old patches in reverse. E.g. for netatalk-1.3 or
+ later
+
+ patch -s -d /sys -R -p0 < sys/ultrix/kpatch-<vers>
+
+ Note that the old patches are not included in this distribution.
+
+ If you haven't saved the old patches, it is possible that the old
+ versions of the patched files are still in your file system. When
+ patch changes a file, it saves the original file as <file>.orig.
+ netatalk-1.3 or later leaves these files
+
+ conf/files
+ data/af_data.c
+ data/if_to_proto_data.c
+ data/uipc_domain_data.c
+ net/net/conf_net.c
+ net/net/netisr.h
+
+ Finally, if there is no way to regenerate the old version of the
+ patched files, you can apply the new patches by hand, skipping the
+ next section. There are two versions of the patches, kpatch-4.1
+ for Ultrix 4.1 and kpatch-4.2 for both 4.2 and 4.3.
+
+2. PATCH FILES IN KERNEL BUILDING AREA. The simplest way to install the
+ kernel patches is with the command
+
+ make kpatch
+
+ This command will determine your system type and apply patches to
+ the following files
+
+ conf/files
+ data/af_data.c
+ data/if_to_proto_data.c
+ data/uipc_domain_data.c
+ net/net/conf_net.c
+ net/net/netisr.h
+
+ To apply these patches by hand, type
+
+ patch -s -d /sys -p0 < sys/ultrix/kpatch-<vers>
+
+ where <vers> is the version of Ultrix you are running.
+
+3. POPULATE /sys/net/netatalk. Do this with
+
+ make kinstall
+
+ This makes a directory called /sys/net/netatalk and copies all
+ pertinent files. To do this by hand,
+
+ mkdir /sys/net/netatalk
+ cp sys/netatalk/*.[ch] /sys/net/netatalk
+ cp sys/ultrix/*.[ch] /sys/net/netatalk
+
+ These files are the complete source for AppleTalk in 4.3BSD kernels
+ and Ultrix.
+
+4. CREATE KERNEL. You should probably start by copying the config file
+ for GENERIC to, for instance, NETATALK. You'll need to add the
+ lines
+
+ options ATALK
+ pseudo-device atalk
+
+ to the NETATALK config file, config the kernel, and then make. You
+ don't have to call your kernel NETATALK, but be sure NOT to call it
+ ATALK, since the name of the kernel and kernel options are part of
+ the same name-space. When your build is finished, save your old
+ kernel, install the new kernel, and reboot.
+
+Research Systems Unix Group
+The University of Michigan netatalk@umich.edu
+c/o Wes Craig +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
--- /dev/null
+# Root of installation. Subdirectories will be ${DESTDIR}/etc,
+# ${DESTDIR}/bin, and ${DESTDIR}/lib.
+DESTDIR=/usr/local/atalk
+
+# for system-level binaries
+SBINDIR=$(DESTDIR)/sbin
+# for user-level binaries
+BINDIR=$(DESTDIR)/bin
+# for program libraries (*.a)
+LIBDIR=$(DESTDIR)/lib
+# for machine-independent resources (pagecount.ps, etc.)
+RESDIR=$(DESTDIR)/etc
+# for configuration files (AppleVolumes.system, etc.)
+ETCDIR=$(DESTDIR)/etc
+# for include files
+INCDIR=$(DESTDIR)/include
+# Root of man pages. Subdirectories will be
+# ${MANDIR}/man1, ${MANDIR}/man4, and ${MANDIR}/man8.
+MANDIR=$(DESTDIR)/man
+
+#INSTALL_PREFIX=
+#SBINDIR=${INSTALL_PREFIX}/usr/sbin
+#BINDIR=${INSTALL_PREFIX}/usr/bin
+#LIBDIR=${INSTALL_PREFIX}/usr/lib
+#RESDIR=${INSTALL_PREFIX}/usr/lib/atalk
+#ETCDIR=${INSTALL_PREFIX}/etc/atalk
+#INCDIR=${INSTALL_PREFIX}/usr/include
+#MANDIR=${INSTALL_PREFIX}/usr/man
+
+# Location of the Berkeley v2 db library and include files.
+# NOTE: leave this commented out for now. it's a placeholder for a future
+# feature.
+#DB2DIR=/usr/local/BerkeleyDB
+
+# Location of the Diffie-Hellman library and include files. Uncomment
+# this out if you want DHX as an allowable UAM for afpd. Currently,
+# this is set up expecting libcrypto from the openssl project. As a
+# result, this option will enable all of the encrypted authentication
+# methods (including the Randnum Exchange ones). DHX expects cast.h,
+# dh.h, and bn.h in $CRYPTODIR/include with -lcrypto in
+# $CRYPTODIR/lib. NOTE: os x server will complain if you use both
+# randnum exchange and DHX.
+CRYPTODIR=/usr/local/ssl
+
+# Location of the DES library and include files. Uncomment this out if
+# you want Randnum Exchange and 2-Way Randnum Exchange as allowable
+# UAMs for afpd. We expect libdes.a in $DESDIR/lib and des.h in
+# $DESDIR/include. This option will get overridden by CRYPTODIR.
+#DESDIR=/usr/local
+
+# Location of the tcp wrapper library and include files. Comment this out
+# if you don't want tcp wrapper support. having tcp wrapper support is
+# highly recommended.
+TCPWRAPDIR=/usr
+
+# Location of PAM support library and include files. Uncomment this if
+# you want to enable PAM support.
+#PAMDIR=/usr
+
+# Location of cracklib support library and include files. This is used
+# in the password changing routines. Uncomment this out if you want to
+# enable support.
+#CRACKDIR=/usr
+
+
+# Location of the AFS and Kerberos libraries and include files. Uncomment
+# and edit these if you want to include AFS or Kerberos support in afpd
+# or Kerberos support in papd.
+#AFSDIR=/usr/local/afs
+#KRBDIR=/usr/local/kerberos
+
+##########################################################################
+all install depend clean tags kernel kinstall kpatch: FRC
+ @case `uname -rs` in \
+ "SunOS 4"*) ARCH=sunos \
+ ;; \
+ "SunOS 5"*) ARCH=solaris \
+ ;; \
+ ULTRIX*) ARCH=ultrix \
+ ;; \
+ Linux*) ARCH=linux \
+ ;; \
+ FreeBSD*) ARCH=freebsd \
+ ;; \
+ NetBSD*) ARCH=netbsd \
+ ;; \
+ OpenBSD*) ARCH=openbsd \
+ ;; \
+ Rhapsody*) ARCH=osx \
+ ;; \
+ *) ARCH=generic \
+ ;; \
+ esac; \
+ echo "Making $@ for $$ARCH..."; \
+ cd sys/$$ARCH && ${MAKE} ${MFLAGS} \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}"\
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" MANDIR="${MANDIR}" \
+ TCPWRAPDIR="${TCPWRAPDIR}" PAMDIR="${PAMDIR}" DB2DIR="${DB2DIR}" \
+ AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" DESDIR="${DESDIR}" \
+ CRYPTODIR="${CRYPTODIR}" CRACKDIR="${CRACKDIR}" \
+ OSVERSION="`uname -r`" MACHINETYPE="`uname -m`" \
+ $@
+
+FRC: include/netatalk
+
+include/netatalk:
+ -ln -s ../sys/netatalk include/netatalk
+
+SYS=sunos ultrix solaris
+VERSION=`date +%y%m%d`
+DISTDIR=../netatalk-${VERSION}
+
+sysclean : FRC
+ for i in ${SYS}; \
+ do (cd sys/$$i; ${MAKE} ${MFLAGS} sysclean); \
+ done
+
+dist : sysclean clean
+ mkdir ${DISTDIR}
+ tar cfFFX - EXCLUDE . | (cd ${DISTDIR}; tar xvf - )
+ chmod +w ${DISTDIR}/Makefile ${DISTDIR}/include/atalk/paths.h \
+ ${DISTDIR}/sys/solaris/Makefile
+ echo ${VERSION} > ${DISTDIR}/VERSION
--- /dev/null
+This is the README file for netatalk.
+
+Contents:
+ o A Brief Description
+ o BUILDING & INSTALLING
+ o Getting Help
+
+netatalk is an implementation of the AppleTalk Protocol Suite. The
+current release contains support for EtherTalk Phase I and II, DDP,
+RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and AFP. The complete stack looks
+like this on a BSD-derived system:
+
+ AFP
+ |
+ ASP PAP
+ \ /
+ ATP RTMP NBP ZIP AEP
+ | | | | |
+ -+---------------------------------------------------+- (kernel boundary)
+ | Socket |
+ +-----------------------+------------+--------------+
+ | | TCP | UDP |
+ | DDP +------------+--------------+
+ | | IP |
+ +-----------------------+---------------------------+
+ | Network-Interface |
+ +---------------------------------------------------+
+
+DDP is in the kernel. "atalkd" implements RTMP, NBP, ZIP, and AEP. It
+is the AppleTalk equivalent of Unix "routed". There is also a
+client-stub library for NBP. ATP and ASP are implemented as
+libraries. "papd" allows Macs to spool to "lpd", and "pap" allows Unix
+machines to print to AppleTalk connected printers. "psf" is a
+PostScript printer filter for "lpd", designed to use "pap". "psorder"
+is a PostScript reverser, called by "psf" to reverse pages printed to
+face-up stacking printers. "afpd" provides Macs with an interface to
+the Unix file system. Refer to the appropriate man pages for
+operational information.
+
+netatalk runs on five operating systems:
+
+ OS Versions Hardware Notes
+ -- -------- -------- -----
+ Solaris 2.5 Sparc
+ Linux 1.3.x,2.x PC
+ FreeBSD 2.2-current PC after 12 Sept 96
+ SunOS 4.1+ Sparc kernel must have VDDRV
+ option installed
+ Ultrix 4.[1-4] 3100,5000
+
+Instructions for installing the kernel portions of netatalk and system
+dependent FAQs are in the README file for your system, e.g.
+INSTALL/README.SUNOS, INSTALL/README.ULTRIX.
+
+Building netatalk:
+
+0. To build afpd for use with an AFS filesystem, first follow the
+ instructions in INSTALL/README.AFS, then complete these
+ instructions.
+
+1. Set DESTDIR in the root Makefile. DESTDIR is the directory below
+ which all binaries will be installed. Setting it causes all
+ installation-relative pathnames to be set correctly. You may also
+ wish to set MANDIR. (If you do not want all binaries to go under
+ DESTDIR, you can instead set SBINDIR, BINDIR, ETCDIR, and LIBDIR,
+ to control the locations of the individual sections.)
+
+2. When you've completed the configuration, type "make" at the root of
+ the source. This will make all binaries.
+
+Installing netatalk:
+
+1. To install the binaries, type "make install" at the root of the
+ source tree. This will install all of the binaries.
+
+2. Sample config files for the daemons are in the config directory,
+ e.g. config/AppleVolumes.system. Install these files, or a version
+ of these files, in ETCDIR (as distributed DESTDIR/etc), e.g.
+ ETCDIR/AppleVolumes.system. See the daemon's man page for a
+ description of it's configuration file.
+
+3. psf uses the script SBINDIR/etc2ps to convert anything it
+ doesn't understand to PostScript. If you have a troff or dvi to
+ PostScript filter on your machine, you might wish to edit etc2ps,
+ to use your locally installed PostScript utilities.
+
+4. Add the contents of services.atalk to your /etc/services database.
+ If you're using NIS (YP), add the contents of services.atalk to the
+ NIS master's maps and push them.
+
+5. The file rc.atalk is installed in ETCDIR. It should be called
+ from your /etc/rc file, e.g. "sh ETCDIR/rc.atalk". For more
+ information on what this script does, read the man pages for the
+ appropriate commands.
+
+You might be interested in looking at the netatalk home page at
+http://www.umich.edu/~rsug/netatalk. It has an archive of patches,
+trouble shooting hints, and some links to other netatalk and file
+service related sites.
+
+You may report bugs and get help by sending mail to the developers at
+netatalk@umich.edu. If you're reporting bugs, you MUST use the format
+provided in the BUGS file! We will do our best to help you.
+
+You may wish to join the netatalk-admins@umich.edu (moderated) mailing
+list. It carries announcements of new releases and general
+discussion. You can join (or resign from) this list by sending mail to
+netatalk-admins-request@umich.edu. Submissions (NOT requests to join
+or resign) to this list should be sent to netatalk-admins@umich.edu.
+
+Research Systems Unix Group
+The University of Michigan netatalk@umich.edu
+c/o Wesley Craig +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
--- /dev/null
+this version of netatalk represents changes i have made to incorporate
+AFP 2.2 (AppleShare TCP/IP) support. it is based upon 1.4b2 and is not
+currently supported by umich. i hope to eventually get it incorporated
+into a future version.
+
+i hope you find this code useful. as such, i am releasing my changes
+under a copyright similar to the rest of the netatalk code.
+
+i would appreciate users of my patches letting me know of any problems
+or difficulties they have with it. i can only tested it on a limited
+number of machines. as a result, improved compatability and fixes can
+only come if i hear of problems. you can find my patches at
+<ftp://ftp.cobaltnet.com/pub/users/asun>.
+
+the patches currently include the following features:
+ AFP/TCP
+ 64-bit clean
+ large volume support -- you'll need at least 3.7.2seed3
+ and os > 7.6.1 for this to to be used.
+
+ If your compiler can't generate 64-bit
+ ints, you'll need to disable this
+ feature. add -DNO_LARGE_VOL_SUPPORT to
+ the DEFS line in your system's
+ Makefile. NOTE: gcc can generate
+ 64-bit ints.
+
+ ADDITIONAL NOTE: gcc sometimes has
+ problems with 64-bit ints. i already
+ have a workaround in the code to deal
+ with this issue.
+
+ server messages -- at this point, there is no mechanism to send
+ an arbitrary server message.
+
+ all of AFP 2.2. All of AFP 2.1 except for FPCatSearch is
+ is implemented if fixed id support is compiled in.
+
+ tcp wrapper support. if TCPWRAPDIR is uncommented in the
+ main Makefile, tcp wrapper support will get built.
+ i recommend building w/ it to enable host restrictions.
+
+ a number of bug fixes (SO_BROADCAST, server info, file/dir
+ case insensitive comparisons, and more probably)
+
+ working quota support for linux and bsd4.4. nfs rquota support
+ is also available. it hasn't been extensively tested on all
+ the platforms yet. NOTE: there's bug in the linux kernel code
+ pre-2.2.8 and pre-2.0.37 that prevents quota support from working
+ properly under linux.
+
+ you can now specify server options in an afpd.conf file. it's
+ pretty useless unless you want to start multiple servers up.
+ anyways, look at config/afpd.conf to see what's available.
+ in addition, you can use kill -HUP to force a re-read of
+ afpd.conf. as the first kill -HUP turns off connections,
+ you'll have to send another one to force a re-read.
+
+ i've also merged a slightly modified version of redhat's pam
+ patches. you need to make sure that the PAMDIR entry in the main
+ Makefile is uncommented and pointing to the right directory for
+ this to work. in case you don't know what pam is, it stands for
+ pluggable authentication modules. for more information, here's
+ a web page: <http://www.redhat.com/linux-info/pam/>
+
+ i've merged in <shirsch@ibm.net>'s apple II ProDOS support.
+
+ i've added Randnum and 2-Way Randnum support. part of the code is
+ compliments of<shirsch@ibm.net>. as afp doesn't do the
+ fallback thing in case of failure, Randnum and 2-Way Randnum
+ are only available via afpd.conf. To get them to work, each
+ user must have a ~/.passwd file (not read-/writeable by anyone
+ else) with a password. this is a potential security problem as
+ root can read the password. this may be compensated, to some
+ extent, by the fact that your password never goes onto the wire
+ when mounting a volume.
+
+ NOTE: you will need to get a copy of the des library if you
+ don't already have one for this option to work. i got mine
+ from <ftp://ftp.psy.uq.oz.au/pub/Crypto/DES/libdes-x.xx.tar.gz>
+
+ A Diffie-Hellman-based UAM is also available. This requires
+ libcrypto from either the SSLeay package (available at the
+ above site) or OpenSSL (ftp.openssl.org).
+
+ ADDITIONAL NOTE: the absence of a /dev/urandom or running out
+ of entropy will result a non truly-random number being used as
+ the challenge. you have been warned. for all intents and
+ purposes, however, linux' /dev/urandom should provide a
+ sufficiently random number to be considered secure even when
+ the entropy pool gets drained. it certainly does a much better
+ job than gettimeofday(); random().
+
+ the bad file descriptor bug should now be fixed. thanks to
+ bsmith@h-e.com for tracking this down.
+
+ this patchset should not have a problem with "dancing icons."
+ if you are still having a problem with this, it's highly
+ likely that files in your .AppleDouble directory have gotten
+ corrupted.
+
+ you can now login in with your "real" user name as specified
+ in your password entry. if you don't want to do this, just add
+ -DNO_REAL_USER_NAME to your DEFS line.
+
+ byte locks should now work. if you want to enable the old way
+ of doing things, add -DUSE_FLOCK_LOCKS.
+
+ you can now specify whether or not you want uservolume files
+ to be read. add -nouservol to afpd.conf if you don't want user-
+ specified .AppleVolumes files to be read.
+
+ afpd now will report the number of kilobytes read/written during
+ a session (from the server's perspective).
+
+ i have merged against netatalk-990130. this includes an
+ improved STREAMS driver and some changes to libatalk. the
+ STREAMS driver still doesn't do setsockopt correctly, but it's
+ supposed to be much more stable. contact the folks at umich if
+ you have questions about it.
+
+ fixed a problem with sys/netatalk/ddp_input.c reported by
+ <abs@anim.dreamworks.com>.
+
+ AppleVolumes.* now has many more configuration options. You
+ can specify newline translation (crlf) on a per-volume basis,
+ utilize a codepage translation file for compatibility with
+ other file serving programs, and restrict access to particular
+ volumes. Please read config/AppleVolumes.default for more
+ information.
+
+platforms compiled on:
+ linux/intel,sparc
+ linux/axp
+ *bsd
+ sunos4.1.4/sparc
+ ultrix/mips
+ solaris 2.5.x, 2.6, and 2.7.
+
+problems with appletalk:
+ certain ethernet card/drivers don't deal well with the fact
+ that appletalk aggressively uses hardware multicast. here are
+ a few ones that may cause problems:
+ ne2000 clones
+ 3Com501 cards (maybe others)
+ intel etherexpress/pro
+ set multicast_filter_limit=3 in linux if you're having
+ problems with this card. to do that, add the following
+ line to /etc/conf.modules:
+ options eepro100 multicast_filter_limit=3
+
+Acknowledgements:
+ i would like to thank leland wallace at apple for a lot of
+ helpful advice on interpreting the appleshare ip documentation.
+
+ i would also like to thank the numerous people who have helped
+ test this program. they greatly improved the compatability of
+ the code.
+
+ REALM Information provided financial support for the
+ AppleDouble v2 and CNID database work.
+
+adrian sun
+asun@cobaltnet.com
--- /dev/null
+desired features (in no particular order):
+ afpd:
+ return the right version-specific error codes
+ honor the readonly/deleteinhibit/renameinhibit/copyprotect bits.
+ via a database (that handles ro media, -db <path>):
+ Add afp_fileid capabilities - done
+ Add afp_catalogue
+ afp_enumerate optimization
+ cname expansion to handle 8+3 and 255-unicode.
+ server messages in useful places
+ Other useful bits of AFP 2.0/2.1. are there any?
+ change afp/ddp child handling to be in line w/ generic?
+ administrative control program (using asip API?)
+ appledouble v2 (gets us persistent DIDs). we'll need a did
+ database for non-hfs volumes. i think mmapping a
+ bitmap with dids at the root level should work.
+ also, v2 gets us much better prodos support and
+ provides for shortname support. - done
+ various toggles to afpd.conf (-timeout, etc.)
+ figure out more ways of optimizing things. current places
+ of interest:
+ 1) reading/writing. currently there's character
+ replacement. we should have a better way of doing
+ it that speeds things up the no-replacement case.
+ i've already done that for the crlf case.
+ 2) use of mmap where appropriate. NOTE: this will only
+ be a win if we mmap heavily used files. likely
+ candidates include file-based databases. mmapping
+ header files is actually pretty slow under linux.
+ 3) change lookup algorithms where appropriate.
+ ability to interact with hfs desktop databases (either by
+ changing hfs or changing afpd).
+
+ papd: implementing uams would be a good idea
+
+
+things to fix:
+ cleaner separation of layers.
+ fcntl-style byte locks - almost done. it just needs to do a
+ little sorting to get around a race condition.
+ AFP <-> Unix permissions. there are a couple cases where they
+ don't map perfectly. in particular, gid == 0 means
+ ignore group permissions while uid == 0 means anybody
+ can fiddle with this file. in addition, we need to be
+ able to still change permissions on a directory with u-a
+ set. finally, we need to adjust the offspring count
+ for directories based upon what permissions they
+ have. i.e., search -> can see directories.
+ read -> can see files.
+ we need to map permissions so that these semantics get
+ followed.
+ real variable DIDs for now: it turns out that afpd lies about
+ this and says that it's doing fixed dids. bad bad
+ bad. i just have to implement Open/CloseDir and fiddle
+ a little with did assignment. argh. apple's appleshare
+ client doesn't like non-fixed directory ids.
+
+added features:
+ more of AFP 2.0!
+ sped up of_findname and of_dealloc.
+ increased AFP compliance w.r.t. open forks and renames:
+ wordperfect and simpletext now both work!
+ nfs quota support
+ solaris beta STREAMS driver added.
+ 64-bit cleanup
+ cleaner startup/takedown
+ added debug messages to dsi_write areas.
+ fixed server info unexpected disconnects (due to OT bug).
+ afp/ddp and afp/tcp cohabitation. afp/ddp and afp/tcp can
+ operate separately now (-T turns off tcp, -D turns off ddp).
+ incorporated the netbsd patches
+ [source: wrstuden@loki.stanford.edu (Bill Studenmund)]
+ added/fixed quota support for linux/bsd4.4
+ codepage support/casefolding on a per volume basis.
+ expanded per-volume options
+ added "generic" platform support for AFP/tcp.
+ dynamically loaded uams
+ :ETCDIR:/afppasswd file for randnum passwds
+ can turn off .AppleDouble generation.
+ AppleVolumes variable substitions
+ atalkd: zones on single interfaces and the ability to control
+ which interfaces get routes between them.
--- /dev/null
+1.4b2+asun2.1.4
--- /dev/null
+ALL= aecho getzones nbp psorder megatron pap adv1tov2 afppasswd
+TAGSFILE=tags
+INSTALL= install
+
+all: ${ALL}
+
+${ALL}: FRC
+ cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}"\
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}"
+
+FRC:
+
+tags:
+ for i in ${ALL}; do \
+ (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" \
+ TAGSFILE=../${TAGSFILE} tags); \
+ done
+
+install:
+ -mkdir ${BINDIR}
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" INSTALL="${INSTALL}" install); \
+ done
+
+clean:
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+ done
+
+depend:
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} DEFS=${DEFS} depend); \
+ done
--- /dev/null
+SRC= adv1tov2.c
+OBJ= adv1tov2.o
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH}
+LIBS= -latalk ${ADDLIBS}
+TAGSFILE= tags
+CC= cc
+INSTALL= install
+LIBDIRS= -L../../libatalk
+
+TARGET= adv1tov2
+
+all : ${TARGET}
+
+${TARGET} : ${OBJ}
+ ${CC} ${CFLAGS} -o ${TARGET} ${OBJ} ${LIBDIRS} ${LIBS}
+
+install : all
+ ${INSTALL} -c ${TARGET} ${BINDIR}
+clean :
+ rm -f a.out core* *.o *.bak *[Ee]rrs tags
+ rm -f ${TARGET}
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/* v1tov2: given a root directory, run down and convert all the
+ * files/directories into appledouble v2. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <atalk/adouble.h>
+
+#if AD_VERSION == AD_VERSION2
+/* translate characters */
+static int xlate(char *name, int flags) {
+ static const char hexdig[] = "0123456789abcdef";
+ char upath[MAXPATHLEN + 1];
+ char *m, *u;
+ int doit = 0;
+
+ m = name;
+ u = upath;
+ while (*m != '\0') {
+ if (isascii(*m) && !isprint(*m)) {
+ doit = 1;
+ *u++ = ':';
+ *u++ = hexdig[(*m & 0xf0) >> 4];
+ *u++ = hexdig[*m & 0x0f];
+ } else
+ *u++ = *m;
+ m++;
+ }
+
+ if (doit) {
+ *u = '\0';
+ rename(name, upath);
+ if ((flags & ADFLAGS_DIR) == 0)
+ rename(ad_path(name, flags), ad_path(upath, flags)); /* rename rfork */
+ }
+}
+
+
+#define MAXDESCEND 0xFFFF
+/* recursively descend subdirectories.
+ * oh the stack space we use up! */
+void descend(DIR *dp)
+{
+ DIR *dpnew;
+ struct dirent *de;
+ unsigned char *m, *u;
+ struct stat st;
+ struct adouble ad;
+ int flags;
+ static int count = 0;
+
+ if (count++ > MAXDESCEND) {
+ fprintf(stderr, "FAILURE: too many subdirectories! possible infinite recursion.");
+ return;
+ }
+
+ putc('(', stderr);
+ for (de = readdir(dp); de; de = readdir(dp)) {
+ if (de->d_name[0] == '.')
+ continue;
+
+ if (stat(de->d_name, &st) < 0) {
+ fprintf(stderr, "FAILURE: can't stat %s\n", de->d_name);
+ continue;
+ }
+
+ /* go down subdirectory */
+ flags = ADFLAGS_HF;
+ if (S_ISDIR(st.st_mode) && (dpnew = opendir(de->d_name))) {
+ chdir(de->d_name);
+ descend(dpnew);
+ closedir(dpnew);
+ chdir("..");
+ flags |= ADFLAGS_DIR;
+ }
+
+ if (ad_open(de->d_name, flags, O_RDWR, 0, &ad) < 0) {
+ fprintf(stderr, "FAILURE: can't convert %s\n", de->d_name);
+ continue;
+ }
+ ad_close(&ad, ADFLAGS_HF);
+ xlate(de->d_name, flags);
+ fputc('.', stderr);
+ }
+ putc(')', stderr);
+}
+
+
+int main(int argc, char **argv)
+{
+ DIR *dp;
+ struct adouble ad;
+
+ if (argc != 2) {
+ fprintf(stderr, "%s <directory>\n", *argv);
+ return -1;
+ }
+
+ /* convert main directory */
+ if (ad_open(argv[1], ADFLAGS_HF | ADFLAGS_DIR, O_RDWR, 0,
+ &ad) < 0) {
+ fprintf(stderr, "FAILURE: can't convert %s\n", argv[1]);
+ return -1;
+ }
+
+ ad_close(&ad, ADFLAGS_HF);
+ if ((dp = opendir(argv[1])) == NULL) {
+ fprintf(stderr, "%s: unable to open %s\n", *argv, argv[1]);
+ return -1;
+ }
+
+ chdir(argv[1]);
+ descend(dp);
+ closedir(dp);
+ chdir("..");
+
+ xlate(argv[1], ADFLAGS_HF | ADFLAGS_DIR);
+ putc('\n', stderr);
+ return 0;
+}
+
+#else
+int main(int argc, char **argv)
+{
+ fprintf(stderr, "%s not built for v2 AppleDouble files.\n", *argv);
+ return -1;
+}
+#endif
--- /dev/null
+SRC = aecho.c
+OBJ = aecho.o
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH}
+LIBS= -latalk ${ADDLIBS}
+TAGSFILE= tags
+CC= cc
+INSTALL= install
+LIBDIRS= -L../../libatalk
+
+all : aecho
+
+aecho : ${OBJ} ../../libatalk/libatalk.a
+ ${CC} ${CFLAGS} -o aecho ${OBJ} ${LIBDIRS} ${LIBS}
+
+install : all
+ ${INSTALL} -c aecho ${BINDIR}
+
+clean :
+ rm -f a.out core* *.o *.bak *[Ee]rrs tags
+ rm -f aecho
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+/*
+ * AppleTalk Echo Protocol Client
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <netdb.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/compat.h>
+#include <atalk/aep.h>
+#include <atalk/nbp.h>
+#include <atalk/netddp.h>
+#include <atalk/ddp.h>
+
+/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature */
+#ifndef SOCKLEN_T
+#define SOCKLEN_T unsigned int
+#endif
+
+struct sockaddr_at target;
+int s, nsent = 0, nrecv = 0;
+time_t totalms = 0, minms = -1, maxms = -1;
+static unsigned int pings = 0;
+
+#if !defined( ibm032 ) && !defined( _IBMR2 )
+ void
+#endif ibm032 _IBMR2
+done()
+{
+ if ( nsent > 0 ) {
+ printf( "\n----%d.%d AEP Statistics----\n",
+ ntohs( target.sat_addr.s_net ), target.sat_addr.s_node );
+ printf( "%d packets sent, %d packets received, %d%% packet loss\n",
+ nsent, nrecv, (( nsent - nrecv ) * 100 ) / nsent );
+ if ( nrecv > 0 ) {
+ printf( "round-trip (ms) min/avg/max = %d/%d/%d\n",
+ minms, totalms / nrecv, maxms );
+ }
+ }
+ exit( 0 );
+}
+
+#if !defined( ibm032 ) && !defined( _IBMR2 )
+ void
+#endif ibm032 _IBMR2
+aep_send()
+{
+ struct timeval tv;
+ char *p, buf[ 1024 ];
+ static unsigned int seq = 0;
+
+ p = buf;
+ *p++ = DDPTYPE_AEP;
+ *p++ = AEPOP_REQUEST;
+ bcopy( &seq, p, sizeof( unsigned int ));
+ p += sizeof( unsigned int );
+ seq++;
+
+ if ( gettimeofday( &tv, (struct timezone *)0 ) < 0 ) {
+ perror( "gettimeofday" );
+ exit( 1 );
+ }
+ bcopy( &tv, p, sizeof( struct timeval ));
+ p += sizeof( struct timeval );
+
+ if ( netddp_sendto( s, buf, p - buf, 0, (struct sockaddr *) &target,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ perror( "sendto" );
+ exit( 1 );
+ }
+ nsent++;
+ if (pings && nsent > pings) done();
+}
+
+main( ac, av )
+ int ac;
+ char **av;
+{
+ struct servent *se;
+ struct sigaction sv;
+ struct itimerval it;
+ struct sockaddr_at sat, saddr;
+ struct timeval tv, atv;
+ struct nbpnve nn;
+ char *obj = NULL, *type = "Workstation", *zone = "*";
+ int cc;
+ SOCKLEN_T satlen;
+ unsigned int seq;
+ time_t ms;
+ char buf[ 1024 ], *p;
+ unsigned char port;
+
+ extern char *optarg;
+ extern int optind;
+
+ memset(&saddr, 0, sizeof(saddr));
+ while (( cc = getopt( ac, av, "c:A:" )) != EOF ) {
+ switch ( cc ) {
+ case 'A':
+ if (!atalk_aton(optarg, &saddr.sat_addr)) {
+ fprintf(stderr, "Bad address.\n");
+ exit(1);
+ }
+ case 'c' :
+ pings = atoi( optarg );
+ break;
+
+ default :
+ usage( av[ 0 ] );
+ exit( 1 );
+ }
+ }
+ if ( ac - optind != 1 ) {
+ usage( av[ 0 ] );
+ exit( 1 );
+ }
+
+ /*
+ * Save the port, since nbp_lookup calls getservbyname() to get the
+ * nbp port.
+ */
+ if (( se = getservbyname( "echo", "ddp" )) == NULL )
+ port = 4;
+ else
+ port = ntohs( se->s_port );
+
+ memset( &target, 0, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+ target.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ target.sat_family = AF_APPLETALK;
+ if ( !atalk_aton( av[ optind ], &target.sat_addr )) {
+ if ( nbp_name( av[ optind ], &obj, &type, &zone ) || !obj ) {
+ fprintf( stderr, "Bad name: %s\n", av[ optind ] );
+ exit( 1 );
+ }
+ if ( nbp_lookup( obj, type, zone, &nn, 1, &saddr.sat_addr) <= 0 ) {
+ fprintf( stderr, "Can't find: %s\n", av[ optind ] );
+ exit( 1 );
+ }
+ memcpy( &target, &nn.nn_sat, sizeof( struct sockaddr_at ));
+ }
+ target.sat_port = port;
+
+ if ((s = netddp_open(saddr.sat_addr.s_net || saddr.sat_addr.s_node ?
+ &saddr : NULL, NULL)) < 0) {
+ perror("ddp_open");
+ exit(1);
+ }
+
+ sv.sa_handler = aep_send;
+ sigemptyset( &sv.sa_mask );
+ sigaddset( &sv.sa_mask, SIGINT );
+ sv.sa_flags = SA_RESTART;
+ if ( sigaction( SIGALRM, &sv, (struct sigaction *)0 ) < 0 ) {
+ perror( "sigaction" );
+ exit( 1 );
+ }
+
+ sv.sa_handler = done;
+ sigemptyset( &sv.sa_mask );
+ sigaddset( &sv.sa_mask, SIGALRM );
+ sv.sa_flags = SA_RESTART;
+ if ( sigaction( SIGINT, &sv, (struct sigaction *)0 ) < 0 ) {
+ perror( "sigaction" );
+ exit( 1 );
+ }
+
+ it.it_interval.tv_sec = 1L;
+ it.it_interval.tv_usec = 0L;
+ it.it_value.tv_sec = 1L;
+ it.it_value.tv_usec = 0L;
+
+ if ( setitimer( ITIMER_REAL, &it, (struct itimerval *)0 ) < 0 ) {
+ perror( "setitimer" );
+ exit( 1 );
+ }
+
+ for (;;) {
+ satlen = sizeof( struct sockaddr_at );
+ if (( cc = netddp_recvfrom( s, buf, sizeof( buf ), 0,
+ (struct sockaddr *) &sat,
+ &satlen )) < 0 ) {
+ if ( errno == EINTR ) {
+ errno = 0;
+ continue;
+ } else {
+ perror( "recvfrom" );
+ exit( 1 );
+ }
+ }
+ p = buf;
+ if ( *p++ != DDPTYPE_AEP || *p++ != AEPOP_REPLY ) {
+ fprintf( stderr, "%s: bad packet!\n", av[ 0 ] );
+ continue;
+ }
+ if ( gettimeofday( &tv, (struct timezone *)0 ) < 0 ) {
+ perror( "gettimeofday" );
+ exit( 1 );
+ }
+ bcopy( p, &seq, sizeof( unsigned int ));
+ p += sizeof( unsigned int );
+ bcopy( p, &atv, sizeof( struct timeval ));
+ nrecv++;
+ ms = ( tv.tv_sec - atv.tv_sec ) * 1000 +
+ ( tv.tv_usec - atv.tv_usec ) / 1000;
+ totalms += ms;
+ if ( ms > maxms ) {
+ maxms = ms;
+ }
+ if ( ms < minms || minms == -1 ) {
+ minms = ms;
+ }
+ printf( "%d bytes from %u.%u: aep_seq=%d. time=%d. ms\n",
+ cc, ntohs( sat.sat_addr.s_net ), sat.sat_addr.s_node,
+ seq, ms );
+ if (pings && seq + 1 >= pings) done();
+ }
+}
+
+usage( av0 )
+{
+ fprintf( stderr, "usage:\t%s [-A source address ] [-c count] ( addr | nbpname )\n", av0 );
+ exit( 1 );
+}
--- /dev/null
+SRC = afppasswd.c
+OBJ = afppasswd.o
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH} ${CRYPTOINCPATH} ${CRYPTODEFS} \
+ ${CRACKINCPATH} ${CRACKDEFS}
+LIBS= ${ADDLIBS} ${CRYPTOLIBS} ${CRACKLIBS}
+TAGSFILE= tags
+CC= cc
+INSTALL= install
+LIBDIRS= ${CRYPTOLIBDIRS} ${CRACKLIBDIRS}
+
+all::
+ if [ x"${DESDIR}" != x ]; then \
+ CRYPTOLIBS="-ldes"; \
+ if [ "${DESDIR}" != "/usr" ]; then \
+ CRYPTOLIBDIRS="-L${DESDIR}/lib"; \
+ CRYPTOINCPATH="-I${DESDIR}/include"; \
+ fi; \
+ CRYPTODEFS="-DUAM_RNDNUM"; \
+ fi; \
+ if [ x"${CRYPTODIR}" != x ]; then \
+ CRYPTOLIBS="-lcrypto"; \
+ if [ "${CRYPTODIR}" != "/usr" ]; then \
+ CRYPTOLIBDIRS="-L${CRYPTODIR}/lib"; \
+ CRYPTOINCPATH="-I${CRYPTODIR}/include -I${CRYPTODIR}/include/openssl"; \
+ fi; \
+ CRYPTODEFS="-DUAM_RNDNUM"; \
+ fi; \
+ if [ x"${CRACKDIR}" != x ]; then \
+ CRACKLIBS="-lcrack"; \
+ if [ "${CRACKDIR}" != "/usr" ]; then \
+ CRACKLIBDIRS="-L${CRACKDIR}/lib"; \
+ CRACKINCPATH="-I${CRACKDIR}/include"; \
+ fi; \
+ CRACKDEFS="-DUSE_CRACKLIB -D_PATH_CRACKLIB=\\\"/usr/lib/cracklib_dict\\\""; \
+ fi; \
+ ${MAKE} ${MFLAGS} CC="${CC}" ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" \
+ OPTOPTS="${OPTOPTS}" DESDIR="${DESDIR}" \
+ CRYPTOLIBS="$${CRYPTOLIBS}" CRYPTOLIBDIRS="$${CRYPTOLIBDIRS}" \
+ CRYPTLIB="$${CRYPTLIB}" \
+ CRYPTOINCPATH="$${CRYPTOINCPATH}" CRYPTODEFS="$${CRYPTODEFS}" \
+ CRACKLIBS="$${CRACKLIBS}" \
+ CRACKINCPATH="$${CRACKINCPATH}" CRACKDEFS="$${CRACKDEFS}" \
+ afppasswd
+
+afppasswd.o: afppasswd.c
+ ${CC} ${CFLAGS} -D_PATH_AFPDPWFILE=\"${RESDIR}/afppasswd\" -c $<
+afppasswd : ${OBJ}
+ ${CC} ${CFLAGS} -o afppasswd ${OBJ} ${LIBDIRS} ${LIBS}
+
+install : all
+ ${INSTALL} -c afppasswd ${BINDIR}
+
+clean :
+ rm -f a.out core* *.o *.bak *[Ee]rrs tags
+ rm -f afppasswd
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/*
+ * Copyright 1999 (c) Adrian Sun (asun@u.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * format of the password file:
+ * name:****************:****************:********
+ * password last login date failed usage count
+ *
+ * ***'s are illegal. they're just place holders for hex values. hex
+ * values that represent actual numbers are in network byte order.
+ *
+ * last login date is currently a 4-byte number representing seconds
+ * since 1970 (UTC). there's enough space to extend it to 8 bytes.
+ *
+ * the last two fields aren't currently used by the randnum uams.
+ *
+ * root syntax: afppasswd [-c] [-a] [-p path] [-f] [username]
+ * user syntax: afppasswd
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <pwd.h>
+
+#include <netatalk/endian.h>
+
+#ifdef UAM_RNDNUM
+#include <des.h>
+
+#ifdef USE_CRACKLIB
+#include <crack.h>
+#endif
+
+#define OPT_ISROOT (1 << 0)
+#define OPT_CREATE (1 << 1)
+#define OPT_FORCE (1 << 2)
+#define OPT_ADDUSER (1 << 3)
+
+#define PASSWD_ILLEGAL '*'
+
+#define FORMAT ":****************:****************:********\n"
+#define FORMAT_LEN 44
+#define OPTIONS "cafu:p:"
+#define UID_START 100
+
+#define HEXPASSWDLEN 16
+#define PASSWDLEN 8
+
+static char buf[MAXPATHLEN + 1];
+
+/* if newpwd is null, convert buf from hex to binary. if newpwd isn't
+ * null, convert newpwd to hex and save it in buf. */
+#define unhex(x) (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
+static void convert_passwd(char *buf, char *newpwd, const int keyfd)
+{
+ u_int8_t key[HEXPASSWDLEN];
+ Key_schedule schedule;
+ int i, j;
+
+ if (!newpwd) {
+ /* convert to binary */
+ for (i = j = 0; i < sizeof(key); i += 2, j++)
+ buf[j] = (unhex(buf[i]) << 4) | unhex(buf[i + 1]);
+ if (j <= DES_KEY_SZ)
+ memset(buf + j, 0, sizeof(key) - j);
+ }
+
+ if (keyfd > -1) {
+ lseek(keyfd, 0, SEEK_SET);
+ read(keyfd, key, sizeof(key));
+ /* convert to binary */
+ for (i = j = 0; i < sizeof(key); i += 2, j++)
+ key[j] = (unhex(key[i]) << 4) | unhex(key[i + 1]);
+ if (j <= DES_KEY_SZ)
+ memset(key + j, 0, sizeof(key) - j);
+ key_sched((C_Block *) key, schedule);
+ memset(key, 0, sizeof(key));
+ if (newpwd) {
+ ecb_encrypt((C_Block *) newpwd, (C_Block *) newpwd, schedule,
+ DES_ENCRYPT);
+ } else {
+ /* decrypt the password */
+ ecb_encrypt((C_Block *) buf, (C_Block *) buf, schedule, DES_DECRYPT);
+ }
+ memset(schedule, 0, sizeof(schedule));
+ }
+
+ if (newpwd) {
+ const unsigned char hextable[] = "0123456789ABCDEF";
+
+ /* convert to hex */
+ for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
+ buf[j] = hextable[(newpwd[i] & 0xF0) >> 4];
+ buf[j + 1] = hextable[newpwd[i] & 0x0F];
+ }
+ }
+}
+
+/* this matches the code in uam_randnum.c */
+static int update_passwd(const char *path, const char *name, int flags)
+{
+ char password[PASSWDLEN + 1], *p, *passwd;
+ FILE *fp;
+ off_t pos;
+ int keyfd = -1, err = 0;
+
+ if ((fp = fopen(path, "r+")) == NULL) {
+ fprintf(stderr, "afppasswd: can't open %s\n", path);
+ return -1;
+ }
+
+ /* open the key file if it exists */
+ strcpy(buf, path);
+ if (strlen(path) < sizeof(buf) - 5) {
+ strcat(buf, ".key");
+ keyfd = open(buf, O_RDONLY);
+ }
+
+ pos = ftell(fp);
+ memset(buf, 0, sizeof(buf));
+ while (fgets(buf, sizeof(buf), fp)) {
+ if ((p = strchr(buf, ':'))) {
+ /* check for a match */
+ if (strncmp(buf, name, p - buf) == 0) {
+ p++;
+ if (!(flags & OPT_ISROOT) && (*p == PASSWD_ILLEGAL)) {
+ fprintf(stderr, "Your password is disabled. Please see your administrator.\n");
+ break;
+ }
+ goto found_entry;
+ }
+ }
+ pos = ftell(fp);
+ memset(buf, 0, sizeof(buf));
+ }
+
+ if (flags & OPT_ADDUSER) {
+ strcpy(buf, name);
+ strcat(buf, FORMAT);
+ p = strchr(buf, ':') + 1;
+ fwrite(buf, strlen(buf), 1, fp);
+ } else {
+ fprintf(stderr, "afppasswd: can't find %s in %s\n", name, path);
+ err = -1;
+ goto update_done;
+ }
+
+found_entry:
+ /* need to verify against old password */
+ if ((flags & OPT_ISROOT) == 0) {
+ passwd = getpass("Enter OLD AFP password: ");
+ convert_passwd(p, NULL, keyfd);
+ if (strncmp(passwd, p, PASSWDLEN)) {
+ fprintf(stderr, "afppasswd: invalid password.\n");
+ err = -1;
+ goto update_done;
+ }
+ }
+
+ /* new password */
+ passwd = getpass("Enter NEW AFP password: ");
+ memcpy(password, passwd, sizeof(password));
+ password[PASSWDLEN] = '\0';
+#ifdef USE_CRACKLIB
+ if ((passwd = FascistCheck(password, _PATH_CRACKLIB))) {
+ fprintf(stderr, "Error: %s\n", passwd);
+ err = -1;
+ goto update_done;
+ }
+#endif
+
+ passwd = getpass("Enter NEW AFP password again: ");
+ if (strcmp(passwd, password) == 0) {
+ struct flock lock;
+ int fd = fileno(fp);
+
+ convert_passwd(p, password, keyfd);
+ lock.l_type = F_WRLCK;
+ lock.l_start = pos;
+ lock.l_len = 1;
+ lock.l_whence = SEEK_SET;
+ fseek(fp, pos, SEEK_SET);
+ fcntl(fd, F_SETLKW, &lock);
+ fwrite(buf, p - buf + HEXPASSWDLEN, 1, fp);
+ lock.l_type = F_UNLCK;
+ fcntl(fd, F_SETLK, &lock);
+ printf("afppasswd: updated password.\n");
+
+ } else {
+ fprintf(stderr, "afppasswd: passwords don't match!\n");
+ err = -1;
+ }
+
+update_done:
+ if (keyfd > -1)
+ close(keyfd);
+ fclose(fp);
+ return err;
+}
+
+
+/* creates a file with all the password entries */
+static int create_file(const char *path, uid_t minuid)
+{
+ struct passwd *pwd;
+ int fd, len, err = 0;
+
+
+ if ((fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0600)) < 0) {
+ fprintf(stderr, "afppasswd: can't create %s\n", path);
+ return -1;
+ }
+
+ setpwent();
+ while ((pwd = getpwent())) {
+ if (pwd->pw_uid < minuid)
+ continue;
+ /* a little paranoia */
+ if (strlen(pwd->pw_name) + FORMAT_LEN > sizeof(buf) - 1)
+ continue;
+ strcpy(buf, pwd->pw_name);
+ strcat(buf, FORMAT);
+ len = strlen(buf);
+ if (write(fd, buf, len) != len) {
+ fprintf(stderr, "afppasswd: problem writing to %s: %m\n", path);
+ err = -1;
+ break;
+ }
+ }
+ endpwent();
+ close(fd);
+
+ return err;
+}
+
+
+int main(int argc, char **argv)
+{
+ struct stat st;
+ int flags;
+ uid_t uid_min = UID_START, uid;
+ char *path = _PATH_AFPDPWFILE;
+ int i, err = 0;
+
+ extern char *optarg;
+ extern int optind, opterr;
+
+ flags = ((uid = getuid()) == 0) ? OPT_ISROOT : 0;
+
+ if (((flags & OPT_ISROOT) == 0) && (argc > 1)) {
+ fprintf(stderr, "Usage: afppasswd\n");
+ return -1;
+ }
+
+ while ((i = getopt(argc, argv, OPTIONS)) != EOF) {
+ switch (i) {
+ case 'c': /* create and initialize password file or specific user */
+ flags |= OPT_CREATE;
+ break;
+ case 'a': /* add a new user */
+ flags |= OPT_ADDUSER;
+ case 'f': /* force an action */
+ flags |= OPT_FORCE;
+ break;
+ case 'u': /* minimum uid to use. default is 100 */
+ uid_min = atoi(optarg);
+ break;
+ case 'p': /* path to afppasswd file */
+ path = optarg;
+ break;
+ default:
+ err++;
+ break;
+ }
+ }
+
+ if (err || (optind + ((flags & OPT_CREATE) ? 0 :
+ (flags & OPT_ISROOT)) != argc)) {
+ fprintf(stderr, "Usage: afppasswd [-acf] [-u minuid] [-p path] [username]\n");
+ return -1;
+ }
+
+ i = stat(path, &st);
+ if (flags & OPT_CREATE) {
+ if ((flags & OPT_ISROOT) == 0) {
+ fprintf(stderr, "afppasswd: only root can create the password file.\n");
+ return -1;
+ }
+
+ if (!i && ((flags & OPT_FORCE) == 0)) {
+ fprintf(stderr, "afppasswd: password file already exists.\n");
+ return -1;
+ }
+ return create_file(path, uid_min);
+
+ } else {
+ struct passwd *pwd = NULL;
+
+ if (i < 0) {
+ fprintf(stderr, "afppasswd: %s doesn't exist.\n", path);
+ return -1;
+ }
+
+ /* if we're root, we need to specify the username */
+ pwd = (flags & OPT_ISROOT) ? getpwnam(argv[optind]) : getpwuid(uid);
+ if (pwd)
+ return update_passwd(path, pwd->pw_name, flags);
+
+ fprintf(stderr, "afppasswd: can't get password entry.\n");
+ return -1;
+ }
+}
+#else
+
+main(int argc, char **argv)
+{
+ fprintf(stderr, "afppasswd is only useful if you're using centralized passwords\n");
+ fprintf(stderr, "for the Random Number authentication methods.\n");
+ return -1;
+}
+#endif
+
--- /dev/null
+SRC = getzones.c
+OBJ = getzones.o
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH}
+LIBS= -latalk ${ADDLIBS}
+TAGSFILE= tags
+CC= cc
+INSTALL= install
+LIBDIRS= -L../../libatalk
+
+all : getzones
+
+getzones : ${OBJ} ../../libatalk/libatalk.a
+ ${CC} ${CFLAGS} -o getzones ${OBJ} ${LIBDIRS} ${LIBS}
+
+install : all
+ ${INSTALL} -c getzones ${BINDIR}
+
+clean :
+ rm -f a.out core* *.o *.bak *[Ee]rrs tags
+ rm -f getzones
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/zip.h>
+
+usage( s )
+ char *s;
+{
+ fprintf( stderr, "usage:\t%s [-m | -l] [address]\n", s );
+ exit( 1 );
+}
+
+main( argc, argv )
+ int argc;
+ char *argv[];
+{
+ struct atp_handle *ah;
+ struct atp_block atpb;
+ struct sockaddr_at saddr;
+ struct servent *se;
+ char reqdata[4], buf[ ATP_MAXDATA ];
+ struct iovec iov;
+ short temp, index = 0;
+ int c, myzoneflg = 0, localzonesflg = 0, errflg = 0;
+ extern int optind;
+
+ reqdata[ 0 ] = ZIPOP_GETZONELIST;
+
+ while (( c = getopt( argc, argv, "ml" )) != EOF ) {
+ switch (c) {
+ case 'm':
+ if ( localzonesflg ) {
+ ++errflg;
+ }
+ ++myzoneflg;
+ reqdata[ 0 ] = ZIPOP_GETMYZONE;
+ break;
+ case 'l':
+ if ( myzoneflg ) {
+ ++errflg;
+ }
+ ++localzonesflg;
+ reqdata[ 0 ] = ZIPOP_GETLOCALZONES;
+ break;
+ default:
+ ++errflg;
+ }
+ }
+
+ if ( errflg || argc - optind > 1 ) {
+ usage( argv[ 0 ] );
+ }
+
+ memset( &saddr, 0, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+ saddr.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ saddr.sat_family = AF_APPLETALK;
+ if (( se = getservbyname( "zip", "ddp" )) == NULL )
+ saddr.sat_port = 6;
+ else
+ saddr.sat_port = ntohs( se->s_port );
+
+ if ( argc == optind ) {
+ saddr.sat_addr.s_net = ATADDR_ANYNET;
+ saddr.sat_addr.s_node = ATADDR_ANYNODE;
+ } else {
+ if ( !atalk_aton( argv[ optind ], &saddr.sat_addr )) {
+ fprintf( stderr, "Bad address.\n" );
+ exit( 1 );
+ }
+ }
+
+ if (( ah = atp_open( ATADDR_ANYPORT, &saddr.sat_addr )) == NULL ) {
+ perror( "atp_open" );
+ exit( 1 );
+ }
+
+ index = ( myzoneflg ? 0 : 1 );
+ reqdata[1] = 0;
+
+ do {
+ atpb.atp_saddr = &saddr;
+ temp = htons( index );
+ memcpy( reqdata + 2, &temp, 2 );
+ atpb.atp_sreqdata = reqdata;
+ atpb.atp_sreqdlen = 4;
+ atpb.atp_sreqto = 2;
+ atpb.atp_sreqtries = 5;
+
+ /* send getzone request zones (or get my zone)
+ */
+ if ( atp_sreq( ah, &atpb, 1, 0 ) < 0 ) {
+ perror( "atp_sreq" );
+ exit( 1 );
+ }
+
+ iov.iov_base = buf;
+ iov.iov_len = ATP_MAXDATA;
+ atpb.atp_rresiov = &iov;
+ atpb.atp_rresiovcnt = 1;
+
+ if ( atp_rresp( ah, &atpb ) < 0 ) {
+ perror( "atp_rresp" );
+ exit( 1 );
+ }
+
+ memcpy( &temp, (char *) iov.iov_base + 2, 2 );
+ temp = ntohs( temp );
+ print_zones( temp, (char *) iov.iov_base+4 );
+ index += temp;
+ } while ( !myzoneflg && !((char *)iov.iov_base)[ 0 ] );
+
+ if ( atp_close( ah ) != 0 ) {
+ perror( "atp_close" );
+ exit( 1 );
+ }
+
+ exit( 0 );
+}
+
+
+print_zones( n, buf )
+ short n; /* number of zones in this packet */
+ char *buf; /* zone length/name pairs */
+{
+ for ( ; n--; buf += (*buf) + 1 ) {
+ printf( "%.*s\n", *buf, buf+1 );
+ }
+}
--- /dev/null
+SRC= hqx.c macbin.c megatron.c nad.c asingle.c updcrc.c
+OBJ= hqx.o macbin.o megatron.o nad.o asingle.o updcrc.o
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH}
+LIBS= -latalk ${ADDLIBS}
+TAGSFILE= tags
+CC= cc
+INSTALL= install
+LIBDIRS= -L../../libatalk
+
+LINKS= unbin unhex unsingle hqx2bin single2bin macbinary binheader nadheader
+TARGET= megatron
+
+all : ${TARGET}
+
+${TARGET} : ${OBJ}
+ ${CC} ${CFLAGS} -o ${TARGET} ${OBJ} ${LIBDIRS} ${LIBS}
+
+install : all
+ ${INSTALL} -c ${TARGET} ${BINDIR}
+ for i in ${LINKS} ; do \
+ rm -f ${BINDIR}/$$i; \
+ ln -s ${TARGET} ${BINDIR}/$$i; \
+ done
+
+clean :
+ rm -f a.out core* *.o *.bak *[Ee]rrs tags
+ rm -f ${TARGET}
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <string.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <atalk/adouble.h>
+#include <netatalk/endian.h>
+#include "megatron.h"
+
+#define DEBUG 0
+
+/* String used to indicate standard input instead of a disk
+ file. Should be a string not normally used for a file
+ */
+#ifndef STDIN
+# define STDIN "-"
+#endif
+
+/* Yes and no
+ */
+#define NOWAY 0
+#define SURETHANG 1
+
+/* This structure holds an entry description, consisting of three
+ four byte entities. The first is the Entry ID, the second is
+ the File Offset and the third is the Length.
+ */
+
+
+/* Both input and output routines use this struct and the
+ following globals; therefore this module can only be used
+ for one of the two functions at a time.
+ */
+struct single_file_data {
+ int filed;
+ char path[ MAXPATHLEN + 1];
+ struct ad_entry entry[ ADEID_MAX ];
+} single;
+
+extern char *forkname[];
+u_char header_buf[ AD_HEADER_LEN ];
+
+/*
+ * single_open must be called first. pass it a filename that is supposed
+ * to contain a AppleSingle file. an single struct will be allocated and
+ * somewhat initialized; single_filed is set.
+ */
+
+single_open( singlefile, flags, fh, options )
+ char *singlefile;
+ int flags, options;
+ struct FHeader *fh;
+{
+ int rc;
+
+ if ( flags == O_RDONLY ) {
+ if ( strcmp( singlefile, STDIN ) == 0 ) {
+ single.filed = fileno( stdin );
+ } else if (( single.filed = open( singlefile, flags )) < 0 ) {
+ perror( singlefile );
+ return( -1 );
+ }
+ strncpy( single.path, singlefile, MAXPATHLEN );
+#if DEBUG
+ fprintf( stderr, "opened %s for read\n", single.path );
+#endif
+ if ((( rc = single_header_test()) > 0 ) &&
+ ( single_header_read( fh, rc ) == 0 )) {
+ return( 0 );
+ }
+ single_close( KEEP );
+ return( -1 );
+ }
+}
+
+/*
+ * single_close must be called before a second file can be opened using
+ * single_open. Upon successful completion, a value of 0 is returned.
+ * Otherwise, a value of -1 is returned.
+ */
+
+single_close( keepflag )
+ int keepflag;
+{
+ if ( keepflag == KEEP ) {
+ return( close( single.filed ));
+ } else if ( keepflag == TRASH ) {
+ if (( strcmp( single.path, STDIN ) != 0 ) &&
+ ( unlink( single.path ) < 0 )) {
+ perror ( single.path );
+ }
+ return( 0 );
+ } else return( -1 );
+}
+
+/*
+ * single_header_read is called by single_open, and before any information
+ * can read from the fh substruct. it must be called before any of the
+ * bytes of the other two forks can be read, as well.
+ */
+
+single_header_read( fh, version )
+ struct FHeader *fh;
+ int version;
+{
+/*
+ * entry_buf is used for reading in entry descriptors, and for reading in
+ * the actual entries of FILEINFO, FINDERINFO, and DATES.
+ */
+ u_char entry_buf[ 16 ];
+ u_int32_t entry_id;
+ u_int32_t time_seconds;
+ u_short mask = 0xfcee;
+ u_short num_entries;
+ int n;
+ int readlen;
+ int date_entry;
+ off_t pos;
+
+/*
+ * Go through and initialize the array of entry_info structs. Read in the
+ * number of entries, and then read in the info for each entry and save it
+ * in the array.
+ */
+
+ for ( n = 0 ; n < ADEID_MAX; n++ ) {
+ single.entry[ n ].ade_off = 0;
+ single.entry[ n ].ade_len = 0;
+ }
+ memcpy( &num_entries, header_buf + 24, sizeof( num_entries ));
+ num_entries = ntohs( num_entries );
+#if DEBUG >= 2
+ fprintf( stderr, "The number of entries is %d\n", num_entries );
+#endif
+ for ( ; num_entries > 0 ; num_entries-- ) {
+ if ( read( single.filed, (char *)entry_buf, AD_ENTRY_LEN )
+ != AD_ENTRY_LEN ) {
+ perror( "Premature end of file :" );
+ return( -1 );
+ }
+ memcpy(&entry_id, entry_buf, sizeof( entry_id ));
+ entry_id = ntohl( entry_id );
+ memcpy(&single.entry[ entry_id ].ade_off,
+ entry_buf + 4,
+ sizeof( single.entry[ entry_id ].ade_off ));
+ single.entry[ entry_id ].ade_off =
+ ntohl( single.entry[ entry_id ].ade_off );
+ memcpy(&single.entry[ entry_id ].ade_len,
+ entry_buf + 8,
+ sizeof( single.entry[ entry_id ].ade_len ));
+ single.entry[ entry_id ].ade_len =
+ ntohl( single.entry[ entry_id ].ade_len );
+#if DEBUG >= 2
+ fprintf( stderr, "entry_id\t%d\n", entry_id );
+ fprintf( stderr, "\toffset\t\t%d\n", single.entry[ entry_id ].ade_off );
+ fprintf( stderr, "\tlength\t\t%d\n", single.entry[ entry_id ].ade_len );
+#endif
+ }
+
+/*
+ * Now that the entries have been identified, check to make sure
+ * it is a Macintosh file if dealing with version two format file.
+ */
+
+ if ( version == 1 ) {
+ if ( single.entry[ ADEID_FILEI ].ade_len > 0 ) {
+ date_entry = ADEID_FILEI;
+ } else {
+ date_entry = 0;
+ }
+ } else if ( version == 2 ) {
+ if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 ) {
+ date_entry = ADEID_FILEDATESI;
+ } else {
+ date_entry = 0;
+ }
+ }
+#if DEBUG
+ fprintf( stderr, "date_entry = %d\n", date_entry );
+#endif
+
+/*
+ * Go through and copy all the information you can get from
+ * the informational entries into the fh struct. The ENTRYID_DATA
+ * must be the last one done, because it leaves the file pointer in
+ * the right place for the first read of the data fork.
+ */
+
+ if ( single.entry[ ADEID_NAME ].ade_off == 0 ) {
+ fprintf( stderr, "%s has no name for the mac file.\n", single.path );
+ return( -1 );
+ } else {
+ pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off,
+ SEEK_SET );
+ readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ?
+ ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len;
+ if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) {
+ perror( "Premature end of file :" );
+ return( -1 );
+ }
+ }
+ if (( single.entry[ ADEID_FINDERI ].ade_len < 16 ) ||
+ ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
+ fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
+ return( -1 );
+ } else {
+ pos = lseek( single.filed,
+ single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
+ if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
+ sizeof( entry_buf )) {
+ perror( "Premature end of file :" );
+ return( -1 );
+ }
+ memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,
+ sizeof( fh->finder_info.fdType ));
+ memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
+ sizeof( fh->finder_info.fdCreator ));
+ memcpy( &fh->finder_info.fdFlags, entry_buf + FINDERIOFF_FLAGS,
+ sizeof( fh->finder_info.fdFlags ));
+ fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
+ memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
+ sizeof( fh->finder_info.fdLocation ));
+ memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
+ sizeof( fh->finder_info.fdFldr ));
+ fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
+ fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);
+
+#if DEBUG
+ {
+ char type[5];
+ char creator[5];
+
+ strncpy( type, &fh->finder_info.fdType, 4 );
+ strncpy( creator, &fh->finder_info.fdCreator, 4 );
+ type[4] = creator[4] = '\0';
+ fprintf( stderr, "type is %s, creator is %s\n", type, creator );
+ }
+#endif
+ }
+ if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) ||
+ ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
+ fh->comment[0] = '\0';
+ } else {
+ pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
+ SEEK_SET );
+ readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
+ ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
+ if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
+ perror( "Premature end of file :" );
+ return( -1 );
+ }
+ }
+/*
+ * If date_entry is 7, we have an AppleSingle version one, do the
+ * appropriate stuff. If it is 8, we have an AppleSingle version two,
+ * do the right thing. If date_entry is neither, just use the current date.
+ * Unless I can't get the current date, in which case use time zero.
+ */
+ if (( date_entry < 7 ) || ( date_entry > 8 )) {
+ if (( time_seconds = time( NULL )) == -1 ) {
+ time_seconds = AD_DATE_START;
+ } else {
+ time_seconds = AD_DATE_FROM_UNIX(time_seconds);
+ }
+ memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
+ memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
+ fh->backup_date = AD_DATE_START;
+ } else if ( single.entry[ date_entry ].ade_len != 16 ) {
+ fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n",
+ single.path );
+ return( -1 );
+ } else if ( date_entry == ADEID_FILEI ) {
+ pos = lseek( single.filed,
+ single.entry[ date_entry ].ade_off, SEEK_SET );
+ if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
+ sizeof( entry_buf )) {
+ perror( "Premature end of file :" );
+ return( -1 );
+ }
+ memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
+ sizeof( fh->create_date ));
+ memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
+ sizeof( fh->mod_date ));
+ memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
+ sizeof(fh->backup_date));
+ } else if ( date_entry == ADEID_FILEDATESI ) {
+ pos = lseek( single.filed,
+ single.entry[ date_entry ].ade_off, SEEK_SET );
+ if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
+ sizeof( entry_buf )) {
+ perror( "Premature end of file :" );
+ return( -1 );
+ }
+ memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
+ sizeof( fh->create_date ));
+ memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
+ sizeof( fh->mod_date ));
+ memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
+ sizeof(fh->backup_date));
+ }
+ if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
+ fh->forklen[ ADEID_RFORK ] = 0;
+ } else {
+ fh->forklen[ ADEID_RFORK ] =
+ htonl( single.entry[ ADEID_RFORK ].ade_len );
+ }
+ if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
+ fh->forklen[ DATA ] = 0;
+ } else {
+ fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
+ pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
+ }
+
+ return( 0 );
+}
+
+/*
+ * single_header_test is called from single_open. It checks certain
+ * values of the file and determines if the file is an AppleSingle version
+ * one file something else, and returns a one, or negative one to indicate
+ * file type.
+ *
+ * The Magic Number of the file, the first four bytes, must be hex
+ * 0x00051600. Bytes 4 through 7 are the version number and must be hex
+ * 0x00010000. Bytes 8 through 23 identify the home file system, and we
+ * are only interested in files from Macs. Therefore these bytes must
+ * contain hex 0x4d6163696e746f736820202020202020 which is ASCII
+ * "Macintosh " (that is seven blanks of padding).
+ */
+#define MACINTOSH "Macintosh "
+u_char sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+
+single_header_test()
+{
+ int cc;
+ u_int32_t templong;
+
+ cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
+ if ( cc < sizeof( header_buf )) {
+ perror( "Premature end of file :" );
+ return( -1 );
+ }
+
+ memcpy( &templong, header_buf, sizeof( templong ));
+ if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
+ fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
+ return( -1 );
+ }
+
+ memcpy(&templong, header_buf + 4, sizeof( templong ));
+ templong = ntohl( templong );
+ if ( templong == AD_VERSION1 ) {
+ cc = 1;
+ if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 )
+ != 0 ) {
+ fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n",
+ single.path );
+ return( -1 );
+ }
+ } else if ( templong == AD_VERSION2 ) {
+ cc = 2;
+ if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
+ != 0 ) {
+ fprintf( stderr,
+ "Warning: %s may be a corrupt AppleSingle file.\n",
+ single.path );
+ return( -1 );
+ }
+ } else {
+ fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
+ single.path );
+ return( -1 );
+ }
+
+ return( cc );
+}
+
+/*
+ * single_read is called until it returns zero for each fork. When
+ * it returns zero for the first fork, it seeks to the proper place
+ * to read in the next, if there is one. single_read must be called
+ * enough times to return zero for each fork and no more.
+ *
+ */
+
+single_read( fork, buffer, length )
+ int fork;
+ char *buffer;
+ int length;
+{
+ u_int32_t entry_id;
+ char *buf_ptr;
+ int readlen;
+ int cc = 1;
+ off_t pos;
+
+ switch ( fork ) {
+ case DATA :
+ entry_id = ADEID_DFORK;
+ break;
+ case RESOURCE :
+ entry_id = ADEID_RFORK;
+ break;
+ default :
+ return( -1 );
+ break;
+ }
+
+ if ( single.entry[ entry_id ].ade_len < 0 ) {
+ fprintf( stderr, "single_read: Trying to read past end of fork!\n" );
+ return( single.entry[ entry_id ].ade_len );
+ }
+ if ( single.entry[ entry_id ].ade_len == 0 ) {
+ if ( fork == DATA ) {
+ pos = lseek( single.filed,
+ single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
+ }
+ return( 0 );
+ }
+
+ if ( single.entry[ entry_id ].ade_len < length ) {
+ readlen = single.entry[ entry_id ].ade_len;
+ } else {
+ readlen = length;
+ }
+
+ buf_ptr = buffer;
+ while (( readlen > 0 ) && ( cc > 0 )) {
+ if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
+ readlen -= cc;
+ buf_ptr += cc;
+ }
+ }
+ if ( cc >= 0 ) {
+ cc = buf_ptr - buffer;
+ single.entry[ entry_id ].ade_len -= cc;
+ }
+
+ return( cc );
+}
--- /dev/null
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#ifdef notdef
+#if BSD >= 199006
+# include <machine/endian.h>
+#else
+# include <netinet/in.h>
+#endif
+#endif notdef
+#include <fcntl.h>
+#include <string.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <atalk/adouble.h>
+#include <netatalk/endian.h>
+#include "megatron.h"
+
+#define DEBUG 0
+#define HEXOUTPUT 0
+
+/* String used to indicate standard input instead of a disk
+ file. Should be a string not normally used for a file
+ */
+#ifndef STDIN
+# define STDIN "-"
+#endif
+
+/* Yes and no
+ */
+#define NOWAY 0
+#define SURETHANG 1
+
+/* Looking for the first or any other line of a binhex file
+ */
+#define FIRST 0
+#define OTHER 1
+
+/* This is the binhex run length encoding character
+ */
+#define RUNCHAR 0x90
+
+/* These are field sizes in bytes of various pieces of the
+ binhex header
+ */
+#define VERSION 1
+#define TCSIZ 8
+#define FLAGSIZ 2
+#define DATASIZ 4
+#define RESSIZ 4
+#define CRCSIZ 2
+#define HEADSIZ 21
+
+u_short updcrc();
+
+#if HEXOUTPUT
+FILE *rawhex, *expandhex;
+#endif
+
+struct hqx_file_data {
+ u_int32_t forklen[ NUMFORKS ];
+ u_short forkcrc[ NUMFORKS ];
+ char path[ MAXPATHLEN + 1];
+ u_short headercrc;
+ int filed;
+} hqx;
+
+extern char *forkname[];
+u_char hqx7_buf[8192];
+u_char *hqx7_first;
+u_char *hqx7_last;
+int first_flag;
+
+/*
+hqx_open must be called first. pass it a filename that is supposed
+to contain a binhqx file. an hqx struct will be allocated and
+somewhat initialized; hqx_fd is set. skip_junk is called from
+here; skip_junk leaves hqx7_first and hqx7_last set.
+ */
+
+hqx_open( hqxfile, flags, fh, options )
+ char *hqxfile;
+ int flags, options;
+ struct FHeader *fh;
+{
+ int maxlen;
+
+#if DEBUG
+ fprintf( stderr, "megatron: entering hqx_open\n" );
+#endif
+ if ( flags == O_RDONLY ) {
+
+#if HEXOUTPUT
+ rawhex = fopen( "rawhex.unhex", "w" );
+ expandhex = fopen( "expandhex.unhex", "w" );
+#endif
+
+ first_flag = 0;
+
+ if ( strcmp( hqxfile, STDIN ) == 0 ) {
+ hqx.filed = fileno( stdin );
+ } else if (( hqx.filed = open( hqxfile, O_RDONLY )) < 0 ) {
+ perror( hqxfile );
+ return( -1 );
+ }
+
+ if ( skip_junk( FIRST ) == 0 ) {
+ if ( hqx_header_read( fh ) == 0 ) {
+#if DEBUG
+ off_t pos;
+
+ pos = lseek( hqx.filed, 0, L_INCR );
+ fprintf( stderr, "megatron: current position is %ld\n", pos );
+#endif
+ return( 0 );
+ }
+ }
+ hqx_close( KEEP );
+ fprintf( stderr, "%s\n", hqxfile );
+ return( -1 );
+ } else {
+ maxlen = sizeof( hqx.path ) -1;
+ strncpy( hqx.path, fh->name, maxlen );
+ strncpy( hqx.path, mtoupath( hqx.path ), maxlen );
+ strncat( hqx.path, ".hqx", maxlen - strlen( hqx.path ));
+ if (( hqx.filed = open( hqx.path, flags, 0666 )) < 0 ) {
+ perror( hqx.path );
+ return( -1 );
+ }
+ if ( hqx_header_write( fh ) != 0 ) {
+ hqx_close( TRASH );
+ fprintf( stderr, "%s\n", hqx.path );
+ return( -1 );
+ }
+ return( 0 );
+ }
+}
+
+/*
+ * hqx_close must be called before a second file can be opened using
+ * hqx_open. Upon successful completion, a value of 0 is returned.
+ * Otherwise, a value of -1 is returned.
+ */
+
+hqx_close( keepflag )
+ int keepflag;
+{
+ if ( keepflag == KEEP ) {
+ return( close( hqx.filed ));
+ } else if ( keepflag == TRASH ) {
+ if (( strcmp( hqx.path, STDIN ) != 0 ) && ( unlink( hqx.path ) < 0 )) {
+ perror( hqx.path );
+ }
+ return( 0 );
+ } else return( -1 );
+}
+
+/*
+ * hqx_read is called until it returns zero for each fork. when it is
+ * and finds that there is zero left to give, it reads in and compares
+ * the crc with the calculated one, and returns zero if all is well.
+ * it returns negative is the crc was bad or if has been called too many
+ * times for the same fork. hqx_read must be called enough times to
+ * return zero and no more than that.
+ */
+
+hqx_read( fork, buffer, length )
+ int fork;
+ char *buffer;
+ int length;
+{
+ u_short storedcrc;
+ int readlen;
+ int cc;
+
+#if DEBUG >= 3
+ {
+ off_t pos;
+ pos = lseek( hqx.filed, 0, L_INCR );
+ fprintf( stderr, "hqx_read: current position is %ld\n", pos );
+ }
+ fprintf( stderr, "hqx_read: fork is %s\n", forkname[ fork ] );
+ fprintf( stderr, "hqx_read: remaining length is %d\n", hqx.forklen[fork] );
+#endif
+
+ if ( hqx.forklen[ fork ] < 0 ) {
+ fprintf( stderr, "This should never happen, dude!\n" );
+ return( hqx.forklen[ fork ] );
+ }
+
+ if ( hqx.forklen[ fork ] == 0 ) {
+ cc = hqx_7tobin( (char *)&storedcrc, sizeof( storedcrc ));
+ if ( cc == sizeof( storedcrc )) {
+ storedcrc = ntohs ( storedcrc );
+#if DEBUG >= 4
+ fprintf( stderr, "hqx_read: storedcrc\t\t%x\n", storedcrc );
+ fprintf( stderr, "hqx_read: observed crc\t\t%x\n\n", hqx.forkcrc[fork] );
+#endif
+ if ( storedcrc == hqx.forkcrc[ fork ] ) {
+ return( 0 );
+ }
+ fprintf( stderr, "hqx_read: Bad %s fork crc, dude\n",
+ forkname[ fork ] );
+ }
+ return( -1 );
+ }
+
+ if ( hqx.forklen[ fork ] < length ) {
+ readlen = hqx.forklen[ fork ];
+ } else {
+ readlen = length;
+ }
+#if DEBUG >= 3
+ fprintf( stderr, "hqx_read: readlen is %d\n", readlen );
+#endif
+
+ cc = hqx_7tobin( buffer, readlen );
+ if ( cc > 0 ) {
+ hqx.forkcrc[ fork ] =
+ updcrc( hqx.forkcrc[ fork ], (u_char *)buffer, cc );
+ hqx.forklen[ fork ] -= cc;
+ }
+#if DEBUG >= 3
+ fprintf( stderr, "hqx_read: chars read is %d\n", cc );
+#endif
+ return( cc );
+}
+
+/*
+ * hqx_header_read is called by hqx_open, and before any information can
+ * read from the hqx_header substruct. it must be called before any
+ * of the bytes of the other two forks can be read, as well.
+ * returns a negative number if it was unable to pull enough information
+ * to fill the hqx_header fields.
+ */
+
+hqx_header_read( fh )
+ struct FHeader *fh;
+{
+ char *headerbuf, *headerptr;
+ u_int32_t time_seconds;
+ u_short mask;
+ u_short header_crc;
+ char namelen;
+
+#if HEXOUTPUT
+ int headerfork;
+ headerfork = open( "headerfork", O_WRONLY|O_CREAT, 0622 );
+#endif
+
+ mask = htons( 0xfcee );
+ hqx.headercrc = 0;
+
+ if ( hqx_7tobin( &namelen, sizeof( namelen )) == 0 ) {
+ fprintf( stderr, "Premature end of file :" );
+ return( -2 );
+ }
+ hqx.headercrc = updcrc( hqx.headercrc, (u_char *)&namelen,
+ sizeof( namelen ));
+
+#if HEXOUTPUT
+ write( headerfork, &namelen, sizeof( namelen ));
+#endif
+
+ if (( headerbuf =
+ (char *)malloc( (unsigned int)( namelen + HEADSIZ ))) == 0 ) {
+ return( -1 );
+ }
+ if ( hqx_7tobin( headerbuf, ( namelen + HEADSIZ )) == 0 ) {
+ free( headerbuf );
+ fprintf( stderr, "Premature end of file :" );
+ return( -2 );
+ }
+ headerptr = headerbuf;
+ hqx.headercrc = updcrc( hqx.headercrc,
+ (u_char *)headerbuf, ( namelen + HEADSIZ - CRCSIZ ));
+
+#if HEXOUTPUT
+ write( headerfork, headerbuf, ( namelen + HEADSIZ ));
+#endif
+
+/*
+ * stuff from the hqx file header
+ */
+
+ memcpy( fh->name, headerptr, (int)namelen );
+ headerptr += namelen;
+ headerptr += VERSION;
+ memcpy(&fh->finder_info, headerptr, TCSIZ );
+ headerptr += TCSIZ;
+ memcpy(&fh->finder_info.fdFlags, headerptr, FLAGSIZ );
+ fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
+ headerptr += FLAGSIZ;
+ memcpy(&fh->forklen[ DATA ], headerptr, DATASIZ );
+ hqx.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
+ headerptr += DATASIZ;
+ memcpy( &fh->forklen[ RESOURCE ], headerptr, RESSIZ );
+ hqx.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
+ headerptr += RESSIZ;
+ memcpy(&header_crc, headerptr, CRCSIZ );
+ headerptr += CRCSIZ;
+ header_crc = ntohs( header_crc );
+
+/*
+ * stuff that should be zero'ed out
+ */
+
+ fh->comment[0] = '\0';
+ fh->finder_info.fdLocation = 0;
+ fh->finder_info.fdFldr = 0;
+
+#if DEBUG >= 5
+ {
+ short flags;
+
+ fprintf( stderr, "Values read by hqx_header_read\n" );
+ fprintf( stderr, "name length\t\t%d\n", namelen );
+ fprintf( stderr, "file name\t\t%s\n", fh->name );
+ fprintf( stderr, "get info comment\t%s\n", fh->comment );
+ fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
+ &fh->finder_info.fdType );
+ fprintf( stderr, "creator\t\t\t%.*s\n",
+ sizeof( fh->finder_info.fdCreator ),
+ &fh->finder_info.fdCreator );
+ memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
+ flags = ntohs( flags );
+ fprintf( stderr, "flags\t\t\t%x\n", flags );
+ fprintf( stderr, "data fork length\t%ld\n", hqx.forklen[DATA] );
+ fprintf( stderr, "resource fork length\t%ld\n", hqx.forklen[RESOURCE] );
+ fprintf( stderr, "header_crc\t\t%x\n", header_crc );
+ fprintf( stderr, "observed crc\t\t%x\n", hqx.headercrc );
+ fprintf( stderr, "\n" );
+ }
+#endif
+
+/*
+ * create and modify times are figured from right now
+ */
+
+ time_seconds = AD_DATE_FROM_UNIX(time( NULL ));
+ memcpy( &fh->create_date, &time_seconds,
+ sizeof( fh->create_date ));
+ memcpy( &fh->mod_date, &time_seconds,
+ sizeof( fh->mod_date ));
+ fh->backup_date = AD_DATE_START;
+
+/*
+ * stuff that should be zero'ed out
+ */
+
+ fh->comment[0] = '\0';
+ memset( &fh->finder_info.fdLocation, 0,
+ sizeof( fh->finder_info.fdLocation ));
+ memset( &fh->finder_info.fdFldr, 0, sizeof( fh->finder_info.fdFldr ));
+
+ hqx.forkcrc[ DATA ] = 0;
+ hqx.forkcrc[ RESOURCE ] = 0;
+
+ free( headerbuf );
+ if ( header_crc != hqx.headercrc ) {
+ fprintf( stderr, "Bad Header crc, dude :" );
+ return( -3 );
+ }
+ return( 0 );
+}
+
+/*
+ * hqx_header_write.
+ */
+
+hqx_header_write( fh )
+ struct FHeader *fh;
+{
+ return( -1 );
+}
+
+/*
+ * hqx7_fill is called from skip_junk and hqx_7tobin. it pulls from the
+ * binhqx file into the hqx7 buffer. returns number of bytes read
+ * or a zero for end of file.
+ * it sets the pointers to the hqx7 buffer up to point to the valid data.
+ */
+
+hqx7_fill( hqx7_ptr )
+ u_char *hqx7_ptr;
+{
+ int cc;
+ int cs;
+
+ cs = hqx7_ptr - hqx7_buf;
+ if ( cs >= sizeof( hqx7_buf )) return( -1 );
+ hqx7_first = hqx7_ptr;
+ cc = read( hqx.filed, (char *)hqx7_first, ( sizeof( hqx7_buf ) - cs ));
+ if ( cc < 0 ) {
+ perror( "" );
+ return( cc );
+ }
+ hqx7_last = ( hqx7_first + cc );
+ return( cc );
+}
+
+/*
+char tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
+ 0 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+ 0 1 2 3
+Input characters are translated to a number between 0 and 63 by direct
+array lookup. 0xFF signals a bad character. 0xFE is signals a legal
+character that should be skipped, namely '\n', '\r'. 0xFD signals ':'.
+0xFC signals a whitespace character.
+*/
+
+u_char hqxlookup[] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFC, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFC, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0xFF, 0xFF,
+ 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xFF,
+ 0x14, 0x15, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
+ 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0xFF,
+ 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0xFF,
+ 0x2C, 0x2D, 0x2E, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF,
+ 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0xFF, 0xFF,
+ 0x3D, 0x3E, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+/*
+ * skip_junk is called from hqx_open. it skips over junk in the file until
+ * it comes to a line containing a valid first line of binhqx encoded file.
+ * returns a 0 for success, negative if it never finds good data.
+ * pass a FIRST when looking for the first valid binhex line, a value of
+ * OTHER when looking for any subsequent line.
+ */
+
+skip_junk( line )
+int line;
+{
+ int found = NOWAY;
+ int stopflag;
+ int nc = 0;
+ u_char c;
+ u_char prevchar;
+
+ if ( line == FIRST ) {
+ if ( hqx7_fill( hqx7_buf ) <= 0 ) {
+ fprintf( stderr, "Premature end of file :" );
+ return( -1 );
+ }
+ }
+
+ while ( found == NOWAY ) {
+ if ( line == FIRST ) {
+ if ( *hqx7_first == ':' ) {
+ nc = c = 0;
+ stopflag = NOWAY;
+ hqx7_first++;
+ while (( stopflag == NOWAY ) &&
+ ( nc < ( hqx7_last - hqx7_first ))) {
+ switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
+ case 0xFC :
+ case 0xFF :
+ case 0xFE :
+ case 0xFD :
+ stopflag = SURETHANG;
+ break;
+ default :
+ nc++;
+ break;
+ }
+ }
+ if (( nc > 30 ) && ( nc < 64 ) &&
+ (( c == 0xFE ) || ( c == 0xFD ))) found = SURETHANG;
+ } else {
+ hqx7_first++;
+ }
+ } else {
+ if (( prevchar = hqxlookup[ *hqx7_first ] ) == 0xFE ) {
+ nc = c = 0;
+ stopflag = NOWAY;
+ hqx7_first++;
+ while (( stopflag == NOWAY ) &&
+ ( nc < ( hqx7_last - hqx7_first ))) {
+ switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
+ case 0xFC :
+ case 0xFE :
+ if (( prevchar == 0xFC ) || ( prevchar == 0xFE )) {
+ nc++;
+ break;
+ }
+ case 0xFF :
+ case 0xFD :
+ stopflag = SURETHANG;
+ break;
+ default :
+ prevchar = c;
+ nc++;
+ break;
+ }
+ }
+ if ( c == 0xFD ) {
+ found = SURETHANG;
+ } else if (( nc > 30 ) && ( c == 0xFE )) {
+ found = SURETHANG;
+ }
+ } else {
+ hqx7_first++;
+ }
+ }
+
+ if (( hqx7_last - hqx7_first ) == nc ) {
+ if ( line == FIRST ) {
+ *hqx7_buf = ':';
+ } else *hqx7_buf = '\n';
+ memcpy(hqx7_buf + 1, hqx7_first, nc );
+ hqx7_first = hqx7_buf + ( ++nc );
+ if ( hqx7_fill( hqx7_first ) <= 0 ) {
+ fprintf( stderr, "Premature end of file :" );
+ return( -1 );
+ }
+ hqx7_first = hqx7_buf;
+ }
+ }
+
+ return( 0 );
+}
+
+/*
+ * hqx_7tobin is used to read the data, converted to binary. It is
+ * called by hqx_header_read to get the header information, and must be
+ * called to get the data for each fork, and the crc data for each
+ * fork. it has the same basic calling structure as unix read. the
+ * number of valid bytes read is returned. It does buffering so as to
+ * return the requested length of data every time, unless the end of
+ * file is reached.
+ */
+
+hqx_7tobin( outbuf, datalen )
+ char *outbuf;
+ int datalen;
+{
+ static u_char hqx8[3];
+ static char hqx8i;
+ static u_char prev_hqx8;
+ static u_char prev_out;
+ static u_char prev_hqx7;
+ static int eofflag;
+ u_char hqx7[4];
+ char hqx7i = 0;
+ char *out_first;
+ char *out_last;
+
+#if DEBUG
+ fprintf( stderr, "hqx_7tobin: datalen entering %d\n", datalen );
+ fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
+#endif
+
+ if ( first_flag == 0 ) {
+ prev_hqx8 = 0;
+ prev_hqx7 = 0;
+ prev_out = 0;
+ hqx8i = 3;
+ first_flag = 1;
+ eofflag = 0;
+ }
+
+#if DEBUG
+ fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
+#endif
+
+ out_first = outbuf;
+ out_last = out_first + datalen;
+
+ while (( out_first < out_last ) && ( eofflag == 0 )) {
+
+ if ( hqx7_first == hqx7_last ) {
+ if ( hqx7_fill( hqx7_buf ) == 0 ) {
+ eofflag = 1;
+ continue;
+ }
+ }
+
+ if ( hqx8i > 2 ) {
+
+ while (( hqx7i < 4 ) && ( hqx7_first < hqx7_last )) {
+ hqx7[ hqx7i ] = hqxlookup[ *hqx7_first ];
+ switch ( hqx7[ hqx7i ] ) {
+ case 0xFC :
+ if (( prev_hqx7 == 0xFC ) || ( prev_hqx7 == 0xFE )) {
+ hqx7_first++;
+ break;
+ }
+ case 0xFD :
+ case 0xFF :
+ eofflag = 1;
+ while ( hqx7i < 4 ) {
+ hqx7[ hqx7i++ ] = 0;
+ }
+ break;
+ case 0xFE :
+ prev_hqx7 = hqx7[ hqx7i ];
+ if ( skip_junk( OTHER ) < 0 ) {
+ fprintf( stderr, "\n" );
+ eofflag = 1;
+ while ( hqx7i < 4 ) {
+ hqx7[ hqx7i++ ] = 0; }
+ }
+ break;
+ default :
+ prev_hqx7 = hqx7[ hqx7i++ ];
+ hqx7_first++;
+ break;
+ }
+ }
+
+ if ( hqx7i == 4 ) {
+ hqx8[ 0 ] = (( hqx7[ 0 ] << 2 ) | ( hqx7[ 1 ] >> 4 ));
+ hqx8[ 1 ] = (( hqx7[ 1 ] << 4 ) | ( hqx7[ 2 ] >> 2 ));
+ hqx8[ 2 ] = (( hqx7[ 2 ] << 6 ) | ( hqx7[ 3 ] ));
+ hqx7i = hqx8i = 0;
+ }
+ }
+
+ while (( hqx8i < 3 ) && ( out_first < out_last )) {
+
+#if HEXOUTPUT
+ putc( hqx8i, rawhex );
+ putc( hqx8[ hqx8i ], rawhex );
+#endif
+
+ if ( prev_hqx8 == RUNCHAR ) {
+ if ( hqx8[ hqx8i ] == 0 ) {
+ *out_first = prev_hqx8;
+#if HEXOUTPUT
+ putc( *out_first, expandhex );
+#endif
+ prev_out = prev_hqx8;
+ out_first++;
+ }
+ while (( out_first < out_last ) && ( hqx8[ hqx8i ] > 1 )) {
+ *out_first = prev_out;
+#if HEXOUTPUT
+ putc( *out_first, expandhex );
+#endif
+ hqx8[ hqx8i ]--;
+ out_first++;
+ }
+ if ( hqx8[ hqx8i ] < 2 ) {
+ prev_hqx8 = hqx8[ hqx8i ];
+ hqx8i++;
+ }
+ continue;
+ }
+
+ prev_hqx8 = hqx8[ hqx8i ];
+ if ( prev_hqx8 != RUNCHAR ) {
+ *out_first = prev_hqx8;
+#if HEXOUTPUT
+ putc( *out_first, expandhex );
+#endif
+ prev_out = prev_hqx8;
+ out_first++;
+ }
+ hqx8i++;
+
+ }
+
+ }
+ return( out_first - outbuf );
+}
--- /dev/null
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <strings.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <time.h>
+
+#include <atalk/adouble.h>
+#include <netatalk/endian.h>
+#include "megatron.h"
+
+#define DEBUG 0
+
+/* String used to indicate standard input instead of a disk
+ file. Should be a string not normally used for a file
+ */
+#ifndef STDIN
+# define STDIN "-"
+#endif
+
+/* Yes and no
+ */
+#define NOWAY 0
+#define SURETHANG 1
+
+/* Size of a macbinary file header
+ */
+#define HEADBUFSIZ 128
+
+u_short updcrc();
+
+/* Both input and output routines use this struct and the
+ following globals; therefore this module can only be used
+ for one of the two functions at a time.
+ */
+struct bin_file_data {
+ u_int32_t forklen[ NUMFORKS ];
+ char path[ MAXPATHLEN + 1];
+ int filed;
+ u_short headercrc;
+ time_t gmtoff; /* to convert from/to localtime */
+} bin;
+
+extern char *forkname[];
+u_char head_buf[HEADBUFSIZ];
+
+/*
+ * bin_open must be called first. pass it a filename that is supposed
+ * to contain a macbinary file. an bin struct will be allocated and
+ * somewhat initialized; bin_filed is set.
+ */
+
+bin_open( binfile, flags, fh, options )
+ char *binfile;
+ int flags, options;
+ struct FHeader *fh;
+{
+ int maxlen;
+ int rc;
+ time_t t;
+ struct tm *tp;
+
+#if DEBUG
+ fprintf( stderr, "entering bin_open\n" );
+#endif
+
+ /* call localtime so that we get the timezone offset */
+ bin.gmtoff = 0;
+#ifndef NO_STRUCT_TM_GMTOFF
+ time(&t);
+ if (tp = localtime(&t))
+ bin.gmtoff = tp->tm_gmtoff;
+#endif
+
+ if ( flags == O_RDONLY ) { /* input */
+ if ( strcmp( binfile, STDIN ) == 0 ) {
+ bin.filed = fileno( stdin );
+ } else if (( bin.filed = open( binfile, flags )) < 0 ) {
+ perror( binfile );
+ return( -1 );
+ }
+#if DEBUG
+ fprintf( stderr, "opened %s for read\n", binfile );
+#endif
+ if ((( rc = test_header() ) > 0 ) &&
+ ( bin_header_read( fh, rc ) == 0 )) {
+ return( 0 );
+ }
+ fprintf( stderr, "%s is not a macbinary file.\n", binfile );
+ return( -1 );
+ } else { /* output */
+ if (options & OPTION_STDOUT)
+ bin.filed = fileno(stdout);
+ else {
+ maxlen = sizeof( bin.path ) - 1;
+#if DEBUG
+ fprintf( stderr, "sizeof bin.path\t\t\t%d\n", sizeof( bin.path ));
+ fprintf( stderr, "maxlen \t\t\t\t%d\n", maxlen );
+#endif
+ strncpy( bin.path, fh->name, maxlen );
+ strncpy( bin.path, mtoupath( bin.path ), maxlen );
+ strncat( bin.path, ".bin", maxlen - strlen( bin.path ));
+ if (( bin.filed = open( bin.path, flags, 0666 )) < 0 ) {
+ perror( bin.path );
+ return( -1 );
+ }
+#if DEBUG
+ fprintf( stderr, "opened %s for write\n",
+ (options & OPTION_STDOUT) ? "(stdout)" : bin.path );
+#endif
+ }
+
+ if ( bin_header_write( fh ) != 0 ) {
+ bin_close( TRASH );
+ fprintf( stderr, "%s\n", bin.path );
+ return( -1 );
+ }
+ return( 0 );
+ }
+}
+
+/*
+ * bin_close must be called before a second file can be opened using
+ * bin_open. Upon successful completion, a value of 0 is returned.
+ * Otherwise, a value of -1 is returned.
+ */
+
+bin_close( keepflag )
+ int keepflag;
+{
+#if DEBUG
+ fprintf( stderr, "entering bin_close\n" );
+#endif
+ if ( keepflag == KEEP ) {
+ return( close( bin.filed ));
+ } else if ( keepflag == TRASH ) {
+ if (( strcmp( bin.path, STDIN ) != 0 ) &&
+ ( unlink( bin.path ) < 0 )) {
+ perror ( bin.path );
+ }
+ return( 0 );
+ } else return( -1 );
+}
+
+/*
+ * bin_read is called until it returns zero for each fork. when it is
+ * and finds that there is zero left to give, it seeks to the position
+ * of the next fork (if there is one ).
+ * bin_read must be called enough times to
+ * return zero and no more than that.
+ */
+
+bin_read( fork, buffer, length )
+ int fork;
+ char *buffer;
+ int length;
+{
+ char *buf_ptr;
+ int readlen;
+ int cc = 1;
+ off_t pos;
+
+#if DEBUG >= 3
+ fprintf( stderr, "bin_read: fork is %s\n", forkname[ fork ] );
+ fprintf( stderr, "bin_read: remaining length is %d\n", bin.forklen[fork] );
+#endif
+
+ if ( bin.forklen[ fork ] < 0 ) {
+ fprintf( stderr, "This should never happen, dude!\n" );
+ return( bin.forklen[ fork ] );
+ }
+
+ if ( bin.forklen[ fork ] == 0 ) {
+ if ( fork == DATA ) {
+ pos = lseek( bin.filed, 0, SEEK_CUR );
+#if DEBUG
+ fprintf( stderr, "current position is %ld\n", pos );
+#endif
+ if (pos = pos % HEADBUFSIZ) {
+ pos = lseek( bin.filed, HEADBUFSIZ - pos, SEEK_CUR );
+ }
+#if DEBUG
+ fprintf( stderr, "current position is %ld\n", pos );
+#endif
+ }
+ return( 0 );
+ }
+
+ if ( bin.forklen[ fork ] < length ) {
+ readlen = bin.forklen[ fork ];
+ } else {
+ readlen = length;
+ }
+#if DEBUG >= 3
+ fprintf( stderr, "bin_read: readlen is %d\n", readlen );
+ fprintf( stderr, "bin_read: cc is %d\n", cc );
+#endif
+
+ buf_ptr = buffer;
+ while (( readlen > 0 ) && ( cc > 0 )) {
+ if (( cc = read( bin.filed, buf_ptr, readlen )) > 0 ) {
+#if DEBUG >= 3
+ fprintf( stderr, "bin_read: cc is %d\n", cc );
+#endif
+ readlen -= cc;
+ buf_ptr += cc;
+ }
+ }
+ if ( cc >= 0 ) {
+ cc = buf_ptr - buffer;
+ bin.forklen[ fork ] -= cc;
+ }
+
+#if DEBUG >= 3
+ fprintf( stderr, "bin_read: chars read is %d\n", cc );
+#endif
+ return( cc );
+}
+
+/*
+ * bin_write
+ */
+
+bin_write( fork, buffer, length )
+ int fork;
+ char *buffer;
+ int length;
+{
+ char *buf_ptr;
+ int writelen;
+ int cc = 0;
+ off_t pos;
+ u_char padchar = 0;
+
+#if DEBUG >= 3
+ fprintf( stderr, "bin_write: fork is %s\n", forkname[ fork ] );
+ fprintf( stderr, "bin_write: remaining length is %d\n", bin.forklen[fork] );
+#endif
+
+ if (( fork == RESOURCE ) && ( bin.forklen[ DATA ] != 0 )) {
+ fprintf( stderr, "Forklength error.\n" );
+ return( -1 );
+ }
+
+ buf_ptr = (char *)buffer;
+ if ( bin.forklen[ fork ] >= length ) {
+ writelen = length;
+ } else {
+ fprintf( stderr, "Forklength error.\n" );
+ return( -1 );
+ }
+
+#if DEBUG >= 3
+ fprintf( stderr, "bin_write: write length is %d\n", writelen );
+#endif
+
+ while (( writelen > 0 ) && ( cc >= 0 )) {
+ cc = write( bin.filed, buf_ptr, writelen );
+ buf_ptr += cc;
+ writelen -= cc;
+ }
+ if ( cc < 0 ) {
+ perror( "Couldn't write to macbinary file:" );
+ return( cc );
+ }
+ bin.forklen[ fork ] -= length;
+
+ if ( bin.forklen[ fork ] < 0 ) {
+ fprintf( stderr, "This should never happen, dude!\n" );
+ return( bin.forklen[ fork ] );
+ }
+
+/*
+ * add the padding at end of data and resource forks
+ */
+
+ if ( bin.forklen[ fork ] == 0 ) {
+ pos = lseek( bin.filed, 0, SEEK_CUR );
+#if DEBUG
+ fprintf( stderr, "current position is %ld\n", pos );
+#endif
+ if (pos = pos % HEADBUFSIZ) { /* pad only if we need to */
+ pos = lseek( bin.filed, HEADBUFSIZ - pos - 1, SEEK_CUR );
+ if ( write( bin.filed, &padchar, 1 ) != 1 ) {
+ perror( "Couldn't write to macbinary file:" );
+ return( -1 );
+ }
+ }
+#if DEBUG
+ fprintf( stderr, "current position is %ld\n", pos );
+#endif
+ }
+
+#if DEBUG
+ fprintf( stderr, "\n" );
+#endif
+
+ return( length );
+}
+
+/*
+ * bin_header_read is called by bin_open, and before any information can
+ * read from the fh substruct. it must be called before any
+ * of the bytes of the other two forks can be read, as well.
+ */
+
+bin_header_read( fh, revision )
+ struct FHeader *fh;
+ int revision;
+{
+ u_short mask;
+
+/*
+ * Set the appropriate finder flags mask for the type of macbinary
+ * file it is, and copy the extra macbinary II stuff from the header.
+ * If it is not a macbinary file revision of I or II, then return
+ * negative.
+ */
+
+ switch ( revision ) {
+ case 3:
+ case 2 :
+ mask = htons( 0xfcee );
+ memcpy(&fh->finder_info.fdFlags + 1, head_buf + 101,1 );
+ break;
+ case 1 :
+ mask = htons( 0xfc00 );
+ break;
+ default :
+ return( -1 );
+ break;
+ }
+
+/*
+ * Go through and copy all the stuff you can get from the
+ * MacBinary header into the fh struct. What fun!
+ */
+
+ memcpy(fh->name, head_buf + 2, head_buf[ 1 ] );
+ memcpy(&fh->create_date, head_buf + 91, 4 );
+ fh->create_date = MAC_DATE_TO_UNIX(fh->create_date) - bin.gmtoff;
+ fh->create_date = AD_DATE_FROM_UNIX(fh->create_date);
+ memcpy( &fh->mod_date, head_buf + 95, 4 );
+ fh->mod_date = MAC_DATE_TO_UNIX(fh->mod_date) - bin.gmtoff;
+ fh->mod_date = AD_DATE_FROM_UNIX(fh->mod_date);
+ fh->backup_date = AD_DATE_START;
+ memcpy( &fh->finder_info, head_buf + 65, 8 );
+ memcpy( &fh->finder_info.fdFlags, head_buf + 73, 1 );
+ fh->finder_info.fdFlags &= mask;
+ memcpy(&fh->finder_info.fdLocation, head_buf + 75, 4 );
+ memcpy(&fh->finder_info.fdFldr, head_buf + 79, 2 );
+ memcpy(&fh->forklen[ DATA ], head_buf + 83, 4 );
+ bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
+ memcpy(&fh->forklen[ RESOURCE ], head_buf + 87, 4 );
+ bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
+ fh->comment[0] = '\0';
+
+ if (revision == 3) {
+ fh->finder_xinfo.fdScript = *(head_buf + 106);
+ fh->finder_xinfo.fdXFlags = *(head_buf + 107);
+ }
+
+#if DEBUG >= 5
+ {
+ short flags;
+
+ fprintf( stderr, "Values read by bin_header_read\n" );
+ fprintf( stderr, "name length\t\t%d\n", head_buf[ 1 ] );
+ fprintf( stderr, "file name\t\t%s\n", fh->name );
+ fprintf( stderr, "get info comment\t%s\n", fh->comment );
+ fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
+ &fh->finder_info.fdType );
+ fprintf( stderr, "creator\t\t\t%.*s\n",
+ sizeof( fh->finder_info.fdCreator ),
+ &fh->finder_info.fdCreator );
+ memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
+ flags = ntohs( flags );
+ fprintf( stderr, "flags\t\t\t%x\n", flags );
+ fprintf( stderr, "data fork length\t%ld\n", bin.forklen[DATA] );
+ fprintf( stderr, "resource fork length\t%ld\n", bin.forklen[RESOURCE] );
+ fprintf( stderr, "\n" );
+ }
+#endif
+
+ return( 0 );
+}
+
+/*
+ * bin_header_write is called by bin_open, and relies on information
+ * from the fh substruct. it must be called before any
+ * of the bytes of the other two forks can be written, as well.
+ * bin_header_write and bin_header_read are opposites.
+ */
+
+bin_header_write( fh )
+ struct FHeader *fh;
+{
+ char *write_ptr;
+ u_int32_t t;
+ int wc;
+ int wr;
+
+ memset(head_buf, 0, sizeof( head_buf ));
+ head_buf[ 1 ] = (u_char)strlen( fh->name );
+ memcpy( head_buf + 2, fh->name, head_buf[ 1 ] );
+ memcpy( head_buf + 65, &fh->finder_info, 8 );
+ memcpy( head_buf + 73, &fh->finder_info.fdFlags, 1);
+ memcpy( head_buf + 75, &fh->finder_info.fdLocation, 4 );
+ memcpy( head_buf + 79, &fh->finder_info.fdFldr, 2 );
+ memcpy( head_buf + 83, &fh->forklen[ DATA ], 4 );
+ memcpy( head_buf + 87, &fh->forklen[ RESOURCE ], 4 );
+ t = AD_DATE_TO_UNIX(fh->create_date) + bin.gmtoff;
+ t = MAC_DATE_FROM_UNIX(t);
+ memcpy( head_buf + 91, &t, sizeof(t) );
+ t = AD_DATE_TO_UNIX(fh->mod_date) + bin.gmtoff;
+ t = MAC_DATE_FROM_UNIX(t);
+ memcpy( head_buf + 95, &t, sizeof(t) );
+ memcpy( head_buf + 101, &fh->finder_info.fdFlags + 1, 1);
+
+ /* macbinary III */
+ memcpy( head_buf + 102, "mBIN", 4);
+ *(head_buf + 106) = fh->finder_xinfo.fdScript;
+ *(head_buf + 107) = fh->finder_xinfo.fdXFlags;
+ head_buf[ 122 ] = 130;
+
+ head_buf[ 123 ] = 129;
+
+ bin.headercrc = htons( updcrc( (u_short) 0, head_buf, 124 ));
+ memcpy(head_buf + 124, &bin.headercrc, sizeof( bin.headercrc ));
+
+ bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
+ bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
+
+#if DEBUG >= 5
+ {
+ fprintf( stderr, "Values written by bin_header_write\n" );
+ fprintf( stderr, "name length\t\t%d\n", head_buf[ 1 ] );
+ fprintf( stderr, "file name\t\t%s\n", (char *)&head_buf[ 2 ] );
+ fprintf( stderr, "type\t\t\t%.4s\n", (char *)&head_buf[ 65 ] );
+ fprintf( stderr, "creator\t\t\t%.4s\n", (char *)&head_buf[ 69 ] );
+ fprintf( stderr, "data fork length\t%ld\n", bin.forklen[DATA] );
+ fprintf( stderr, "resource fork length\t%ld\n", bin.forklen[RESOURCE] );
+ fprintf( stderr, "\n" );
+ }
+#endif
+
+ write_ptr = (char *)head_buf;
+ wc = sizeof( head_buf );
+ wr = 0;
+ while (( wc > 0 ) && ( wr >= 0 )) {
+ wr = write( bin.filed, write_ptr, wc );
+ write_ptr += wr;
+ wc -= wr;
+ }
+ if ( wr < 0 ) {
+ perror( "Couldn't write macbinary header:" );
+ return( wr );
+ }
+
+ return( 0 );
+}
+
+/*
+ * test_header is called from bin_open. it checks certain values of
+ * the first 128 bytes, determines if the file is a MacBinary,
+ * MacBinary II, MacBinary III, or non-MacBinary file, and returns a
+ * one, two, three or negative one to indicate the file type.
+ *
+ * If the signature at 102 is equal to "mBIN," then it's a MacBinary
+ * III file. Bytes 0 and 74 must be zero for the file to be any type
+ * of MacBinary. If the crc of bytes 0 through 123 equals the value
+ * at offset 124 then it is a MacBinary II. If not, then if byte 82
+ * is zero, byte 2 is a valid value for a mac filename length (between
+ * one and sixty-three), and bytes 101 through 125 are all zero, then
+ * the file is a MacBinary.
+ *
+ * NOTE: apple's MacBinary II files have a non-zero value at byte 74.
+ * so, the check for byte 74 isn't very useful.
+ */
+
+test_header()
+{
+ const char zeros[25] = "";
+ u_int32_t cc;
+ u_short header_crc;
+ u_char namelen;
+
+#if DEBUG
+ fprintf( stderr, "entering test_header\n" );
+#endif
+
+ cc = read( bin.filed, (char *)head_buf, sizeof( head_buf ));
+ if ( cc < sizeof( head_buf )) {
+ perror( "Premature end of file :" );
+ return( -1 );
+ }
+
+#if DEBUG
+ fprintf( stderr, "was able to read HEADBUFSIZ bytes\n" );
+#endif
+
+ /* check for macbinary III header */
+ if (memcmp(head_buf + 102, "mBIN", 4) == 0)
+ return 3;
+
+ /* check for macbinary II even if only one of the bytes is zero */
+ if (( head_buf[ 0 ] == 0 ) || ( head_buf[ 74 ] == 0 )) {
+#if DEBUG
+ fprintf( stderr, "byte 0 and 74 are both zero\n" );
+#endif
+ bin.headercrc = updcrc( (u_short) 0, head_buf, 124 );
+ memcpy(&header_crc, head_buf + 124, sizeof( header_crc ));
+ header_crc = ntohs( header_crc );
+ if ( header_crc == bin.headercrc ) {
+ return( 2 );
+ }
+
+#if DEBUG
+ fprintf( stderr, "header crc didn't pan out\n" );
+#endif
+ }
+
+ /* now see if we have a macbinary file. */
+ if ( head_buf[ 82 ] != 0 ) {
+ return( -1 );
+ }
+ memcpy( &namelen, head_buf + 1, sizeof( namelen ));
+#if DEBUG
+ fprintf( stderr, "name length is %d\n", namelen );
+#endif
+ if (( namelen < 1 ) || ( namelen > 63 )) {
+ return( -1 );
+ }
+
+ /* bytes 101 - 125 should be zero */
+ if (memcmp(head_buf + 101, zeros, sizeof(zeros)) != 0)
+ return -1;
+
+ /* macbinary forks aren't larger than 0x7FFFFF */
+ memcpy(&cc, head_buf + 83, sizeof(cc));
+ cc = ntohl(cc);
+ if (cc > 0x7FFFFF)
+ return -1;
+ memcpy(&cc, head_buf + 87, sizeof(cc));
+ cc = ntohl(cc);
+ if (cc > 0x7FFFFF)
+ return -1;
+
+
+#if DEBUG
+ fprintf( stderr, "byte 82 is zero and name length is cool\n" );
+#endif
+
+ return( 1 );
+}
--- /dev/null
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <netatalk/endian.h>
+#include "megatron.h"
+
+#define DEBUG 0
+
+char forkbuf[8192];
+char *forkname[] = { "data", "resource" };
+char *name[] = { "unhex",
+ "unbin",
+ "unsingle",
+ "macbinary",
+ "hqx2bin",
+ "single2bin",
+ "nadheader",
+ "binheader",
+ "megatron" };
+
+int from_open( un, file, fh, flags )
+ int un, flags;
+ char *file;
+ struct FHeader *fh;
+{
+ switch ( un ) {
+ case MEGATRON :
+ case HEX2NAD :
+ case HEX2BIN :
+ return( hqx_open( file, O_RDONLY, fh, flags ));
+ break;
+ case BIN2NAD :
+ case BINHEADER:
+ return( bin_open( file, O_RDONLY, fh, flags ));
+ break;
+ case NAD2BIN :
+ case NADHEADER:
+ return( nad_open( file, O_RDONLY, fh, flags ));
+ break;
+ case SINGLE2NAD :
+ case SINGLE2BIN :
+ return( single_open( file, O_RDONLY, fh, flags ));
+ default :
+ return( -1 );
+ break;
+ }
+}
+
+int from_read( un, fork, buf, len )
+ int un;
+ int fork;
+ char *buf;
+ int len;
+{
+ switch ( un ) {
+ case MEGATRON :
+ case HEX2NAD :
+ case HEX2BIN :
+ return( hqx_read( fork, buf, len ));
+ break;
+ case BIN2NAD :
+ return( bin_read( fork, buf, len ));
+ break;
+ case NAD2BIN :
+ return( nad_read( fork, buf, len ));
+ break;
+ case SINGLE2NAD :
+ case SINGLE2BIN :
+ return( single_read( fork, buf, len ));
+ default :
+ return( -1 );
+ break;
+ }
+}
+
+int from_close( un )
+ int un;
+{
+ switch ( un ) {
+ case MEGATRON :
+ case HEX2NAD :
+ case HEX2BIN :
+ return( hqx_close( KEEP ));
+ break;
+ case BIN2NAD :
+ return( bin_close( KEEP ));
+ break;
+ case NAD2BIN :
+ return( nad_close( KEEP ));
+ break;
+ case SINGLE2NAD :
+ case SINGLE2BIN :
+ return( single_close( KEEP ));
+ default :
+ return( -1 );
+ break;
+ }
+}
+
+int to_open( to, file, fh, flags )
+ int to, flags;
+ char *file;
+ struct FHeader *fh;
+{
+ switch ( to ) {
+ case MEGATRON :
+ case HEX2NAD :
+ case BIN2NAD :
+ case SINGLE2NAD :
+ return( nad_open( file, O_RDWR|O_CREAT|O_EXCL, fh, flags ));
+ break;
+ case NAD2BIN :
+ case HEX2BIN :
+ case SINGLE2BIN :
+ return( bin_open( file, O_RDWR|O_CREAT|O_EXCL, fh, flags ));
+ break;
+ default :
+ return( -1 );
+ break;
+ }
+}
+
+int to_write( to, fork, bufc )
+ int to;
+ int fork;
+ int bufc;
+{
+ switch ( to ) {
+ case MEGATRON :
+ case HEX2NAD :
+ case BIN2NAD :
+ case SINGLE2NAD :
+ return( nad_write( fork, forkbuf, bufc ));
+ break;
+ case NAD2BIN :
+ case HEX2BIN :
+ case SINGLE2BIN :
+ return( bin_write( fork, forkbuf, bufc ));
+ break;
+ default :
+ return( -1 );
+ break;
+ }
+}
+
+int to_close( to, keepflag )
+ int to;
+ int keepflag;
+{
+ switch ( to ) {
+ case MEGATRON :
+ case HEX2NAD :
+ case BIN2NAD :
+ case SINGLE2NAD :
+ return( nad_close( keepflag ));
+ break;
+ case NAD2BIN :
+ case HEX2BIN :
+ case SINGLE2BIN :
+ return( bin_close( keepflag ));
+ break;
+ default :
+ return( -1 );
+ break;
+ }
+}
+
+int megatron( path, module, newname, flags )
+ char *path, *newname;
+ int module, flags;
+{
+ struct stat st;
+ struct FHeader fh;
+ int bufc;
+ int fork;
+ int forkred;
+
+/*
+ * If the source file is not stdin, make sure it exists and
+ * that it is not a directory.
+ */
+
+ if ( strcmp( path, STDIN ) != 0 ) {
+ if ( stat( path, &st ) < 0 ) {
+ perror( path );
+ return( -1 );
+ }
+ if ( S_ISDIR( st.st_mode )) {
+ fprintf( stderr, "%s is a directory.\n", path );
+ return( 0 );
+ }
+ }
+
+/*
+ * Open the source file and fill in the file header structure.
+ */
+
+ memset( &fh, 0, sizeof( fh ));
+ if ( from_open( module, path, &fh ) < 0 ) {
+ return( -1 );
+ }
+
+ if ( flags & OPTION_HEADERONLY ) {
+ time_t t;
+ char buf[5] = "";
+ int i;
+
+ printf("name: %s\n",fh.name);
+ printf("comment: %s\n",fh.comment);
+ memcpy(&buf, &fh.finder_info.fdCreator, sizeof(u_int32_t));
+ printf("creator: '%4s'\n", buf);
+ memcpy(&buf, &fh.finder_info.fdType, sizeof(u_int32_t));
+ printf("type: '%4s'\n", buf);
+ for(i=0; i < NUMFORKS; ++i)
+ printf("fork length[%d]: %u\n", i, ntohl(fh.forklen[i]));
+ t = AD_DATE_TO_UNIX(fh.create_date);
+ printf("creation date: %s", ctime(&t));
+ t = AD_DATE_TO_UNIX(fh.mod_date);
+ printf("modification date: %s", ctime(&t));
+ t = AD_DATE_TO_UNIX(fh.backup_date);
+ printf("backup date: %s", ctime(&t));
+ return( from_close( module ));
+ }
+
+/*
+ * Open the target file and write out the file header info.
+ * set the header to the new filename if it has been supplied.
+ */
+
+ if (*newname)
+ strcpy(fh.name, newname);
+
+ if ( to_open( module, path, &fh, flags ) < 0 ) {
+ (void)from_close( module );
+ return( -1 );
+ }
+
+/*
+ * Read in and write out the data and resource forks.
+ */
+
+ for ( fork = 0; fork < NUMFORKS ; fork++ ) {
+ forkred = 0;
+ while(( bufc = from_read( module, fork, forkbuf, sizeof( forkbuf )))
+ > 0 ) {
+ if ( to_write( module, fork, bufc ) != bufc ) {
+ fprintf( stderr, "%s: Probable write error\n", path );
+ to_close( module, TRASH );
+ (void)from_close( module );
+ return( -1 );
+ }
+ forkred += bufc;
+ }
+#if DEBUG
+ fprintf( stderr, "megatron: forkred is \t\t%d\n", forkred );
+ fprintf( stderr, "megatron: fh.forklen[%d] is \t%d\n", fork,
+ ntohl( fh.forklen[ fork ] ));
+#endif
+ if (( bufc < 0 ) || ( forkred != ntohl( fh.forklen[ fork ] ))) {
+ fprintf( stderr, "%s: Problem with input, dude\n", path );
+ to_close( module, TRASH );
+ (void)from_close( module );
+ return( -1 );
+ }
+ }
+
+/*
+ * Close up the files, and get out of here.
+ */
+
+ if ( to_close( module, KEEP ) < 0 ) {
+ perror( "megatron:" );
+ (void)to_close( module, TRASH );
+ }
+ return( from_close( module ));
+}
+
+int main( argc, argv )
+ int argc;
+ char **argv;
+{
+ int rc, c;
+ int rv = 0;
+ int converts = sizeof(name) / sizeof(char *);
+ int module = -1;
+ int flags = 0;
+ char *progname, newname[ADEDLEN_NAME + 1];
+
+ progname = strrchr( argv[ 0 ], '/' );
+ if (( progname == NULL ) || ( progname == '\0' )) {
+ progname = argv[ 0 ];
+ } else progname++;
+
+#if DEBUG
+ if ( CONVERTS != converts ) {
+ fprintf( stderr, "megatron: list of program links messed up\n" );
+ return( -1 );
+ }
+#endif
+
+ for ( c = 0 ; (( c < converts ) && ( module < 0 )) ; ++c ) {
+ if ( strcmp( name[ c ], progname ) == 0 ) module = c;
+ }
+ if ( module == -1 ) module = ( converts - 1 );
+ if ((module == NADHEADER) || (module == BINHEADER))
+ flags |= OPTION_HEADERONLY;
+
+ if ( argc == 1 ) {
+ return( megatron( STDIN, module, newname, flags ));
+ }
+
+ *newname = '\0';
+ for ( c = 1 ; c < argc ; ++c ) {
+ if ( strcmp( argv [ c ], "--header" ) == 0 ) {
+ flags |= OPTION_HEADERONLY;
+ continue;
+ }
+ if ( strcmp( argv [ c ], "--filename" ) == 0 ) {
+ if(++c < argc) strncpy(newname,argv[c], ADEDLEN_NAME);
+ continue;
+ }
+ if (strcmp(argv[c], "--stdout") == 0) {
+ flags |= OPTION_STDOUT;
+ continue;
+ }
+
+ if ( rc = megatron( argv[ c ], module, newname, flags) != 0 ) {
+ rv = rc;
+ }
+ *newname = '\0';
+ }
+ return( rv );
+}
+
--- /dev/null
+#include <atalk/adouble.h>
+
+#ifndef STDIN
+# define STDIN "-"
+#endif
+
+/*
+ Where it matters, data stored in either of these two structs is in
+ network byte order. Any routines that need to interpret this data
+ locally would need to do the conversion. Mostly this affects the
+ fork length variables. Time values are affected as well, if any
+ routines actually need to look at them.
+ */
+
+#define DATA 0
+#define RESOURCE 1
+#define NUMFORKS 2
+
+#define HEX2NAD 0 /* unhex */
+#define BIN2NAD 1 /* unbin */
+#define SINGLE2NAD 2 /* unsingle */
+#define NAD2BIN 3 /* macbinary */
+#define HEX2BIN 4 /* hqx2bin */
+#define SINGLE2BIN 5 /* single2bin */
+#define NADHEADER 6 /* header */
+#define BINHEADER 7 /* mac binary header */
+#define MEGATRON 8 /* megatron, default, usually HEX2NAD */
+#define CONVERTS 9 /* # conversions defined */
+
+#define OPTION_NONE (0)
+#define OPTION_HEADERONLY (1 << 0)
+#define OPTION_STDOUT (1 << 2)
+
+struct FInfo {
+ u_int32_t fdType;
+ u_int32_t fdCreator;
+ u_int16_t fdFlags;
+ u_int32_t fdLocation;
+ u_int16_t fdFldr;
+};
+
+struct FXInfo {
+ u_int16_t fdIconID;
+ u_int16_t fdUnused[3];
+ u_int8_t fdScript;
+ u_int8_t fdXFlags;
+ u_int16_t fdComment;
+ u_int32_t fdPutAway;
+};
+
+struct FHeader {
+ char name[ ADEDLEN_NAME ];
+ char comment[ ADEDLEN_COMMENT ];
+ u_int32_t forklen[ NUMFORKS ];
+ u_int32_t create_date;
+ u_int32_t mod_date;
+ u_int32_t backup_date;
+ struct FInfo finder_info;
+ struct FXInfo finder_xinfo;
+};
+
+#define MAC_DATE_TO_UNIX(a) (ntohl(a) - 2082844800U)
+#define MAC_DATE_FROM_UNIX(a) (htonl((a) + 2082844800U))
+#define AD_DATE_FROM_MAC(a) (htonl(ntohl(a) - 3029529600U))
+#define AD_DATE_TO_MAC(a) (htonl(ntohl(a) + 3029529600U))
+
+#define FILEIOFF_CREATE 0
+#define FILEIOFF_MODIFY 4
+#define FILEIOFF_BACKUP 8
+#define FILEIOFF_ATTR 14
+#define FINDERIOFF_TYPE 0
+#define FINDERIOFF_CREATOR 4
+#define FINDERIOFF_FLAGS 8
+#define FINDERIOFF_LOC 10
+#define FINDERIOFF_FLDR 14
+#define FINDERIOFF_SCRIPT 24
+#define FINDERIOFF_XFLAGS 25
+
+#define TRASH 0
+#define KEEP 1
+
+#ifndef S_ISDIR
+# define S_ISDIR(s) (( s & S_IFMT ) == S_IFDIR )
+#endif
+
+extern char *mtoupath( char *);
--- /dev/null
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#include <atalk/adouble.h>
+#include <netatalk/endian.h>
+#include "megatron.h"
+
+#define DEBUG 0
+
+static char hexdig[] = "0123456789abcdef";
+
+char *mtoupath( mpath )
+ char *mpath;
+{
+ static char upath[ MAXNAMLEN + 1];
+ char *m, *u;
+ int i = 0;
+
+ m = mpath;
+ u = upath;
+ while ( *m != '\0' ) {
+#if AD_VERSION == AD_VERSION1
+ if ( !isascii( *m ) || *m == '/' || ( i == 0 && *m == '.' )) {
+#else
+ if (!isprint(*m) || *m == '/' || ( i == 0 && (*m == '.' ))) {
+#endif
+ *u++ = ':';
+ *u++ = hexdig[ ( *m & 0xf0 ) >> 4 ];
+ *u++ = hexdig[ *m & 0x0f ];
+ } else {
+#ifdef DOWNCASE
+ *u++ = ( isupper( *m )) ? tolower( *m ) : *m;
+#else DOWNCASE
+ *u++ = *m;
+#endif DOWNCASE
+ }
+ i++;
+ m++;
+ }
+ *u = '\0';
+ return( upath );
+}
+
+
+#define hextoint( c ) ( isdigit( c ) ? c - '0' : c + 10 - 'a' )
+#define islxdigit(x) (!isupper(x)&&isxdigit(x))
+
+char *utompath( upath )
+ char *upath;
+{
+ static char mpath[ MAXNAMLEN + 1];
+ char *m, *u;
+ int h;
+
+ m = mpath;
+ u = upath;
+ while ( *u != '\0' ) {
+ if (*u == ':' && *(u + 1) != '\0' && islxdigit(*(u+1)) &&
+ *(u+2) != '\0' && islxdigit(*(u+2))) {
+ u++;
+ h = hextoint(*u) << 4;
+ u++;
+ h |= hextoint(*u);
+ *m = h;
+ } else {
+#ifdef DOWNCASE
+ *m = diatolower(*u);
+#else DOWNCASE
+ *m = *u;
+#endif DOWNCASE
+ }
+ u++;
+ m++;
+ }
+ *m = '\0';
+ return( mpath );
+}
+
+#if HEXOUTPUT
+ int hexfork[ NUMFORKS ];
+#endif
+
+struct nad_file_data {
+ char macname[ MAXPATHLEN + 1 ];
+ char adpath[ 2 ][ MAXPATHLEN + 1];
+ int offset[ NUMFORKS ];
+ struct adouble ad;
+} nad;
+
+nad_open( path, openflags, fh, options )
+ char *path;
+ int openflags, options;
+ struct FHeader *fh;
+{
+ struct stat st;
+ int fork;
+
+/*
+ * Depending upon openflags, set up nad.adpath for the open. If it
+ * is for write, then stat the current directory to get its mode.
+ * Open the file. Either fill or grab the adouble information.
+ */
+ memset(&nad.ad, 0, sizeof(nad.ad));
+ if ( openflags == O_RDONLY ) {
+ strcpy( nad.adpath[0], path );
+ strcpy( nad.adpath[1],
+ ad_path( nad.adpath[0], ADFLAGS_DF|ADFLAGS_HF ));
+ for ( fork = 0 ; fork < NUMFORKS ; fork++ ) {
+ if ( stat( nad.adpath[ fork ], &st ) < 0 ) {
+ if ( errno == ENOENT ) {
+ fprintf( stderr, "%s is not an adouble file.\n", path );
+ } else {
+ perror( "stat of adouble file failed" );
+ }
+ return( -1 );
+ }
+ }
+
+#if DEBUG
+ fprintf(stderr, "%s is adpath[0]\n", nad.adpath[0]);
+ fprintf(stderr, "%s is adpath[1]\n", nad.adpath[1]);
+#endif
+ if ( ad_open( nad.adpath[ 0 ], ADFLAGS_DF|ADFLAGS_HF,
+ openflags, (int)( st.st_mode & 0666 ), &nad.ad) < 0 ) {
+ perror( nad.adpath[ 0 ] );
+ return( -1 );
+ }
+ return( nad_header_read( fh ));
+
+ } else {
+ strcpy( nad.macname, fh->name );
+ strcpy( nad.adpath[0], mtoupath( nad.macname ));
+ strcpy( nad.adpath[1],
+ ad_path( nad.adpath[0], ADFLAGS_DF|ADFLAGS_HF ));
+#if DEBUG
+ fprintf(stderr, "%s\n", nad.macname);
+ fprintf(stderr, "%s is adpath[0]\n", nad.adpath[0]);
+ fprintf(stderr, "%s is adpath[1]\n", nad.adpath[1]);
+#endif
+ if ( stat( ".", &st ) < 0 ) {
+ perror( "stat of . failed" );
+ return( -1 );
+ }
+ (void)umask( 0 );
+ if ( ad_open( nad.adpath[ 0 ], ADFLAGS_DF|ADFLAGS_HF,
+ openflags, (int)( st.st_mode & 0666 ), &nad.ad) < 0 ) {
+ perror( nad.adpath[ 0 ] );
+ return( -1 );
+ }
+ return( nad_header_write( fh ));
+ }
+}
+
+nad_header_read( fh )
+ struct FHeader *fh;
+{
+ u_int32_t temptime;
+ struct stat st;
+
+ memcpy( nad.macname, ad_entry( &nad.ad, ADEID_NAME ),
+ ad_getentrylen( &nad.ad, ADEID_NAME ));
+ nad.macname[ ad_getentrylen( &nad.ad, ADEID_NAME ) ] = '\0';
+ strcpy( fh->name, nad.macname );
+
+ /* just in case there's nothing in macname */
+ if (*fh->name == '\0')
+ strcpy(fh->name, utompath(nad.adpath[DATA]));
+
+ if ( stat( nad.adpath[ DATA ], &st ) < 0 ) {
+ perror( "stat of datafork failed" );
+ return( -1 );
+ }
+ fh->forklen[ DATA ] = htonl( st.st_size );
+ fh->forklen[ RESOURCE ] = htonl( ad_getentrylen( &nad.ad, ADEID_RFORK ));
+ fh->comment[0] = '\0';
+
+#if DEBUG
+ fprintf( stderr, "macname of file\t\t\t%.*s\n", strlen( fh->name ),
+ fh->name );
+ fprintf( stderr, "size of data fork\t\t%d\n",
+ ntohl( fh->forklen[ DATA ] ));
+ fprintf( stderr, "size of resource fork\t\t%d\n",
+ ntohl( fh->forklen[ RESOURCE ] ));
+ fprintf( stderr, "get info comment\t\t\"%s\"\n", fh->comment );
+#endif
+
+ ad_getdate(&nad.ad, AD_DATE_CREATE, &temptime);
+ memcpy( &fh->create_date, &temptime, sizeof( temptime ));
+ ad_getdate(&nad.ad, AD_DATE_MODIFY, &temptime);
+ memcpy( &fh->mod_date, &temptime, sizeof( temptime ));
+ ad_getdate(&nad.ad, AD_DATE_BACKUP, &temptime);
+ memcpy( &fh->backup_date, &temptime, sizeof( temptime ));
+
+#if DEBUG
+ memcpy( &temptime, &fh->create_date, sizeof( temptime ));
+ temptime = AD_DATE_TO_UNIX(temptime);
+ fprintf( stderr, "create_date seconds\t\t%lu\n", temptime );
+ memcpy( &temptime, &fh->mod_date, sizeof( temptime ));
+ temptime = AD_DATE_TO_UNIX(temptime);
+ fprintf( stderr, "mod_date seconds\t\t%lu\n", temptime );
+ memcpy( &temptime, &fh->backup_date, sizeof( temptime ));
+ temptime = AD_DATE_TO_UNIX(temptime);
+ fprintf( stderr, "backup_date seconds\t\t%lu\n", temptime );
+ fprintf( stderr, "size of finder_info\t\t%d\n", sizeof( fh->finder_info ));
+#endif
+
+ memcpy(&fh->finder_info.fdType,
+ ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_TYPE,
+ sizeof( fh->finder_info.fdType ));
+ memcpy(&fh->finder_info.fdCreator,
+ ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_CREATOR,
+ sizeof( fh->finder_info.fdCreator ));
+ memcpy(&fh->finder_info.fdFlags,
+ ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_FLAGS,
+ sizeof( fh->finder_info.fdFlags ));
+ memcpy(&fh->finder_info.fdLocation,
+ ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_LOC,
+ sizeof( fh->finder_info.fdLocation ));
+ memcpy(&fh->finder_info.fdFldr,
+ ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_FLDR,
+ sizeof( fh->finder_info.fdFldr ));
+ memcpy(&fh->finder_xinfo.fdScript,
+ ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_SCRIPT,
+ sizeof(fh->finder_xinfo.fdScript));
+ memcpy(&fh->finder_xinfo.fdXFlags,
+ ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_XFLAGS,
+ sizeof(fh->finder_xinfo.fdXFlags));
+
+#if DEBUG
+ {
+ short flags;
+ fprintf( stderr, "finder_info.fdType\t\t%.*s\n",
+ sizeof( fh->finder_info.fdType ), &fh->finder_info.fdType );
+ fprintf( stderr, "finder_info.fdCreator\t\t%.*s\n",
+ sizeof( fh->finder_info.fdCreator ),
+ &fh->finder_info.fdCreator );
+ fprintf( stderr, "nad type and creator\t\t%.*s\n\n",
+ sizeof( fh->finder_info.fdType ) +
+ sizeof( fh->finder_info.fdCreator ),
+ ad_entry( &nad.ad, ADEID_FINDERI ));
+ memcpy(&flags, ad_entry( &nad.ad, ADEID_FINDERI ) +
+ FINDERIOFF_FLAGS, sizeof( flags ));
+ fprintf( stderr, "nad.ad flags\t\t\t%x\n", flags );
+ fprintf( stderr, "fh flags\t\t\t%x\n", fh->finder_info.fdFlags );
+ fprintf(stderr, "fh script\t\t\t%x\n", fh->finder_xinfo.fdScript);
+ fprintf(stderr, "fh xflags\t\t\t%x\n", fh->finder_xinfo.fdXFlags);
+ }
+#endif
+
+ nad.offset[ DATA ] = nad.offset[ RESOURCE ] = 0;
+
+ return( 0 );
+
+}
+
+nad_header_write( fh )
+ struct FHeader *fh;
+{
+ u_int32_t temptime;
+
+ ad_setentrylen( &nad.ad, ADEID_NAME, strlen( nad.macname ));
+ memcpy( ad_entry( &nad.ad, ADEID_NAME ), nad.macname,
+ ad_getentrylen( &nad.ad, ADEID_NAME ));
+ ad_setentrylen( &nad.ad, ADEID_COMMENT, strlen( fh->comment ));
+ memcpy( ad_entry( &nad.ad, ADEID_COMMENT ), fh->comment,
+ ad_getentrylen( &nad.ad, ADEID_COMMENT ));
+ ad_setentrylen( &nad.ad, ADEID_RFORK, ntohl( fh->forklen[ RESOURCE ] ));
+
+#if DEBUG
+ fprintf( stderr, "ad_getentrylen\n" );
+ fprintf( stderr, "ADEID_FINDERI\t\t\t%d\n",
+ ad_getentrylen( &nad.ad, ADEID_FINDERI ));
+ fprintf( stderr, "ADEID_RFORK\t\t\t%d\n",
+ ad_getentrylen( &nad.ad, ADEID_RFORK ));
+ fprintf( stderr, "ADEID_NAME\t\t\t%d\n",
+ ad_getentrylen( &nad.ad, ADEID_NAME ));
+ fprintf( stderr, "ad_entry of ADEID_NAME\t\t%.*s\n",
+ ad_getentrylen( &nad.ad, ADEID_NAME ),
+ ad_entry( &nad.ad, ADEID_NAME ));
+ fprintf( stderr, "ADEID_COMMENT\t\t\t%d\n",
+ ad_getentrylen( &nad.ad, ADEID_COMMENT ));
+#endif
+
+ memcpy( &temptime, &fh->create_date, sizeof( temptime ));
+ ad_setdate(&nad.ad, AD_DATE_CREATE, temptime);
+ memcpy( &temptime, &fh->mod_date, sizeof( temptime ));
+ ad_setdate(&nad.ad, AD_DATE_MODIFY, temptime);
+
+#if DEBUG
+ ad_getdate(&nad.ad, AD_DATE_CREATE, &temptime);
+ temptime = AD_DATE_TO_UNIX(temptime);
+ fprintf(stderr, "FILEIOFF_CREATE seconds\t\t%ld\n", temptime );
+ ad_getdate(&nad.ad, AD_DATE_MODIFY, &temptime);
+ temptime = AD_DATE_TO_UNIX(temptime);
+ fprintf(stderr, "FILEIOFF_MODIFY seconds\t\t%ld\n", temptime );
+#endif
+
+ memset( ad_entry( &nad.ad, ADEID_FINDERI ), 0, ADEDLEN_FINDERI );
+ memcpy( ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_TYPE,
+ &fh->finder_info.fdType, sizeof( fh->finder_info.fdType ));
+ memcpy( ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_CREATOR,
+ &fh->finder_info.fdCreator, sizeof( fh->finder_info.fdCreator ));
+ memcpy( ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_FLAGS,
+ &fh->finder_info.fdFlags, sizeof( fh->finder_info.fdFlags ));
+ memcpy( ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_LOC,
+ &fh->finder_info.fdLocation,sizeof( fh->finder_info.fdLocation ));
+ memcpy( ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_FLDR,
+ &fh->finder_info.fdFldr, sizeof( fh->finder_info.fdFldr ));
+ memcpy( ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_SCRIPT,
+ &fh->finder_xinfo.fdScript, sizeof( fh->finder_xinfo.fdScript ));
+ memcpy( ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_XFLAGS,
+ &fh->finder_xinfo.fdXFlags, sizeof( fh->finder_xinfo.fdXFlags));
+
+
+#if DEBUG
+ {
+ short flags;
+ memcpy(&flags, ( ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_FLAGS,
+ sizeof( flags ));
+ fprintf( stderr, "nad.ad flags\t\t\t%x\n", flags );
+ fprintf( stderr, "fh flags\t\t\t%x\n", fh->finder_info.fdFlags );
+ fprintf( stderr, "fh xflags\t\t\t%x\n", fh->finder_xinfo.fdXFlags );
+ fprintf( stderr, "type and creator\t\t%.*s\n\n",
+ sizeof( fh->finder_info.fdType ) +
+ sizeof( fh->finder_info.fdCreator ),
+ ad_entry( &nad.ad, ADEID_FINDERI ));
+ }
+#endif
+
+#if HEXOUTPUT
+ hexfork[ DATA ] = open( "datafork", O_WRONLY|O_CREAT, 0622 );
+ hexfork[ RESOURCE ] = open( "resfork", O_WRONLY|O_CREAT, 0622 );
+#endif
+
+ nad.offset[ DATA ] = nad.offset[ RESOURCE ] = 0;
+ ad_flush( &nad.ad, ADFLAGS_DF|ADFLAGS_HF );
+
+ return( 0 );
+}
+
+int forkeid[] = { ADEID_DFORK, ADEID_RFORK };
+
+nad_read( fork, forkbuf, bufc )
+ int fork;
+ char *forkbuf;
+ int bufc;
+{
+ int cc = 0;
+
+#if DEBUG
+ fprintf( stderr, "Entering nad_read\n" );
+#endif
+
+ if (( cc = ad_read( &nad.ad, forkeid[ fork ], nad.offset[ fork ],
+ forkbuf, bufc)) < 0 ) {
+ perror( "Reading the appledouble file:" );
+ return( cc );
+ }
+ nad.offset[ fork ] += cc;
+
+#if DEBUG
+ fprintf( stderr, "Exiting nad_read\n" );
+#endif
+
+ return( cc );
+}
+
+nad_write( fork, forkbuf, bufc )
+ int fork;
+ char *forkbuf;
+ int bufc;
+{
+ char *buf_ptr;
+ int writelen;
+ int cc = 0;
+
+#if DEBUG
+ fprintf( stderr, "Entering nad_write\n" );
+#endif
+
+#if HEXOUTPUT
+ write( hexfork[ fork ], forkbuf, bufc );
+#endif
+
+ writelen = bufc;
+ buf_ptr = forkbuf;
+
+ while (( writelen > 0 ) && ( cc >= 0 )) {
+ cc = ad_write( &nad.ad, forkeid[ fork ], nad.offset[ fork ],
+ 0, buf_ptr, writelen);
+ nad.offset[ fork ] += cc;
+ buf_ptr += cc;
+ writelen -= cc;
+ }
+ if ( cc < 0 ) {
+ perror( "Writing the appledouble file:" );
+ return( cc );
+ }
+
+ return( bufc );
+}
+
+nad_close( status )
+int status;
+{
+ int rv;
+ if ( status == KEEP ) {
+ if (( rv = ad_flush( &nad.ad, ADFLAGS_DF|ADFLAGS_HF )) < 0 ) {
+ fprintf( stderr, "nad_close rv for flush %d\n", rv );
+ return( rv );
+ }
+ if (( rv = ad_close( &nad.ad, ADFLAGS_DF|ADFLAGS_HF )) < 0 ) {
+ fprintf( stderr, "nad_close rv for close %d\n", rv );
+ return( rv );
+ }
+ } else if ( status == TRASH ) {
+ if ( unlink( nad.adpath[ 0 ] ) < 0 ) {
+ perror ( nad.adpath[ 0 ] );
+ }
+ if ( unlink( nad.adpath[ 1 ] ) < 0 ) {
+ perror ( nad.adpath[ 1 ] );
+ }
+ return( 0 );
+ } else return( -1 );
+}
--- /dev/null
+/* updcrc(3), crc(1) - calculate crc polynomials
+ *
+ * Calculate, intelligently, the CRC of a dataset incrementally given a
+ * buffer full at a time.
+ *
+ * Usage:
+ * newcrc = updcrc( oldcrc, bufadr, buflen )
+ * unsigned int oldcrc, buflen;
+ * char *bufadr;
+ *
+ * Compiling with -DTEST creates a program to print the CRC of stdin to stdout.
+ * Compile with -DMAKETAB to print values for crctab to stdout. If you change
+ * the CRC polynomial parameters, be sure to do this and change
+ * crctab's initial value.
+ *
+ * Notes:
+ * Regards the data stream as an integer whose MSB is the MSB of the first
+ * byte recieved. This number is 'divided' (using xor instead of subtraction)
+ * by the crc-polynomial P.
+ * XMODEM does things a little differently, essentially treating the LSB of
+ * the first data byte as the MSB of the integer. Define SWAPPED to make
+ * things behave in this manner.
+ *
+ * Author: Mark G. Mendel, 7/86
+ * UUCP: ihnp4!umn-cs!hyper!mark, GEnie: mgm
+ */
+
+/* The CRC polynomial.
+ * These 4 values define the crc-polynomial.
+ * If you change them, you must change crctab[]'s initial value to what is
+ * printed by initcrctab() [see 'compile with -DMAKETAB' above].
+ */
+ /* Value used by: CCITT XMODEM ARC */
+#define P 0x1021 /* the poly: 0x1021 0x1021 A001 */
+#define INIT_CRC 0L /* init value: -1 0 0 */
+/*#define SWAPPED /* bit order: undef defined defined */
+#define W 16 /* bits in CRC:16 16 16 */
+
+ /* data type that holds a W-bit unsigned integer */
+#if W <= 16
+# define WTYPE unsigned short
+#else
+# define WTYPE u_int32_t
+#endif
+
+ /* the number of bits per char: don't change it. */
+#define B 8
+
+static WTYPE crctab[1<<B] = /* as calculated by initcrctab() */ {
+0x0, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
+0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+0x1231, 0x210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
+0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
+0x2462, 0x3443, 0x420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
+0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+0x3653, 0x2672, 0x1611, 0x630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
+0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+0x48c4, 0x58e5, 0x6886, 0x78a7, 0x840, 0x1861, 0x2802, 0x3823,
+0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
+0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0xa50, 0x3a33, 0x2a12,
+0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0xc60, 0x1c41,
+0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0xe70,
+0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
+0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+0x1080, 0xa1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
+0x2b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
+0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+0x34e2, 0x24c3, 0x14a0, 0x481, 0x7466, 0x6447, 0x5424, 0x4405,
+0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
+0x26d3, 0x36f2, 0x691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
+0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x8e1, 0x3882, 0x28a3,
+0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+0x4a75, 0x5a54, 0x6a37, 0x7a16, 0xaf1, 0x1ad0, 0x2ab3, 0x3a92,
+0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
+0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0xcc1,
+0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
+0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0xed1, 0x1ef0,
+} ;
+
+WTYPE
+updcrc( icrc, icp, icnt )
+ WTYPE icrc;
+ unsigned char *icp;
+ int icnt;
+{
+ register WTYPE crc = icrc;
+ register unsigned char *cp = icp;
+ register int cnt = icnt;
+
+ while( cnt-- ) {
+#ifndef SWAPPED
+ crc = (crc<<B) ^ crctab[(crc>>(W-B)) ^ *cp++];
+#else
+ crc = (crc>>B) ^ crctab[(crc & ((1<<B)-1)) ^ *cp++];
+#endif SWAPPED
+ }
+
+ return( crc );
+}
+
+#ifdef MAKETAB
+
+#include <stdio.h>
+main()
+{
+ initcrctab();
+}
+
+initcrctab()
+{
+ register int b, i;
+ WTYPE v;
+
+
+ for( b = 0; b <= (1<<B)-1; ++b ) {
+#ifndef SWAPPED
+ for( v = b<<(W-B), i = B; --i >= 0; )
+ v = v & ((WTYPE)1<<(W-1)) ? (v<<1)^P : v<<1;
+#else
+ for( v = b, i = B; --i >= 0; )
+ v = v & 1 ? (v>>1)^P : v>>1;
+#endif
+ crctab[b] = v;
+
+ printf( "0x%lx,", v & ((1L<<W)-1L));
+ if( (b&7) == 7 )
+ printf("\n" );
+ else
+ printf(" ");
+ }
+}
+#endif
+
+#ifdef TEST
+
+#include <stdio.h>
+#include <fcntl.h>
+
+#define MAXBUF 4096
+
+
+
+main( ac, av )
+ int ac; char **av;
+{
+ int fd;
+ int nr;
+ int i;
+ char buf[MAXBUF];
+ WTYPE crc, crc2;
+
+ fd = 0;
+ if( ac > 1 )
+ if( (fd = open( av[1], O_RDONLY )) < 0 ) {
+ perror( av[1] );
+ exit( -1 );
+ }
+ crc = crc2 = INIT_CRC;
+
+ while( (nr = read( fd, buf, MAXBUF )) > 0 ) {
+ crc = updcrc( crc, buf, nr );
+ }
+
+ if( nr != 0 )
+ perror( "reading" );
+ else {
+ printf( "%lx\n", crc );
+ }
+
+#ifdef MAGICCHECK
+ /* tack one's complement of crc onto data stream, and
+ continue crc calculation. Should get a constant (magic number)
+ dependent only on P, not the data.
+ */
+ crc2 = crc ^ -1L;
+ for( nr = W-B; nr >= 0; nr -= B ) {
+ buf[0] = (crc2 >> nr);
+ crc = updcrc(crc, buf, 1);
+ }
+
+ /* crc should now equal magic */
+ buf[0] = buf[1] = buf[2] = buf[3] = 0;
+ printf( "magic test: %lx =?= %lx\n", crc, updcrc(-1, buf, W/B));
+#endif MAGICCHECK
+}
+
+#endif
--- /dev/null
+TARGETS= nbplkup nbprgstr nbpunrgstr
+SRC= nbplkup.c nbprgstr.c nbpunrgstr.c
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH}
+LIBS= -latalk ${ADDLIBS}
+TAGSFILE= tags
+CC= cc
+INSTALL= install
+LIBDIRS= -L../../libatalk
+
+all : ${TARGETS}
+
+nbplkup : nbplkup.o ../../libatalk/libatalk.a
+ ${CC} ${CFLAGS} -o nbplkup nbplkup.o ${LIBDIRS} ${LIBS}
+
+nbprgstr : nbprgstr.o ../../libatalk/libatalk.a
+ ${CC} ${CFLAGS} -o nbprgstr nbprgstr.o ${LIBDIRS} ${LIBS}
+
+nbpunrgstr : nbpunrgstr.o ../../libatalk/libatalk.a
+ ${CC} ${CFLAGS} -o nbpunrgstr nbpunrgstr.o ${LIBDIRS} ${LIBS}
+
+install : all
+ for i in ${TARGETS}; do ${INSTALL} -c $$i ${BINDIR}; done
+ chmod 700 ${BINDIR}/nbpunrgstr
+ chmod 700 ${BINDIR}/nbprgstr
+
+clean:
+ rm -f a.out core* *.o *.bak *[Ee]rrs tags
+ rm -f ${TARGETS}
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <sys/types.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/nbp.h>
+#include <atalk/util.h>
+#include <string.h>
+#include <stdio.h>
+#if !defined( sun ) || !defined( i386 )
+#include <stdlib.h>
+#endif sun i386
+
+char *Obj = "=";
+char *Type = "=";
+char *Zone = "*";
+
+Usage( av0 )
+ char *av0;
+{
+ char *p;
+
+ if (( p = strrchr( av0, '/' )) == 0 ) {
+ p = av0;
+ } else {
+ p++;
+ }
+
+ printf( "Usage:\t%s [ -A address ] [ -r responses] [ obj:type@zone ]\n", p );
+ exit( 1 );
+}
+
+main( ac, av )
+ int ac;
+ char **av;
+{
+ struct nbpnve *nn;
+ char *name;
+ int i, c, nresp = 1000;
+ struct at_addr addr;
+
+ extern char *optarg;
+ extern int optind;
+
+ memset(&addr, 0, sizeof(addr));
+ while (( c = getopt( ac, av, "r:A:" )) != EOF ) {
+ switch ( c ) {
+ case 'A':
+ if (!atalk_aton(optarg, &addr)) {
+ fprintf(stderr, "Bad address.\n");
+ exit(1);
+ }
+ break;
+ case 'r' :
+ nresp = atoi( optarg );
+ break;
+
+ default :
+ Usage( av[ 0 ] );
+ exit( 1 );
+ }
+ }
+
+ if (( nn = (struct nbpnve *)malloc( nresp * sizeof( struct nbpnve )))
+ == NULL ) {
+ perror( "malloc" );
+ exit( 1 );
+ }
+
+ if ( ac - optind > 1 ) {
+ Usage( av[ 0 ] );
+ exit( 1 );
+ }
+
+ /*
+ * Get default values from the environment. We need to copy out
+ * the results, here, since nbp_name returns it's parameters
+ * in static space, and we'll clobber them when we call it again
+ * later.
+ */
+ if (( name = getenv( "NBPLKUP" )) != NULL ) {
+ if ( nbp_name( name, &Obj, &Type, &Zone )) {
+ fprintf( stderr,
+ "Environment variable syntax error: NBPLKUP = %s\n",
+ name );
+ exit( 1 );
+ }
+
+ if (( name = (char *)malloc( strlen( Obj ) + 1 )) == NULL ) {
+ perror( "malloc" );
+ exit( 1 );
+ }
+ strcpy( name, Obj );
+ Obj = name;
+
+ if (( name = (char *)malloc( strlen( Type ) + 1 )) == NULL ) {
+ perror( "malloc" );
+ exit( 1 );
+ }
+ strcpy( name, Type );
+ Type = name;
+
+ if (( name = (char *)malloc( strlen( Zone ) + 1 )) == NULL ) {
+ perror( "malloc" );
+ exit( 1 );
+ }
+ strcpy( name, Zone );
+ Zone = name;
+
+ }
+
+ if ( ac - optind == 1 ) {
+ if ( nbp_name( av[ optind ], &Obj, &Type, &Zone )) {
+ Usage( av[ 0 ] );
+ exit( 1 );
+ }
+ }
+
+ if (( c = nbp_lookup( Obj, Type, Zone, nn, nresp, &addr)) < 0 ) {
+ perror( "nbp_lookup" );
+ exit( -1 );
+ }
+ for ( i = 0; i < c; i++ ) {
+ printf( "%31.*s:%-34.*s %u.%u:%u\n",
+ nn[ i ].nn_objlen, nn[ i ].nn_obj,
+ nn[ i ].nn_typelen, nn[ i ].nn_type,
+ ntohs( nn[ i ].nn_sat.sat_addr.s_net ),
+ nn[ i ].nn_sat.sat_addr.s_node,
+ nn[ i ].nn_sat.sat_port );
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/netddp.h>
+#include <atalk/util.h>
+
+Usage( av0 )
+ char *av0;
+{
+ char *p;
+
+ if (( p = strrchr( av0, '/' )) == 0 ) {
+ p = av0;
+ } else {
+ p++;
+ }
+
+ fprintf( stderr, "Usage: %s [ -A address ] obj:type@zone\n", p );
+ exit( 1 );
+}
+
+main( ac, av )
+ int ac;
+ char **av;
+{
+ struct sockaddr_at addr;
+ struct at_addr ataddr;
+ char *Obj = 0, *Type = 0, *Zone = 0;
+ int s, c, port = 0;
+
+ extern char *optarg;
+ extern int optind;
+
+ memset(&ataddr, 0, sizeof(ataddr));
+ while (( c = getopt( ac, av, "p:A:" )) != EOF ) {
+ switch ( c ) {
+ case 'A':
+ if (!atalk_aton(optarg, &ataddr)) {
+ fprintf(stderr, "Bad address.\n");
+ exit(1);
+ }
+ break;
+
+ case 'p' :
+ port = atoi( optarg );
+ break;
+
+ default :
+ Usage( av[ 0 ] );
+ }
+ }
+
+ if ( ac - optind != 1 ) {
+ Usage( av[ 0 ] );
+ }
+
+ /*
+ * Get the name. If Type or Obj aren't specified, error.
+ */
+ if ( nbp_name( av[ optind ], &Obj, &Type, &Zone ) || !Obj || !Type ) {
+ Usage( av[ 0 ] );
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ memcpy(&addr.sat_addr, &ataddr, sizeof(addr.sat_addr));
+ if ((s = netddp_open(&addr, NULL)) < 0)
+ return( -1 );
+
+ if ( port ) {
+ addr.sat_port = port;
+ }
+
+ if ( nbp_rgstr( &addr, Obj, Type, Zone ) < 0 ) {
+ perror( "nbp_rgstr" );
+ fprintf( stderr, "Can't register %s:%s@%s\n", Obj, Type,
+ Zone ? Zone : "*" );
+ exit( 1 );
+ }
+ netddp_close(s);
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <sys/types.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <stdio.h>
+#include <string.h>
+
+Usage( av0 )
+ char *av0;
+{
+ char *p;
+
+ if (( p = strrchr( av0, '/' )) == 0 ) {
+ p = av0;
+ } else {
+ p++;
+ }
+
+ fprintf( stderr, "Usage: %s [ -A address ] obj:type@zone\n", p );
+ exit( 1 );
+}
+
+main( ac, av )
+ int ac;
+ char **av;
+{
+ char *Obj = 0, *Type = 0, *Zone = 0;
+ struct at_addr addr;
+ int c;
+
+ extern char *optarg;
+ extern int optind;
+
+ memset(&addr, 0, sizeof(addr));
+ while ((c = getopt(ac, av, "A:")) != EOF) {
+ switch (c) {
+ case 'A':
+ if (!atalk_aton(optarg, &addr)) {
+ fprintf(stderr, "Bad address.\n");
+ exit(1);
+ }
+ break;
+ default:
+ Usage(av[0]);
+ break;
+ }
+ }
+
+ if (ac - optind != 1) {
+ Usage( av[ 0 ] );
+ }
+
+ /*
+ * Get the name. If Type or Obj aren't specified, error.
+ */
+ if ( nbp_name( av[optind], &Obj, &Type, &Zone ) || !Obj || !Type ) {
+ Usage( av[ 0 ] );
+ }
+
+ if ( nbp_unrgstr( Obj, Type, Zone, &addr ) < 0 ) {
+ fprintf( stderr, "Can't unregister %s:%s@%s\n", Obj, Type,
+ Zone ? Zone : "*" );
+ exit( 1 );
+ }
+}
--- /dev/null
+TARGETS= pap papstatus
+SRC = pap.c papstatus.c
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH} -DZEROCONNID -DNONZEROSTATUS
+
+LIBS= -latalk ${ADDLIBS}
+TAGSFILE= tags
+CC= cc
+INSTALL= install
+LIBDIRS= -L../../libatalk
+
+all : ${TARGETS}
+
+pap : pap.o ../../libatalk/libatalk.a
+ ${CC} ${CFLAGS} -o pap pap.o ${LIBDIRS} ${LIBS}
+
+papstatus : papstatus.o ../../libatalk/libatalk.a
+ ${CC} ${CFLAGS} -o papstatus papstatus.o ${LIBDIRS} ${LIBS}
+
+install : all
+ for i in ${TARGETS}; do ${INSTALL} -c $$i ${BINDIR}; done
+
+clean :
+ rm -f a.out core* *.o *.bak *[Ee]rrs tags
+ rm -f ${TARGETS}
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/pap.h>
+#include <atalk/nbp.h>
+#include <atalk/util.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <string.h>
+#include <errno.h>
+
+#define FUCKED
+
+#define _PATH_PAPRC ".paprc"
+char *nbpfailure = "AppleTalk printer offline";
+
+#ifdef EBUG
+#define DEBUG(x,y) (x,y)
+#else /*EBUG*/
+#define DEBUG(x,y)
+#endif /*EBUG*/
+
+usage( path )
+ char *path;
+{
+ char *p;
+
+ if (( p = strrchr( path, '/' )) == NULL ) {
+ p = path;
+ } else {
+ p++;
+ }
+ fprintf( stderr,
+ "Usage:\t%s [ -A address ] [ -p printername ] [ -s statusfile ] [ file ] ...\n", p );
+ exit( 2 );
+}
+
+char *
+paprc()
+{
+ static char s[ 32 + 1 + 32 + 1 + 32 ];
+ char *name = NULL;
+ FILE *f;
+
+ if (( f = fopen( _PATH_PAPRC, "r" )) == NULL ) {
+ if ( errno == ENOENT ) {
+ return( NULL );
+ } else {
+ perror( _PATH_PAPRC );
+ exit( 2 );
+ }
+ }
+ while ( fgets( s, sizeof( s ), f ) != NULL ) {
+ s[ strlen( s ) - 1 ] = '\0'; /* remove trailing newline */
+ if ( *s == '#' ) {
+ continue;
+ }
+ name = s;
+ break;
+ }
+ fclose( f );
+ return( name );
+}
+
+char *printer = NULL;
+char *status = NULL;
+int noeof = 0;
+int waitforprinter = 0;
+
+unsigned char connid, quantum, oquantum = PAP_MAXQUANTUM;
+struct sockaddr_at sat;
+
+char cbuf[ 8 ];
+struct nbpnve nn;
+ATP satp;
+
+char fbuf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
+struct iovec rfiov[ PAP_MAXQUANTUM ] = {
+ { fbuf[ 0 ] + 4, 0 },
+ { fbuf[ 1 ] + 4, 0 },
+ { fbuf[ 2 ] + 4, 0 },
+ { fbuf[ 3 ] + 4, 0 },
+ { fbuf[ 4 ] + 4, 0 },
+ { fbuf[ 5 ] + 4, 0 },
+ { fbuf[ 6 ] + 4, 0 },
+ { fbuf[ 7 ] + 4, 0 },
+};
+struct iovec sniov[ PAP_MAXQUANTUM ] = {
+ { fbuf[ 0 ], 0 },
+ { fbuf[ 1 ], 0 },
+ { fbuf[ 2 ], 0 },
+ { fbuf[ 3 ], 0 },
+ { fbuf[ 4 ], 0 },
+ { fbuf[ 5 ], 0 },
+ { fbuf[ 6 ], 0 },
+ { fbuf[ 7 ], 0 },
+};
+
+char nbuf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
+struct iovec rniov[ PAP_MAXQUANTUM ] = {
+ { nbuf[ 0 ], 0 },
+ { nbuf[ 1 ], 0 },
+ { nbuf[ 2 ], 0 },
+ { nbuf[ 3 ], 0 },
+ { nbuf[ 4 ], 0 },
+ { nbuf[ 5 ], 0 },
+ { nbuf[ 6 ], 0 },
+ { nbuf[ 7 ], 0 },
+};
+struct iovec sfiov[ PAP_MAXQUANTUM ] = {
+ { nbuf[ 0 ] + 4, 0 },
+ { nbuf[ 1 ] + 4, 0 },
+ { nbuf[ 2 ] + 4, 0 },
+ { nbuf[ 3 ] + 4, 0 },
+ { nbuf[ 4 ] + 4, 0 },
+ { nbuf[ 5 ] + 4, 0 },
+ { nbuf[ 6 ] + 4, 0 },
+ { nbuf[ 7 ] + 4, 0 },
+};
+
+main( ac, av )
+ int ac;
+ char **av;
+{
+ ATP atp;
+ struct atp_block atpb;
+ int c, err = 0, fd, cuts = 0;
+ char *obj = NULL, *type = "LaserWriter", *zone = "*";
+ struct timeval stv, tv;
+ char rbuf[ ATP_MAXDATA ];
+ struct iovec iov;
+ unsigned short waiting, result;
+ int connattempts = 10;
+ int waitforidle = 0;
+ struct at_addr addr;
+
+ extern char *optarg;
+ extern int optind;
+
+ memset(&addr, 0, sizeof(addr));
+ while (( c = getopt( ac, av, "Wwcep:s:EA:" )) != EOF ) {
+ switch ( c ) {
+#ifdef FUCKED
+ case 'w' :
+ waitforprinter = 1;
+ break;
+
+ case 'W' :
+ waitforidle = 1;
+ break;
+#endif /*FUCKED*/
+
+ case 'c' :
+ cuts++;
+ break;
+
+ case 'e' : /* send stdout to stderr */
+ dup2( 2, 1 );
+ break;
+
+ case 'p' :
+ printer = optarg;
+ break;
+
+ case 's' :
+ status = optarg;
+ break;
+
+ case 'E' :
+ noeof = 1;
+ break;
+
+ case 'A':
+ if (!atalk_aton(optarg, &addr)) {
+ fprintf(stderr, "Bad address.\n");
+ exit(1);
+ }
+ break;
+
+ default :
+ err++;
+ }
+ }
+ if ( err ) {
+ usage( *av );
+ }
+ if ( printer == NULL && (( printer = paprc()) == NULL )) {
+ fprintf( stderr, "No printer specified and ./.paprc not found.\n" );
+ exit( 2 );
+ }
+
+ /*
+ * Open connection.
+ */
+ if ( nbp_name( printer, &obj, &type, &zone ) < 0 ) {
+ fprintf( stderr, "%s: Bad name\n", printer );
+ exit( 2 );
+ }
+ if ( obj == NULL ) {
+ fprintf( stderr, "%s: Bad name\n", printer );
+ exit( 2 );
+ }
+
+ if ( nbp_lookup( obj, type, zone, &nn, 1, &addr ) <= 0 ) {
+ if ( errno != 0 ) {
+ perror( "nbp_lookup" );
+ exit( 2 );
+ }
+ fprintf( stderr, "%s:%s@%s: NBP Lookup failed\n", obj, type, zone );
+ exit( 1 );
+ }
+
+ if ( isatty( 0 )) {
+ printf( "Trying %u.%d:%d ...\n", ntohs( nn.nn_sat.sat_addr.s_net ),
+ nn.nn_sat.sat_addr.s_node, nn.nn_sat.sat_port );
+ }
+
+ if (( atp = atp_open( ATADDR_ANYPORT, &addr )) == NULL ) {
+ perror( "atp_open" );
+ exit( 2 );
+ }
+ if (( satp = atp_open( ATADDR_ANYPORT, &addr )) == NULL ) {
+ perror( "atp_open" );
+ exit( 2 );
+ }
+
+ while ( waitforidle ) {
+ char st_buf[ 1024 ]; /* XXX too big */
+
+ cbuf[ 0 ] = 0;
+ cbuf[ 1 ] = PAP_SENDSTATUS;
+ cbuf[ 2 ] = cbuf[ 3 ] = 0;
+ atpb.atp_saddr = &nn.nn_sat;
+ atpb.atp_sreqdata = cbuf;
+ atpb.atp_sreqdlen = 4; /* bytes in SendStatus request */
+ atpb.atp_sreqto = 2; /* retry timer */
+ atpb.atp_sreqtries = 5; /* retry count */
+ if ( atp_sreq( satp, &atpb, 1, 0 ) < 0 ) {
+ perror( "atp_sreq" );
+ exit( 1 );
+ }
+
+DEBUG( printf( "SENDSTATUS >\n" ), fflush( stdout ));
+
+ atpb.atp_saddr = &nn.nn_sat;
+ rniov[ 0 ].iov_len = PAP_MAXDATA + 4;
+ atpb.atp_rresiov = rniov;
+ atpb.atp_rresiovcnt = 1;
+ if ( atp_rresp( satp, &atpb ) < 0 ) {
+ perror( "atp_rresp" );
+ continue;
+ }
+
+#ifndef NONZEROSTATUS
+ /*
+ * The stinking LaserWriter IINTX puts crap in this
+ * field.
+ */
+ if ( ((char *)rniov[ 0 ].iov_base)[ 0 ] != 0 ) {
+ fprintf( stderr, "Bad status response!\n" );
+ exit( 1 );
+ }
+#endif /*NONZEROSTATUS*/
+
+ if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_STATUS ||
+ atpb.atp_rresiovcnt != 1 ) {
+ fprintf( stderr, "Bad status response!\n" );
+ exit( 1 );
+ }
+
+DEBUG( printf( "< STATUS\n" ), fflush( stdout ));
+
+ memcpy( st_buf, (char *) rniov[ 0 ].iov_base + 9,
+ ((char *)rniov[ 0 ].iov_base)[ 8 ] );
+ st_buf[ ((char *)rniov[ 0 ].iov_base)[ 8 ]] = '\0';
+ if ( strstr( st_buf, "idle" ) != NULL ) {
+ waitforidle = 0;
+ } else {
+ updatestatus( (char *) rniov[ 0 ].iov_base + 9,
+ ((char *)rniov[ 0 ].iov_base)[ 8 ] );
+ sleep( 5 );
+ }
+ }
+
+ cbuf[ 0 ] = connid = getpid() & 0xff;
+ cbuf[ 1 ] = PAP_OPEN;
+ cbuf[ 2 ] = cbuf[ 3 ] = 0;
+ cbuf[ 4 ] = atp_sockaddr( atp )->sat_port;
+ cbuf[ 5 ] = oquantum; /* flow quantum */
+ if ( gettimeofday( &stv, 0 ) < 0 ) {
+ perror( "gettimeofday" );
+ exit( 2 );
+ }
+ for (;;) {
+ if ( cuts ) {
+ waiting = 0xffff;
+ } else {
+ if ( gettimeofday( &tv, 0 ) < 0 ) {
+ perror( "gettimeofday" );
+ exit( 2 );
+ }
+ waiting = htons( tv.tv_sec - stv.tv_sec );
+ }
+ memcpy(cbuf + 6, &waiting, sizeof( waiting ));
+
+ atpb.atp_saddr = &nn.nn_sat;
+ atpb.atp_sreqdata = cbuf;
+ atpb.atp_sreqdlen = 8; /* bytes in OpenConn request */
+ atpb.atp_sreqto = 2; /* retry timer */
+ atpb.atp_sreqtries = 5; /* retry count */
+ if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
+ perror( "atp_sreq" );
+ exit( 1 );
+ }
+
+DEBUG( printf( "OPEN >\n" ), fflush( stdout ));
+
+ iov.iov_base = rbuf;
+ iov.iov_len = sizeof( rbuf );
+ atpb.atp_rresiov = &iov;
+ atpb.atp_rresiovcnt = 1;
+ if ( atp_rresp( atp, &atpb ) < 0 ) {
+ perror( "atp_rresp" );
+ if ( connattempts-- <= 0 ) {
+ fprintf( stderr, "Can't connect!\n" );
+ exit( 1 );
+ }
+ continue;
+ }
+
+ /* sanity */
+ if ( iov.iov_len < 8 || (unsigned char)rbuf[ 0 ] != connid ||
+ rbuf[ 1 ] != PAP_OPENREPLY ) {
+ fprintf( stderr, "Bad response!\n" );
+ continue; /* This is weird, since TIDs must match... */
+ }
+
+DEBUG( printf( "< OPENREPLY\n" ), fflush( stdout ));
+
+ if ( isatty( 1 )) {
+ printf( "%.*s\n", iov.iov_len - 9, (char *) iov.iov_base + 9 );
+ }
+ updatestatus( (char *) iov.iov_base + 9, iov.iov_len - 9 );
+
+ memcpy( &result, rbuf + 6, sizeof( result ));
+ if ( result != 0 ) {
+ sleep( 2 );
+ } else {
+ memcpy( &sat, &nn.nn_sat, sizeof( struct sockaddr_at ));
+ sat.sat_port = rbuf[ 4 ];
+ quantum = rbuf[ 5 ];
+ break;
+ }
+ }
+
+ if ( isatty( 1 )) {
+ printf( "Connected to %.*s:%.*s@%.*s.\n",
+ nn.nn_objlen, nn.nn_obj,
+ nn.nn_typelen, nn.nn_type,
+ nn.nn_zonelen, nn.nn_zone );
+ }
+
+ if ( optind == ac ) {
+ send_file( 0, atp, 1 );
+ } else {
+ for (; optind < ac; optind++ ) {
+ if ( strcmp( av[ optind ], "-" ) == 0 ) {
+ fd = 0;
+ } else if (( fd = open( av[ optind ], O_RDONLY )) < 0 ) {
+ perror( av[ optind ] );
+ continue;
+ }
+ send_file( fd, atp, ( optind == ac - 1 ) ? 1 : 0 );
+ if ( fd != 0 ) {
+ close( fd );
+ }
+ }
+ }
+
+ /*
+ * Close connection.
+ */
+ cbuf[ 0 ] = connid;
+ cbuf[ 1 ] = PAP_CLOSE;
+ cbuf[ 2 ] = cbuf[ 3 ] = 0;
+
+ atpb.atp_saddr = &sat;
+ atpb.atp_sreqdata = cbuf;
+ atpb.atp_sreqdlen = 4; /* bytes in CloseConn request */
+ atpb.atp_sreqto = 2; /* retry timer */
+ atpb.atp_sreqtries = 5; /* retry count */
+ if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
+ perror( "atp_sreq" );
+ exit( 1 );
+ }
+
+DEBUG( printf( "CLOSE >\n" ), fflush( stdout ));
+
+ iov.iov_base = rbuf;
+ iov.iov_len = sizeof( rbuf );
+ atpb.atp_rresiov = &iov;
+ atpb.atp_rresiovcnt = 1;
+ if ( atp_rresp( atp, &atpb ) < 0 ) {
+ perror( "atp_rresp" );
+ exit( 1 );
+ }
+
+ /* sanity */
+ if ( iov.iov_len != 4 || rbuf[ 1 ] != PAP_CLOSEREPLY ) {
+ fprintf( stderr, "Bad response!\n" );
+ exit( 1 );
+ }
+
+#ifndef ZEROCONNID
+ /*
+ * The AGFA Viper Rip doesn't have the connection id in the close request.
+ */
+ if ((unsigned char)rbuf[ 0 ] != connid ) {
+ fprintf( stderr, "Bad connid in close!\n" );
+ exit( 1 );
+ }
+#endif /*ZEROCONNID*/
+
+DEBUG( printf( "< CLOSEREPLY\n" ), fflush( stdout ));
+
+ if ( isatty( 1 )) {
+ printf( "Connection closed.\n" );
+ }
+ exit( 0 );
+}
+
+int data = 0;
+unsigned char port;
+u_int16_t seq = 0, rseq = 1;
+
+send_file( fd, atp, lastfile )
+ int fd;
+ ATP atp;
+ int lastfile;
+{
+ struct timeval stv, tv;
+ struct sockaddr_at ssat;
+ struct atp_block atpb;
+ fd_set fds;
+ int fiovcnt = 0, eof = 0, senteof = 0, to = 0;
+ int cc, i;
+ unsigned short netseq;
+
+ if ( gettimeofday( &stv, 0 ) < 0 ) {
+ perror( "gettimeofday" );
+ exit( 2 );
+ }
+
+ /*
+ * Ask for more data.
+ */
+ cbuf[ 0 ] = connid;
+ cbuf[ 1 ] = PAP_READ;
+ if ( ++seq == 0 ) seq = 1;
+ netseq = htons( seq );
+ memcpy( cbuf + 2, &netseq, sizeof( netseq ));
+ atpb.atp_saddr = &sat;
+ atpb.atp_sreqdata = cbuf;
+ atpb.atp_sreqdlen = 4; /* bytes in SendData request */
+ atpb.atp_sreqto = 15; /* retry timer */
+ atpb.atp_sreqtries = -1; /* retry count */
+ if ( atp_sreq( atp, &atpb, oquantum, ATP_XO ) < 0 ) {
+ perror( "atp_sreq" );
+ exit( 1 );
+ }
+
+DEBUG( printf( "READ %d >\n", seq ), fflush( stdout ));
+
+ for (;;) {
+ if ( gettimeofday( &tv, 0 ) < 0 ) {
+ perror( "gettimeofday" );
+ exit( 2 );
+ }
+
+ if (( tv.tv_sec - stv.tv_sec ) >= 60 ) {
+ stv = tv;
+
+ /*
+ * Send a tickle.
+ */
+ cbuf[ 0 ] = connid;
+ cbuf[ 1 ] = PAP_TICKLE;
+ cbuf[ 2 ] = cbuf[ 3 ] = 0;
+ atpb.atp_saddr = &sat;
+ atpb.atp_sreqdata = cbuf;
+ atpb.atp_sreqdlen = 4; /* bytes in Tickle request */
+ atpb.atp_sreqto = 0; /* retry timer */
+ atpb.atp_sreqtries = 1; /* retry count */
+ if ( atp_sreq( satp, &atpb, 0, 0 ) < 0 ) {
+ perror( "atp_sreq" );
+ exit( 1 );
+ }
+
+DEBUG( printf( "TICKLE >\n" ), fflush( stdout ));
+ }
+
+ tv.tv_sec = stv.tv_sec + 60 - tv.tv_sec;
+ tv.tv_usec = 0;
+
+ FD_ZERO( &fds );
+ if ( !waitforprinter && !eof && fiovcnt == 0 ) {
+ FD_SET( fd, &fds );
+ }
+ FD_SET( atp_fileno( atp ), &fds );
+
+ if (( cc = select( FD_SETSIZE, &fds, 0, 0, &tv )) < 0 ) {
+ perror( "select" );
+ exit( 2 );
+ }
+
+ /*
+ * A timeout has occured. Keep track of it.
+ */
+ if ( cc == 0 ) {
+ if ( to++ > 2 ) {
+ fprintf( stderr, "Connection timed out.\n" );
+ exit( 1 );
+ }
+ continue;
+ }
+
+ /*
+ * Read data.
+ */
+ if ( !fiovcnt && FD_ISSET( fd, &fds )) {
+ for ( i = 0; i < quantum; i++ ) {
+ rfiov[ i ].iov_len = PAP_MAXDATA;
+ }
+ if (( cc = readv( fd, rfiov, quantum )) < 0 ) {
+ perror( "readv" );
+ exit( 2 );
+ }
+ if ( cc == 0 ) {
+ eof = 1;
+ }
+ fiovcnt = cc / PAP_MAXDATA + ( cc % PAP_MAXDATA > 0 );
+ for ( i = 0; cc > 0; i++ ) {
+ rfiov[ i ].iov_len = ( cc > PAP_MAXDATA ) ? PAP_MAXDATA : cc;
+ cc -= ( cc > PAP_MAXDATA ) ? PAP_MAXDATA : cc;
+ }
+ }
+
+ if ( FD_ISSET( atp_fileno( atp ), &fds )) {
+ ssat = sat;
+ ssat.sat_port = ATADDR_ANYPORT;
+ switch( atp_rsel( atp, &ssat, ATP_TRESP | ATP_TREQ )) {
+ case ATP_TREQ :
+ atpb.atp_saddr = &ssat;
+ atpb.atp_rreqdata = cbuf;
+ atpb.atp_rreqdlen = sizeof( cbuf );
+ if ( atp_rreq( atp, &atpb ) < 0 ) {
+ perror( "atp_rreq" );
+ exit( 1 );
+ }
+
+ if ( (unsigned char)cbuf[ 0 ] != connid ) {
+ break;
+ }
+
+ /* reset timeout counter for all valid requests */
+ to = 0;
+
+ switch ( cbuf[ 1 ] ) {
+ case PAP_READ :
+ memcpy( cbuf + 2, &netseq, sizeof( netseq ));
+DEBUG( printf( "< READ %d\n", ntohs( netseq )), fflush( stdout ));
+#ifdef notdef
+ if ( netseq != 0 ) {
+ if ( rseq != ntohs( netseq )) {
+DEBUG( printf( "| DUP %d\n", rseq ), fflush( stdout ));
+ break;
+ }
+ if ( rseq++ == 0xffff ) rseq = 1;
+ }
+#endif /*notdef*/
+
+ data = 1;
+ port = ssat.sat_port;
+ break;
+
+ case PAP_CLOSE :
+
+DEBUG( printf( "< CLOSE\n" ), fflush( stdout ));
+
+ /*
+ * Respond to the close request, and fail.
+ */
+ sniov[ 0 ].iov_len = 4;
+ ((char *)sniov[ 0 ].iov_base)[ 0 ] = connid;
+ ((char *)sniov[ 0 ].iov_base)[ 1 ] = PAP_CLOSEREPLY;
+ ((char *)sniov[ 0 ].iov_base)[ 2 ] =
+ ((char *)sniov[ 0 ].iov_base)[ 3 ] = 0;
+ atpb.atp_sresiov = sniov;
+ atpb.atp_sresiovcnt = 1;
+ if ( atp_sresp( atp, &atpb ) < 0 ) {
+ perror( "atp_sresp" );
+ exit( 1 );
+ }
+
+DEBUG( printf( "CLOSEREPLY >\n" ), fflush( stdout ));
+
+ fprintf( stderr, "Connection closed by foreign host.\n" );
+ exit( 1 );
+
+ case PAP_TICKLE :
+
+DEBUG( printf( "< TICKLE\n" ), fflush( stdout ));
+
+ break;
+ default :
+ fprintf( stderr, "Bad PAP request!\n" );
+ exit( 1 );
+ }
+ break;
+
+ case ATP_TRESP :
+ /* reset timeout counter for all valid requests */
+ to = 0;
+
+ atpb.atp_saddr = &ssat;
+ for ( i = 0; i < oquantum; i++ ) {
+ rniov[ i ].iov_len = PAP_MAXDATA + 4;
+ }
+ atpb.atp_rresiov = rniov;
+ atpb.atp_rresiovcnt = oquantum;
+ if ( atp_rresp( atp, &atpb ) < 0 ) {
+ perror( "atp_rresp" );
+ exit( 1 );
+ }
+
+#ifndef ZEROCONNID
+ /*
+ * The HP LJIIISI w/ BridgePort LocalTalk card sends
+ * zero instead of the connid.
+ */
+ if ( ((unsigned char *)rniov[ 0 ].iov_base)[ 0 ] != connid ) {
+ fprintf( stderr, "Bad data response!\n" );
+ exit( 1 );
+ }
+#endif /*ZEROCONNID*/
+ if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_DATA ) {
+ fprintf( stderr, "Bad data response!\n" );
+ exit( 1 );
+ }
+
+ for ( cc = 0, i = 0; i < atpb.atp_rresiovcnt; i++ ) {
+ sfiov[ i ].iov_len = rniov[ i ].iov_len - 4;
+ cc += sfiov[ i ].iov_len;
+ }
+ if ( cc && writev( 1, sfiov, atpb.atp_rresiovcnt ) < cc ) {
+ perror( "writev" );
+ exit( 2 );
+ }
+
+ /* eof */
+ if ( ((char *)rniov[ 0 ].iov_base)[ 2 ] ) {
+
+DEBUG( printf( "< DATA (eof)\n" ), fflush( stdout ));
+
+ return( 0 );
+ }
+
+DEBUG( printf( "< DATA\n" ), fflush( stdout ));
+
+
+ /*
+ * Ask for more data.
+ */
+ cbuf[ 0 ] = connid;
+ cbuf[ 1 ] = PAP_READ;
+ if ( ++seq == 0 ) seq = 1;
+ netseq = htons( seq );
+ memcpy( cbuf + 2, &netseq, sizeof( netseq ));
+ atpb.atp_saddr = &sat;
+ atpb.atp_sreqdata = cbuf;
+ atpb.atp_sreqdlen = 4; /* bytes in SendData request */
+ atpb.atp_sreqto = 15; /* retry timer */
+ atpb.atp_sreqtries = -1; /* retry count */
+ if ( atp_sreq( atp, &atpb, oquantum, ATP_XO ) < 0 ) {
+ perror( "atp_sreq" );
+ exit( 1 );
+ }
+
+DEBUG( printf( "READ %d >\n", seq ), fflush( stdout ));
+
+ break;
+
+ case 0:
+
+DEBUG( printf( "| RETRANS\n" ), fflush( stdout ));
+
+ break;
+
+ default:
+ perror( "atp_rsel" );
+ exit( 1 );
+ }
+ }
+
+ /*
+ * Send whatever is pending.
+ */
+ if ( !waitforprinter && !senteof && data && ( fiovcnt || eof )) {
+ ssat.sat_port = port;
+ atpb.atp_saddr = &ssat;
+ if ( fiovcnt ) {
+ for ( i = 0; i < fiovcnt; i++ ) {
+ sniov[ i ].iov_len = rfiov[ i ].iov_len + 4;
+ ((char *)sniov[ i ].iov_base)[ 0 ] = connid;
+ ((char *)sniov[ i ].iov_base)[ 1 ] = PAP_DATA;
+ senteof = ((char *)sniov[ i ].iov_base)[ 2 ] = eof;
+ ((char *)sniov[ i ].iov_base)[ 3 ] = 0;
+ }
+ } else {
+ sniov[ 0 ].iov_len = 4;
+ ((char *)sniov[ 0 ].iov_base)[ 0 ] = connid;
+ ((char *)sniov[ 0 ].iov_base)[ 1 ] = PAP_DATA;
+ senteof = ((char *)sniov[ 0 ].iov_base)[ 2 ] = eof;
+ ((char *)sniov[ 0 ].iov_base)[ 3 ] = 0;
+ }
+ atpb.atp_sresiov = sniov;
+ atpb.atp_sresiovcnt = fiovcnt ? fiovcnt : 1;
+ if ( atp_sresp( atp, &atpb ) < 0 ) {
+ perror( "atp_sresp" );
+ exit( 1 );
+ }
+ data = fiovcnt = 0;
+
+DEBUG( printf( "DATA %s\n", eof ? "(eof) >" : ">" ), fflush( stdout ));
+
+ /*
+ * The Apple LaserWriter IIf, the HP LWIIISi, and IV, don't
+ * seem to send us an EOF on large jobs. To work around
+ * this heinous protocol violation, we won't wait for their
+ * EOF before closing.
+ */
+ if ( eof && noeof && lastfile ) {
+ return( 0 );
+ }
+ } else {
+ /*
+ * If we can't send data right now, go ahead and get the
+ * status. This is cool, because we get here reliably
+ * if there is a problem.
+ */
+ cbuf[ 0 ] = 0;
+ cbuf[ 1 ] = PAP_SENDSTATUS;
+ cbuf[ 2 ] = cbuf[ 3 ] = 0;
+ atpb.atp_saddr = &nn.nn_sat;
+ atpb.atp_sreqdata = cbuf;
+ atpb.atp_sreqdlen = 4; /* bytes in SendStatus request */
+ atpb.atp_sreqto = 2; /* retry timer */
+ atpb.atp_sreqtries = 5; /* retry count */
+ if ( atp_sreq( satp, &atpb, 1, 0 ) < 0 ) {
+ perror( "atp_sreq" );
+ exit( 1 );
+ }
+
+DEBUG( printf( "SENDSTATUS >\n" ), fflush( stdout ));
+
+ atpb.atp_saddr = &nn.nn_sat;
+ rniov[ 0 ].iov_len = PAP_MAXDATA + 4;
+ atpb.atp_rresiov = rniov;
+ atpb.atp_rresiovcnt = 1;
+ if ( atp_rresp( satp, &atpb ) < 0 ) {
+ perror( "atp_rresp" );
+ continue;
+ }
+
+#ifndef NONZEROSTATUS
+ /*
+ * The stinking LaserWriter IINTX puts crap in this
+ * field.
+ */
+ if ( ((char *)rniov[ 0 ].iov_base)[ 0 ] != 0 ) {
+ fprintf( stderr, "Bad status response!\n" );
+ exit( 1 );
+ }
+#endif /*NONZEROSTATUS*/
+
+ if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_STATUS ||
+ atpb.atp_rresiovcnt != 1 ) {
+ fprintf( stderr, "Bad status response!\n" );
+ exit( 1 );
+ }
+
+DEBUG( printf( "< STATUS\n" ), fflush( stdout ));
+
+#ifdef FUCKED
+ if ( waitforprinter ) {
+ char st_buf[ 1024 ]; /* XXX too big */
+
+ memcpy( st_buf, (char *) rniov[ 0 ].iov_base + 9,
+ ((char *)rniov[ 0 ].iov_base)[ 8 ] );
+ st_buf[ ((char *)rniov[ 0 ].iov_base)[ 8 ]] = '\0';
+ if ( strstr( st_buf, "waiting" ) != NULL ) {
+ waitforprinter = 0;
+ }
+ }
+#endif /*FUCKED*/
+
+ updatestatus( (char *) rniov[ 0 ].iov_base + 9,
+ ((char *)rniov[ 0 ].iov_base)[ 8 ] );
+ }
+ }
+}
+
+updatestatus( s, len )
+ char *s;
+ int len;
+{
+ int fd;
+ struct iovec iov[ 2 ];
+
+ if ( !status ) {
+ return;
+ }
+
+ if (( fd = open( status, O_WRONLY|O_TRUNC )) < 0 ) {
+ perror( status );
+ status = NULL;
+ return;
+ }
+ iov[ 0 ].iov_base = s;
+ iov[ 0 ].iov_len = len;
+ iov[ 1 ].iov_base = "\n";
+ iov[ 1 ].iov_len = 1;
+ writev( fd, iov, 2 );
+ close( fd );
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/file.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/pap.h>
+#include <atalk/nbp.h>
+#include <atalk/util.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#define _PATH_PAPRC ".paprc"
+
+usage( path )
+ char *path;
+{
+ char *p;
+
+ if (( p = strrchr( path, '/' )) == NULL ) {
+ p = path;
+ } else {
+ p++;
+ }
+ fprintf( stderr,
+ "Usage:\t%s [ -A address ] [ -p printername ]\n", p );
+ exit( 1 );
+}
+
+char *
+paprc()
+{
+ static char s[ 32 + 1 + 32 + 1 + 32 ];
+ char *name = NULL;
+ FILE *f;
+
+ if (( f = fopen( _PATH_PAPRC, "r" )) == NULL ) {
+ return( NULL );
+ }
+ while ( fgets( s, sizeof( s ), f ) != NULL ) {
+ s[ strlen( s ) - 1 ] = '\0'; /* remove trailing newline */
+ if ( *s == '#' ) {
+ continue;
+ }
+ name = s;
+ break;
+ }
+ fclose( f );
+ return( name );
+}
+
+char *printer = NULL;
+
+char cbuf[ 8 ];
+struct nbpnve nn;
+
+main( ac, av )
+ int ac;
+ char **av;
+{
+ ATP atp;
+ int wait, c, err = 0;
+ char *obj = NULL, *type = "LaserWriter", *zone = "*";
+ struct at_addr addr;
+
+ extern char *optarg;
+ extern int optind;
+
+ memset(&addr, 0, sizeof(addr));
+ while (( c = getopt( ac, av, "p:s:A:" )) != EOF ) {
+ switch ( c ) {
+ case 'A':
+ if (!atalk_aton(optarg, &addr)) {
+ fprintf(stderr, "Bad address.\n");
+ exit(1);
+ }
+ break;
+ case 'p' :
+ printer = optarg;
+ break;
+
+ default :
+ fprintf( stderr, "Unknown option: '%c'\n", c );
+ err++;
+ }
+ }
+ if ( err ) {
+ usage( *av );
+ }
+ if ( printer == NULL && (( printer = paprc()) == NULL )) {
+ usage( *av );
+ }
+
+ /*
+ * Open connection.
+ */
+ if ( nbp_name( printer, &obj, &type, &zone ) < 0 ) {
+ fprintf( stderr, "%s: Bad name\n", printer );
+ exit( 1 );
+ }
+ if ( obj == NULL ) {
+ fprintf( stderr, "%s: Bad name\n", printer );
+ exit( 1 );
+ }
+ if ( nbp_lookup( obj, type, zone, &nn, 1, &addr ) <= 0 ) {
+ if ( errno != 0 ) {
+ perror( "nbp_lookup" );
+ } else {
+ fprintf( stderr, "%s:%s@%s: NBP Lookup failed\n", obj, type, zone );
+ }
+ exit( 1 );
+ }
+
+ if (( atp = atp_open( ATADDR_ANYPORT, &addr )) == NULL ) {
+ perror( "atp_open" );
+ exit( 1 );
+ }
+
+ if ( optind == ac ) {
+ getstatus( atp, &nn.nn_sat );
+ exit( 0 );
+ }
+ if ( optind - ac > 1 ) {
+ usage( *av );
+ }
+ wait = atoi( av[ optind ] );
+ for (;;) {
+ getstatus( atp, &nn.nn_sat );
+ sleep( wait );
+ }
+}
+
+getstatus( atp, sat )
+ ATP atp;
+ struct sockaddr_at *sat;
+{
+ struct iovec iov;
+ struct atp_block atpb;
+ char rbuf[ ATP_MAXDATA ];
+
+ cbuf[ 0 ] = 0;
+ cbuf[ 1 ] = PAP_SENDSTATUS;
+ cbuf[ 2 ] = cbuf[ 3 ] = 0;
+
+ atpb.atp_saddr = sat;
+ atpb.atp_sreqdata = cbuf;
+ atpb.atp_sreqdlen = 4; /* bytes in SendStatus request */
+ atpb.atp_sreqto = 2; /* retry timer */
+ atpb.atp_sreqtries = 5; /* retry count */
+ if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
+ perror( "atp_sreq" );
+ exit( 1 );
+ }
+
+ iov.iov_base = rbuf;
+ iov.iov_len = sizeof( rbuf );
+ atpb.atp_rresiov = &iov;
+ atpb.atp_rresiovcnt = 1;
+ if ( atp_rresp( atp, &atpb ) < 0 ) {
+ perror( "atp_rresp" );
+ exit( 1 );
+ }
+
+ /* sanity */
+ if ( iov.iov_len < 8 ||
+ rbuf[ 1 ] != PAP_STATUS ) {
+ fprintf( stderr, "Bad response!\n" );
+ return; /* This is weird, since TIDs must match... */
+ }
+
+ printf( "%.*s\n", iov.iov_len - 9, (char *) iov.iov_base + 9 );
+}
--- /dev/null
+SRC = psorder.c pa.c
+OBJ = psorder.o pa.o
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH}
+LIBS= -latalk ${ADDLIBS}
+TAGSFILE= tags
+CC= cc
+INSTALL= install
+LIBDIRS= -L../../libatalk
+
+all : psorder
+
+psorder : ${OBJ} ../../libatalk/libatalk.a
+ ${CC} ${CFLAGS} -o psorder ${OBJ} ${LIBDIRS} ${LIBS}
+
+install : all
+ ${INSTALL} -c psorder ${BINDIR}
+
+lint :
+ lint ${DEFS} ${INCPATH} ${SRC}
+
+clean :
+ rm -f a.out core* *.o *.bak *[Ee]rrs tags
+ rm -f psorder
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+tar :
+ rm -f ../psorder.tar
+ (cd .. ; tar cf psorder.tar psorder ; mv psorder.tar psorder)
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+/* This is used with pa.h */
+
+#include <stdlib.h>
+
+#include "pa.h"
+
+pa_buf_t *pa_init( fd )
+ int fd;
+{
+ pa_buf_t *h;
+ int rc;
+
+ h = (pa_buf_t *) malloc( sizeof( pa_buf_t ));
+ h->buf = (char *) malloc( PA_BUFBLK * 2 );
+ h->bufsz = PA_BUFBLK * 2;
+
+ if (( rc = read( fd, h->buf, PA_BUFBLK )) < 0 ) {
+ return( 0 );
+ }
+
+ h->cur = h->buf - 1;
+ h->end = h->buf + rc - 1;
+ h->state = PA_NORMAL;
+ h->fd = fd;
+
+ return( h );
+}
+
+char *pa_gettok( h )
+ pa_buf_t *h;
+{
+ h->state = PA_NORMAL;
+ h->tmp = *(h->cur);
+ *(h->cur) = 0;
+ return( h->mark );
+}
+
+char _pa_fixbuf( h )
+ pa_buf_t *h;
+{
+ int rc;
+ char *t;
+
+ if ( h->state == PA_NORMAL ) {
+ *(h->buf) = *(h->cur);
+ h->cur = h->buf;
+ } else {
+ bcopy( h->mark, h->buf, h->end - h->mark + 1 );
+ h->cur = h->buf + ( h->cur - h->mark );
+ h->end = h->buf + ( h->end - h->mark );
+ h->mark = h->buf;
+ }
+
+ if ( h->bufsz - (( h->cur - h->buf ) + 1 ) < PA_BUFBLK ) {
+ t = h->buf;
+ h->buf = (char *) realloc( h->buf, h->bufsz + PA_BUFBLK );
+ h->bufsz += PA_BUFBLK;
+ h->mark = h->buf + ( h->mark - t );
+ h->cur = h->buf + ( h->cur - t );
+ h->end = h->buf + ( h->end - t );
+ }
+
+ if (( rc = read( h->fd, h->cur + 1, PA_BUFBLK )) <= 0 ) {
+ return( (char) 0 );
+ }
+
+ h->end = h->cur + rc;
+ return( *(++(h->cur)) );
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+/*
+ * Functions to aid in parsing:
+ *
+ * pa_init( fd ) Allocate and return a handle. Reads
+ * from fd. Call this first, always.
+ * pa_getchar( h ) Return a single character or nul, 0,
+ * to indicate end-of-file.
+ * pa_match( h ) Mark the character last read as the start
+ * of a match.
+ * pa_gettok( h ) The match up to but excluding the last
+ * character is returned.
+ * pa_cont( h ) Continue match with previous match. Call
+ * immediately after pa_gettok() to get
+ * correct behavior.
+ * pa_cancel( h ) Cancel previous match start.
+ */
+
+#ifndef FILE_H
+#include <stdio.h>
+#endif
+
+#define PA_BUFBLK 1024
+
+#define PA_NORMAL 0
+#define PA_MATCHING 1
+
+typedef struct pa_buf_t {
+ char *buf;
+ char *cur;
+ char *mark;
+ char *end;
+ int fd;
+ int state;
+ unsigned bufsz;
+ char tmp;
+} pa_buf_t;
+
+extern pa_buf_t *pa_init();
+extern char _pa_fixbuf();
+extern char *pa_gettok();
+
+#define pa_getchar(h) (((h)->cur==(h)->end)?(_pa_fixbuf(h)):\
+ (*(++((h)->cur))))
+#define pa_match(h) ((h)->state=PA_MATCHING,(h)->mark=(h)->cur)
+#define pa_cont(h) (*((h)->cur)=(h)->tmp,(h)->state=PA_MATCHING)
+#define pa_cancel(h) ((h)->state=PA_NORMAL)
+#define pa_back(h) (--((h)->cur))
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/file.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <unistd.h>
+#include "pa.h"
+#include "psorder.h"
+
+#include <atalk/paths.h>
+
+#define DEBUG 0
+
+/*
+ * Global Variables
+ */
+
+u_char psbuf[ 8192 ];
+struct psinfo_st psinfo;
+int orderflag, forceflag;
+
+main( argc, argv )
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ char *progname;
+ int errflag = 0;
+ int c;
+
+ while (( c = getopt( argc, argv, OPTSTR )) != -1 ) {
+ switch ( c ) {
+ case REVCHAR:
+ if ( orderflag ) errflag++;
+ else orderflag = REVERSE;
+ break;
+ case FORWCHAR:
+ if ( orderflag ) errflag++;
+ else orderflag = FORWARD;
+ break;
+ case FORCECHAR:
+ if ( forceflag ) errflag++;
+ else forceflag++;
+ break;
+ }
+ }
+ if ( errflag ) {
+ if (( progname = strrchr( argv[ 0 ], '/' )) == NULL ) {
+ progname = argv[ 0 ];
+ } else progname++;
+ fprintf( stderr, "usage: %s [-duf] [sourcefile]\n", progname );
+ return( -1 );
+ } else if ( !orderflag ) orderflag = FORWARD;
+
+ if ( optind >= argc ) {
+ return( psorder( STDIN ));
+ }
+ return( psorder( argv[ optind ] ));
+}
+
+int
+psorder( path )
+ char *path;
+{
+ int tempfd;
+ int inputfd;
+ char tempfile[MAXNAMLEN];
+
+ filesetup( path, &inputfd, tempfile, &tempfd );
+ readps( inputfd, tempfd, tempfile );
+ if ( lseek( tempfd, REWIND, SEEK_SET ) < 0 ) {
+ perror( tempfile );
+ filecleanup( -1, tempfd, tempfile );
+ }
+ writeps( tempfd, tempfile );
+ filecleanup( 0, tempfd, tempfile );
+ return( 0 );
+}
+
+void
+filesetup( inputfile, infd, tfile, tfd )
+ char *inputfile;
+ int *infd;
+ char *tfile;
+ int *tfd;
+{
+ struct stat st;
+ char *template = _PATH_TMPPAGEORDER;
+
+ if ( strcmp( inputfile, STDIN ) != 0 ) {
+ if ( stat( inputfile, &st ) < 0 ) {
+ perror( inputfile );
+ filecleanup( -1, -1, "" );
+ }
+ if ( st.st_mode & S_IFMT & S_IFDIR ) {
+ fprintf( stderr, "%s is a directory.\n", inputfile );
+ filecleanup( 0, -1, "" );
+ }
+ if (( *infd = open( inputfile, O_RDONLY, 0600 )) < 0 ) {
+ perror( inputfile );
+ filecleanup( -1, -1, "" );
+ }
+ } else {
+ *infd = 0;
+ }
+
+#if DEBUG
+ fprintf( stderr, "Input file or stdin and stdout opened.\n" );
+ fprintf( stderr, "Input file descriptor is %d .\n", *infd );
+#endif
+
+/*
+ make temporary file
+ */
+
+ (void *)strncpy( tfile, template, MAXNAMLEN );
+ if (( *tfd = mkstemp( tfile )) == -1 ) {
+ fprintf( stderr, "can't create temporary file %s\n", tfile );
+ filecleanup( -1, -1, "" );
+ }
+
+#if DEBUG
+ fprintf( stderr, "Temporary file %s created and opened.\n", tfile );
+ fprintf( stderr, "Temporary file descriptor is %d .\n", *tfd );
+#endif
+
+ psinfo.firstpage = NULL;
+ psinfo.lastpage = NULL;
+ psinfo.trailer = 0;
+ psinfo.pages.offset = 0;
+ psinfo.pages.end = 0;
+ psinfo.pages.num[0] = '\0';
+ psinfo.pages.order[0] = '\0';
+
+ return;
+}
+
+void
+readps( inputfd, tempfd, tempfile )
+ int inputfd;
+ int tempfd;
+ char *tempfile;
+{
+ off_t ccread = 0;
+ off_t ccmatch;
+ char *curtok = 0;
+ FILE *tempstream;
+ pa_buf_t *pb;
+ int n;
+ char c = -1;
+ char pc, cc = 0;
+
+ pb = pa_init( inputfd );
+ if (( tempstream = fdopen( tempfd, "w" )) == NULL ) {
+ perror( "fdopen fails for tempfile" );
+ filecleanup( -1, tempfd, tempfile );
+ }
+
+ if (( c = pa_getchar( pb )) != 0 ) {
+ ccread++;
+ (void)putc( c, tempstream );
+ pc = 0;
+ cc = c;
+ pa_match( pb );
+ n = strlen( PPSADOBE );
+ for ( ; ( n > 0 ) && (( c = pa_getchar( pb )) != 0 ) ; n-- ) {
+ ccread++ ;
+ (void)putc( c, tempstream );
+ pc = cc;
+ cc = c;
+ }
+ curtok = pa_gettok( pb );
+ }
+#if DEBUG
+ fprintf( stderr, "%s\n", curtok );
+#endif
+
+/*
+ * not postscript
+ */
+ if ( strcmp( curtok, PPSADOBE ) != 0 ) {
+#if DEBUG
+ fprintf( stderr, "in the not postscript section of readps\n" );
+#endif
+ while (( c = pa_getchar( pb )) != 0 ) {
+ ccread++;
+ (void)putc( c, tempstream );
+ pc = cc;
+ cc = c;
+ }
+
+ (void)fflush( tempstream );
+ return;
+ }
+
+/*
+ * postscript
+ */
+#if DEBUG
+ fprintf( stderr, "in the postscript section of readps\n" );
+#endif
+ while (( c = pa_getchar( pb )) != 0 ) {
+ ccread++;
+ (void)putc( c, tempstream );
+ pc = cc;
+ cc = c;
+ if ((( pc == '\r' ) || ( pc == '\n' )) && ( cc == '%' )) {
+#if DEBUG
+ fprintf( stderr, "supposed start of match, cc = %c\n", cc );
+#endif
+ pa_match( pb );
+ ccmatch = ccread - 1;
+ while ( c = pa_getchar( pb )) {
+ if ( c != 0 ) {
+ ccread++;
+ (void)putc( c, tempstream );
+ pc = cc;
+ cc = c;
+ }
+ if (( c == '\r' ) || ( c == '\n' ) || ( cc == '\0' )) {
+ curtok = pa_gettok( pb );
+#if DEBUG
+ fprintf( stderr, "%s\n", curtok );
+#endif
+ if ( handletok( ccmatch, curtok ) < 0 ) {
+ perror( "malloc died" );
+ filecleanup( -1, tempfd, tempfile );
+ }
+ break;
+ }
+ if ( c == 0 ) break;
+ }
+ if ( c == 0 ) break;
+ }
+ }
+
+ (void)fflush( tempstream );
+ return;
+}
+
+int
+handletok( count, token )
+ off_t count;
+ char *token;
+{
+ int incdoc = 0;
+ struct pspage_st *newpage;
+ char *tmp;
+
+ if (( strncmp( PENDDOC, token, strlen( PENDDOC )) == 0 ) && incdoc ) {
+ incdoc--;
+#if DEBUG
+ fprintf( stderr, "found an EndDoc\n" );
+#endif
+
+ } else if ( strncmp( PBEGINDOC, token, strlen( PBEGINDOC )) == 0 ) {
+ incdoc++;
+#if DEBUG
+ fprintf( stderr, "found a BeginDoc\n" );
+#endif
+
+ } else if ( !incdoc &&
+ ( strncmp( PPAGE, token, strlen( PPAGE )) == 0 )) {
+#if DEBUG
+ fprintf( stderr, "found a Page\n" );
+#endif
+ if (( newpage = getpspage( count )) == NULL ) {
+ return( -1 );
+ }
+ if ( psinfo.firstpage == NULL ) {
+ newpage->prevpage = NULL;
+ psinfo.firstpage = newpage;
+ } else {
+ newpage->prevpage = psinfo.lastpage;
+ psinfo.lastpage->nextpage = newpage;
+ }
+ psinfo.lastpage = newpage;
+ while ( *token++ != ':' );
+ if (( tmp = strtok( token, WHITESPACE )) != NULL ) {
+ (void)strncpy( newpage->lable, tmp, NUMLEN );
+ if (( tmp = strtok( NULL, WHITESPACE )) != NULL ) {
+ (void)strncpy( newpage->ord, tmp, ORDLEN );
+ }
+ }
+#if DEBUG
+ fprintf( stderr, "page lable %s, page ord %s\n", newpage->lable,
+ newpage->ord );
+#endif
+
+ } else if ( !incdoc &&
+ ( strncmp( PPAGES, token, strlen( PPAGES )) == 0 )) {
+#if DEBUG
+ fprintf( stderr, "found a Pages\n" );
+#endif
+ psinfo.pages.offset = count;
+ psinfo.pages.end = strlen( token ) + count;
+ while ( *token++ != ':' );
+ while ( isspace( *token )) token++;
+ if ( strncmp( ATEND, token, strlen( ATEND )) == 0 ) {
+#if DEBUG
+ fprintf( stderr, "it is a Pages: (atend)\n" );
+#endif
+ psinfo.pages.offset = 0;
+ psinfo.pages.end = 0;
+ } else {
+ if (( tmp = strtok( token, WHITESPACE )) != NULL ) {
+ (void)strncpy( psinfo.pages.num, tmp, NUMLEN );
+ if (( tmp = strtok( NULL, WHITESPACE )) != NULL ) {
+ (void)strncpy( psinfo.pages.order, tmp, ORDERLEN );
+ }
+ }
+#if DEBUG
+ fprintf( stderr, "number of pages %s\n", psinfo.pages.num );
+ fprintf( stderr, "order control number %s\n", psinfo.pages.order );
+#endif
+ }
+
+ } else if ( !incdoc &&
+ ( strncmp( PTRAILER, token, strlen( PTRAILER )) == 0 )) {
+#if DEBUG
+ fprintf( stderr, "found the Trailer\n" );
+#endif
+ if ( psinfo.trailer == 0 ) {
+ psinfo.trailer = count;
+ }
+ }
+
+ return( 0 );
+}
+
+void
+writeps( tempfd, tempfile )
+ int tempfd;
+ char *tempfile;
+{
+ struct stat st;
+ off_t endofpage;
+ int order;
+
+ if ( stat( tempfile, &st ) < 0 ) {
+ perror( "stat failed" );
+ filecleanup( -1, tempfd, tempfile );
+ }
+ if ( psinfo.trailer == 0 ) {
+ endofpage = st.st_size;
+ } else endofpage = psinfo.trailer;
+
+ if (( psinfo.firstpage == NULL ) ||
+ ( psinfo.firstpage == psinfo.lastpage )) {
+ order = FORWARD;
+ } else if ( psinfo.pages.offset == 0 ) {
+ order = orderflag;
+ } else if (( strncmp( psinfo.pages.order, "", ORDERLEN ) == 0 ) ||
+ ( strncmp( psinfo.pages.order, "1", ORDERLEN ) == 0 )) {
+ order = orderflag;
+ if ( order == REVERSE ) strcpy( psinfo.pages.order, "-1" );
+ } else if ( strncmp( psinfo.pages.order, "-1", ORDERLEN ) == 0 ) {
+ if ( orderflag == FORWARD ) {
+ order = REVERSE;
+ strcpy( psinfo.pages.order, "1" );
+ } else order = FORWARD;
+ } else if (( strncmp( psinfo.pages.order, "0", ORDERLEN ) == 0 ) &&
+ forceflag ) {
+ order = orderflag;
+ } else order = FORWARD;
+
+ if ( order == FORWARD ) {
+ temp2out( tempfd, tempfile, st.st_size );
+ } else {
+/*
+ * output the header stuff and rewrite the $$Pages line
+ * if it is in the header and not %%Pages: (atend)
+ */
+ if ( psinfo.firstpage->offset > 0 ) {
+ if (( psinfo.firstpage->offset > psinfo.pages.offset ) &&
+ ( psinfo.pages.offset != 0 )) {
+ temp2out( tempfd, tempfile, psinfo.pages.offset );
+ writelable( tempfd, tempfile, PPAGES );
+ if ( lseek( tempfd, psinfo.pages.end, SEEK_SET ) < 0 ) {
+ perror( tempfile );
+ filecleanup( -1, tempfd, tempfile );
+ }
+ temp2out( tempfd, tempfile,
+ psinfo.firstpage->offset - psinfo.pages.end );
+ } else temp2out( tempfd, tempfile, psinfo.firstpage->offset );
+ }
+/*
+ * output the pages, last to first
+ */
+ while ( psinfo.lastpage != NULL ) {
+ if ( lseek( tempfd, psinfo.lastpage->offset, SEEK_SET ) < 0 ) {
+ perror( tempfile );
+ filecleanup( -1, tempfd, tempfile );
+ }
+ temp2out( tempfd, tempfile, endofpage - psinfo.lastpage->offset );
+ endofpage = psinfo.lastpage->offset;
+ psinfo.lastpage = psinfo.lastpage->prevpage;
+ if ( psinfo.lastpage != NULL ) {
+ (void)free( psinfo.lastpage->nextpage );
+ psinfo.lastpage->nextpage = NULL;
+ }
+ }
+/*
+ * output the trailer stuff and rewrite the $$Pages line
+ * if it is in the trailer
+ */
+ if ( psinfo.trailer != 0 ) {
+ if ( lseek( tempfd, psinfo.trailer, SEEK_SET ) < 0 ) {
+ perror( tempfile );
+ filecleanup( -1, tempfd, tempfile );
+ }
+ if ( psinfo.trailer < psinfo.pages.offset ) {
+ temp2out( tempfd, tempfile,
+ psinfo.pages.offset - psinfo.trailer );
+ writelable( tempfd, tempfile, PPAGES );
+ if ( lseek( tempfd, psinfo.pages.end, SEEK_SET ) < 0 ) {
+ perror( tempfile );
+ filecleanup( -1, tempfd, tempfile );
+ }
+ temp2out( tempfd, tempfile, st.st_size - psinfo.pages.end );
+ } else temp2out( tempfd, tempfile, st.st_size - psinfo.trailer );
+ }
+ }
+
+ return;
+}
+
+void
+writelable( tempfd, tempfile, lable )
+ int tempfd;
+ char *tempfile;
+ char *lable;
+{
+ char line[256];
+ int ccwrite;
+ int linelen;
+ char *argone;
+ char *argtwo;
+
+ if ( strcmp( lable, PPAGES ) == 0 ) {
+ argone = psinfo.pages.num;
+ argtwo = psinfo.pages.order;
+ } else {
+ argone = argtwo = NULL;
+ }
+ (void)sprintf( line, "%s %s %s", lable, argone, argtwo );
+ linelen = strlen( line );
+
+ ccwrite = write( 1, line, linelen );
+ if ( ccwrite < 0 ) {
+ perror( "stdout" );
+ filecleanup( ccwrite, tempfd, tempfile );
+ } else {
+ linelen -= ccwrite;
+ }
+}
+
+void
+temp2out( tempfd, tempfile, length )
+ int tempfd;
+ char *tempfile;
+ off_t length;
+{
+ int ccread;
+ int ccwrite;
+ int size;
+
+ while ( length > 0 ) {
+ if ( length > sizeof( psbuf )) {
+ size = sizeof( psbuf );
+ } else size = length;
+ if (( ccread = read( tempfd, psbuf, size )) > 0 ) {
+ size = ccread;
+ while ( ccread > 0 ) {
+ ccwrite = write( 1, psbuf, ccread );
+ if ( ccwrite < 0 ) {
+ perror( "stdout" );
+ filecleanup( ccwrite, tempfd, tempfile );
+ } else {
+ ccread -= ccwrite;
+ }
+ }
+ }
+ if ( ccread < 0 ) {
+ perror( "temporary file" );
+ filecleanup( ccread, tempfd, tempfile );
+ }
+ length -= size;
+ }
+}
+
+struct pspage_st
+*getpspage( off )
+ off_t off;
+{
+ struct pspage_st *newpspage;
+
+ newpspage = (struct pspage_st *)malloc( sizeof( struct pspage_st ));
+ if ( newpspage != NULL ) {
+ newpspage->offset = off;
+ newpspage->nextpage = NULL;
+ *newpspage->lable = '\0';
+ *newpspage->ord = '\0';
+ }
+ return( newpspage );
+}
+
+void
+filecleanup( errorcode, tfd, tfile )
+ int errorcode;
+ int tfd;
+ char *tfile;
+{
+
+/*
+ Close and unlink the temporary file.
+ */
+
+ if ( tfd != 0 ) {
+ if ( close( tfd ) != 0 ) {
+ perror( tfile );
+ exit( errorcode );
+ }
+ if ( unlink( tfile ) != 0 ) {
+ perror( tfile );
+ exit( errorcode );
+ }
+ }
+
+ exit( errorcode );
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef STDIN
+# define STDIN "-"
+#endif
+#ifndef FALSE
+# define FALSE 0
+# define TRUE 1
+#endif
+
+#define REVCHAR 'd'
+#define FORWCHAR 'u'
+#define FORCECHAR 'f'
+#define OPTSTR "duf"
+
+#define WHITESPACE " \t"
+#define ATEND "(atend)"
+#define PPSADOBE "%!PS-Adobe-"
+#define PPAGE "%%Page:"
+#define PPAGES "%%Pages:"
+#define PTRAILER "%%Trailer"
+#define PBEGINDOC "%%BeginDocument:"
+#define PENDDOC "%%EndDocument"
+#define PBEGINBIN "%%BeginBinary:"
+#define PENDBIN "%%EndBinary"
+
+#define REWIND 0L
+#define REVERSE 1
+#define FORWARD 2
+
+#define LABELLEN 32
+#define ORDLEN 4
+struct pspage_st {
+ struct pspage_st *nextpage;
+ struct pspage_st *prevpage;
+ off_t offset;
+ char lable[ LABELLEN ];
+ char ord[ ORDLEN ];
+};
+
+#define NUMLEN 10
+#define ORDERLEN 3
+struct pages_st {
+ off_t offset;
+ off_t end;
+ char num[ NUMLEN ];
+ char order[ ORDERLEN ];
+};
+
+struct psinfo_st {
+ struct pspage_st *firstpage;
+ struct pspage_st *lastpage;
+ off_t trailer;
+ struct pages_st pages;
+};
+
+int
+psorder();
+
+void
+filesetup();
+
+void
+readps();
+
+int
+handletok();
+
+void
+writeps();
+
+void
+writelable();
+
+void
+temp2out();
+
+struct pspage_st
+*getpspage();
+
+void
+filecleanup();
--- /dev/null
+# This file looks empty when viewed with "vi". In fact, there is one
+# '~', so users with no AppleVolumes file in their home directory get
+# their home directory by default.
+#
+# volume format:
+# :DEFAULT: [all of the default options except volume name]
+# path [name] [casefold:x] [codepage:y] [options:z,l,j] \
+# [allow:a,@b,c,d] [deny:a,@b,c,d] [dbpath:path] [password:p] \
+# [rwlist:a,@b,c,d] [rolist:a,@b,c,d] [limitsize:value in bytes]
+#
+#
+# name: volume name. it can't include the ':' character and is limited
+# to 27 characters in length.
+#
+# variable substitutions:
+# you can use variables for both <path> and <name> now. here are the
+# rules:
+# 1) if you specify an unknown variable, it will not get converted.
+# 2) if you specify a known variable, but that variable doesn't have
+# a value, it will get ignored.
+#
+# the variables:
+# $c -> client's ip or appletalk address
+# $f -> full name (whatever's in the gecos field)
+# $g -> group
+# $h -> hostname
+# $s -> server name (can be the hostname)
+# $u -> username (if guest, it's whatever user guest is running as)
+# $v -> volume name (either ADEID_NAME or basename of path)
+# $z -> zone (may not exist)
+# $$ -> $
+#
+# casefold options [syntax: casefold:option]:
+# tolower -> lowercases names in both directions
+# toupper -> uppercases names in both directions
+# xlatelower -> client sees lowercase, server sees uppercase
+# xlateupper -> client sees uppercase, server sees lowercase
+#
+# allow/deny/rwlist/rolist format [syntax: allow:user1,@group]:
+# user1,@group,user2 -> allows/denies access from listed users/groups
+# rwlist/rolist control whether or not the
+# volume is ro for those users.
+#
+# miscellaneous options [syntax: options:option1,option2]:
+# prodos -> make compatible with appleII clients.
+# crlf -> enable crlf translation for TEXT files.
+# noadouble -> don't create .AppleDouble unless a resource
+# fork needs to be created.
+# ro -> mount the volume as read-only.
+# mswindows -> enforce filename restrictions imposed by MS
+# Windows. this will also invoke a default
+# codepage (iso8859-1) if one isn't already
+# specified.
+# nohex -> don't do :hex translations for anything
+# except dot files. specify usedots as well if
+# you want that turned off. note: this option
+# makes the / character illegal.
+# usedots -> don't do :hex translation for dot files. note: when
+# this option gets set, certain file names
+# become illegal. these are .Parent and
+# anything that starts with .Apple. also, dot
+# files created on the unix side are marked
+# invisible.
+# limitsize -> limit disk size reporting to 2GB. this is
+# here for older macintoshes using newer
+# appleshare clients. yucko.
+#
+# codepage:filename -> load filename from nls directory.
+# dbpath:path -> store the database stuff in the following path.
+# password:password -> set a volume password (8 characters max)
+#
+~
--- /dev/null
+# Last Updated July 8, 1999
+# Use at your own risk. No guarantees express or implied.
+#
+# Try to use MacPerl script 'ICDumpSuffixMap' included in /usr/doc
+# to download file mapping list from your Internet Config Preference.
+#
+# inoue@ma.ns.musashi-tech.ac.jp.
+#
+
+. "TEXT" "ttxt" ASCII Text SimpleText text/plain
+
+.mf "TEXT" "*MF*" Metafont Metafont
+.sty "TEXT" "*TEX" TeX Style Textures
+.psd "8BPS" "8BIM" PhotoShop Document Photoshop
+.pxr "PXR " "8BIM" Pixar Image Photoshop
+.sea "APPL" "????" Self-Extracting Archive Self Extracting Archive
+.apd "TEXT" "ALD3" Aldus Printer Description Aldus PageMaker
+.pm3 "ALB3" "ALD3" PageMaker 3 Document PageMaker
+.pm4 "ALB4" "ALD4" PageMaker 4 Document PageMaker
+.pt4 "ALT4" "ALD4" PageMaker 4 Template PageMaker
+.pm5 "ALB5" "ALD5" PageMaker 5 Document PageMaker
+.pt5 "ALT5" "ALD5" PageMaker 5 Template PageMaker
+.pdx "TEXT" "ALD5" Printer Description PageMaker
+.ppd "TEXT" "ALD5" Printer Description PageMaker
+.dl "DL " "AnVw" DL Animation MacAnim Viewer
+.gl "GL " "AnVw" GL Animation MacAnim Viewer
+.url "AURL" "Arch" URL Bookmark Anarchie message/external-body
+.zoo "Zoo " "Booz" Zoo Archive MacBooz
+.pdf "PDF " "CARO" Portable Document Format Acrobat Reader application/pdf
+.h "TEXT" "CWIE" C Include File CodeWarrior
+.hp "TEXT" "CWIE" C Include File CodeWarrior
+.hpp "TEXT" "CWIE" C Include File CodeWarrior
+.c "TEXT" "CWIE" C Source CodeWarrior
+.cp "TEXT" "CWIE" C++ Source CodeWarrior
+.cpp "TEXT" "CWIE" C++ Source CodeWarrior
+.class "Clss" "CWIE" Java Class File CodeWarrior
+.java "TEXT" "CWIE" Java Source File CodeWarrior
+.p "TEXT" "CWIE" Pascal Source CodeWarrior
+.pas "TEXT" "CWIE" Pascal Source CodeWarrior
+.cvs "drw2" "DAD2" Canvas Drawing Canvas
+.arj "BINA" "DArj" ARJ Archive DeArj
+.evy "EVYD" "ENVY" Envoy Document Envoy
+.fm "FMPR" "FMPR" FileMaker Pro Database FileMaker Pro
+.dbf "COMP" "FOX+" DBase Document FoxBase+
+.mif "TEXT" "Fram" FrameMaker MIF FrameMaker application/x-framemaker
+.arr "ARR " "GKON" Amber ARR image GraphicConverter
+.iff "ILBM" "GKON" Amiga IFF Image GraphicConverter
+.lbm "ILBM" "GKON" Amiga IFF Image GraphicConverter
+.ilbm "ILBM" "GKON" Amiga ILBM Image GraphicConverter
+.ani "ANIi" "GKON" Animated NeoChrome GraphicConverter
+.pcs "PICS" "GKON" Animated PICTs GraphicConverter
+.pc1 "Dega" "GKON" Atari Degas Image GraphicConverter
+.pc2 "Dega" "GKON" Atari Degas Image GraphicConverter
+.pc3 "Dega" "GKON" Atari Degas Image GraphicConverter
+.pi1 "Dega" "GKON" Atari Degas Image GraphicConverter
+.pi2 "Dega" "GKON" Atari Degas Image GraphicConverter
+.pi3 "Dega" "GKON" Atari Degas Image GraphicConverter
+.ic1 "IMAG" "GKON" Atari Image GraphicConverter
+.ic2 "IMAG" "GKON" Atari Image GraphicConverter
+.ic3 "IMAG" "GKON" Atari Image GraphicConverter
+.neo "NeoC" "GKON" Atari NeoChrome GraphicConverter
+.pac "STAD" "GKON" Atari STAD Image GraphicConverter
+.spc "Spec" "GKON" Atari Spectrum 512 GraphicConverter
+.tny "TINY" "GKON" Atari TINY Bitmap GraphicConverter
+.pm "PMpm" "GKON" Bitmap from xv GraphicConverter
+.scg "RIX3" "GKON" ColoRIX GraphicConverter
+.sci "RIX3" "GKON" ColoRIX GraphicConverter
+.scp "RIX3" "GKON" ColoRIX GraphicConverter
+.scr "RIX3" "GKON" ColoRIX GraphicConverter
+.scu "RIX3" "GKON" ColoRIX GraphicConverter
+.cgm "CGMm" "GKON" Computer Graphics Meta GraphicConverter
+.vff "VFFf" "GKON" DESR VFF Greyscale Image GraphicConverter
+.cut "Halo" "GKON" Dr Halo Image GraphicConverter
+.art "ART " "GKON" First Publisher GraphicConverter
+.fts "FITS" "GKON" Flexible Image Transport GraphicConverter
+.fit "FITS" "GKON" Flexible Image Transport GraphicConverter image/x-fits
+.gem "GEM-" "GKON" GEM Metafile GraphicConverter
+.img "IMGg" "GKON" GEM bit image/XIMG GraphicConverter
+.grp "GRPp" "GKON" GRP Image GraphicConverter
+.hpgl "HPGL" "GKON" HP GL/2 GraphicConverter
+.plt "HPGL" "GKON" HP GL/2 GraphicConverter
+.ief "IEF " "GKON" IEF image GraphicConverter image/ief
+.scc "MSX " "GKON" MSX pitcure GraphicConverter
+.msp "MSPp" "GKON" Microsoft Paint GraphicConverter
+.pcx "PCXx" "GKON" PC PaintBrush GraphicConverter
+.pbm "PPGM" "GKON" Portable Bitmap GraphicConverter image/x-portable-bitmap
+.pgm "PPGM" "GKON" Portable Graymap GraphicConverter image/x-portable-graymap
+.ppm "PPGM" "GKON" Portable Pixmap GraphicConverter image/x-portable-pixmap
+.shp "SHPp" "GKON" Printmaster Icon Library GraphicConverter
+.qdv "QDVf" "GKON" QDV image GraphicConverter
+.rif "RIFF" "GKON" RIFF Graphic GraphicConverter
+.rle "RLE " "GKON" RLE image GraphicConverter
+.raw "BINA" "GKON" Raw Image GraphicConverter
+.bw "SGI " "GKON" SGI Image GraphicConverter
+.rgb "SGI " "GKON" SGI Image GraphicConverter image/x-rgb
+.rgba "SGI " "GKON" SGI Image GraphicConverter image/x-rgb
+.six "SIXE" "GKON" SIXEL image GraphicConverter
+.ct "..CT" "GKON" Scitex-CT GraphicConverter
+.dcx "DCXx" "GKON" Some PCX Images GraphicConverter
+.sup "SCRN" "GKON" StartupScreen GraphicConverter
+.sr "SUNn" "GKON" Sun Raster Image GraphicConverter
+.sun "SUNn" "GKON" Sun Raster Image GraphicConverter
+.targa "TPIC" "GKON" Truevision Image GraphicConverter
+.tga "TPIC" "GKON" Truevision Image GraphicConverter
+.clp "CLPp" "GKON" Windows Clipboard GraphicConverter
+.icn "ICO " "GKON" Windows Icon GraphicConverter
+.ico "ICO " "GKON" Windows Icon GraphicConverter
+.wmf "WMF " "GKON" Windows Metafile GraphicConverter
+.wpg "WPGf" "GKON" WordPerfect Graphic GraphicConverter
+.xbm "XBM " "GKON" X-Windows Bitmap GraphicConverter image/x-xbm
+.x10 "XWDd" "GKON" X-Windows Dump GraphicConverter image/x-xwd
+.x11 "XWDd" "GKON" X-Windows Dump GraphicConverter image/x-xwd
+.xwd "XWDd" "GKON" X-Windows Dump GraphicConverter image/x-xwd
+.xpm "XPM " "GKON" X-Windows Pixmap GraphicConverter image/x-xpm
+.htm "TEXT" "MOSS" HyperText Netscape Communicator text/html
+.html "TEXT" "MOSS" HyperText Netscape Communicator text/html
+.m2v "MPG2" "MPG2" MPEG-2 IPB videostream MPEG2decoder
+.for "TEXT" "MPS " Fortran Source MPW Shell
+.wri "WDBN" "MSWD" MS Write/Windows Microsoft Word
+.mcw "WDBN" "MSWD" Mac Word Document Microsoft Word
+.rtf "TEXT" "MSWD" Rich Text Format Microsoft Word application/rtf
+.doc "WDBN" "MSWD" Word Document Microsoft Word application/msword
+.dot "sDBN" "MSWD" Word for Windows Template Microsoft Word
+.mw "MW2D" "MWII" MacWrite Document MacWrite II application/macwriteii
+.mwii "MW2D" "MWII" MacWrite Document MacWrite II application/macwriteii
+.pl "TEXT" "McPL" Perl Source MacPerl
+.asf "ASF_" "Ms01" Netshow Player Netshow Server video/x-ms-asf
+.asx "ASX_" "Ms01" Netshow Player Netshow Server video/x-ms-asf
+.oda "ODIF" "ODA " ODA Document MacODA XTND Translator application/oda
+.latex "TEXT" "OTEX" Latex OzTex application/x-latex
+.texi "TEXT" "OTEX" TeX Document OzTeX
+.tex "TEXT" "OTEX" TeX Document OzTeX application/x-tex
+.texinfo "TEXT" "OTEX" TeX Document OzTeX application/x-texinfo
+.diz "TEXT" "R*Ch" BBS Descriptive Text BBEdit
+.ml "TEXT" "R*ch" ML Source BBEdit
+.mak "TEXT" "R*ch" Makefile BBEdit
+.m2 "TEXT" "R*ch" Modula 2 Source BBEdit
+.i3 "TEXT" "R*ch" Modula 3 Interface BBEdit
+.m3 "TEXT" "R*ch" Modula 3 Source BBEdit
+.prn "TEXT" "R*ch" Printer Output File BBEdit
+.rtx "TEXT" "R*ch" Rich Text BBEdit text/richtext
+.rpl "FRL!" "REP!" Replica Document Replica
+.rib "TEXT" "RINI" Renderman 3D Data Renderman
+.rsc "rsrc" "RSED" Resource File ResEdit
+.rsrc "rsrc" "RSED" Resource File ResEdit
+.pdb "TEXT" "RSML" Brookhaven PDB file RasMac
+.mol "TEXT" "RSML" MDL Molfile RasMac
+.bar "BARF" "S691" Unix BAR Archive SunTar
+.aif "AIFF" "SCPL" AIFF Sound SoundApp audio/x-aiff
+.aiff "AIFF" "SCPL" AIFF Sound SoundApp audio/x-aiff
+.aifc "AIFC" "SCPL" AIFF Sound Compressed SoundApp audio/x-aiff
+.al "ALAW" "SCPL" ALAW Sound SoundApp
+.8svx "8SVX" "SCPL" Amiga 8-bit sound SoundApp
+.svx "8SVX" "SCPL" Amiga IFF Sound SoundApp
+.med "STrk" "SCPL" Amiga MED Sound SoundApp
+.8med "STrk" "SCPL" Amiga OctaMed music SoundApp
+.mod "STrk" "SCPL" MOD Music SoundApp
+.nst "STrk" "SCPL" MOD Music SoundApp
+.okt "OKTA" "SCPL" Oktalyser MOD Music SoundApp
+.wve "BINA" "SCPL" PSION sound SoundApp
+.snd "BINA" "SCPL" Sound of various types SoundApp
+.hcom "FSSD" "SCPL" SoundEdit Sound ex SOX SoundApp
+.voc "VOC " "SCPL" VOC Sound SoundApp
+.sf "IRCM" "SDHK" IRCAM Sound SoundHack
+.pkg "HBSF" "SITx" AppleLink Package StuffIt Expander
+.hqx "TEXT" "SITx" BinHex StuffIt Expanderª application/mac-binhex40
+.sithqx "TEXT" "SITx" BinHexed StuffIt Archive StuffIt Expander application/mac-binhex40
+.cpt "PACT" "SITx" Compact Pro Archive StuffIt Expander
+.taz "ZIVU" "SITx" Compressed Tape ARchive StuffIt Expander application/x-compress
+.gz "SIT!" "SITx" Gnu ZIP Archive StuffIt Expander application/x-gzip
+.tgz "Gzip" "SITx" Gnu ZIPed Tape ARchive StuffIt Expander application/x-gzip
+.lha "LHA " "SITx" LHArc Archive StuffIt Expander
+.lzh "LHA " "SITx" LHArc Archive StuffIt Expander
+.mime "TEXT" "SITx" MIME Message StuffIt Expander message/rfc822
+.bin "SIT!" "SITx" MacBinary StuffIt Expander application/macbinary
+.arc "mArc" "SITx" PC ARChive StuffIt Expander
+.zip "ZIP " "SITx" PC ZIP Archive StuffIt Expander application/zip
+.pit "PIT " "SITx" PackIt Archive StuffIt Expander
+.pf "CSIT" "SITx" Private File StuffIt Expander
+.sit "SIT!" "SITx" StuffIt 1.5.1 Archive StuffIt Expander application/x-stuffit
+.uu "TEXT" "SITx" UUEncode StuffIt Expander
+.uue "TEXT" "SITx" UUEncode StuffIt Expander
+.Z "ZIVU" "SITx" Unix Compress Archive StuffIt Expander application/x-compress
+.tar "TARF" "SITx" Unix Tape ARchive StuffIt Expander application/x-tar
+.669 "6669" "SNPL" 669 MOD Music PlayerPro
+.xm "XM " "SNPL" FastTracker MOD Music PlayerPro
+.mtm "MTM " "SNPL" MultiMOD Music PlayerPro
+.s3m "S3M " "SNPL" ScreamTracker 3 MOD PlayerPro
+.com "PCFA" "SWIN" MS-DOS Executable SoftWindows
+.exe "PCFA" "SWIN" MS-DOS Executable SoftWindows
+.obj "PCFL" "SWIN" Object (DOS/Windows) SoftWindows
+.ovl "PCFL" "SWIN" Overlay (DOS/Windows) SoftWindows
+.dll "PCFL" "SWIN" Windows DLL SoftWindows
+.dxf "TEXT" "SWVL" AutoCAD 3D Data Swivel Pro
+.avi "VfW " "TVOD" AVI Movie QuickTime Player video/avi
+.fli "FLI " "TVOD" FLI Animation QuickTime Player
+.flc "FLI " "TVOD" FLIC Animation QuickTime Player
+.mid "Midi" "TVOD" MIDI Music MoviePlayer
+.midi "Midi" "TVOD" MIDI Music MoviePlayer
+.mpe "MPEG" "TVOD" MPEG Movie of some sort MoviePlayer video/mpeg
+.mpeg "MPEG" "TVOD" MPEG Movie of some sort MoviePlayer video/mpeg
+.mpg "MPEG" "TVOD" MPEG Movie of some sort MoviePlayer video/mpeg
+.m1v "M1V " "TVOD" MPEG-1 IPB videostream MoviePlayer video/mpeg
+.m1a "MPEG" "TVOD" MPEG-1 audiostream MoviePlayer audio/x-mpeg
+.mp2 "MPEG" "TVOD" MPEG-1 audiostream MoviePlayer audio/x-mpeg
+.mp3 "MPG3" "TVOD" MPEG-3 audiostream MoviePlayer audio/x-mpeg
+.mpa "MPEG" "TVOD" MPEG-1 audiostream MoviePlayer audio/x-mpeg
+.m1s "MPEG" "TVOD" MPEG-1 systemstream MoviePlayer
+.ul "ULAW" "TVOD" Mu-Law Sound MoviePlayer audio/basic
+.moov "MooV" "TVOD" QuickTime Movie MoviePlayer video/quicktime
+.mov "MooV" "TVOD" QuickTime Movie MoviePlayer video/quicktime
+.qt "MooV" "TVOD" QuickTime Movie MoviePlayer video/quicktime
+.au "ULAW" "TVOD" Sun Sound QuickTime Player audio/basic
+.wav "WAVE" "TVOD" Windows WAV Sound MoviePlayer audio/x-wav
+.sha "TEXT" "UnSh" Unix Shell Archive UnShar application/x-shar
+.shar "TEXT" "UnSh" Unix Shell Archive UnShar application/x-shar
+.wpm "WPD1" "WPC2" WordPerfect Mac WordPerfect
+.wp4 ".WP4" "WPC2" WordPerfect PC 4.2 Doc WordPerfect
+.w51 ".WP5" "WPC2" WordPerfect PC 5.1 Doc WordPerfect application/wordperfect5.1
+.wp ".WP5" "WPC2" WordPerfect PC 5.1 Doc WordPerfect application/wordperfect5.1
+.wp5 ".WP5" "WPC2" WordPerfect PC 5.x Doc WordPerfect application/wordperfect5.1
+.wp6 ".WP6" "WPC2" WordPerfect PC 6.x Doc WordPerfect
+.csv "TEXT" "XCEL" Comma Separated Vars Excel
+.dif "TEXT" "XCEL" Data Interchange Format Excel
+.xlc "XLC " "XCEL" Excel Chart Excel
+.xlm "XLM " "XCEL" Excel Macro Excel
+.xl "XLS " "XCEL" Excel Spreadsheet Excel
+.xls "XLS " "XCEL" Excel Spreadsheet Excel
+.xlw "XLW " "XCEL" Excel Workspace Excel
+.wks "XLBN" "XCEL" Lotus Spreadsheet r1.x Excel
+.wk1 "XLBN" "XCEL" Lotus Spreadsheet r2.1 Excel
+.slk "TEXT" "XCEL" SYLK Spreadsheet Excel
+.syk "TEXT" "XCEL" SYLK Spreadsheet Excel
+.sylk "TEXT" "XCEL" SYLK Spreadsheet Excel
+.tsv "TEXT" "XCEL" Tab Separated Values Excel text/tab-separated-values
+.qxd "XDOC" "XPR3" QuarkXpress Document QuarkXpress
+.qxt "XTMP" "XPR3" QuarkXpress Template QuarkXpress
+.image "dImg" "ddsk" Apple DiskCopy Image Disk Copy
+.etx "TEXT" "ezVu" SEText Easy View text/x-setext
+.out "BINA" "hDmp" Output File HexEdit
+.binary "BINA" "hDmp" Untyped Binary Data HexEdit application/octet-stream
+.gif "GIFf" "ogle" GIF Picture PictureViewer image/gif
+.jfif "JPEG" "ogle" JFIF Image PictureViewer
+.jpe "JPEG" "ogle" JPEG Picture PictureViewer image/jpeg
+.jpeg "JPEG" "ogle" JPEG Picture PictureViewer image/jpeg
+.jpg "JPEG" "ogle" JPEG Picture PictureViewer image/jpeg
+.pntg "PNTG" "ogle" Macintosh Painting PictureViewer
+.bga "BMPp" "ogle" OS/2 Bitmap PictureViewer
+.vga "BMPp" "ogle" OS/2 Bitmap PictureViewer
+.pict "PICT" "ogle" PICT Picture PictureViewer image/x-macpict
+.mac "PICT" "ogle" PICT Picture PictureViewer image/x-pict
+.pct "PICT" "ogle" PICT Picture PictureViewer image/x-pict
+.pic "PICT" "ogle" PICT Picture PictureViewer image/x-pict
+.png "PNG " "ogle" Portable Network Graphic PictureViewer
+.sgi ".SGI" "ogle" SGI Image PictureViewer
+.tif "TIFF" "ogle" TIFF Picture PictureViewer image/tiff
+.tiff "TIFF" "ogle" TIFF Picture PictureViewer image/tiff
+.bmp "BMPp" "ogle" Windows Bitmap PictureViewer
+.tx8 "TEXT" "ttxt" 8-bit ASCII Text SimpleText
+.asc "TEXT" "ttxt" ASCII Text SimpleText text/plain
+.ascii "TEXT" "ttxt" ASCII Text SimpleText text/plain
+.text "TEXT" "ttxt" ASCII Text SimpleText text/plain
+.txt "TEXT" "ttxt" ASCII Text SimpleText text/plain
+.faq "TEXT" "ttxt" ASCII Text SimpleText text/x-usenet-faq
+.a "TEXT" "ttxt" Assembly Source SimpleText
+.asm "TEXT" "ttxt" Assembly Source SimpleText
+.bas "TEXT" "ttxt" BASIC Source SimpleText
+.boo "TEXT" "ttxt" BOO encoded SimpleText
+.bib "TEXT" "ttxt" BibTex Bibliography SimpleText
+.bst "TEXT" "ttxt" BibTex Style SimpleText
+.nfo "TEXT" "ttxt" Info Text SimpleText application/text
+.bat "TEXT" "ttxt" MS-DOS Batch File SimpleText
+.cmd "TEXT" "ttxt" OS/2 Batch File SimpleText
+.me "TEXT" "ttxt" Text Readme SimpleText
+.rme "TEXT" "ttxt" Text Readme SimpleText
+.1st "TEXT" "ttxt" Text Readme SimpleText application/text
+.readme "TEXT" "ttxt" Text Readme SimpleText application/text
+.ini "TEXT" "ttxt" Windows INI File SimpleText
+.ps "TEXT" "vgrd" PostScript LaserWriter 8 application/postscript
+.eps "EPSF" "vgrd" Postscript LaserWriter 8 application/postscript
+.epsf "EPSF" "vgrd" Postscript LaserWriter 8 application/postscript
+.dvi "ODVI" "xdvi" TeX DVI Document xdvi application/x-dvi
--- /dev/null
+# Last Updated July 8, 1999
+# Use at your own risk. No guarantees express or implied.
+#
+# Try to use MacPerl script 'ICDumpSuffixMap' included in /usr/doc
+# to download file mapping list from your Internet Config Preference.
+#
+# inoue@ma.ns.musashi-tech.ac.jp.
+#
+
+:DEFAULT: options:nohex,usedots
+# Cobalt config start (do not remove this line!)
+# Cobalt config stop (do not remove this line!)
+
+. "TEXT" "ttxt" ASCII Text SimpleText text/plain
+
+.mf "TEXT" "*MF*" Metafont Metafont
+.sty "TEXT" "*TEX" TeX Style Textures
+.psd "8BPS" "8BIM" PhotoShop Document Photoshop
+.pxr "PXR " "8BIM" Pixar Image Photoshop
+.sea "APPL" "????" Self-Extracting Archive Self Extracting Archive
+.apd "TEXT" "ALD3" Aldus Printer Description Aldus PageMaker
+.pm3 "ALB3" "ALD3" PageMaker 3 Document PageMaker
+.pm4 "ALB4" "ALD4" PageMaker 4 Document PageMaker
+.pt4 "ALT4" "ALD4" PageMaker 4 Template PageMaker
+.pm5 "ALB5" "ALD5" PageMaker 5 Document PageMaker
+.pt5 "ALT5" "ALD5" PageMaker 5 Template PageMaker
+.pdx "TEXT" "ALD5" Printer Description PageMaker
+.ppd "TEXT" "ALD5" Printer Description PageMaker
+.dl "DL " "AnVw" DL Animation MacAnim Viewer
+.gl "GL " "AnVw" GL Animation MacAnim Viewer
+.url "AURL" "Arch" URL Bookmark Anarchie message/external-body
+.zoo "Zoo " "Booz" Zoo Archive MacBooz
+.pdf "PDF " "CARO" Portable Document Format Acrobat Reader application/pdf
+.h "TEXT" "CWIE" C Include File CodeWarrior
+.hp "TEXT" "CWIE" C Include File CodeWarrior
+.hpp "TEXT" "CWIE" C Include File CodeWarrior
+.c "TEXT" "CWIE" C Source CodeWarrior
+.cp "TEXT" "CWIE" C++ Source CodeWarrior
+.cpp "TEXT" "CWIE" C++ Source CodeWarrior
+.class "Clss" "CWIE" Java Class File CodeWarrior
+.java "TEXT" "CWIE" Java Source File CodeWarrior
+.p "TEXT" "CWIE" Pascal Source CodeWarrior
+.pas "TEXT" "CWIE" Pascal Source CodeWarrior
+.cvs "drw2" "DAD2" Canvas Drawing Canvas
+.arj "BINA" "DArj" ARJ Archive DeArj
+.evy "EVYD" "ENVY" Envoy Document Envoy
+.fm "FMPR" "FMPR" FileMaker Pro Database FileMaker Pro
+.dbf "COMP" "FOX+" DBase Document FoxBase+
+.mif "TEXT" "Fram" FrameMaker MIF FrameMaker application/x-framemaker
+.arr "ARR " "GKON" Amber ARR image GraphicConverter
+.iff "ILBM" "GKON" Amiga IFF Image GraphicConverter
+.lbm "ILBM" "GKON" Amiga IFF Image GraphicConverter
+.ilbm "ILBM" "GKON" Amiga ILBM Image GraphicConverter
+.ani "ANIi" "GKON" Animated NeoChrome GraphicConverter
+.pcs "PICS" "GKON" Animated PICTs GraphicConverter
+.pc1 "Dega" "GKON" Atari Degas Image GraphicConverter
+.pc2 "Dega" "GKON" Atari Degas Image GraphicConverter
+.pc3 "Dega" "GKON" Atari Degas Image GraphicConverter
+.pi1 "Dega" "GKON" Atari Degas Image GraphicConverter
+.pi2 "Dega" "GKON" Atari Degas Image GraphicConverter
+.pi3 "Dega" "GKON" Atari Degas Image GraphicConverter
+.ic1 "IMAG" "GKON" Atari Image GraphicConverter
+.ic2 "IMAG" "GKON" Atari Image GraphicConverter
+.ic3 "IMAG" "GKON" Atari Image GraphicConverter
+.neo "NeoC" "GKON" Atari NeoChrome GraphicConverter
+.pac "STAD" "GKON" Atari STAD Image GraphicConverter
+.spc "Spec" "GKON" Atari Spectrum 512 GraphicConverter
+.tny "TINY" "GKON" Atari TINY Bitmap GraphicConverter
+.pm "PMpm" "GKON" Bitmap from xv GraphicConverter
+.scg "RIX3" "GKON" ColoRIX GraphicConverter
+.sci "RIX3" "GKON" ColoRIX GraphicConverter
+.scp "RIX3" "GKON" ColoRIX GraphicConverter
+.scr "RIX3" "GKON" ColoRIX GraphicConverter
+.scu "RIX3" "GKON" ColoRIX GraphicConverter
+.cgm "CGMm" "GKON" Computer Graphics Meta GraphicConverter
+.vff "VFFf" "GKON" DESR VFF Greyscale Image GraphicConverter
+.cut "Halo" "GKON" Dr Halo Image GraphicConverter
+.art "ART " "GKON" First Publisher GraphicConverter
+.fts "FITS" "GKON" Flexible Image Transport GraphicConverter
+.fit "FITS" "GKON" Flexible Image Transport GraphicConverter image/x-fits
+.gem "GEM-" "GKON" GEM Metafile GraphicConverter
+.img "IMGg" "GKON" GEM bit image/XIMG GraphicConverter
+.grp "GRPp" "GKON" GRP Image GraphicConverter
+.hpgl "HPGL" "GKON" HP GL/2 GraphicConverter
+.plt "HPGL" "GKON" HP GL/2 GraphicConverter
+.ief "IEF " "GKON" IEF image GraphicConverter image/ief
+.scc "MSX " "GKON" MSX pitcure GraphicConverter
+.msp "MSPp" "GKON" Microsoft Paint GraphicConverter
+.pcx "PCXx" "GKON" PC PaintBrush GraphicConverter
+.pbm "PPGM" "GKON" Portable Bitmap GraphicConverter image/x-portable-bitmap
+.pgm "PPGM" "GKON" Portable Graymap GraphicConverter image/x-portable-graymap
+.ppm "PPGM" "GKON" Portable Pixmap GraphicConverter image/x-portable-pixmap
+.shp "SHPp" "GKON" Printmaster Icon Library GraphicConverter
+.qdv "QDVf" "GKON" QDV image GraphicConverter
+.rif "RIFF" "GKON" RIFF Graphic GraphicConverter
+.rle "RLE " "GKON" RLE image GraphicConverter
+.raw "BINA" "GKON" Raw Image GraphicConverter
+.bw "SGI " "GKON" SGI Image GraphicConverter
+.rgb "SGI " "GKON" SGI Image GraphicConverter image/x-rgb
+.rgba "SGI " "GKON" SGI Image GraphicConverter image/x-rgb
+.six "SIXE" "GKON" SIXEL image GraphicConverter
+.ct "..CT" "GKON" Scitex-CT GraphicConverter
+.dcx "DCXx" "GKON" Some PCX Images GraphicConverter
+.sup "SCRN" "GKON" StartupScreen GraphicConverter
+.sr "SUNn" "GKON" Sun Raster Image GraphicConverter
+.sun "SUNn" "GKON" Sun Raster Image GraphicConverter
+.targa "TPIC" "GKON" Truevision Image GraphicConverter
+.tga "TPIC" "GKON" Truevision Image GraphicConverter
+.clp "CLPp" "GKON" Windows Clipboard GraphicConverter
+.icn "ICO " "GKON" Windows Icon GraphicConverter
+.ico "ICO " "GKON" Windows Icon GraphicConverter
+.wmf "WMF " "GKON" Windows Metafile GraphicConverter
+.wpg "WPGf" "GKON" WordPerfect Graphic GraphicConverter
+.xbm "XBM " "GKON" X-Windows Bitmap GraphicConverter image/x-xbm
+.x10 "XWDd" "GKON" X-Windows Dump GraphicConverter image/x-xwd
+.x11 "XWDd" "GKON" X-Windows Dump GraphicConverter image/x-xwd
+.xwd "XWDd" "GKON" X-Windows Dump GraphicConverter image/x-xwd
+.xpm "XPM " "GKON" X-Windows Pixmap GraphicConverter image/x-xpm
+.htm "TEXT" "MOSS" HyperText Netscape Communicator text/html
+.html "TEXT" "MOSS" HyperText Netscape Communicator text/html
+.m2v "MPG2" "MPG2" MPEG-2 IPB videostream MPEG2decoder
+.for "TEXT" "MPS " Fortran Source MPW Shell
+.wri "WDBN" "MSWD" MS Write/Windows Microsoft Word
+.mcw "WDBN" "MSWD" Mac Word Document Microsoft Word
+.rtf "TEXT" "MSWD" Rich Text Format Microsoft Word application/rtf
+.doc "WDBN" "MSWD" Word Document Microsoft Word application/msword
+.dot "sDBN" "MSWD" Word for Windows Template Microsoft Word
+.mw "MW2D" "MWII" MacWrite Document MacWrite II application/macwriteii
+.mwii "MW2D" "MWII" MacWrite Document MacWrite II application/macwriteii
+.pl "TEXT" "McPL" Perl Source MacPerl
+.asf "ASF_" "Ms01" Netshow Player Netshow Server video/x-ms-asf
+.asx "ASX_" "Ms01" Netshow Player Netshow Server video/x-ms-asf
+.oda "ODIF" "ODA " ODA Document MacODA XTND Translator application/oda
+.latex "TEXT" "OTEX" Latex OzTex application/x-latex
+.texi "TEXT" "OTEX" TeX Document OzTeX
+.tex "TEXT" "OTEX" TeX Document OzTeX application/x-tex
+.texinfo "TEXT" "OTEX" TeX Document OzTeX application/x-texinfo
+.diz "TEXT" "R*Ch" BBS Descriptive Text BBEdit
+.ml "TEXT" "R*ch" ML Source BBEdit
+.mak "TEXT" "R*ch" Makefile BBEdit
+.m2 "TEXT" "R*ch" Modula 2 Source BBEdit
+.i3 "TEXT" "R*ch" Modula 3 Interface BBEdit
+.m3 "TEXT" "R*ch" Modula 3 Source BBEdit
+.prn "TEXT" "R*ch" Printer Output File BBEdit
+.rtx "TEXT" "R*ch" Rich Text BBEdit text/richtext
+.rpl "FRL!" "REP!" Replica Document Replica
+.rib "TEXT" "RINI" Renderman 3D Data Renderman
+.rsc "rsrc" "RSED" Resource File ResEdit
+.rsrc "rsrc" "RSED" Resource File ResEdit
+.pdb "TEXT" "RSML" Brookhaven PDB file RasMac
+.mol "TEXT" "RSML" MDL Molfile RasMac
+.bar "BARF" "S691" Unix BAR Archive SunTar
+.aif "AIFF" "SCPL" AIFF Sound SoundApp audio/x-aiff
+.aiff "AIFF" "SCPL" AIFF Sound SoundApp audio/x-aiff
+.aifc "AIFC" "SCPL" AIFF Sound Compressed SoundApp audio/x-aiff
+.al "ALAW" "SCPL" ALAW Sound SoundApp
+.8svx "8SVX" "SCPL" Amiga 8-bit sound SoundApp
+.svx "8SVX" "SCPL" Amiga IFF Sound SoundApp
+.med "STrk" "SCPL" Amiga MED Sound SoundApp
+.8med "STrk" "SCPL" Amiga OctaMed music SoundApp
+.mod "STrk" "SCPL" MOD Music SoundApp
+.nst "STrk" "SCPL" MOD Music SoundApp
+.okt "OKTA" "SCPL" Oktalyser MOD Music SoundApp
+.wve "BINA" "SCPL" PSION sound SoundApp
+.snd "BINA" "SCPL" Sound of various types SoundApp
+.hcom "FSSD" "SCPL" SoundEdit Sound ex SOX SoundApp
+.voc "VOC " "SCPL" VOC Sound SoundApp
+.sf "IRCM" "SDHK" IRCAM Sound SoundHack
+.pkg "HBSF" "SITx" AppleLink Package StuffIt Expander
+.hqx "TEXT" "SITx" BinHex StuffIt Expanderª application/mac-binhex40
+.sithqx "TEXT" "SITx" BinHexed StuffIt Archive StuffIt Expander application/mac-binhex40
+.cpt "PACT" "SITx" Compact Pro Archive StuffIt Expander
+.taz "ZIVU" "SITx" Compressed Tape ARchive StuffIt Expander application/x-compress
+.gz "SIT!" "SITx" Gnu ZIP Archive StuffIt Expander application/x-gzip
+.tgz "Gzip" "SITx" Gnu ZIPed Tape ARchive StuffIt Expander application/x-gzip
+.lha "LHA " "SITx" LHArc Archive StuffIt Expander
+.lzh "LHA " "SITx" LHArc Archive StuffIt Expander
+.mime "TEXT" "SITx" MIME Message StuffIt Expander message/rfc822
+.bin "SIT!" "SITx" MacBinary StuffIt Expander application/macbinary
+.arc "mArc" "SITx" PC ARChive StuffIt Expander
+.zip "ZIP " "SITx" PC ZIP Archive StuffIt Expander application/zip
+.pit "PIT " "SITx" PackIt Archive StuffIt Expander
+.pf "CSIT" "SITx" Private File StuffIt Expander
+.sit "SIT!" "SITx" StuffIt 1.5.1 Archive StuffIt Expander application/x-stuffit
+.uu "TEXT" "SITx" UUEncode StuffIt Expander
+.uue "TEXT" "SITx" UUEncode StuffIt Expander
+.Z "ZIVU" "SITx" Unix Compress Archive StuffIt Expander application/x-compress
+.tar "TARF" "SITx" Unix Tape ARchive StuffIt Expander application/x-tar
+.669 "6669" "SNPL" 669 MOD Music PlayerPro
+.xm "XM " "SNPL" FastTracker MOD Music PlayerPro
+.mtm "MTM " "SNPL" MultiMOD Music PlayerPro
+.s3m "S3M " "SNPL" ScreamTracker 3 MOD PlayerPro
+.com "PCFA" "SWIN" MS-DOS Executable SoftWindows
+.exe "PCFA" "SWIN" MS-DOS Executable SoftWindows
+.obj "PCFL" "SWIN" Object (DOS/Windows) SoftWindows
+.ovl "PCFL" "SWIN" Overlay (DOS/Windows) SoftWindows
+.dll "PCFL" "SWIN" Windows DLL SoftWindows
+.dxf "TEXT" "SWVL" AutoCAD 3D Data Swivel Pro
+.avi "VfW " "TVOD" AVI Movie QuickTime Player video/avi
+.fli "FLI " "TVOD" FLI Animation QuickTime Player
+.flc "FLI " "TVOD" FLIC Animation QuickTime Player
+.mid "Midi" "TVOD" MIDI Music MoviePlayer
+.midi "Midi" "TVOD" MIDI Music MoviePlayer
+.mpe "MPEG" "TVOD" MPEG Movie of some sort MoviePlayer video/mpeg
+.mpeg "MPEG" "TVOD" MPEG Movie of some sort MoviePlayer video/mpeg
+.mpg "MPEG" "TVOD" MPEG Movie of some sort MoviePlayer video/mpeg
+.m1v "M1V " "TVOD" MPEG-1 IPB videostream MoviePlayer video/mpeg
+.m1a "MPEG" "TVOD" MPEG-1 audiostream MoviePlayer audio/x-mpeg
+.mp2 "MPEG" "TVOD" MPEG-1 audiostream MoviePlayer audio/x-mpeg
+.mpa "MPEG" "TVOD" MPEG-1 audiostream MoviePlayer audio/x-mpeg
+.m1s "MPEG" "TVOD" MPEG-1 systemstream MoviePlayer
+.ul "ULAW" "TVOD" Mu-Law Sound MoviePlayer audio/basic
+.moov "MooV" "TVOD" QuickTime Movie MoviePlayer video/quicktime
+.mov "MooV" "TVOD" QuickTime Movie MoviePlayer video/quicktime
+.qt "MooV" "TVOD" QuickTime Movie MoviePlayer video/quicktime
+.au "ULAW" "TVOD" Sun Sound QuickTime Player audio/basic
+.wav "WAVE" "TVOD" Windows WAV Sound MoviePlayer audio/x-wav
+.sha "TEXT" "UnSh" Unix Shell Archive UnShar application/x-shar
+.shar "TEXT" "UnSh" Unix Shell Archive UnShar application/x-shar
+.wpm "WPD1" "WPC2" WordPerfect Mac WordPerfect
+.wp4 ".WP4" "WPC2" WordPerfect PC 4.2 Doc WordPerfect
+.w51 ".WP5" "WPC2" WordPerfect PC 5.1 Doc WordPerfect application/wordperfect5.1
+.wp ".WP5" "WPC2" WordPerfect PC 5.1 Doc WordPerfect application/wordperfect5.1
+.wp5 ".WP5" "WPC2" WordPerfect PC 5.x Doc WordPerfect application/wordperfect5.1
+.wp6 ".WP6" "WPC2" WordPerfect PC 6.x Doc WordPerfect
+.csv "TEXT" "XCEL" Comma Separated Vars Excel
+.dif "TEXT" "XCEL" Data Interchange Format Excel
+.xlc "XLC " "XCEL" Excel Chart Excel
+.xlm "XLM " "XCEL" Excel Macro Excel
+.xl "XLS " "XCEL" Excel Spreadsheet Excel
+.xls "XLS " "XCEL" Excel Spreadsheet Excel
+.xlw "XLW " "XCEL" Excel Workspace Excel
+.wks "XLBN" "XCEL" Lotus Spreadsheet r1.x Excel
+.wk1 "XLBN" "XCEL" Lotus Spreadsheet r2.1 Excel
+.slk "TEXT" "XCEL" SYLK Spreadsheet Excel
+.syk "TEXT" "XCEL" SYLK Spreadsheet Excel
+.sylk "TEXT" "XCEL" SYLK Spreadsheet Excel
+.tsv "TEXT" "XCEL" Tab Separated Values Excel text/tab-separated-values
+.qxd "XDOC" "XPR3" QuarkXpress Document QuarkXpress
+.qxt "XTMP" "XPR3" QuarkXpress Template QuarkXpress
+.image "dImg" "ddsk" Apple DiskCopy Image Disk Copy
+.etx "TEXT" "ezVu" SEText Easy View text/x-setext
+.out "BINA" "hDmp" Output File HexEdit
+.binary "BINA" "hDmp" Untyped Binary Data HexEdit application/octet-stream
+.gif "GIFf" "ogle" GIF Picture PictureViewer image/gif
+.jfif "JPEG" "ogle" JFIF Image PictureViewer
+.jpe "JPEG" "ogle" JPEG Picture PictureViewer image/jpeg
+.jpeg "JPEG" "ogle" JPEG Picture PictureViewer image/jpeg
+.jpg "JPEG" "ogle" JPEG Picture PictureViewer image/jpeg
+.pntg "PNTG" "ogle" Macintosh Painting PictureViewer
+.bga "BMPp" "ogle" OS/2 Bitmap PictureViewer
+.vga "BMPp" "ogle" OS/2 Bitmap PictureViewer
+.pict "PICT" "ogle" PICT Picture PictureViewer image/x-macpict
+.mac "PICT" "ogle" PICT Picture PictureViewer image/x-pict
+.pct "PICT" "ogle" PICT Picture PictureViewer image/x-pict
+.pic "PICT" "ogle" PICT Picture PictureViewer image/x-pict
+.png "PNG " "ogle" Portable Network Graphic PictureViewer
+.sgi ".SGI" "ogle" SGI Image PictureViewer
+.tif "TIFF" "ogle" TIFF Picture PictureViewer image/tiff
+.tiff "TIFF" "ogle" TIFF Picture PictureViewer image/tiff
+.bmp "BMPp" "ogle" Windows Bitmap PictureViewer
+.tx8 "TEXT" "ttxt" 8-bit ASCII Text SimpleText
+.asc "TEXT" "ttxt" ASCII Text SimpleText text/plain
+.ascii "TEXT" "ttxt" ASCII Text SimpleText text/plain
+.text "TEXT" "ttxt" ASCII Text SimpleText text/plain
+.txt "TEXT" "ttxt" ASCII Text SimpleText text/plain
+.faq "TEXT" "ttxt" ASCII Text SimpleText text/x-usenet-faq
+.a "TEXT" "ttxt" Assembly Source SimpleText
+.asm "TEXT" "ttxt" Assembly Source SimpleText
+.bas "TEXT" "ttxt" BASIC Source SimpleText
+.boo "TEXT" "ttxt" BOO encoded SimpleText
+.bib "TEXT" "ttxt" BibTex Bibliography SimpleText
+.bst "TEXT" "ttxt" BibTex Style SimpleText
+.nfo "TEXT" "ttxt" Info Text SimpleText application/text
+.bat "TEXT" "ttxt" MS-DOS Batch File SimpleText
+.cmd "TEXT" "ttxt" OS/2 Batch File SimpleText
+.me "TEXT" "ttxt" Text Readme SimpleText
+.rme "TEXT" "ttxt" Text Readme SimpleText
+.1st "TEXT" "ttxt" Text Readme SimpleText application/text
+.readme "TEXT" "ttxt" Text Readme SimpleText application/text
+.ini "TEXT" "ttxt" Windows INI File SimpleText
+.ps "TEXT" "vgrd" PostScript LaserWriter 8 application/postscript
+.eps "EPSF" "vgrd" Postscript LaserWriter 8 application/postscript
+.epsf "EPSF" "vgrd" Postscript LaserWriter 8 application/postscript
+.dvi "ODVI" "xdvi" TeX DVI Document xdvi application/x-dvi
--- /dev/null
+#
+# CONFIGURATION FOR AFPD
+#
+# Each line defines a virtual server that should be available.
+# Empty lines and lines beginning with `#' are ignored.
+# Options in this file will override both compiled-in defaults
+# and command line options.
+#
+# Format:
+# - [options] to specify options for the default server
+# "Server name" [options] to specify an additional server
+#
+# The following options are available:
+# Transport Protocols:
+# -[no]tcp Make AFP-over-TCP [not] available
+# -[no]ddp Make AFP over AppleTalk [not] available. if you
+# have -proxy specified, specify -uamlist "" to
+# prevent ddp connections from working.
+#
+# -transall Make both available (default)
+#
+# Transport Options:
+# -ipaddr <w.x.y.z> Specifies the IP address the server should respond
+# to (default is the first IP address of the system)
+# This option also allows one machine to advertise
+# TCP/IP for another machine.
+# -server_quantum <number>
+# Specifies the DSI server quantum. The minimum
+# value is 1MB. The max value is 0xFFFFFFFF. If you
+# specify a value that is out of range, you'll get
+# the default value (currently the minimum).
+# -ddpaddr x.y Specifies the DDP address of the server. the
+# default is to auto-assign an address
+# (0.0). this is only useful if you're running
+# on a multihomed host.
+# -port <number> Specifies the TCP port the server should respond
+# to (default is 548)
+# -fqdn <name:port> specify a fully-qualified domain name (+
+# optional port). this gets discarded if the
+# server can't resolve it. this is not honored
+# by appleshare clients <= 3.8.3 (default: none)
+# -proxy Run an AppleTalk proxy server for specified AFP/TCP
+# server (if address/port aren't given, then
+# first IP address of the system/548 will be used).
+# if you don't want the proxy server to act as
+# a ddp server as well, set -uamlist to an
+# empty string.
+#
+#
+# Authentication Methods:
+# -uampath <path> Use this path to look for User Authentication Modules.
+# (default: :RESDIR:/uams)
+# -uamlist <a,b,c> Comma-separated list of UAMs. (default:
+# uams_guest.so,uams_clrtxt.so,uams_dhx.so)
+#
+# some commonly available UAMs:
+# uams_guest.so: Allow guest logins
+#
+# uams_clrtxt.so: (uams_pam.so or uams_passwd.so)
+# Allow logins with passwords
+# transmitted in the clear.
+#
+# uams_randnum.so: Allow Random Number and Two-Way
+# Random Number exchange for
+# authentication.
+#
+# uams_dhx.so: (uams_dhx_pam.so or uams_dhx_passwd.so)
+# Allow Diffie-Hellman eXchange
+# (DHX) for authentication.
+#
+# Password Options:
+# -[no]savepassword [Don't] Allow clients to save password locally
+# -passwdfile <path> Use this path to store Randnum
+# passwords. (default: ~/.passwd. the only other
+# userful value is :RESDIR:/etc/afppasswd.)
+# -passwdminlen <#> minimum password length. may be ignored.
+# -[no]setpassword [Don't] Allow clients to change their passwords.
+# -loginmaxfail <#> maximum number of failed logins. this may be
+# ignored if the uam can't handle it.
+#
+# AppleVolumes files:
+# -defaultvol <path> Specifies path to AppleVolumes.default file
+# (default :ETCDIR:/AppleVolumes.default,
+# same as -f on command line)
+# -systemvol <path> Specifies path to AppleVolumes.system file
+# (default :ETCDIR:/AppleVolumes.system,
+# same as -s on command line)
+# -[no]uservolfirst [Don't] read the user's ~/AppleVolumes or
+# ~/.AppleVolumes before reading
+# :ETCDIR:/AppleVolumes.default
+# (same as -u on command line)
+# -[no]uservol [Don't] Read the user's volume file
+#
+# -nlspath <path> Prepend this path to each code page filename
+# in volume options (default: :RESDIR:/nls).
+#
+# Miscellaneous:
+# -guestname "user" Specifies the user name for the guest login
+# (default "nobody", same as -g on command line)
+# -loginmesg "Message" Client will display "Message" upon logging in
+# (no default, same as -l "Message" on commandline)
+# -nodebug Switch off debugging
+# -tickleval <number> Specify the tickle timeout interval (in seconds)
+# -icon Use the platform-specific icon.
+#
+# Some examples:
+#
+# The simplest case is to not have an afpd.conf.
+#
+# 4 servers w/ names server1-3 and one w/ the hostname. servers
+# 1-3 get routed to different ports with server 3 being bound
+# specifically to address 192.168.1.3
+# -
+# server1 -port 12000
+# server2 -port 12001
+# server3 -port 12002 -ipaddr 192.168.1.3
+#
+# a dedicated guest server, a user server, and a special
+# ddp-only server:
+# "Guest Volume" -uamlist uams_guest.so -loginmesg "Welcome guest!"
+# "User Volume" -uamlist uams_clrtxt.so -port 12000
+# "special" -notcp -defaultvol <path> -systemvol <path>
+#
+# default:
+# - -transall -uamlist uams_guest.so,uams_clrtxt.so,uams_dhx.so -nosavepassword
--- /dev/null
+#
+# Format of lines in this file:
+#
+# interface [ -seed ] [ -router | -dontroute ]
+# [ -phase { 1 | 2 } ] [ -addr net.node ]
+# [ -net first[-last] ] [ -zone ZoneName ] ...
+#
+# -seed only works if you have multi-interfaces. Any missing arguments are
+# automatically configured from the network. Note: lines can't actually be
+# split, tho it's a good idea.
+#
+# -router is like -seed but it allows single-interface routing. -dontroute
+# disables routing for the specified interface.
+#
+# Some examples:
+#
+# The simplest case is no atalkd.conf. This works on most platforms
+# (notably not Solaris), since atalkd can discover the local interfaces
+# on the machine.
+#
+# Very slightly more complicated:
+#
+# le0
+# or
+# eth0
+#
+# for Solaris/SunOS or Linux.
+#
+# A much more complicated example:
+#
+# le0 -phase 1
+# le1 -seed -phase 2 -addr 66.6 -net 66-67 -zone "No Parking"
+#
+# This turns on transition routing between the le0 and le1
+# interfaces on a Sun. It also causes atalkd to fail if other
+# routers disagree about it's configuration of le1.
+#
--- /dev/null
+#
+# Format of lines in this file:
+#
+# interface [ -seed ] [ -router | -dontroute ]
+# [ -phase { 1 | 2 } ] [ -addr net.node ]
+# [ -net first[-last] ] [ -zone ZoneName ] ...
+#
+# -seed only works if you have multi-interfaces. Any missing arguments are
+# automatically configured from the network. Note: lines can't actually be
+# split, tho it's a good idea.
+#
+# -router is like -seed but it allows single-interface routing. -dontroute
+# disables routing for the specified interface.
+#
+# Some examples:
+#
+# The simplest case is no atalkd.conf. This works on most platforms
+# (notably not Solaris), since atalkd can discover the local interfaces
+# on the machine.
+#
+# Very slightly more complicated:
+#
+# le0
+# or
+# eth0
+#
+# for Solaris/SunOS or Linux.
+#
+# A much more complicated example:
+#
+# le0 -phase 1
+# le1 -seed -phase 2 -addr 66.6 -net 66-67 -zone "No Parking"
+#
+# This turns on transition routing between the le0 and le1
+# interfaces on a Sun. It also causes atalkd to fail if other
+# routers disagree about it's configuration of le1.
+#
+eth0
--- /dev/null
+# Appletalk configuration
+# Change this to increase the maximum number of clients that can connect:
+AFPD_MAX_CLIENTS=20
+
+# Change this to set the machine's atalk name and zone.
+# NOTE: if you're zone has spaces in it, you're better off specifying
+# it in afpd.conf
+#ATALK_ZONE=@zone
+ATALK_NAME=`echo ${HOSTNAME}|cut -d. -f1`
+
+# specify this if you don't want guest, clrtxt, and dhx
+# available options: uams_guest.so, uams_clrtxt.so, uams_dhx.so,
+# uams_randnum.so
+#AFPD_UAMLIST="-U uams_clrtxt.so,uams_randnum.so"
+
+# Change this to set the id of the guest user
+AFPD_GUEST=nobody
+
+# Set which daemons to run (papd is dependent upon atalkd):
+ATALKD_RUN=yes
+PAPD_RUN=yes
+AFPD_RUN=yes
+
+# Control whether the daemons are started in the background
+ATALK_BGROUND=yes
--- /dev/null
+# Appletalk configuration
+# Change this to increase the maximum number of clients that can connect:
+AFPD_MAX_CLIENTS=100
+
+# Change this to set the machine's atalk name:
+#ATALK_ZONE=@zone
+ATALK_NAME=`hostname|sed 's/\..*$//'`
+
+# specify this if you don't want guest, clrtxt, and dhx
+# available options: uams_guest.so, uams_clrtxt.so, uams_dhx.so,
+# uams_randnum.so
+AFPD_UAMLIST="-U uams_clrtxt.so,uams_dhx.so"
+
+# Change this to set the id of the guest user
+AFPD_GUEST=nobody
+
+# Set which daemons to run:
+PAPD_RUN=no
+AFPD_RUN=yes
+
+# Control whether the daemons are started in the background
+ATALK_BGROUND=yes
+
--- /dev/null
+#%PAM-1.0
+auth required /lib/security/pam_pwdb.so shadow
+account required /lib/security/pam_pwdb.so
+#password required /lib/security/pam_cracklib.so
+#password required /lib/security/pam_pwdb.so shadow use_authtok
+session required /lib/security/pam_pwdb.so
--- /dev/null
+# Attributes are:
+#
+# Name Type Default Description
+# pd str ".ppd" Pathname to ppd file.
+# pr str "lp" LPD printer name.
+# pa str "0.0" AppleTalk address (not usually needed).
+# op str "operator" Operator name, for LPD spooling.
+#
+# Some examples:
+#
+# On many systems (notably not Solaris), no papd.conf is required,
+# since papd shares the same defaults as lpd.
+#
+# A simple example:
+#
+# terminator:\
+# :pr=lp:op=wes:\
+# :pd=/usr/share/lib/ppd/HPLJ_4M.PPD:
+#
+# Note also that papd.conf can list several printers.
--- /dev/null
+#!perl\r#\r# ICDumpMap\r# --- Dump suffix mappings from your Internet Config extension.\r#\r# iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>\r#\r\ruse Mac::InternetConfig;\r\ropen MAP, ">AppleVolumes";\rprintf MAP "%-9s \"%4s\" \"%4s\" %-30s %-25s %-15s\n\n",\r".", "TEXT", "ttxt", "ASCII Text", "SimpleText", "text/plain";\rprint MAP "\# The following lines are extracted from Internet Config Preference.\n\n";\rfor my $entry (keys %InternetConfigMap) {\r next unless $entry->extension =~ /^\./;\r $_ = sprintf "%-9s \"%4s\" \"%4s\" %-30s %-25s %-15s",\r $entry->extension, $entry->file_type, $entry->file_creator,\r $entry->entry_name, $entry->creator_app_name,\r $entry->MIME_type;\r s/\s*$/\n/;\r print MAP;\r}\rclose MAP;\r
\ No newline at end of file
--- /dev/null
+#!/bin/sh
+#
+# A lovely script to add netatalk printers
+#
+#ident "@(#)netatalk 0.5 99/06/22 job@uchicago.edu" /* Netatalk 1.4*/
+#
+# This File is released under the Perl Artistic Licence.
+# See http://www.perl.org for details
+#
+# Or you can use it under the licence that accompanies Netatalk 1.3 =)
+#
+# This file is maintained by Job Bogan <job@uchicago.edu>
+# Please contact me at this address for bug reports, problems, etc.
+# Please do not bother the netatalk maintainers with problems from
+# this program.
+#
+#
+#
+# Exit Codes ... 1 for missed path or user input
+# 2 for an aborted run (by not saying yes to a 'are you sure?'
+# 3 for failed lpadmin commands
+# 4 for possible security problem
+
+# make base name for temp files.
+
+TMP_STUFF="/tmp/`basename $0`.$$"
+export TMP_STUFF
+rm -rf ${TMP_STUFF}
+mkdir -m 700 ${TMP_STUFF} || exit 4
+trap 'rm -f $TMP_STUFF > /dev/null 2>&1' 0
+trap 'exit' 1 2 3
+
+# grab the pathname the script was called w/
+# if it was run by ./add... then make the path == `pwd`
+
+RUNHOME=`dirname $0`
+
+if [ "$RUNHOME" = "." ]; then
+ RUNHOME=`pwd`
+fi
+
+# allow for the env NETATALKHOME to override the guessed one from above
+
+NETATALKHOME=${NETATALKHOME:-$RUNHOME}
+export NETATALKHOME
+
+PATH=/bin:${PATH}:${NETATALKHOME}/bin:${NETATALKHOME}/etc:${NETATALKHOME}/etc/filters:/usr/lib:/usr/sbin
+
+if [ ! -x ${NETATALKHOME}/bin/pap ]; then
+ echo "OPPS: I don't see ${NETATALKHOME}/bin/pap ,"
+ echo ' Check that it is executable or set ${NETATALKHOME}'
+ echo ' so i can find pap at ${NETATALKHOME}/bin/pap'
+ echo '[We are guessing the location of the binary from the'
+ exho 'path that was used to run this sctipt or $NETATALKHOME]'
+ exit 1
+fi
+
+# get the user to tell us where to look for things, and list things there.
+
+echo ''
+echo "Enter Appletalk zone [default: local]: \c"
+
+read ZONE
+
+echo ""
+echo "Looking for LaserWriters in Zone ${ZONE} ..."
+$NETATALKHOME/bin/nbplkup ":LaserWriter@${ZONE}"
+
+echo ""
+echo "Enter AppleTalk printer name: \c"
+
+read DEST
+
+if [ "$DEST" = "" ]; then
+ echo "OPPS: you need to tell me a printer name"
+ exit 1
+fi
+
+echo "checking nbplkup ${DEST}:LaserWriter@${ZONE}"
+echo ""
+TestDEST=`$NETATALKHOME/bin/nbplkup "${DEST}:LaserWriter@${ZONE}"`
+echo "${TestDEST}"
+echo ""
+
+if [ "${TestDEST}" = "" ]; then
+ echo "I don't see that printer on the network. You may have entered an incorrect"
+ echo "printer name - if so, exit and restart."
+ echo "[You should only enter the printer name, not the :LaserWriter portion]"
+ sleep 3
+fi
+
+unset TestDEST
+
+NBPNAME="${DEST}@${ZONE}"
+
+echo ""
+echo ""
+
+# scrunch all of the whitespace and / out of the appletalk name and suggest
+# that as the unix name
+
+SUGGEST=`echo "$DEST" | cut -d: -f1 | sed 's/ //g' | sed 's#/##g` ; export SUGGEST
+
+# truncate the suggested name to 14 chars to conform to lp specs.
+
+SUGGEST1=`expr ${SUGGEST} : '\(..............\)'`
+SUGGEST=${SUGGEST1:-$SUGGEST}
+
+echo "Enter Unix printer name - [default: ${SUGGEST}] : \c"
+read UNIXPRINT
+echo ''
+if [ "${UNIXPRINT}" = "" ]; then
+ UNIXPRINT="${SUGGEST}"
+fi
+export UNIXPRINT
+
+#####
+# Here we check for legal names again. >14 chars
+#####
+
+UNIXPRINT1=`expr "${UNIXPRINT}" : '\(..............\)'`
+
+# if UNIXPRINT is shorter than 14chars, UNIXPRINT1 ends up null ("")
+# if UNIXPRINT was longer, then we get a 14char version of it. bleah.
+
+if [ "${UNIXPRINT1}" = "" ]; then
+ UNIXPRINT1="${UNIXPRINT}"
+else
+#if [ "${UNIXPRINT1}" != "${UNIXPRINT}" ]; then
+ echo "Opps, that name was too long... Truncating to 14 chars."
+ echo "setting printer name to '${UNIXPRINT}'"
+ UNIXPRINT="${UNIXPRINT1}"
+fi
+
+echo "Enter a Description for this printer. [The Appletalk name will"
+echo "be included by default, and must be included for the printing"
+echo "filters to work.] : \c"
+read DESC
+
+DESC="${DESC} [${NBPNAME}]"
+
+echo ""
+echo "Do you want all printjobs to print out in reversed page order?"
+echo "This is usually only desired if this printer stacks jobs"
+echo "face up in the output bin. [N/y] \c"
+read REV
+case ${REV} in [Yy]*) REV="Netatalk-R" ;;
+ *) REV="Netatalk" ;;
+esac
+export REV
+
+if lpstat -p ${UNIXPRINT} >/dev/null 2>&1 ; then
+ echo '\a'
+ echo "OPPS: There already exists a printer named '${UNIXPRINT}'"
+ echo " here are the comments from /etc/lp/printers/${UNIXPRINT}/comment"
+ cat /etc/lp/printers/${UNIXPRINT}/comment
+ echo ""
+ echo "Do you still want to do this? This will flush "
+ echo "all pending jobs! [Y/n]? \c"
+ read DOTHIS
+ case ${DOTHIS} in [Yy]*) ;;
+ *) exit 2 ;;
+ esac
+
+ echo "Rejecting all new jobs for ${UNIXPRINT}"
+ reject -r "Making ${UNIXPRINT} a netatalk printer" ${UNIXPRINT}
+ echo "Disabling and Flushing the ${UNIXPRINT} queue"
+ disable -c -r "Making ${UNIXPRINT} a netatalk printer" ${UNIXPRINT}
+fi
+
+#####
+# Check if we need to do silly tricks to share the printer... only on 5.5.1 or older
+#####
+
+if [ "`uname -r`" -lt "5.6" ]; then
+ echo ""
+ echo "Do you want to share this printer w/ other machines [Y/n]?\c"
+ read SHARE
+else
+ echo "You're running SunOS 5.6 or higher... skipping 'sacadm' and 'pmadm'"
+fi
+
+if nistest printers.org_dir; then
+ echo "Do you want to add this printer to your NIS printer map? [y/N]"
+ echo "(You will need to share this printer w/ other machines)"
+ read NIS
+ case ${NIS} in [Yy]*) ADD_NIS=1 ;;
+ *) ;;
+ esac
+fi
+
+echo "Do you want to save the error messages from pap in"
+echo "'/var/spool/lp/tmp/Netatalk/${UNIXPRINT}' [y/N]"
+echo "(If you answer N, error messages will go to /dev/null.)"
+read SAVEerr
+case ${SAVEerr} in [Yy]*) SAVEerr=1 ;;
+ *) unset SAVEerr ;;
+esac
+
+echo "Making ${UNIXPRINT} print to ${DEST} via netatalk"
+
+if [ "${SAVEerr}" = "" ]; then
+ LOCKDEV=/dev/null
+else
+ LOCKS=/var/spool/lp/tmp/Netatalk
+ if [ ! -d ${LOCKS} ]; then
+ mkdir -m 0771 ${LOCKS}
+ chown lp ${LOCKS}
+ chgrp lp ${LOCKS}
+ fi
+ LOCKDEV=${LOCKS}/${UNIXPRINT}
+ touch ${LOCKDEV}
+ chown lp:lp ${LOCKDEV}
+ chmod 600 ${LOCKDEV}
+fi
+
+echo ""
+
+if [ ! -r /etc/lp/filter.table ]; then
+ echo "Setting up the existing print filters..."
+ for i in `ls /etc/lp/fd | sed 's/.fd$//'`; do
+ lpfilter -f ${i} -F/etc/lp/fd/${i}.fd
+ done
+fi
+if lpfilter -f netatalk -l > /dev/null 2>&1 ; then
+ echo 'Looks like you have a Netatalk printer filter defined. Good!'
+else
+ echo "Humm... You need a Netatalk printer filter..."
+ echo "... making you one ..."
+ cat > /etc/lp/fd/netatalk.fd << EOF
+#ident "@(#)netatalk.fd 0.2 97/09/04 job@uchicago.edu" /* Netatalk 1.4 */
+
+Input types: postscript
+Output types: PS
+Printer types: Netatalk
+Printers: any
+Filter type: fast
+Command: ${NETATALKHOME}/etc/filters/ifpap 2>&1 > /dev/null
+EOF
+ chown lp:lp /etc/lp/fd/netatalk.fd
+ chmod 664 /etc/lp/fd/netatalk.fd
+ lpfilter -f netatalk -F /etc/lp/fd/netatalk.fd
+fi
+
+if lpfilter -f netatalk-r -l 2>&1 > /dev/null ; then
+ echo 'Looks like you have a Reverse Netatalk printer filter defined. Good!'
+else
+ echo "Humm... You need a Reverse Netatalk printer filter..."
+ echo "... making you one ..."
+ cat > /etc/lp/fd/netatalk-r.fd << EOF
+#ident "@(#)netatalk-r.fd 0.2 97/09/04 job@uchicago.edu" /* Netatalk 1.4 */
+
+Input types: postscript
+Output types: PS
+Printer types: Netatalk-R
+Printers: any
+Filter type: fast
+Command: "/usr/lib/lp/postscript/postreverse | ${NETATALKHOME}/etc/filters/ifpap 2>&1 >/dev/null"
+EOF
+ chown lp:lp /etc/lp/fd/netatalk-r.fd
+ chmod 664 /etc/lp/fd/netatalk-r.fd
+ lpfilter -f netatalk-r -F /etc/lp/fd/netatalk-r.fd
+fi
+
+if [ ! -r /usr/share/lib/terminfo/N/Netatalk ]; then
+ echo "Making a Terminfo entry for Netatalk printers"
+ cat > ${TMP_STUFF}/terminfo << EOF
+Netatalk,
+ cols#80, lines#66,
+ cpi=null, csnm=^D, lpi=null, scs=^D, slines=^D, u9=^D,
+EOF
+ tic ${TMP_STUFF}/terminfo
+ chown bin:bin /usr/share/lib/terminfo/N/Netatalk
+ chmod 644 /usr/share/lib/terminfo/N/Netatalk
+fi
+
+if [ ! -r /usr/share/lib/terminfo/N/Netatalk-R ]; then
+ echo "Making a Terminfo entry for Reversed Netatalk printers"
+ cat > ${TMP_STUFF}/terminfoR << EOF
+Netatalk-R,
+ cols#80, lines#66,
+ cpi=null, csnm=^D, lpi=null, scs=^D, slines=^D, u9=^D,
+EOF
+ tic "${TMP_STUFF}/terminfoR"
+ chown bin:bin /usr/share/lib/terminfo/N/Netatalk-R
+ chmod 644 /usr/share/lib/terminfo/N/Netatalk-R
+fi
+
+###
+# this is old cruft... we should not have done this in the 1st place.
+# we need to edit the template interface file to point to the local netatalk bins
+#cat ${RUNHOME}/etc/netatalk.template | sed "s#DEFAULT_NETATALK_HOME#${RUNHOME}#g" \
+# > "/etc/lp/interfaces/${UNIXPRINT}"
+#chown lp:lp /etc/lp/interfaces/${UNIXPRINT}
+#chmod 0775 /etc/lp/interfaces/${UNIXPRINT}
+# below is the correct way.
+###
+
+echo "Setting up ${UNIXPRINT} ... Edit options later as you see fit."
+lpadmin -p ${UNIXPRINT} -D "${DESC}" -T ${REV} \
+ -i ${RUNHOME}/netatalk.template -I PS -v ${LOCKDEV} -A none || exit 3
+accept ${UNIXPRINT} || exit 3
+enable ${UNIXPRINT} || exit 3
+
+case ${SHARE} in [Nn]*) exit; ;;
+ *) ;;
+esac
+
+echo "Setting up network sharing for ${UNIXPRINT}"
+
+# from p925 in the Solaris Administration Guide, Vol. II (2.5)
+
+echo ""
+if sacadm -l -p tcp > /dev/null; then
+ echo "already running a tcp listener"
+else
+ echo "Defining tcp listener ..."
+ sacadm -a -p tcp -t listen -c "/usr/lib/saf/listen tcp" \
+ -v `nlsadmin -V` -n 999
+fi
+
+# this is extra, but a good idea...
+
+LPD_ADDR="\\x`lpsystem -A`" ; export LPD_ADDR
+ADDR_0=`echo $LPD_ADDR | sed -e 's/\\x00020203/\\x00020ACE/g'`; export ADDR_0
+
+if pmadm -l -p tcp -s 0 > /dev/null; then
+ echo "<0> service is already defined."
+else
+ echo "Defining <0>/tcp service ..."
+ pmadm -a -p tcp -s 0 -i root \
+ -m `nlsadmin -c /usr/lib/saf/nlps_server -A ${ADDR_0}` \
+ -v `nlsadmin -V`
+fi
+
+# again from the solaris book noted above.
+
+if pmadm -l -p tcp -s lp > /dev/null; then
+ echo "<lp> service is already defined."
+else
+ echo "Defining <lp>/tcp service ..."
+ pmadm -a -p tcp -s lp -i root \
+ -m `nlsadmin -o /var/spool/lp/fifos/listenS5` \
+ -v `nlsadmin -V`
+fi
+
+if pmadm -l -p tcp -s lpd > /dev/null; then
+ echo "<lpd> service is already defined."
+else
+ echo "Defining <lpd>/tcp service ..."
+ pmadm -a -p tcp -s lpd -i root \
+ -m `nlsadmin -o /var/spool/lp/fifos/listenBSD -A ${LPD_ADDR}` \
+ -v `nlsadmin -V`
+fi
+
+if [ "${ADD_NIS}" = 1 ] ; then
+ if nistest "[printer_name=${UNIXPRINT}]printers.org_dir"; then
+ nistbladm -m printer_host=`uname -n` \
+ "[printer_name=${UNIXPRINT}]printers.org_dir" description="$DESC"
+ else
+ nistbladm -a printer_name=${UNIXPRINT} \
+ description="$DESC" printer_host=`uname -n` printers.org_dir
+ fi
+fi
--- /dev/null
+#ident "@(#)netatalk 0.7 99/06/22 job@uchicago.edu" /* Netatalk 1.4*/
+
+#####
+# User configuration:
+#
+# Set timeout for pap($time) and papstatus($time2). both are in seconds.
+# extraneous if you do not have NETATALKHOME/bin/timeout
+# set how many times to loop before we just abort entirely ($attempts)
+# what flags pap is run w/. -c makes pap claim to have been waiting forever
+#####
+
+time=1800
+time2=60
+attempts=3
+pap_flags="-c"
+
+#####
+# this should get "fixed" to something like
+# NETATALKHOME=/opt ; export NETATALKHOME
+# by the add_netatalk_printer script
+#
+# DO NOT use the user's env for this or the PATH above.
+#####
+
+NETATALKHOME=DEFAULT_NETATALK_HOME ; export NETATALKHOME
+#NETATALKHOME=/opt ; export NETATALKHOME
+
+if [ "${NETATALKHOME}" = "DEFAULT_NETATALK_HOME" ]; then
+ echo "bleah, NETATALKHOME not set, exiting..." ; exit 5
+fi
+
+#####
+# BUGS/TODO:
+# move all TMP stuff to use a directory for security reasons
+# run nbplkup to check if the reason we cannot print, is that it's not on
+# the net
+# move to "${foo}" from $foo and ${foo}
+# add debuging info that gets sent to "logger lpd.debug"
+# if we timeout while printing the banner page, we do not keep trying to print.
+# perhaps filter_timeout should only complain once per job; like badfile.
+# check if the timeout messages are duplicated by the lp system
+# psa will not drop in for pap to use accting. perhaps lp does not need it?
+# make pap print all the files at once; kill for file in ($files)
+# move badfile error to printfile function
+#####
+
+# This File is released under the Perl Artistic Licence.
+# See http://www.perl.org for details
+#
+# Or you can use it under the licence that accompanies Netatalk 1.3 =)
+
+###########
+## Netatalk printer interface. Heavily modified from
+## /usr/lib/lp/model/standard on Sparc Solaris 2.5.1 (May 97)
+##
+## Meant to be used w/ add_netatalk_printer
+###########
+
+#####
+# This program is invoked as
+#
+# ${SPOOLDIR}/.../printer request-id user title copies options files...
+#
+# The first three arguments are simply reprinted on the banner page,
+# the fourth (copies) is used to control the number of copies to print,
+# the fifth (options) is a blank separated list (in a single argument)
+# of user or Spooler supplied options (without the -o prefix),
+# and the last argument(s) is/are the file(s) to print.
+#####
+
+#####
+#
+# The protocol between the interface program and the Spooler
+# is fairly simple:
+#
+# All standard error output is assumed to indicate a
+# fault WITH THE REQUEST. The output is mailed to the
+# user who submitted the print request and the print
+# request is finished.
+#
+# If the interface program sets a zero exit code,
+# it is assumed that the file printed correctly.
+# If the interface program sets a non-zero exit code
+# less than 128, it is assumed that the file did not
+# print correctly, and the user will be notified.
+# In either case the print request is finished.
+#
+# If the interface program sets an exit code greater
+# than 128, it is assumed that the file did not print
+# because of a printer fault. If an alert isn't already
+# active (see below) one will be activated. (Exit code
+# 128 should not be used at all. The shell, which executes
+# this program, turns SIGTERM, used to kill this program
+# for a cancellation or disabling, into exit 128. The
+# Spooler thus interpretes 128 as SIGTERM.)
+#
+# A message sent to the standard input of the ${LPTELL}
+# program is assumed to describe a fault WITH THE PRINTER.
+# The output is used in an alert (if alerts are defined).
+# If the fault recovery is "wait" or "begin", the printer
+# is disabled (killing the interface program if need be),
+# and the print request is left on the queue.
+# If the fault recovery is "continue", the interface program
+# is allowed to wait for the printer fault to be cleared so
+# it can resume printing.
+#
+#####
+
+###########################################################################
+#
+# Set up the basic traps. and other important things
+#
+###########################################################################
+
+#####
+# For the time being, just exit if we are poked.
+#####
+
+# SIGTERM handler
+
+trap 'exit' 15
+
+#####
+# We can be clever about getting a hangup or interrupt, though, at least
+# until the filter runs. Do this early, even though $LPTELL
+# isn't defined, so that we're covered.
+#####
+
+trap 'catch_hangup; exit_code=129 exit 129' 1
+trap 'catch_interrupt; exit_code=129 exit 129' 2 3
+
+#####
+# VARIBLE DECLARED - put here so we don't ever run the trap below w/o
+# TMPPREFIX defined. We hard code /tmp for the moment, but fix that later
+#
+# Use ${TMPPREFIX} as the prefix for all temporary files, so
+# that cleanup is easy. The prefix may be up to 13 characters
+# long, so you only have space for one more character to make
+# a file name. If necessary, make a directory using this prefix
+# for better management of unique temporary file names.
+#####
+
+TMPPREFIX=/tmp/`uname -n`$$
+
+#####
+# Before exiting, set ${exit_code} to the value with which to exit.
+# Otherwise, the exit from this script will be 0.
+#####
+
+trap 'rm -fr ${TMPPREFIX}*; exit ${exit_code}' 0
+
+catch_hangup () {
+ if [ -n "${LPTELL}" ]
+ then
+ echo \
+ "Humm, we got a SIGHUP. Not sure what it means, but... we'll keep going anyway" \
+ | ${LPTELL} "${printer}"
+ fi
+ return 0
+}
+
+catch_interrupt () {
+ if [ -n "${LPTELL}" ]
+ then
+ echo \
+ "Received an interrupt from the printer. The reason is unknown." \
+ | ${LPTELL} "${printer}"
+ fi
+ return 0
+}
+
+#####
+# Most of the time we don't want the standard error to be captured
+# by the Spooler, mainly to avoid "Terminated" messages that the
+# shell puts out when we get a SIGTERM. We'll save the standard
+# error channel under another number, so we can use it when it
+# should be captured.
+#
+# Open another channel to the printer port, for use when the
+# regular standard output won't be directed there, such as in
+# command substitution (`cmd`).
+#####
+
+exec 5>&2 2>/dev/null 3>&1
+
+###########################################################################
+#
+# Define local varibles and such
+#
+###########################################################################
+
+#####
+# There is one more varible set by the shell that execs us.
+# FILTER The filter to run ; we ignore this directive
+#####
+
+#####
+# Use the user set env, or else default to standard values.
+#####
+
+: ${SPOOLDIR:=/usr/spool/lp}
+: ${TMPDIR:=/tmp} ; export TMPDIR
+: ${LOCALPATH:=${SPOOLDIR}/bin} ; export LOCALPATH
+
+PATH="/bin:/usr/bin:${LOCALPATH}:${NETATALKHOME}/bin:${NETATALKHOME}/etc"
+export PATH
+
+TMPPREFIX=${TMPDIR}/`uname -n`$$
+
+#####
+# Error levels for the errmsg() func.
+#####
+
+LP_ERR_LABEL="UX:lp" ; export LP_ERR_LABEL
+
+E_IP_ARGS=1
+E_IP_OPTS=2
+E_IP_UNKNOWN=5
+E_IP_BADFILE=6
+
+#####
+# Error message formatter:
+#
+# Invoke as
+#
+# errmsg severity message-number problem help
+#
+# where severity is "ERROR" or "WARNING", message-number is
+# a unique identifier, problem is a short description of the
+# problem, and help is a short suggestion for fixing the problem.
+#####
+
+errmsg () {
+ case $1 in
+ ERROR )
+ sev=" ERROR";
+ ;;
+ WARNING )
+ sev="WARNING";
+ ;;
+ esac
+ echo "${LP_ERR_LABEL}: ${sev}: $3
+ TO FIX: $4" >&5
+}
+
+parse () {
+ echo "`expr \"$1\" : \"^[^=]*=\(.*\)\"`"
+}
+
+#####
+# die quickly if we do not have the right number of arguments.
+#####
+
+if [ $# -lt 5 ]
+then
+ errmsg ERROR ${E_IP_ARGS} \
+ "wrong number of arguments to interface program" \
+ "consult your system administrator"
+ exit 1
+fi
+
+printer=`basename $0`
+request_id=$1
+
+# this will formated be machine!username, so we want to split that up...
+
+user_name=$2
+machine=`echo $user_name | cut -d! -f1`
+user_name=`echo $user_name | cut -d! -f2`
+
+title=$3
+copies=$4
+option_list=$5
+
+shift 5
+files="$*"
+
+nobanner="yes"
+
+inlist=
+
+for i in ${option_list}
+do
+case "${inlist}${i}" in
+ nobanner )
+ nobanner="yes" ;;
+ banner )
+ nobanner="no" ;;
+#####
+#
+# If you want to add simple options (e.g. -o simple)
+# identify them here.
+#####
+# simple )
+# simple="yes" ;;
+
+#####
+# These get ignored, but would matter little anyway since all we see
+# here is PS anyway.
+#####
+ cpi=* )
+# cpi=`parse ${i}` ;;
+ true ;;
+ lpi=* )
+# lpi=`parse ${i}` ;;
+ true ;;
+ length=* )
+# length=`parse ${i}` ;;
+ true ;;
+ width=* )
+# width=`parse ${i}` ;;
+ true ;;
+
+ #####
+ # If you want to add simple-value options (e.g. -o value=a)
+ # identify them here.
+ #####
+ #value=* )
+ # value=`parse ${i}` ;;
+
+ flist=* )
+ flist=`parse ${i}` ;;
+ input* )
+ true ;;
+ * )
+ errmsg WARNING ${E_IP_OPTS} \
+ "unrecognized \"-o ${i}\" option" \
+ "check the option, resubmit if necessary
+ printing continues" ;;
+
+ esac
+done
+
+#####
+# A bit ugly, but grabs the appletalk printer name from the lp system printer
+# description, so it's right up there in admintool. the appletalk name must
+# be delimited by [ and ].
+#
+# eg - 'this is the printer [hp-mrsec-l114:lasershared@Research Insitutes] that i use.'
+#####
+
+PAPDEST="`lpstat -D -p "${printer}" | grep -i descrip | sed 's/.*Description:.*\[//g' | sed 's/\].*//g'`"
+
+export PAPDEST
+
+###########################################################################
+#
+# Define our local functions (parse is declared above option parsing)
+#
+###########################################################################
+
+banner () {
+ echo "##### User: ${user_name}"
+ echo ""
+ echo "##### Machine: ${user_name}"
+ echo ""
+
+ if [ -n "${title}" ]
+ then
+ echo "##### Title: ${title}"
+ echo ""
+ fi
+
+ echo "##### Files: ${flist}"
+ echo ""
+
+#####
+# this should deal w/ the year 2000 ok. But will die in 2038. =)
+#####
+
+ YEAR=`date '+%y'`
+ YEAR=`expr 1900 + ${YEAR}`
+
+ echo "##### Date: `date '+%a %H:%M %h %d,'` ${YEAR}"
+ echo ""
+ echo "##### Job: ${request_id}"
+ echo ""
+
+}
+
+print_banner() {
+ if [ -x ${NETATALKHOME}/bin/timeout ]
+ then
+ banner | ${NETATALKHOME}/etc/psf \
+ | ${NETATALKHOME}/bin/timeout "${time}" ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}"
+ else
+ banner | ${NETATALKHOME}/etc/psf \
+ | ${NETATALKHOME}/bin/pap -c -p "{PAPDEST}"
+ fi
+
+ if [ ${?} -ne 0 ]
+ then
+ filter_timeout
+ fi
+}
+
+#####
+# ${LPTELL} is the name of a program that will send its
+# standard input to the Spooler. It is used to forward
+# the description of a printer fault to the Spooler,
+# which uses it in an alert to the administrator.
+#####
+if [ ! -x "${LPTELL:=${LOCALPATH}/lp.tell}" ]
+then
+ fake_lptell(){
+ header="no"
+ while read line
+ do
+ if [ "no" = "${header}" ]
+ then
+ errmsg ERROR ${E_IP_UNKNOWN} \
+ "unknown printer/interface failure" \
+ "consult your system administrator; \
+ reasons for failure (if any) follow:"
+ header=yes
+ fi
+ echo "${line}" >&2
+ done
+ return 1
+ }
+ LPTELL=fake_lptell
+fi
+
+
+#####
+# timeout catcher for the printing filter
+#####
+
+filter_timeout() {
+
+ cat > ${TMPPREFIX}D <<EOF
+
+The printer ${printer} either timed out at ${time} seconds or pap exited
+abnormally. As well, we may have exceeded ${print_tries} print attempts.
+The job ${request_id} from ${user_name} on ${machine} was
+printing when this happened.
+
+It may be that the only problem is the size of the job and the speed
+of the printer.
+
+Here is what $NETATALKHOME/bin/papstatus reports as the current
+state of the printer:
+
+EOF
+#####
+# We don't need to test for timeout, since we cannot get here w/o it.
+#####
+
+ ${NETATALKHOME}/bin/timeout ${time2} ${NETATALKHOME}/bin/papstatus -p "${PAPDEST}" 2>&1 >> ${TMPPREFIX}D
+ paperr=${?}
+
+ errmsg WARNING ${E_IP_UNKNOWN} "`cat ${TMPPREFIX}D`" "printing continues"
+#####
+# This ought to deal w/ the problem of nonexistent appletalk names, but...
+# for the moment, it calls filter_death. But it sends the papstatus
+# info to LPTELL anyhow, so you should be able to see the error.
+#####
+
+ if [ ${paperr} -ne 0 -o ${too_many} = "1" ]; then
+ paperr=
+ filter_death
+ fi
+ paperr=
+ echo "serverdict begin 0 exitserver systemdict /quit get exec" | \
+ ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}"
+ return 0
+}
+
+#####
+# Death catcher for filter_timeout
+#####
+
+filter_death() {
+
+ cat > ${TMPPREFIX}Z <<EOF
+Excessive delays w/ the printer ${printer}!
+
+While processing on printer ${printer} the job ${request_id}
+from ${user_name} on ${machine} timed out at ${time} seconds.
+
+Then while cleaning that timeout, the cleanup operation failed as
+well.
+
+EOF
+ errmsg ERROR ${E_IP_UNKNOWN} "`cat ${TMPPREFIX}Z`"
+ exit 129
+#####
+# Exit and fault the printer.
+#####
+}
+
+#####
+# Print the job
+#####
+printfile() {
+ trap '' 1 # Let the filter handle a hangup
+ trap '' 2 3 # and interrupts
+#####
+# We use timeout so as to not hang the print queue indefinately. (pap does not
+# timeout on it's own.)
+#
+# Put the 0<${files} before the "eval" to keep clever users from giving
+# a file name that evaluates as something to execute.
+#####
+ if [ "${TERM}" == "Netatalk-R" ]; then
+ if [ -x ${NETATALKHOME}/bin/timeout ]; then
+ 0<${file} /usr/lib/lp/postscript/postreverse | ${NETATALKHOME}/bin/timeout ${time} ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}"
+ else
+ 0<${file} /usr/lib/lp/postscript/postreverse | ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}"
+ fi
+ else
+ if [ -x ${NETATALKHOME}/bin/timeout ]; then
+ 0<${file} ${NETATALKHOME}/bin/timeout ${time} ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}"
+ else
+ 0<${file} ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}"
+ fi
+ fi
+ paperr=${?}
+ print_tries=`expr "${print_tries}" + 1`
+ if [ "${paperr}" != "0" -a "${print_tries}" -gt "${attempts}" ]; then
+ too_many=1
+ fi
+ trap 'catch_hangup; exit_code=129 exit 129' 1
+ trap 'catch_interrupt; exit_code=129 exit 129' 2 3
+ return ${paperr}
+}
+
+#####
+# Some basic sanity checking:
+#####
+
+if [ ! -x $NETATALKHOME/bin/pap ]
+then
+ echo "Opps, cannot find $NETATALKHOME/bin/pap, so I don't know how to"
+ echo "print things"
+ # exit w/ less than 128 to mark an error w/ the job, and call it done
+ exit 1
+fi
+
+###########################################################################
+#
+# Start the main section of the program.
+#
+###########################################################################
+
+#####
+# Here i should have a "job canceled" page ready in a trap in case of getting killed
+# but, alas, that would most likely muck up the PS. So we just drop the job on the
+# floor.
+#####
+
+#####
+# If you want a custom banner, change the code up in the functions section.
+#####
+
+if [ "no" = "${nobanner}" -a "${TERM}" != "Netatalk-R" ]
+then
+ print_banner
+fi
+
+#####
+# Print some copies of the file(s)
+#####
+
+badfileyet=
+i=1
+while [ $i -le $copies ]
+do
+ for file in ${files}
+ do
+ if [ -r "${file}" ]
+ then
+ print_tries=0
+ until printfile
+ do
+ filter_timeout;
+ done
+ else
+#####
+# Don't complain about not being able to read a file on second and
+# subsequent copies, unless we've not complained yet. This removes
+# repeated messages about the same file yet reduces the chance that the
+# user can remove a file and not know that we had trouble finding it.
+#####
+ if [ "${i}" -le 1 -o -z "${badfileyet}" ]
+ then
+ errmsg WARNING ${E_IP_BADFILE} \
+ "cannot read file \"${file}\" " \
+ "see if the file still exists and is readable by the user\
+ lp (or world), or consult your system administrator; \
+ We will keep trying to print the other files or copies"
+ badfileyet=yes
+ fi
+ fi
+ done
+ i=`expr $i + 1`
+done
+
+#####
+# print the banner page if we are a reversed queue
+#####
+if [ "no" = "${nobanner}" -a "${TERM}" == "Netatalk-R" ]
+then
+ print_banner
+fi
+
+echo "serverdict begin 0 exitserver systemdict /quit get exec" | \
+ ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}"
+
+Exit_code=0 exit 0
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+#include <unistd.h>
+
+char **av;
+
+struct slist {
+ char *name;
+ int num;
+} sigs[] = {
+#ifdef SIGHUP
+ "HUP", SIGHUP,
+#endif
+#ifdef SIGINT
+ "INT", SIGINT,
+#endif
+#ifdef SIGQUIT
+ "QUIT", SIGQUIT,
+#endif
+#ifdef SIGILL
+ "ILL", SIGILL,
+#endif
+#ifdef SIGTRAP
+ "TRAP", SIGTRAP,
+#endif
+#ifdef SIGABRT
+ "ABRT", SIGABRT,
+#endif
+#ifdef SIGIOT
+ "IOT", SIGIOT,
+#endif
+#ifdef SIGEMT
+ "EMT", SIGEMT,
+#endif
+#ifdef SIGFPE
+ "FPE", SIGFPE,
+#endif
+#ifdef SIGKILL
+ "KILL", SIGKILL,
+#endif
+#ifdef SIGBUS
+ "BUS", SIGBUS,
+#endif
+#ifdef SIGSEGV
+ "SEGV", SIGSEGV,
+#endif
+#ifdef SIGSYS
+ "SYS", SIGSYS,
+#endif
+#ifdef SIGPIPE
+ "PIPE", SIGPIPE,
+#endif
+#ifdef SIGALRM
+ "ALRM", SIGALRM,
+#endif
+#ifdef SIGTERM
+ "TERM", SIGTERM,
+#endif
+#ifdef SIGURG
+ "URG", SIGURG,
+#endif
+#ifdef SIGSTOP
+ "STOP", SIGSTOP,
+#endif
+#ifdef SIGTSTP
+ "TSTP", SIGTSTP,
+#endif
+#ifdef SIGCONT
+ "CONT", SIGCONT,
+#endif
+#ifdef SIGCHLD
+ "CHLD", SIGCHLD,
+#endif
+#ifdef SIGCLD
+ "CLD", SIGCLD,
+#endif
+#ifdef SIGTTIN
+ "TTIN", SIGTTIN,
+#endif
+#ifdef SIGTTOU
+ "TTOU", SIGTTOU,
+#endif
+#ifdef SIGIO
+ "IO", SIGIO,
+#endif
+#ifdef SIGXCPU
+ "XCPU", SIGXCPU,
+#endif
+#ifdef SIGXFSZ
+ "XFSZ", SIGXFSZ,
+#endif
+#ifdef SIGVTALRM
+ "VTALRM", SIGVTALRM,
+#endif
+#ifdef SIGPROF
+ "PROF", SIGPROF,
+#endif
+#ifdef SIGWINCH
+ "WINCH", SIGWINCH,
+#endif
+#ifdef SIGINFO
+ "INFO", SIGINFO,
+#endif
+#ifdef SIGUSR1
+ "USR1", SIGUSR1,
+#endif
+#ifdef SIGUSR2
+ "USR2", SIGUSR2,
+#endif
+#ifdef SIGPWR
+ "PWR", SIGPWR,
+#endif
+ 0, 0
+};
+
+void
+usage()
+{
+ int i;
+
+ fprintf (stderr, "Usage: %s [-s signal] seconds program [args]\n\n",
+ *av);
+ fprintf (stderr, "You can use a numerical signal, or one of these:\n");
+
+#define COLS 8
+
+ for (i = 0; sigs[i].name; i++) {
+ if (i % COLS == 0)
+ fprintf (stderr, "\n\t");
+
+ fprintf (stderr, "%s", sigs[i].name);
+
+ if ((i + 1) % COLS != 0)
+ fprintf (stderr, "\t");
+ }
+
+ fprintf (stderr, "\n\n");
+}
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+ int whatsig = SIGTERM;
+ extern char *optarg;
+ extern int optind;
+ int pid;
+
+ av = argv;
+
+ while ((i = getopt (argc, argv, "s:")) != -1) {
+ switch (i) {
+ case 's':
+ if (isdigit (*optarg)) {
+ whatsig = atoi (optarg);
+ } else {
+ for (i = 0; sigs[i].name; i++) {
+ if (!strcmp (sigs[i].name, optarg)) {
+ whatsig = sigs[i].num;
+ break;
+ }
+ }
+
+ if (!sigs[i].name) {
+ fprintf (stderr,
+ "%s: Unknown signal %s\n",
+ *av, optarg);
+ usage();
+ exit (1);
+ }
+ }
+
+ break;
+
+ default:
+ usage();
+ exit (1);
+ }
+ }
+
+ if (optind > argc - 2) {
+ usage();
+ exit (1);
+ }
+
+ if (!isdigit (*argv[optind])) {
+ fprintf (stderr, "%s: \"seconds\" must be numeric, not %s\n",
+ *av, argv[optind]);
+ usage();
+ exit (1);
+ }
+
+ pid = fork();
+
+ if (pid < 0) {
+ fprintf (stderr, "%s: fork failed: ", *av);
+ perror ("fork");
+ exit (1);
+ } else if (pid == 0) {
+ int seconds = atoi (argv[optind]);
+
+ pid = getppid();
+
+ fclose (stdin);
+ fclose (stdout);
+ fclose (stderr);
+
+ while (seconds-- > 0) {
+ /*
+ * too bad there's no SIGPARENT so we have to keep
+ * polling to find out if it's still there
+ */
+
+ if (kill (pid, 0) != 0)
+ exit (0);
+
+ sleep (1);
+ }
+
+ kill (pid, whatsig);
+ exit (0);
+ } else {
+ execvp (argv[optind + 1], argv + optind + 1);
+
+ fprintf (stderr, "%s: can't execute ", *av);
+ perror (argv[optind + 1]);
+ exit (1);
+ }
+}
--- /dev/null
+#
+# AppleTalk daemons. Make sure not to start atalkd in the background:
+# its data structures must have time to stablize before running the
+# other processes.
+#
+
+#
+# SUNOS: UNCOMMENT THESE LINES TO LOAD THE KERNEL MODULE. Note that
+# modunload-ing netatalk may cause your machine to panic or hang.
+#
+##echo -n 'loading netatalk: '
+##if [ -f :ETCDIR:/netatalk.o ]; then
+## /usr/etc/modload -sym :ETCDIR:/netatalk.o;
+##fi
+
+echo -n 'starting appletalk daemons:'
+if [ -x :SBINDIR:/atalkd ]; then
+ :SBINDIR:/atalkd; echo -n ' atalkd'
+fi
+
+if [ -x :BINDIR:/nbprgstr ]; then
+ :BINDIR:/nbprgstr -p 4 `hostname|sed 's/\..*$//'`:Workstation
+ :BINDIR:/nbprgstr -p 4 `hostname|sed 's/\..*$//'`:netatalk
+ echo -n ' nbprgstr'
+fi
+
+if [ -x :SBINDIR:/papd ]; then
+ :SBINDIR:/papd; echo -n ' papd'
+fi
+
+if [ -x :SBINDIR:/afpd ]; then
+ :SBINDIR:/afpd; echo -n ' afpd'
+fi
+
+if [ -x :SBINDIR:/timelord ]; then
+ :SBINDIR:/timelord; echo -n ' timelord'
+fi
+
+ echo '.'
--- /dev/null
+#!/bin/sh
+#
+# chkconfig: 345 91 35
+# description: Starts and stops the atalk, afpd & papd daemons for
+# providing AppleTalk networking services.
+#
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# set lcd stuff up if necessary
+if [ x"$LCD_STOP" = x"" ]; then
+ LCD_STOP="/sbin/stoplcd"
+fi
+if [ x"$LCD_SWRITE" = x"" ]; then
+ LCD_SWRITE="/sbin/swritelcd"
+fi
+
+# set up i18n stuff if necessary
+if [ -x /usr/local/sbin/getmsg ]; then
+ GETMSG=/usr/local/sbin/getmsg
+ START_MSG1=atalkStart1
+ START_MSG2=atalkStart2
+ STOP_MSG1=atalkStop1
+ STOP_MSG2=atalkStop2
+else
+ GETMSG=getmsg
+ START_MSG1=atalk_start_1
+ START_MSG2=atalk_start_2
+ STOP_MSG1=atalk_stop_1
+ STOP_MSG2=atalk_stop_2
+fi
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+test -x /usr/sbin/atalkd || exit 0
+
+test -f /etc/atalk/netatalk.conf || exit 0
+
+# read in netatalk configuration
+. /etc/atalk/netatalk.conf
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+# initialize return values
+RETVAL=1
+RETVAL_ATALKD=0
+RETVAL_PAPD=0
+RETVAL_AFPD=0
+
+# startup code for everything
+atalk_startup() {
+ if [ x"${ATALKD_RUN}" != x"no" ]; then
+ daemon /usr/sbin/atalkd
+ RETVAL_ATALKD=$?
+
+ if [ -x /usr/bin/nbprgstr ]; then
+ /usr/bin/nbprgstr -p 4 "${ATALK_NAME}:Workstation${ATALK_ZONE}"
+ /usr/bin/nbprgstr -p 4 "${ATALK_NAME}:netatalk${ATALK_ZONE}"
+ fi
+
+ if [ x"${PAPD_RUN}" = x"yes" -a -x /usr/sbin/papd ]; then
+ daemon /usr/sbin/papd
+ RETVAL_PAPD=$?
+ fi
+
+ if [ -x /usr/sbin/timelord ]; then
+ daemon /usr/sbin/timelord
+ fi
+ fi
+
+ if [ x"${AFPD_RUN}" = x"yes" -a -x /usr/sbin/afpd ] ; then
+ daemon /usr/sbin/afpd ${AFPD_UAMLIST} -g ${AFPD_GUEST} \
+ -c ${AFPD_MAX_CLIENTS} -n "${ATALK_NAME}${ATALK_ZONE}"
+ RETVAL_AFPD=$?
+ fi
+
+ if [ $RETVAL_ATALKD -eq 0 -a $RETVAL_PAPD -eq 0 -a $RETVAL_AFPD -eq 0 ]; then
+ RETVAL=0
+ touch /var/lock/subsys/atalk || RETVAL=1
+ fi
+}
+
+case "$1" in
+'start')
+ LINE1=`$GETMSG $START_MSG1`
+ LINE2=`$GETMSG $START_MSG2`
+ $LCD_STOP
+ $LCD_SWRITE "$LINE1" "$LINE2" &>/dev/null &
+ echo -n 'Starting AppleTalk services: '
+ if [ x"${ATALK_BGROUND}" = x"yes" ]; then
+ echo -n "(backgrounded)"
+ atalk_startup >& /dev/null &
+ else
+ atalk_startup
+ fi
+ echo
+ touch /var/lock/subsys/atalk
+ ;;
+'stop')
+ LINE1=`$GETMSG $STOP_MSG1`
+ LINE2=`$GETMSG $STOP_MSG2`
+ $LCD_STOP
+ $LCD_SWRITE "$LINE1" "$LINE2" &>/dev/null &
+ echo -n 'Shutting down AppleTalk services: '
+ if [ x"${ATALKD_RUN}" != x"no" ]; then
+ if [ x"${PAPD_RUN}" = x"yes" -a -x /usr/sbin/papd ]; then
+ killproc papd
+ RETVAL_PAPD=$?
+ fi
+
+ /usr/bin/nbpunrgstr "${ATALK_NAME}:Workstation${ATALK_ZONE}"
+ /usr/bin/nbpunrgstr "${ATALK_NAME}:netatalk${ATALK_ZONE}"
+
+ # kill atalkd last, since without it the plumbing goes away.
+ if [ -x /usr/sbin/atalkd ]; then
+ killproc atalkd
+ RETVAL_ATALKD=$?
+ fi
+ fi
+
+ # kill this separately as we also do AFP/tcp
+ if [ x"${AFPD_RUN}" = x"yes" -a -x /usr/sbin/afpd ]; then
+ killproc afpd
+ RETVAL_AFPD=$?
+ fi
+
+ if [ $RETVAL_ATALKD -eq 0 -a $RETVAL_PAPD -eq 0 -a $RETVAL_AFPD -eq 0 ] ; then
+ RETVAL=0
+ rm -f /var/lock/subsys/atalk || RETVAL=1
+ fi
+ echo ""
+ ;;
+ restart|reload)
+ $0 stop
+ $0 start
+ ;;
+ status)
+ status atalkd
+ ;;
+ *)
+ echo "Usage: atalk {start|stop|restart|status}"
+ exit 1
+esac
+
+exit $RETVAL
--- /dev/null
+#! /bin/sh
+# chkconfig: 345 91 35
+# description: This package enables Linux to talk to Macintosh
+# computers via the AppleTalk networking protocol and
+# provides printer, file sharing, and AppleTalk routing
+# services.
+#
+# AppleTalk daemons. Make sure not to start atalkd in the background:
+# its data structures must have time to stablize before running the
+# other processes.
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+test -x :SBINDIR:/atalkd || exit 0
+
+test -f :ETCDIR:/netatalk.conf || exit 0
+
+# read in netatalk configuration
+. :ETCDIR:/netatalk.conf
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+# initialize return values
+RETVAL=1
+RETVAL_ATALKD=0
+RETVAL_PAPD=0
+RETVAL_AFPD=0
+
+# startup code for everything
+atalk_startup() {
+ if [ x"${ATALKD_RUN}" != x"no" ]; then
+ daemon :SBINDIR:/atalkd
+ RETVAL_ATALKD=$?
+
+ if [ -x :BINDIR:/nbprgstr ]; then
+ :BINDIR:/nbprgstr -p 4 "${ATALK_NAME}:Workstation${ATALK_ZONE}"
+ :BINDIR:/nbprgstr -p 4 "${ATALK_NAME}:netatalk${ATALK_ZONE}"
+ fi
+
+ if [ x"${PAPD_RUN}" = x"yes" -a -x :SBINDIR:/papd ]; then
+ daemon :SBINDIR:/papd
+ RETVAL_PAPD=$?
+ fi
+
+ if [ -x :SBINDIR:/timelord ]; then
+ daemon :SBINDIR:/timelord
+ fi
+
+ fi
+
+ if [ x"${AFPD_RUN}" = x"yes" -a -x :SBINDIR:/afpd ] ; then
+ daemon :SBINDIR:/afpd ${AFPD_UAMLIST} -g ${AFPD_GUEST} \
+ -c ${AFPD_MAX_CLIENTS} -n "${ATALK_NAME}${ATALK_ZONE}"
+ RETVAL_AFPD=$?
+ fi
+
+ if [ $RETVAL_ATALKD -eq 0 -a $RETVAL_PAPD -eq 0 -a $RETVAL_AFPD -eq 0 ]; then
+ RETVAL=0
+ touch /var/lock/subsys/atalk || RETVAL=1
+ fi
+}
+
+case "$1" in
+'start')
+ echo -n 'Starting AppleTalk services: '
+ if [ x"${ATALK_BGROUND}" = x"yes" ]; then
+ echo -n "(backgrounded)"
+ atalk_startup >& /dev/null &
+ else
+ atalk_startup
+ fi
+ echo
+ ;;
+'stop')
+ echo -n 'Shutting down AppleTalk services: '
+ if [ x"${ATALKD_RUN}" != x"no" ]; then
+ if [ x"${PAPD_RUN}" = x"yes" -a -x :SBINDIR:/papd ]; then
+ killproc papd
+ RETVAL_PAPD=$?
+ fi
+
+ if [ -x :SBINDIR:/timelord ]; then
+ killproc timelord
+ fi
+
+ :BINDIR:/nbpunrgstr "${ATALK_NAME}:Workstation${ATALK_ZONE}"
+ :BINDIR:/nbpunrgstr "${ATALK_NAME}:netatalk${ATALK_ZONE}"
+
+ # kill atalkd last, since without it the plumbing goes away.
+ if [ -x :SBINDIR:/atalkd ]; then
+ killproc atalkd
+ RETVAL_ATALKD=$?
+ fi
+ fi
+
+ # kill this separately as we also do AFP/tcp
+ if [ x"${AFPD_RUN}" = x"yes" -a -x :SBINDIR:/afpd ]; then
+ killproc afpd
+ RETVAL_AFPD=$?
+ fi
+
+ if [ $RETVAL_ATALKD -eq 0 -a $RETVAL_PAPD -eq 0 -a $RETVAL_AFPD -eq 0 ] ; then
+ RETVAL=0
+ rm -f /var/lock/subsys/atalk || RETVAL=1
+ fi
+ echo ""
+ ;;
+ 'restart'|'reload')
+ $0 stop
+ $0 start
+ RETVAL=$?
+ ;;
+ 'status')
+ status atalkd
+ RETVAL=$?
+ ;;
+ *)
+ echo "Usage: atalk {start|stop|restart|status}"
+ exit 1
+esac
+
+exit $RETVAL
--- /dev/null
+#! /bin/sh
+#
+# Start/stop the AppleTalk daemons.
+#
+# AppleTalk daemons. Make sure not to start atalkd in the background:
+# its data structures must have time to stablize before running the
+# other processes.
+#
+
+#
+# kill the named process(es)
+#
+killproc() {
+ pid=`/usr/bin/ps -e |
+ /usr/bin/grep $1 |
+ /usr/bin/sed -e 's/^ *//' -e 's/ .*//'`
+ [ "$pid" != "" ] && kill $pid
+}
+
+case "$1" in
+
+#
+# Start the appletalk server processes.
+#
+
+'start')
+
+ echo 'starting appletalk daemons: \c'
+ if [ -x :SBINDIR:/atalkd ]; then
+ :SBINDIR:/atalkd; echo ' atalkd\c'
+ fi
+
+ if [ -x :BINDIR:/nbprgstr ]; then
+ :BINDIR:/nbprgstr -p 4 `hostname|sed 's/\..*$//'`:Workstation
+ :BINDIR:/nbprgstr -p 4 `hostname|sed 's/\..*$//'`:netatalk
+ echo ' nbprgstr\c'
+ fi
+
+ if [ -x :SBINDIR:/papd ]; then
+ :SBINDIR:/papd; echo ' papd\c'
+ fi
+
+ if [ -x :SBINDIR:/afpd ]; then
+ :SBINDIR:/afpd; echo ' afpd\c'
+ fi
+
+ if [ -x :SBINDIR:/timelord ]; then
+ :SBINDIR:/timelord; echo ' timelord\c'
+ fi
+
+ echo '.'
+
+ ;;
+
+#
+# Stop the appletalk server processes.
+#
+
+'stop')
+
+ echo 'stopping appletalk daemons:\c'
+
+ if [ -x :SBINDIR:/papd ]; then
+ killproc papd; echo ' papd\c'
+ fi
+
+ if [ -x :SBINDIR:/afpd ]; then
+ killproc afpd; echo ' afpd\c'
+ fi
+
+ if [ -x :SBINDIR:/timelord ]; then
+ killproc timelord; echo ' timelord\c'
+ fi
+
+ # kill atalkd last, since without it the plumbing goes away.
+ if [ -x :SBINDIR:/atalkd ]; then
+ killproc atalkd; echo ' atalkd\c'
+ fi
+
+ echo '.'
+ ;;
+
+#
+# Usage statement.
+#
+
+*)
+ echo "usage: $0 {start|stop}"
+ exit 1
+ ;;
+esac
--- /dev/null
+#!/bin/sh
+#
+# buildrpm
+# $Id: buildrpm,v 1.1 2000-07-25 21:08:59 rufustfirefly Exp $
+#
+# automates the process of building the netatalk rpm
+
+REDHAT_DIR=/usr/src/redhat
+CVSNAME=netatalk+asun
+
+# copy spec and patch into place
+cp netatalk-asun-cobalt.spec ${REDHAT_DIR}/SPECS
+cp netatalk-asun.makefile.patch ${REDHAT_DIR}/SOURCES
+
+# clean out any object files
+cd ../../
+make clean
+
+# tar up the archive
+cd ../
+tar cvfz ${REDHAT_DIR}/SOURCES/netatalk+asun.tar.gz \
+ --exclude="*/CVS" --exclude="*~" $CVSNAME
+
+# build the rpm
+rpm -ba ${REDHAT_DIR}/SPECS/netatalk-asun-cobalt.spec
+
--- /dev/null
+Summary: AppleTalk networking programs
+Name: netatalk
+Version: 1.4b2+asun2.1.4
+Release: pre39
+Packager: iNOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+Copyright: BSD
+Group: Networking
+Source0: netatalk+asun.tar.gz
+Patch0: netatalk-asun.makefile.patch
+Requires: pam >= 0.56
+BuildRoot: /var/tmp/atalk
+
+%description
+This package enables Linux to talk to Macintosh computers via the
+AppleTalk networking protocol. It includes a daemon to allow Linux
+to act as a file server over AppleTalk or IP for Mac's.
+
+%package devel
+Summary: Headers and static libraries for Appletalk development
+Group: Development/Libraries
+
+%description devel
+This packge contains the header files, and static libraries for building
+Appletalk networking programs.
+
+%prep
+%setup -n netatalk+asun
+%patch0 -p1
+
+%build
+make OPTOPTS="$RPM_OPT_FLAGS -fomit-frame-pointer -fsigned-char" OSVERSION=2.0
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/etc/atalk
+mkdir -p $RPM_BUILD_ROOT/etc/pam.d
+mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
+for i in 0 1 2 3 4 5 6; do
+ mkdir -p $RPM_BUILD_ROOT/etc/rc.d/rc$i.d
+done
+mkdir -p $RPM_BUILD_ROOT/usr/lib/atalk
+
+make install INSTALL_PREFIX=$RPM_BUILD_ROOT
+
+for i in aecho getzones megatron nbplkup nbprgstr nbpunrgstr pap \
+ papstatus psorder; do
+ strip $RPM_BUILD_ROOT/usr/bin/$i
+done
+for i in afpd atalkd psf psa papd; do
+ strip $RPM_BUILD_ROOT/usr/sbin/$i
+done
+
+#install -m644 config/AppleVolumes.system.cobalt $RPM_BUILD_ROOT/etc/atalk/AppleVolumes.system
+#install -m644 config/AppleVolumes.default $RPM_BUILD_ROOT/etc/atalk/AppleVolumes.default
+install -m644 config/atalkd.conf.cobalt $RPM_BUILD_ROOT/etc/atalk/atalkd.conf
+install -m644 config/papd.conf $RPM_BUILD_ROOT/etc/atalk/papd.conf
+install -m755 distrib/initscripts/rc.atalk.cobalt $RPM_BUILD_ROOT/etc/rc.d/init.d/atalk
+install -m644 config/netatalk.conf.cobalt $RPM_BUILD_ROOT/etc/atalk/netatalk.conf
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+/sbin/chkconfig --add atalk
+ldconfig
+# Do only for the first install
+if [ "$1" = 1 ] ; then
+ # Add the ddp lines to /etc/services
+ if (grep '[0-9][0-9]*/ddp' /etc/services >/dev/null); then
+ cat <<'_EOD1_' >&2
+warning: The DDP services appear to be present in /etc/services.
+warning: Please check them against services.atalk in the documentation.
+_EOD1_
+ true
+ else
+ cat <<'_EOD2_' >>/etc/services
+# start of DDP services
+#
+# Everything between the 'start of DDP services' and 'end of DDP services'
+# lines will be automatically deleted when the netatalk package is removed.
+#
+rtmp 1/ddp # Routing Table Maintenance Protocol
+nbp 2/ddp # Name Binding Protocol
+echo 4/ddp # AppleTalk Echo Protocol
+zip 6/ddp # Zone Information Protocol
+
+afpovertcp 548/tcp # AFP over TCP
+afpovertcp 548/udp
+# end of DDP services
+_EOD2_
+ fi
+fi
+
+%postun
+# Do only for the last un-install
+if [ "$1" = 0 ] ; then
+ /sbin/chkconfig --del atalk
+ # remove the ddp lines from /etc/services
+ if (grep '^# start of DDP services$' /etc/services >/dev/null && \
+ grep '^# end of DDP services$' /etc/services >/dev/null ); then
+ sed -e '/^# start of DDP services$/,/^# end of DDP services$/d' \
+ </etc/services >/tmp/services.tmp$$
+ cat /tmp/services.tmp$$ >/etc/services
+ rm /tmp/services.tmp$$
+ else
+ cat <<'_EOD3_' >&2
+warning: Unable to find the lines `# start of DDP services' and
+warning: `# end of DDP services' in the file /etc/services.
+warning: You should remove the DDP services from /etc/service manually.
+_EOD3_
+ fi
+fi
+
+%files
+%doc BUGS CHANGES CONTRIBUTORS COPYRIGHT ChangeLog INSTALL/ README* TODO VERSION contrib/ services.atalk
+%dir /etc/atalk
+#%config /etc/atalk/AppleVolumes.default
+#%config /etc/atalk/AppleVolumes.system
+%config /etc/atalk/netatalk.conf
+%config /etc/atalk/afpd.conf
+%config /etc/atalk/atalkd.conf
+%config /etc/atalk/papd.conf
+%config /etc/rc.d/init.d/atalk
+%config /etc/pam.d/netatalk
+/usr/sbin/afpd
+/usr/sbin/atalkd
+/usr/sbin/papd
+/usr/sbin/psa
+/usr/sbin/etc2ps
+/usr/sbin/psf
+/usr/bin/adv1tov2
+/usr/bin/aecho
+/usr/bin/afppasswd
+/usr/bin/binheader
+/usr/bin/getzones
+/usr/bin/hqx2bin
+/usr/bin/macbinary
+/usr/bin/megatron
+/usr/bin/nadheader
+/usr/bin/nbplkup
+/usr/bin/nbprgstr
+/usr/bin/nbpunrgstr
+/usr/bin/pap
+/usr/bin/papstatus
+/usr/bin/psorder
+/usr/bin/single2bin
+/usr/bin/unbin
+/usr/bin/unhex
+/usr/bin/unsingle
+%dir /usr/lib/atalk
+/usr/lib/atalk/filters/
+/usr/lib/atalk/nls/
+/usr/lib/atalk/pagecount.ps
+/usr/lib/atalk/uams/
+/usr/man/man1/aecho.1
+/usr/man/man1/getzones.1
+/usr/man/man1/hqx2bin.1
+/usr/man/man1/macbinary.1
+/usr/man/man1/megatron.1
+/usr/man/man1/nbp.1
+/usr/man/man1/nbplkup.1
+/usr/man/man1/nbprgstr.1
+/usr/man/man1/pap.1
+/usr/man/man1/papstatus.1
+/usr/man/man1/psorder.1
+/usr/man/man1/single2bin.1
+/usr/man/man1/unbin.1
+/usr/man/man1/unhex.1
+/usr/man/man1/unsingle.1
+/usr/man/man3/atalk_aton.3
+/usr/man/man3/nbp_name.3
+/usr/man/man4/atalk.4
+/usr/man/man8/afpd.8
+/usr/man/man8/atalkd.8
+/usr/man/man8/papd.8
+/usr/man/man8/psf.8
+
+%files devel
+/usr/lib/libatalk.a
+/usr/lib/libatalk_p.a
+/usr/include/atalk/
+/usr/include/netatalk/
+
+%changelog
+* Fri Aug 13 1999 John D. Blair <jdblair@cobaltnet.com>
+ - updated to latest build from Adrian
+
+* Thu Jul 22 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- /etc/atalk/netatalk.config -> /etc/atalk/netatalk.conf
+ Many parts of patch are merged into the original source code.
+* Tue Jul 13 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- AppleVolumes.system is merged into the original source code.
+ /etc/atalk/config -> /etc/atalk/netatalk.config.
+ Merge original rc.atalk.redhat and /etc/rc.d/init.d/atalk.
+ Remove last sample line of patched afpd.conf.
+* Fri Jul 9 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-30]
+* Sun Jun 20 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-28]
+* Thu Jun 3 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-22]
+* Wed May 19 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-15]
+ Make BerkleyDB=/usr.
+* Sun May 2 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-11]
+ Integrate three patches into netatalk-asun.makefile.patch.
+ Change /etc/uams dir to /usr/lib/atalk/uams.
+ Add configuration line to /etc/atalk/afpd.conf and remove needless
+ variables from /etc/atalk/config and /etc/rc.d/init.d/atalk.
+* Wed Apr 21 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-9]
+ Move %chengelog section last.
+* Wed Mar 31 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- Comment out -DNEED_QUOTA_WRAPPER in sys/linux/Makefile.
+* Sat Mar 20 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- Correct symbolic links to psf.
+ Remove asciize function from nbplkup so as to display Japanese hostname.
+* Thu Mar 11 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- Included MacPerl 5 script ICDumpSuffixMap which dumps suffix mapping
+ containd in Internet Config Preference.
+* Tue Mar 2 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [asun2.1.3]
+* Mon Feb 15 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.2-8]
+* Sun Feb 7 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.2-6]
+* Mon Jan 25 1999 iNOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.2-3]
+* Thu Dec 17 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.2]
+ Remove crlf patch. It is now a server's option.
+* Thu Dec 3 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use stable version source netatalk-1.4b2+asun2.1.1.tar.gz
+ Add uams directory
+* Sat Nov 28 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.1-3 source.
+* Mon Nov 23 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.1-2 source.
+* Mon Nov 16 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Fix rcX.d's symbolic links.
+* Wed Oct 28 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0a-2 source. Remove '%exclusiveos linux' line.
+* Sat Oct 24 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use stable version source netatalk-1.4b2+asun2.1.0.tar.gz.
+* Mon Oct 5 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-10a source.
+* Thu Sep 19 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-8 source. Add chkconfig support.
+* Sat Sep 12 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Comment out -DCRLF. Use RPM_OPT_FLAGS.
+* Mon Sep 8 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-7 source. Rename atalk.init to atalk.
+* Mon Aug 22 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-6 source.
+* Mon Jul 27 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-5 source.
+* Tue Jul 21 1998 INOUE Koichi <inoue@ma.ns.musashi-techa.c.jp>
+- Use pre-asun2.1.0-3 source.
+* Tue Jul 7 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Add afpovertcp entries to /etc/services
+- Remove BuildRoot in man8 pages
+* Mon Jun 29 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use modified sources 1.4b2+asun2.1.0 produced by Adrian Sun
+ <asun@saul9.u.washington.edu> to provide an AppleShareIP file server
+- Included AppleVolumes.system file maintained by Johnson
+ <johnson@stpt.usf.edu>
+* Mon Aug 25 1997 David Gibson <D.Gibson@student.anu.edu.au>
+- Used a buildroot
+- Use RPM_OPT_FLAGS
+- Moved configuration parameters/files from atalk.init to /etc/atalk
+- Separated devel package
+- Built with shared libraries
+* Sun Jul 13 1997 Paul H. Hargrove <hargrove@sccm.Stanford.EDU>
+- Updated sources from 1.3.3 to 1.4b2
+- Included endian patch for Linux/SPARC
+- Use all the configuration files supplied in the source. This has the
+ following advantages over the ones in the previous rpm release:
+ + The printer 'lp' isn't automatically placed in papd.conf
+ + The default file conversion is binary rather than text.
+- Automatically add and remove DDP services from /etc/services
+- Placed the recommended /etc/services in the documentation
+- Changed atalk.init to give daemons a soft kill
+- Changed atalk.init to make configuration easier
+
+* Wed May 28 1997 Mark Cornick <mcornick@zorak.gsfc.nasa.gov>
+Updated for /etc/pam.d
--- /dev/null
+--- netatalk-1.4b2+asun2.1.4.orig/Makefile.base Fri Jul 30 09:43:38 1999
++++ netatalk-1.4b2+asun2.1.4.orig/Makefile Fri Jul 30 09:44:29 1999
+@@ -1,31 +1,31 @@
+ # Root of installation. Subdirectories will be ${DESTDIR}/etc,
+ # ${DESTDIR}/bin, and ${DESTDIR}/lib.
+-DESTDIR=/usr/local/atalk
++#DESTDIR=/usr/local/atalk
+
+ # for system-level binaries
+-SBINDIR=$(DESTDIR)/sbin
++#SBINDIR=$(DESTDIR)/sbin
+ # for user-level binaries
+-BINDIR=$(DESTDIR)/bin
++#BINDIR=$(DESTDIR)/bin
+ # for program libraries (*.a)
+-LIBDIR=$(DESTDIR)/lib
++#LIBDIR=$(DESTDIR)/lib
+ # for machine-independent resources (pagecount.ps, etc.)
+-RESDIR=$(DESTDIR)/etc
++#RESDIR=$(DESTDIR)/etc
+ # for configuration files (AppleVolumes.system, etc.)
+-ETCDIR=$(DESTDIR)/etc
++#ETCDIR=$(DESTDIR)/etc
+ # for include files
+-INCDIR=$(DESTDIR)/include
++#INCDIR=$(DESTDIR)/include
+ # Root of man pages. Subdirectories will be
+ # ${MANDIR}/man1, ${MANDIR}/man4, and ${MANDIR}/man8.
+-MANDIR=$(DESTDIR)/man
++#MANDIR=$(DESTDIR)/man
+
+-#INSTALL_PREFIX=
+-#SBINDIR=${INSTALL_PREFIX}/usr/sbin
+-#BINDIR=${INSTALL_PREFIX}/usr/bin
+-#LIBDIR=${INSTALL_PREFIX}/usr/lib
+-#RESDIR=${INSTALL_PREFIX}/usr/lib/atalk
+-#ETCDIR=${INSTALL_PREFIX}/etc/atalk
+-#INCDIR=${INSTALL_PREFIX}/usr/include
+-#MANDIR=${INSTALL_PREFIX}/usr/man
++INSTALL_PREFIX=
++SBINDIR=${INSTALL_PREFIX}/usr/sbin
++BINDIR=${INSTALL_PREFIX}/usr/bin
++LIBDIR=${INSTALL_PREFIX}/usr/lib
++RESDIR=${INSTALL_PREFIX}/usr/lib/atalk
++ETCDIR=${INSTALL_PREFIX}/etc/atalk
++INCDIR=${INSTALL_PREFIX}/usr/include
++MANDIR=${INSTALL_PREFIX}/usr/man
+
+ # Location of the Berkeley v2 db library and include files.
+ # NOTE: leave this commented out for now. it's a placeholder for a future
+@@ -55,12 +55,12 @@
+
+ # Location of PAM support library and include files. Uncomment this if
+ # you want to enable PAM support.
+-#PAMDIR=/usr
++PAMDIR=/usr
+
+ # Location of cracklib support library and include files. This is used
+ # in the password changing routines. Uncomment this out if you want to
+ # enable support.
+-#CRACKDIR=/usr
++CRACKDIR=/usr
+
+
+ # Location of the AFS and Kerberos libraries and include files. Uncomment
--- /dev/null
+Summary: AppleTalk networking programs
+Name: netatalk
+Version: 1.4b2+asun2.1.4
+Release: pre39
+Packager: iNOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+Copyright: BSD
+Group: Networking
+Source0: ftp://ftp.cobaltnet.com/pub/users/asun/testing/pre-asun2.1.4-36.tar.gz
+Patch0: netatalk-asun.makefile.patch
+Requires: pam >= 0.56
+BuildRoot: /var/tmp/atalk
+
+%description
+This package enables Linux to talk to Macintosh computers via the
+AppleTalk networking protocol. It includes a daemon to allow Linux
+to act as a file server over AppleTalk or IP for Mac's.
+
+%package devel
+Summary: Headers and static libraries for Appletalk development
+Group: Development/Libraries
+
+%description devel
+This packge contains the header files, and static libraries for building
+Appletalk networking programs.
+
+%prep
+%setup
+%patch0 -p1
+
+%build
+make OPTOPTS="$RPM_OPT_FLAGS -fomit-frame-pointer -fsigned-char" OSVERSION=2.0
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/etc/atalk
+mkdir -p $RPM_BUILD_ROOT/etc/pam.d
+mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
+for i in 0 1 2 3 4 5 6; do
+ mkdir -p $RPM_BUILD_ROOT/etc/rc.d/rc$i.d
+done
+mkdir -p $RPM_BUILD_ROOT/usr/lib/atalk
+
+make install INSTALL_PREFIX=$RPM_BUILD_ROOT
+
+for i in aecho getzones megatron nbplkup nbprgstr nbpunrgstr pap \
+ papstatus psorder; do
+ strip $RPM_BUILD_ROOT/usr/bin/$i
+done
+for i in afpd atalkd psf psa papd; do
+ strip $RPM_BUILD_ROOT/usr/sbin/$i
+done
+
+install -m644 config/AppleVolumes.system $RPM_BUILD_ROOT/etc/atalk/AppleVolumes.system
+install -m644 config/AppleVolumes.default $RPM_BUILD_ROOT/etc/atalk/AppleVolumes.default
+install -m644 config/atalkd.conf $RPM_BUILD_ROOT/etc/atalk/atalkd.conf
+install -m644 config/papd.conf $RPM_BUILD_ROOT/etc/atalk/papd.conf
+
+# This is not necessary because chkconfig will make the links.
+for i in 0 1 2 6; do
+ ln -sf ../init.d/atalk $RPM_BUILD_ROOT/etc/rc.d/rc$i.d/K35atalk
+done
+for i in 3 4 5; do
+ ln -sf ../init.d/atalk $RPM_BUILD_ROOT/etc/rc.d/rc$i.d/S91atalk
+done
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+/sbin/chkconfig --add atalk
+ldconfig
+# Do only for the first install
+if [ "$1" = 1 ] ; then
+ # Add the ddp lines to /etc/services
+ if (grep '[0-9][0-9]*/ddp' /etc/services >/dev/null); then
+ cat <<'_EOD1_' >&2
+warning: The DDP services appear to be present in /etc/services.
+warning: Please check them against services.atalk in the documentation.
+_EOD1_
+ true
+ else
+ cat <<'_EOD2_' >>/etc/services
+# start of DDP services
+#
+# Everything between the 'start of DDP services' and 'end of DDP services'
+# lines will be automatically deleted when the netatalk package is removed.
+#
+rtmp 1/ddp # Routing Table Maintenance Protocol
+nbp 2/ddp # Name Binding Protocol
+echo 4/ddp # AppleTalk Echo Protocol
+zip 6/ddp # Zone Information Protocol
+
+afpovertcp 548/tcp # AFP over TCP
+afpovertcp 548/udp
+# end of DDP services
+_EOD2_
+ fi
+fi
+
+%postun
+# Do only for the last un-install
+if [ "$1" = 0 ] ; then
+ /sbin/chkconfig --del atalk
+ # remove the ddp lines from /etc/services
+ if (grep '^# start of DDP services$' /etc/services >/dev/null && \
+ grep '^# end of DDP services$' /etc/services >/dev/null ); then
+ sed -e '/^# start of DDP services$/,/^# end of DDP services$/d' \
+ </etc/services >/tmp/services.tmp$$
+ cat /tmp/services.tmp$$ >/etc/services
+ rm /tmp/services.tmp$$
+ else
+ cat <<'_EOD3_' >&2
+warning: Unable to find the lines `# start of DDP services' and
+warning: `# end of DDP services' in the file /etc/services.
+warning: You should remove the DDP services from /etc/service manually.
+_EOD3_
+ fi
+fi
+
+%files
+%doc BUGS CHANGES CONTRIBUTORS COPYRIGHT ChangeLog INSTALL/ README* TODO VERSION contrib/ services.atalk
+%dir /etc/atalk
+%config /etc/atalk/AppleVolumes.default
+%config /etc/atalk/AppleVolumes.system
+%config /etc/atalk/netatalk.conf
+%config /etc/atalk/afpd.conf
+%config /etc/atalk/atalkd.conf
+%config /etc/atalk/papd.conf
+%config /etc/rc.d/init.d/atalk
+%config /etc/pam.d/netatalk
+/etc/rc.d/rc0.d/K35atalk
+/etc/rc.d/rc1.d/K35atalk
+/etc/rc.d/rc2.d/K35atalk
+/etc/rc.d/rc3.d/S91atalk
+/etc/rc.d/rc4.d/S91atalk
+/etc/rc.d/rc5.d/S91atalk
+/etc/rc.d/rc6.d/K35atalk
+/usr/sbin/afpd
+/usr/sbin/atalkd
+/usr/sbin/papd
+/usr/sbin/psa
+/usr/sbin/etc2ps
+/usr/sbin/psf
+/usr/bin/adv1tov2
+/usr/bin/aecho
+/usr/bin/afppasswd
+/usr/bin/binheader
+/usr/bin/getzones
+/usr/bin/hqx2bin
+/usr/bin/macbinary
+/usr/bin/megatron
+/usr/bin/nadheader
+/usr/bin/nbplkup
+/usr/bin/nbprgstr
+/usr/bin/nbpunrgstr
+/usr/bin/pap
+/usr/bin/papstatus
+/usr/bin/psorder
+/usr/bin/single2bin
+/usr/bin/unbin
+/usr/bin/unhex
+/usr/bin/unsingle
+%dir /usr/lib/atalk
+/usr/lib/atalk/filters/
+/usr/lib/atalk/nls/
+/usr/lib/atalk/pagecount.ps
+/usr/lib/atalk/uams/
+/usr/man/man1/aecho.1
+/usr/man/man1/getzones.1
+/usr/man/man1/hqx2bin.1
+/usr/man/man1/macbinary.1
+/usr/man/man1/megatron.1
+/usr/man/man1/nbp.1
+/usr/man/man1/nbplkup.1
+/usr/man/man1/nbprgstr.1
+/usr/man/man1/pap.1
+/usr/man/man1/papstatus.1
+/usr/man/man1/psorder.1
+/usr/man/man1/single2bin.1
+/usr/man/man1/unbin.1
+/usr/man/man1/unhex.1
+/usr/man/man1/unsingle.1
+/usr/man/man3/atalk_aton.3
+/usr/man/man3/nbp_name.3
+/usr/man/man4/atalk.4
+/usr/man/man8/afpd.8
+/usr/man/man8/atalkd.8
+/usr/man/man8/papd.8
+/usr/man/man8/psf.8
+
+%files devel
+/usr/lib/libatalk.a
+/usr/lib/libatalk_p.a
+/usr/include/atalk/
+/usr/include/netatalk/
+
+%changelog
+* Thu Jul 22 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- /etc/atalk/netatalk.config -> /etc/atalk/netatalk.conf
+ Many parts of patch are merged into the original source code.
+* Tue Jul 13 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- AppleVolumes.system is merged into the original source code.
+ /etc/atalk/config -> /etc/atalk/netatalk.config.
+ Merge original rc.atalk.redhat and /etc/rc.d/init.d/atalk.
+ Remove last sample line of patched afpd.conf.
+* Fri Jul 9 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-30]
+* Sun Jun 20 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-28]
+* Thu Jun 3 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-22]
+* Wed May 19 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-15]
+ Make BerkleyDB=/usr.
+* Sun May 2 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-11]
+ Integrate three patches into netatalk-asun.makefile.patch.
+ Change /etc/uams dir to /usr/lib/atalk/uams.
+ Add configuration line to /etc/atalk/afpd.conf and remove needless
+ variables from /etc/atalk/config and /etc/rc.d/init.d/atalk.
+* Wed Apr 21 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-9]
+ Move %chengelog section last.
+* Wed Mar 31 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- Comment out -DNEED_QUOTA_WRAPPER in sys/linux/Makefile.
+* Sat Mar 20 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- Correct symbolic links to psf.
+ Remove asciize function from nbplkup so as to display Japanese hostname.
+* Thu Mar 11 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- Included MacPerl 5 script ICDumpSuffixMap which dumps suffix mapping
+ containd in Internet Config Preference.
+* Tue Mar 2 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [asun2.1.3]
+* Mon Feb 15 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.2-8]
+* Sun Feb 7 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.2-6]
+* Mon Jan 25 1999 iNOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.2-3]
+* Thu Dec 17 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.2]
+ Remove crlf patch. It is now a server's option.
+* Thu Dec 3 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use stable version source netatalk-1.4b2+asun2.1.1.tar.gz
+ Add uams directory
+* Sat Nov 28 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.1-3 source.
+* Mon Nov 23 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.1-2 source.
+* Mon Nov 16 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Fix rcX.d's symbolic links.
+* Wed Oct 28 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0a-2 source. Remove '%exclusiveos linux' line.
+* Sat Oct 24 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use stable version source netatalk-1.4b2+asun2.1.0.tar.gz.
+* Mon Oct 5 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-10a source.
+* Thu Sep 19 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-8 source. Add chkconfig support.
+* Sat Sep 12 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Comment out -DCRLF. Use RPM_OPT_FLAGS.
+* Mon Sep 8 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-7 source. Rename atalk.init to atalk.
+* Mon Aug 22 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-6 source.
+* Mon Jul 27 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-5 source.
+* Tue Jul 21 1998 INOUE Koichi <inoue@ma.ns.musashi-techa.c.jp>
+- Use pre-asun2.1.0-3 source.
+* Tue Jul 7 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Add afpovertcp entries to /etc/services
+- Remove BuildRoot in man8 pages
+* Mon Jun 29 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use modified sources 1.4b2+asun2.1.0 produced by Adrian Sun
+ <asun@saul9.u.washington.edu> to provide an AppleShareIP file server
+- Included AppleVolumes.system file maintained by Johnson
+ <johnson@stpt.usf.edu>
+* Mon Aug 25 1997 David Gibson <D.Gibson@student.anu.edu.au>
+- Used a buildroot
+- Use RPM_OPT_FLAGS
+- Moved configuration parameters/files from atalk.init to /etc/atalk
+- Separated devel package
+- Built with shared libraries
+* Sun Jul 13 1997 Paul H. Hargrove <hargrove@sccm.Stanford.EDU>
+- Updated sources from 1.3.3 to 1.4b2
+- Included endian patch for Linux/SPARC
+- Use all the configuration files supplied in the source. This has the
+ following advantages over the ones in the previous rpm release:
+ + The printer 'lp' isn't automatically placed in papd.conf
+ + The default file conversion is binary rather than text.
+- Automatically add and remove DDP services from /etc/services
+- Placed the recommended /etc/services in the documentation
+- Changed atalk.init to give daemons a soft kill
+- Changed atalk.init to make configuration easier
+
+* Wed May 28 1997 Mark Cornick <mcornick@zorak.gsfc.nasa.gov>
+Updated for /etc/pam.d
--- /dev/null
+ALL= afpd uams psf atalkd papd
+TAGSFILE=tags
+CC=cc
+INSTALL= install
+
+all: ${ALL}
+
+${ALL}: FRC
+ cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+ DESDIR="${DESDIR}" TCPWRAPDIR="${TCPWRAPDIR}" PAMDIR="${PAMDIR}" \
+ CRYPTODIR="${CRYPTODIR}" DB2DIR="${DB2DIR}" all
+
+FRC:
+
+tags:
+ for i in ${ALL}; do \
+ (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" \
+ TAGSFILE=../${TAGSFILE} tags); \
+ done
+
+install:
+ -mkdir ${ETCDIR}
+ -mkdir ${SBINDIR}
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+ TCPWRAPDIR="${TCPWRAPDIR}" PAMDIR="${PAMDIR}" \
+ INSTALL="${INSTALL}" DESDIR="${DESDIR}" install); \
+ done
+
+clean:
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+ done
+
+depend:
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} DEFS=${DEFS} depend); \
+ done
--- /dev/null
+
+##########################################################################
+
+SRC = unix.c ofork.c main.c switch.c auth.c volume.c directory.c file.c \
+ enumerate.c desktop.c filedir.c fork.c appl.c gettok.c \
+ status.c afp_options.c afp_asp.c afp_dsi.c messages.c config.c \
+ nfsquota.c codepage.c quota.c uam.c afs.c
+
+OBJ = unix.o ofork.o main.o switch.o auth.o volume.o directory.o file.o \
+ enumerate.o desktop.o filedir.o fork.o appl.o gettok.o \
+ status.o afp_options.o afp_asp.o afp_dsi.o messages.o config.o \
+ nfsquota.o codepage.o quota.o uam.o afs.o
+
+INCPATH= -I../../include ${AFSINCPATH}
+CFLAGS= ${DEFS} ${AFSDEFS} ${OPTOPTS} ${INCPATH} -DAPPLCNAME
+LIBS = -latalk ${AFSLIBS} ${ADDLIBS} ${TCPWRAPLIBS} ${DB2LIBS} \
+ ${RPCSVCLIB} ${AFPLIBS} ${PAMLIBS} ${LIBSHARED}
+LIBDIRS= -L../../libatalk ${AFSLIBDIRS} ${TCPWRAPLIBDIRS} \
+ ${DB2LIBDIRS} ${PAMLIBDIRS}
+TAGSFILE= tags
+CC= cc
+INSTALL= install
+
+SUBDIRS = nls
+
+all : ${SUBDIRS}
+ if [ x"${TCPWRAPDIR}" != x ]; then \
+ TCPWRAPLIBS="-lwrap -lnsl"; \
+ if [ "${TCPWRAPDIR}" != "/usr" ]; then \
+ TCPWRAPLIBDIRS="-L${TCPWRAPDIR}/lib"; \
+ fi; \
+ fi; \
+ if [ x"${DB2DIR}" != x ]; then \
+ DB2LIBS="-ldb"; \
+ if [ "${DB2DIR}" != "/usr" ]; then \
+ DB2LIBDIRS="-L${DB2DIR}/lib"; \
+ DB2INCPATH="-I${DB2DIR}/include"; \
+ fi; \
+ fi; \
+ if [ x"${PAMDIR}" != x ]; then \
+ PAMLIBS="-lpam"; \
+ if [ "${PAMDIR}" != "/usr" ]; then \
+ PAMLIBDIRS="-L${PAMDIR}/lib"; \
+ PAMINCPATH="-I${PAMDIR}/include"; \
+ fi; \
+ PAMDEFS="-DUSE_PAM"; \
+ fi; \
+ if [ -f /usr/lib/librpcsvc.a -o -f /lib/librpcsvc.a ]; then \
+ RPCSVCLIB=-lrpcsvc; \
+ fi; \
+ ${MAKE} ${MFLAGS} CC="${CC}" ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" \
+ OPTOPTS="${OPTOPTS}" DESTDIR="${DESTDIR}" DESDIR="${DESDIR}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ TCPWRAPDIR="${TCPWRAPDIR}" DB2DIR="${DB2DIR}" \
+ AFSLIBS="$${AFSLIBS}" AFSLIBDIRS="$${AFSLIBDIRS}" \
+ TCPWRAPLIBS="$${TCPWRAPLIBS}" TCPWRAPLIBDIRS="$${TCPWRAPLIBDIRS}" \
+ DB2LIBS="$${DB2LIBS}" DB2LIBDIRS="$${DB2LIBDIRS}" \
+ LIBSHARED="$${LIBSHARED}" PAMLIBS="$${PAMLIBS}" \
+ PAMLIBDIR="$${PAMLIBDIR}" RPCSVCLIB="$${RPCSVCLIB}" \
+ AFSINCPATH="$${AFSINCPATH}" AFSDEFS="$${AFSDEFS}" \
+ DB2INCPATH="$${DB2INCPATH}" \
+ afpd
+
+${SUBDIRS}: FRC
+ cd $@; ${MAKE} ${MFLAGS} CC="${CC}" DEFS="${DEFS}" \
+ OPTOPTS="${OPTOPTS}" DESTDIR="${DESTDIR}" DESDIR="${DESDIR}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ TCPWRAPDIR="${TCPWRAPDIR}" \
+ AFSLIBS="$${AFSLIBS}" AFSLIBDIRS="$${AFSLIBDIRS}" \
+ AFSINCPATH="$${AFSINCPATH}" AFSDEFS="$${AFSDEFS}"
+
+FRC:
+
+afpd : ${OBJ} ../../libatalk/libatalk.a
+ ${CC} ${CFLAGS} ${LDFLAGS_EXPORT} -o afpd ${OBJ} ${LIBDIRS} ${LIBS}
+
+afp_options.o : afp_options.c
+ ${CC} ${CFLAGS} \
+ -D_PATH_AFPDDEFVOL=\"${ETCDIR}/AppleVolumes.default\" \
+ -D_PATH_AFPDSYSVOL=\"${ETCDIR}/AppleVolumes.system\" \
+ -D_PATH_AFPDPWFILE=\"~/.passwd\" \
+ -D_PATH_AFPDCONF=\"${ETCDIR}/afpd.conf\" \
+ -D_PATH_AFPDUAMPATH=\"${RESDIR}/uams/\" \
+ -D_PATH_AFPDNLSPATH=\"${RESDIR}/nls/\" \
+ ${CPPFLAGS} -c $<
+
+config.o: config.c
+ ${CC} ${CFLAGS} \
+ -DVERSION=\"`cat ../../VERSION`\" \
+ ${CPPFLAGS} -c $<
+
+install : all
+ ${INSTALL} -c afpd ${SBINDIR}
+ for i in ${SUBDIRS}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" INSTALL="${INSTALL}" \
+ install); \
+ done
+
+clean :
+ for i in ${SUBDIRS}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" INSTALL="${INSTALL}" \
+ clean); \
+ done
+ rm -f a.out core* *.o *.bak *[Ee]rrs tags
+ rm -f afpd
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * modified from main.c. this handles afp over asp.
+ */
+
+#ifndef NO_DDP
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <netatalk/endian.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+#include <atalk/compat.h>
+#include <atalk/util.h>
+
+#include "globals.h"
+#include "switch.h"
+#include "auth.h"
+#include "fork.h"
+
+extern struct oforks *writtenfork;
+
+static AFPObj *child;
+
+static __inline__ void afp_asp_close(AFPObj *obj)
+{
+ ASP asp = obj->handle;
+
+ if (obj->logout)
+ (*obj->logout)();
+
+ asp_close( asp );
+ syslog(LOG_INFO, "%.2fKB read, %.2fKB written",
+ asp->read_count / 1024.0, asp->write_count / 1024.0);
+}
+
+static void afp_asp_die(const int sig)
+{
+ ASP asp = child->handle;
+
+ asp_attention(asp, AFPATTN_SHUTDOWN);
+ if ( asp_shutdown( asp ) < 0 ) {
+ syslog( LOG_ERR, "afp_die: asp_shutdown: %m" );
+ }
+
+ afp_asp_close(child);
+ if (sig == SIGTERM || sig == SIGALRM)
+ exit( 0 );
+ else
+ exit(sig);
+}
+
+static void afp_asp_timedown()
+{
+ struct sigaction sv;
+ struct itimerval it;
+
+ /* shutdown and don't reconnect. server going down in 5 minutes. */
+ asp_attention(child->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
+ AFPATTN_TIME(5));
+
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 0;
+ it.it_value.tv_sec = 300;
+ it.it_value.tv_usec = 0;
+ if ( setitimer( ITIMER_REAL, &it, 0 ) < 0 ) {
+ syslog( LOG_ERR, "afp_timedown: setitimer: %m" );
+ afp_asp_die(1);
+ }
+
+ memset(&sv, 0, sizeof(sv));
+ sv.sa_handler = afp_asp_die;
+ sigemptyset( &sv.sa_mask );
+ sv.sa_flags = SA_RESTART;
+ if ( sigaction( SIGALRM, &sv, 0 ) < 0 ) {
+ syslog( LOG_ERR, "afp_timedown: sigaction: %m" );
+ afp_asp_die(1);
+ }
+}
+
+void afp_over_asp(AFPObj *obj)
+{
+ ASP asp;
+ struct sigaction action;
+ int func, ccnt = 0, reply = 0;
+
+ obj->exit = afp_asp_die;
+ obj->reply = (int (*)()) asp_cmdreply;
+ obj->attention = (int (*)(void *, AFPUserBytes)) asp_attention;
+ child = obj;
+ asp = (ASP) obj->handle;
+
+ /* install signal handlers */
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = afp_asp_timedown;
+ sigemptyset( &action.sa_mask );
+ action.sa_flags = SA_RESTART;
+ if ( sigaction( SIGHUP, &action, 0 ) < 0 ) {
+ syslog( LOG_ERR, "afp_over_asp: sigaction: %m" );
+ afp_asp_die(1);
+ }
+
+ action.sa_handler = afp_asp_die;
+ sigemptyset( &action.sa_mask );
+ action.sa_flags = SA_RESTART;
+ if ( sigaction( SIGTERM, &action, 0 ) < 0 ) {
+ syslog( LOG_ERR, "afp_over_asp: sigaction: %m" );
+ afp_asp_die(1);
+ }
+
+ syslog( LOG_INFO, "session from %u.%u:%u on %u.%u:%u",
+ ntohs( asp->asp_sat.sat_addr.s_net ),
+ asp->asp_sat.sat_addr.s_node, asp->asp_sat.sat_port,
+ ntohs( atp_sockaddr( asp->asp_atp )->sat_addr.s_net ),
+ atp_sockaddr( asp->asp_atp )->sat_addr.s_node,
+ atp_sockaddr( asp->asp_atp )->sat_port );
+
+ while ((reply = asp_getrequest(asp))) {
+ switch (reply) {
+ case ASPFUNC_CLOSE :
+ afp_asp_close(obj);
+ syslog( LOG_INFO, "done" );
+ if ( obj->options.flags & OPTION_DEBUG ) {
+ printf( "done\n" );
+ }
+ return;
+ break;
+
+ case ASPFUNC_CMD :
+#ifdef AFS
+ if ( writtenfork ) {
+ if ( flushfork( writtenfork ) < 0 ) {
+ syslog( LOG_ERR, "main flushfork: %m" );
+ }
+ writtenfork = NULL;
+ }
+#endif AFS
+ func = (u_char) asp->commands[0];
+ if ( obj->options.flags & OPTION_DEBUG ) {
+ printf( "command: %d\n", func );
+ bprint( asp->commands, asp->cmdlen );
+ }
+ if ( afp_switch[ func ] != NULL ) {
+ /*
+ * The function called from afp_switch is expected to
+ * read its parameters out of buf, put its
+ * results in replybuf (updating rbuflen), and
+ * return an error code.
+ */
+ asp->datalen = ASP_DATASIZ;
+ reply = (*afp_switch[ func ])(obj,
+ asp->commands, asp->cmdlen,
+ asp->data, &asp->datalen);
+ } else {
+ syslog( LOG_ERR, "bad function %X", func );
+ asp->datalen = 0;
+ reply = AFPERR_NOOP;
+ }
+ if ( obj->options.flags & OPTION_DEBUG ) {
+ printf( "reply: %d, %d\n", reply, ccnt++ );
+ bprint( asp->data, asp->datalen );
+ }
+
+ if ( asp_cmdreply( asp, reply ) < 0 ) {
+ syslog( LOG_ERR, "asp_cmdreply: %m" );
+ afp_asp_die(1);
+ }
+ break;
+
+ case ASPFUNC_WRITE :
+ func = (u_char) asp->commands[0];
+ if ( obj->options.flags & OPTION_DEBUG ) {
+ printf( "(write) command: %d\n", func );
+ bprint( asp->commands, asp->cmdlen );
+ }
+ if ( afp_switch[ func ] != NULL ) {
+ asp->datalen = ASP_DATASIZ;
+ reply = (*afp_switch[ func ])(obj,
+ asp->commands, asp->cmdlen,
+ asp->data, &asp->datalen);
+ } else {
+ syslog( LOG_ERR, "(write) bad function %X", func );
+ asp->datalen = 0;
+ reply = AFPERR_NOOP;
+ }
+ if ( obj->options.flags & OPTION_DEBUG ) {
+ printf( "(write) reply code: %d, %d\n", reply, ccnt++ );
+ bprint( asp->data, asp->datalen );
+ }
+ if ( asp_wrtreply( asp, reply ) < 0 ) {
+ syslog( LOG_ERR, "asp_wrtreply: %m" );
+ afp_asp_die(1);
+ }
+ break;
+ default:
+ /*
+ * Bad asp packet. Probably should have asp filter them,
+ * since they are typically things like out-of-order packet.
+ */
+ syslog( LOG_INFO, "main: asp_getrequest: %d", reply );
+ break;
+ }
+
+ if ( obj->options.flags & OPTION_DEBUG ) {
+#ifdef notdef
+ pdesc( stdout );
+#endif notdef
+ of_pforkdesc( stdout );
+ fflush( stdout );
+ }
+ }
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * modified from main.c. this handles afp over tcp.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <syslog.h>
+
+#include <atalk/dsi.h>
+#include <atalk/compat.h>
+#include <atalk/util.h>
+
+#include "globals.h"
+#include "switch.h"
+#include "auth.h"
+#include "fork.h"
+
+extern struct oforks *writtenfork;
+
+#define CHILD_DIE (1 << 0)
+#define CHILD_RUNNING (1 << 1)
+
+static struct {
+ AFPObj *obj;
+ unsigned char tickle, flags;
+} child;
+
+
+static __inline__ void afp_dsi_close(AFPObj *obj)
+{
+ DSI *dsi = obj->handle;
+
+ if (obj->logout)
+ (*obj->logout)();
+
+ dsi_close(dsi);
+ syslog(LOG_INFO, "%.2fKB read, %.2fKB written",
+ dsi->read_count/1024.0, dsi->write_count/1024.0);
+}
+
+/* a little bit of code duplication. */
+static void afp_dsi_die(int sig)
+{
+ dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN);
+ afp_dsi_close(child.obj);
+ if (sig == SIGTERM || sig == SIGALRM)
+ exit( 0 );
+ else
+ exit(sig);
+}
+
+static void afp_dsi_timedown()
+{
+ struct sigaction sv;
+ struct itimerval it;
+
+ child.flags |= CHILD_DIE;
+ /* shutdown and don't reconnect. server going down in 5 minutes. */
+ setmessage("The server is going down for maintenance.");
+ dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
+ AFPATTN_MESG | AFPATTN_TIME(5));
+
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 0;
+ it.it_value.tv_sec = 300;
+ it.it_value.tv_usec = 0;
+ if ( setitimer( ITIMER_REAL, &it, 0 ) < 0 ) {
+ syslog( LOG_ERR, "afp_timedown: setitimer: %m" );
+ afp_dsi_die(1);
+ }
+
+ memset(&sv, 0, sizeof(sv));
+ sv.sa_handler = afp_dsi_die;
+ sigemptyset( &sv.sa_mask );
+ sigaddset(&sv.sa_mask, SIGHUP);
+ sigaddset(&sv.sa_mask, SIGTERM);
+ sv.sa_flags = SA_RESTART;
+ if ( sigaction( SIGALRM, &sv, 0 ) < 0 ) {
+ syslog( LOG_ERR, "afp_timedown: sigaction: %m" );
+ afp_dsi_die(1);
+ }
+}
+
+static void alarm_handler()
+{
+ /* if we're in the midst of processing something,
+ don't die. we'll allow 3 missed tickles before we die (2 minutes) */
+ if ((child.flags & CHILD_RUNNING) || (child.tickle++ < 4)) {
+ dsi_tickle(child.obj->handle);
+ } else { /* didn't receive a tickle. close connection */
+ syslog(LOG_ERR, "afp_alarm: child timed out");
+ afp_dsi_die(1);
+ }
+}
+
+/* afp over dsi. this never returns. */
+void afp_over_dsi(AFPObj *obj)
+{
+ DSI *dsi = (DSI *) obj->handle;
+ u_int32_t err, cmd;
+ u_int8_t function;
+ struct sigaction action;
+
+ obj->exit = afp_dsi_die;
+ obj->reply = (int (*)()) dsi_cmdreply;
+ obj->attention = (int (*)(void *, AFPUserBytes)) dsi_attention;
+
+ child.obj = obj;
+ child.tickle = child.flags = 0;
+
+ /* install SIGTERM and SIGHUP */
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = afp_dsi_timedown;
+ sigemptyset( &action.sa_mask );
+ sigaddset(&action.sa_mask, SIGALRM);
+ sigaddset(&action.sa_mask, SIGTERM);
+ action.sa_flags = SA_RESTART;
+ if ( sigaction( SIGHUP, &action, 0 ) < 0 ) {
+ syslog( LOG_ERR, "afp_over_dsi: sigaction: %m" );
+ afp_dsi_die(1);
+ }
+
+ action.sa_handler = afp_dsi_die;
+ sigemptyset( &action.sa_mask );
+ sigaddset(&action.sa_mask, SIGALRM);
+ sigaddset(&action.sa_mask, SIGHUP);
+ action.sa_flags = SA_RESTART;
+ if ( sigaction( SIGTERM, &action, 0 ) < 0 ) {
+ syslog( LOG_ERR, "afp_over_dsi: sigaction: %m" );
+ afp_dsi_die(1);
+ }
+
+ /* tickle handler */
+ action.sa_handler = alarm_handler;
+ sigemptyset(&action.sa_mask);
+ sigaddset(&action.sa_mask, SIGHUP);
+ sigaddset(&action.sa_mask, SIGTERM);
+ action.sa_flags = SA_RESTART;
+ if ((sigaction(SIGALRM, &action, NULL) < 0) ||
+ (setitimer(ITIMER_REAL, &dsi->timer, NULL) < 0)) {
+ afp_dsi_die(1);
+ }
+
+ /* get stuck here until the end */
+ while ((cmd = dsi_receive(dsi))) {
+ child.tickle = 0;
+
+ if (cmd == DSIFUNC_TICKLE) {
+ /* so we don't get killed on the client side. */
+ if (child.flags & CHILD_DIE)
+ dsi_tickle(dsi);
+ continue;
+ } else if (!(child.flags & CHILD_DIE)) /* reset tickle timer */
+ setitimer(ITIMER_REAL, &dsi->timer, NULL);
+
+ switch(cmd) {
+ case DSIFUNC_CLOSE:
+ afp_dsi_close(obj);
+ syslog(LOG_INFO, "done");
+ if (obj->options.flags & OPTION_DEBUG )
+ printf("done\n");
+ return;
+ break;
+
+ case DSIFUNC_CMD:
+#ifdef AFS
+ if ( writtenfork ) {
+ if ( flushfork( writtenfork ) < 0 ) {
+ syslog( LOG_ERR, "main flushfork: %m" );
+ }
+ writtenfork = NULL;
+ }
+#endif AFS
+
+ function = (u_char) dsi->commands[0];
+ if (obj->options.flags & OPTION_DEBUG ) {
+ printf("command: %d\n", function);
+ bprint(dsi->commands, dsi->cmdlen);
+ }
+
+ /* send off an afp command. in a couple cases, we take advantage
+ * of the fact that we're a stream-based protocol. */
+ if (afp_switch[function]) {
+ dsi->datalen = DSI_DATASIZ;
+ child.flags |= CHILD_RUNNING;
+ err = (*afp_switch[function])(obj,
+ dsi->commands, dsi->cmdlen,
+ dsi->data, &dsi->datalen);
+ child.flags &= ~CHILD_RUNNING;
+ } else {
+ syslog(LOG_ERR, "bad function %X", function);
+ dsi->datalen = 0;
+ err = AFPERR_NOOP;
+ }
+
+ /* single shot toggle that gets set by dsi_readinit. */
+ if (dsi->noreply) {
+ dsi->noreply = 0;
+ break;
+ }
+
+ if (obj->options.flags & OPTION_DEBUG ) {
+ printf( "reply: %d, %d\n", err, dsi->clientID);
+ bprint(dsi->data, dsi->datalen);
+ }
+
+ if (!dsi_cmdreply(dsi, err)) {
+ syslog(LOG_ERR, "dsi_cmdreply(%d): %m", dsi->socket);
+ afp_dsi_die(1);
+ }
+ break;
+
+ case DSIFUNC_WRITE: /* FPWrite and FPAddIcon */
+ function = (u_char) dsi->commands[0];
+ if ( obj->options.flags & OPTION_DEBUG ) {
+ printf("(write) command: %d, %ld\n", function, dsi->cmdlen);
+ bprint(dsi->commands, dsi->cmdlen);
+ }
+
+ if ( afp_switch[ function ] != NULL ) {
+ dsi->datalen = DSI_DATASIZ;
+ child.flags |= CHILD_RUNNING;
+ err = (*afp_switch[function])(obj, dsi->commands, dsi->cmdlen,
+ dsi->data, &dsi->datalen);
+ child.flags &= ~CHILD_RUNNING;
+ } else {
+ syslog( LOG_ERR, "(write) bad function %x", function);
+ dsi->datalen = 0;
+ err = AFPERR_NOOP;
+ }
+
+ if (obj->options.flags & OPTION_DEBUG ) {
+ printf( "(write) reply code: %d, %d\n", err, dsi->clientID);
+ bprint(dsi->data, dsi->datalen);
+ }
+
+ if (!dsi_wrtreply(dsi, err)) {
+ syslog( LOG_ERR, "dsi_wrtreply: %m" );
+ afp_dsi_die(1);
+ }
+ break;
+
+ case DSIFUNC_ATTN: /* attention replies */
+ continue;
+ break;
+
+ /* error. this usually implies a mismatch of some kind
+ * between server and client. if things are correct,
+ * we need to flush the rest of the packet if necessary. */
+ default:
+ syslog(LOG_INFO,"afp_dsi: spurious command %d", cmd);
+ dsi_writeinit(dsi, dsi->data, DSI_DATASIZ);
+ dsi_writeflush(dsi);
+ break;
+ }
+
+ if ( obj->options.flags & OPTION_DEBUG ) {
+#ifdef notdef
+ pdesc( stdout );
+#endif notdef
+ of_pforkdesc( stdout );
+ fflush( stdout );
+ }
+ }
+
+ /* error */
+ afp_dsi_die(1);
+}
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * modified from main.c. this handles afp options.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <syslog.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <atalk/paths.h>
+#include "globals.h"
+#include "status.h"
+#include "auth.h"
+
+#include <atalk/compat.h>
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define OPTIONS "dn:f:s:uc:g:P:ptDS:TL:F:U:I"
+#define LENGTH 512
+
+/* return an option. this uses an internal array, so it's necessary
+ * to duplicate it if you want to hold it for long. this is probably
+ * non-optimal. */
+static char *getoption(char *buf, const char *option)
+{
+ static char string[LENGTH + 1];
+ char *end;
+ int len;
+
+ if (option && (buf = strstr(buf, option)))
+ buf = strpbrk(buf, " \t");
+
+ while (buf && isspace(*buf))
+ buf++;
+
+ if (!buf)
+ return NULL;
+
+ /* search for any quoted stuff */
+ if (*buf == '"' && (end = strchr(buf + 1, '"'))) {
+ buf++;
+ len = MIN(end - buf, LENGTH);
+ } else if ((end = strpbrk(buf, " \t\n"))) /* option or eoln */
+ len = MIN(end - buf, LENGTH);
+ else
+ len = MIN(strlen(buf), LENGTH);
+
+ strncpy(string, buf, len);
+ string[len] = '\0';
+ return string;
+}
+
+/* get rid of any allocated afp_option buffers. */
+void afp_options_free(struct afp_options *opt,
+ const struct afp_options *save)
+{
+ if (opt->defaultvol && (opt->defaultvol != save->defaultvol))
+ free(opt->defaultvol);
+ if (opt->systemvol && (opt->systemvol != save->systemvol))
+ free(opt->systemvol);
+ if (opt->loginmesg && (opt->loginmesg != save->loginmesg))
+ free(opt->loginmesg);
+ if (opt->guest && (opt->guest != save->guest))
+ free(opt->guest);
+ if (opt->server && (opt->server != save->server))
+ free(opt->server);
+ if (opt->ipaddr && (opt->ipaddr != save->ipaddr))
+ free(opt->ipaddr);
+ if (opt->fqdn && (opt->fqdn != save->fqdn))
+ free(opt->fqdn);
+ if (opt->uampath && (opt->uampath != save->uampath))
+ free(opt->uampath);
+ if (opt->uamlist && (opt->uamlist != save->uamlist))
+ free(opt->uamlist);
+ if (opt->nlspath && (opt->nlspath != save->nlspath))
+ free(opt->nlspath);
+ if (opt->passwdfile && (opt->passwdfile != save->passwdfile))
+ free(opt->passwdfile);
+}
+
+/* initialize options */
+void afp_options_init(struct afp_options *options)
+{
+ memset(options, 0, sizeof(struct afp_options));
+ options->connections = 20;
+ options->pidfile = _PATH_AFPDLOCK;
+ options->defaultvol = _PATH_AFPDDEFVOL;
+ options->systemvol = _PATH_AFPDSYSVOL;
+ options->configfile = _PATH_AFPDCONF;
+ options->nlspath = _PATH_AFPDNLSPATH;
+ options->uampath = _PATH_AFPDUAMPATH;
+ options->uamlist = "uams_guest.so,uams_clrtxt.so,uams_dhx.so";
+ options->guest = "nobody";
+ options->loginmesg = "";
+ options->transports = AFPTRANS_ALL;
+ options->passwdfile = _PATH_AFPDPWFILE;
+ options->tickleval = 30;
+}
+
+/* parse an afpd.conf line. i'm doing it this way because it's
+ * easy. it is, however, massively hokey. sample afpd.conf:
+ * server:AFPServer@zone -loginmesg "blah blah blah" -nodsi
+ * "private machine"@zone2 -noguest -port 11012
+ * server2 -nocleartxt -nodsi
+ *
+ * NOTE: this ignores unknown options
+ */
+int afp_options_parseline(char *buf, struct afp_options *options)
+{
+ char *c, *opt;
+
+ /* handle server */
+ if (*buf != '-' && (c = getoption(buf, NULL)) && (opt = strdup(c)))
+ options->server = opt;
+
+ /* parse toggles */
+ if (strstr(buf, " -nodebug"))
+ options->flags &= ~OPTION_DEBUG;
+
+ if (strstr(buf, " -nouservolfirst"))
+ options->flags &= ~OPTION_USERVOLFIRST;
+ if (strstr(buf, " -uservolfirst"))
+ options->flags |= OPTION_USERVOLFIRST;
+ if (strstr(buf, " -nouservol"))
+ options->flags |= OPTION_NOUSERVOL;
+ if (strstr(buf, " -uservol"))
+ options->flags &= ~OPTION_NOUSERVOL;
+ if (strstr(buf, " -proxy"))
+ options->flags |= OPTION_PROXY;
+ if (strstr(buf, " -noicon"))
+ options->flags &= ~OPTION_CUSTOMICON;
+ if (strstr(buf, " -icon"))
+ options->flags |= OPTION_CUSTOMICON;
+
+ /* passwd bits */
+ if (strstr(buf, " -nosavepassword"))
+ options->passwdbits |= PASSWD_NOSAVE;
+ if (strstr(buf, " -savepassword"))
+ options->passwdbits &= ~PASSWD_NOSAVE;
+ if (strstr(buf, " -nosetpassword"))
+ options->passwdbits &= ~PASSWD_SET;
+ if (strstr(buf, " -setpassword"))
+ options->passwdbits |= PASSWD_SET;
+
+ /* transports */
+ if (strstr(buf, " -transall"))
+ options->transports = AFPTRANS_ALL;
+ if (strstr(buf, " -notransall"))
+ options->transports = AFPTRANS_NONE;
+ if (strstr(buf, " -tcp"))
+ options->transports |= AFPTRANS_TCP;
+ if (strstr(buf, " -notcp"))
+ options->transports &= ~AFPTRANS_TCP;
+ if (strstr(buf, " -ddp"))
+ options->transports |= AFPTRANS_DDP;
+ if (strstr(buf, " -noddp"))
+ options->transports &= ~AFPTRANS_DDP;
+
+ /* figure out options w/ values. currently, this will ignore the setting
+ * if memory is lacking. */
+ if ((c = getoption(buf, "-defaultvol")) && (opt = strdup(c)))
+ options->defaultvol = opt;
+ if ((c = getoption(buf, "-systemvol")) && (opt = strdup(c)))
+ options->systemvol = opt;
+ if ((c = getoption(buf, "-loginmesg")) && (opt = strdup(c)))
+ options->loginmesg = opt;
+ if ((c = getoption(buf, "-guestname")) && (opt = strdup(c)))
+ options->guest = opt;
+ if ((c = getoption(buf, "-passwdfile")) && (opt = strdup(c)))
+ options->passwdfile = opt;
+ if ((c = getoption(buf, "-passwdminlen")))
+ options->passwdminlen = MIN(1, atoi(c));
+ if ((c = getoption(buf, "-loginmaxfail")))
+ options->loginmaxfail = atoi(c);
+ if ((c = getoption(buf, "-tickleval")))
+ options->tickleval = atoi(c);
+
+ if (c = getoption(buf, "-server_quantum"))
+ options->server_quantum = strtoul(c, NULL, 0);
+
+
+ if ((c = getoption(buf, "-uampath")) && (opt = strdup(c)))
+ options->uampath = opt;
+ if ((c = getoption(buf, "-uamlist")) && (opt = strdup(c)))
+ options->uamlist = opt;
+ if ((c = getoption(buf, "-nlspath")) && (opt = strdup(c)))
+ options->nlspath = opt;
+
+ if (c = getoption(buf, "-ipaddr")) {
+ struct in_addr inaddr;
+ if (inet_aton(c, &inaddr) && (opt = strdup(c))) {
+ if (!gethostbyaddr((const char *) &inaddr, sizeof(inaddr), AF_INET))
+ syslog(LOG_INFO, "WARNING: can't find %s\n", opt);
+ options->ipaddr = opt;
+ }
+ }
+
+ if ((c = getoption(buf, "-port")))
+ options->port = atoi(c);
+ if (c = getoption(buf, "-ddpaddr"))
+ atalk_aton(c, &options->ddpaddr);
+
+ /* do a little checking for the domain name. */
+ if (c = getoption(buf, "-fqdn")) {
+ char *p = strchr(c, ':');
+ if (p)
+ *p = '\0';
+ if (gethostbyname(c)) {
+ if (p)
+ *p = ':';
+ if (opt = strdup(c))
+ options->fqdn = opt;
+ }
+ }
+
+ return 1;
+}
+
+int afp_options_parse(int ac, char **av, struct afp_options *options)
+{
+ extern char *optarg;
+ extern int optind;
+
+ char *p;
+ int c, err = 0;
+
+ if (gethostname(options->hostname, sizeof(options->hostname )) < 0 ) {
+ perror( "gethostname" );
+ return 0;
+ }
+ if (( p = strchr(options->hostname, '.' )) != 0 ) {
+ *p = '\0';
+ }
+
+ if (( p = strrchr( av[ 0 ], '/' )) == NULL ) {
+ p = av[ 0 ];
+ } else {
+ p++;
+ }
+
+ while (( c = getopt( ac, av, OPTIONS )) != EOF ) {
+ switch ( c ) {
+ case 'd' :
+ options->flags |= OPTION_DEBUG;
+ break;
+ case 'n' :
+ options->server = optarg;
+ break;
+ case 'f' :
+ options->defaultvol = optarg;
+ break;
+ case 's' :
+ options->systemvol = optarg;
+ break;
+ case 'u' :
+ options->flags |= OPTION_USERVOLFIRST;
+ break;
+ case 'c' :
+ options->connections = atoi( optarg );
+ break;
+ case 'g' :
+ options->guest = optarg;
+ break;
+
+ case 'P' :
+ options->pidfile = optarg;
+ break;
+
+ case 'p':
+ options->passwdbits |= PASSWD_NOSAVE;
+ break;
+ case 't':
+ options->passwdbits |= PASSWD_SET;
+ break;
+
+ case 'D':
+ options->transports &= ~AFPTRANS_DDP;
+ break;
+ case 'S':
+ options->port = atoi(optarg);
+ break;
+ case 'T':
+ options->transports &= ~AFPTRANS_TCP;
+ break;
+ case 'L':
+ options->loginmesg = optarg;
+ break;
+ case 'F':
+ options->configfile = optarg;
+ break;
+ case 'U':
+ options->uamlist = optarg;
+ break;
+ case 'I':
+ options->flags |= OPTION_CUSTOMICON;
+ default :
+ err++;
+ }
+ }
+ if ( err || optind != ac ) {
+ fprintf( stderr,
+ "Usage:\t%s [ -dpDTIt ] [ -n nbpname ] [ -f defvols ] \
+[ -P pidfile ] [ -s sysvols ] \n", p );
+ fprintf( stderr,
+ "\t[ -u ] [ -c maxconn ] [ -g guest ] \
+[ -S port ] [ -L loginmesg ] [ -F configfile ] [ -U uamlist ]\n" );
+ return 0;
+ }
+
+#ifdef ultrix
+ openlog( p, LOG_PID );
+#else ultrix
+ openlog( p, LOG_NDELAY|LOG_PID, LOG_DAEMON );
+#endif ultrix
+
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifdef AFS
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/syslog.h>
+#include <netatalk/endian.h>
+#include <netinet/in.h>
+#include <afs/venus.h>
+#include <afs/afsint.h>
+#include <atalk/afp.h>
+#include <unistd.h>
+
+#include "globals.h"
+#include "directory.h"
+#include "volume.h"
+#include "misc.h"
+
+afs_getvolspace( vol, bfree, btotal, bsize )
+ struct vol *vol;
+ VolSpace *bfree, *btotal;
+ u_int32_t *bsize;
+{
+ struct ViceIoctl vi;
+ struct VolumeStatus *vs;
+ char venuspace[ sizeof( struct VolumeStatus ) + 3 ];
+ int total, free;
+
+ vi.in_size = 0;
+ vi.out_size = sizeof( venuspace );
+ vi.out = venuspace;
+ if ( pioctl( vol->v_path, VIOCGETVOLSTAT, &vi, 1 ) < 0 ) {
+ return( AFPERR_PARAM );
+ }
+
+ vs = (struct VolumeStatus *)venuspace;
+
+ if ( vs->PartBlocksAvail > 0 ) {
+ if ( vs->MaxQuota != 0 ) {
+#define min(x,y) (((x)<(y))?(x):(y))
+ free = min( vs->MaxQuota - vs->BlocksInUse, vs->PartBlocksAvail );
+ } else {
+ free = vs->PartBlocksAvail;
+ }
+ } else {
+ free = 0;
+ }
+
+ if ( vs->MaxQuota != 0 ) {
+ total = free + vs->BlocksInUse;
+ } else {
+ total = vs->PartMaxBlocks;
+ }
+
+ *bsize = 1024;
+ *bfree = (VolSpace) free * 1024;
+ *btotal = (VolSpace) total * 1024;
+
+ return( AFP_OK );
+}
+
+afp_getdiracl(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct ViceIoctl vi;
+ struct vol *vol;
+ struct dir *dir;
+ char *path;
+ u_int32_t did;
+ u_int16_t vid;
+
+ ibuf += 2;
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( short );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_PARAM );
+ }
+
+ memcpy( &did, ibuf, sizeof( did ));
+ ibuf += sizeof( int );
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_NOOBJ );
+ }
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_NOOBJ );
+ }
+ if ( *path != '\0' ) {
+ *rbuflen = 0;
+ return( AFPERR_BITMAP );
+ }
+
+ vi.in_size = 0;
+ vi.out_size = *rbuflen;
+ vi.out = rbuf;
+ if ( pioctl( ".", VIOCGETAL, &vi, 1 ) < 0 ) {
+ *rbuflen = 0;
+ return( AFPERR_PARAM );
+ }
+ *rbuflen = strlen( vi.out ) + 1;
+ return( AFP_OK );
+}
+
+/*
+ * Calculate the mode for a directory in AFS. First, make sure the
+ * directory is in AFS. Could probably use something less heavy than
+ * VIOCGETAL. If the directory is on AFS, use access() calls to
+ * estimate permission, a la mdw.
+ */
+afsmode( path, ma, dir )
+ char *path;
+ struct maccess *ma;
+ struct dir *dir;
+{
+ struct ViceIoctl vi;
+ char buf[ 1024 ];
+
+ if (( dir->d_flags & DIRF_FSMASK ) == DIRF_NOFS ) {
+ vi.in_size = 0;
+ vi.out_size = sizeof( buf );
+ vi.out = buf;
+ if ( pioctl( path, VIOCGETAL, &vi, 1 ) < 0 ) {
+ dir->d_flags |= DIRF_UFS;
+ } else {
+ dir->d_flags |= DIRF_AFS;
+ }
+ }
+
+ if (( dir->d_flags & DIRF_FSMASK ) != DIRF_AFS ) {
+ return;
+ }
+
+ if ( access( path, R_OK|W_OK|X_OK ) == 0 ) {
+ ma->ma_user = AR_UREAD|AR_UWRITE|AR_USEARCH|AR_UOWN;
+ ma->ma_owner = AR_UREAD|AR_UWRITE|AR_USEARCH;
+ } else if ( access( path, R_OK|X_OK ) == 0 ) {
+ ma->ma_user = AR_UREAD|AR_USEARCH;
+ ma->ma_owner = AR_UREAD|AR_USEARCH;
+ } else {
+ ma->ma_user = ma->ma_owner = 0;
+ if ( access( path, R_OK ) == 0 ) {
+ ma->ma_user |= AR_UREAD;
+ ma->ma_owner |= AR_UREAD;
+ }
+ if ( access( path, X_OK ) == 0 ) {
+ ma->ma_user |= AR_USEARCH;
+ ma->ma_owner |= AR_USEARCH;
+ }
+ if ( access( path, W_OK ) == 0 ) {
+ ma->ma_user |= AR_UWRITE|AR_UOWN;
+ ma->ma_owner |= AR_UWRITE;
+ }
+ }
+
+ return;
+}
+
+extern struct dir *curdir;
+/*
+ * cmd | 0 | vid | did | pathtype | pathname | 0 | acl
+ */
+afp_setdiracl(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct ViceIoctl vi;
+ struct vol *vol;
+ struct dir *dir;
+ char *path, *iend;
+ u_int32_t did;
+ u_int16_t vid;
+
+ *rbuflen = 0;
+ iend = ibuf + ibuflen;
+ ibuf += 2;
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( short );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_PARAM );
+ }
+
+ memcpy( &did, ibuf, sizeof( did ));
+ ibuf += sizeof( int );
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_NOOBJ );
+ }
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_NOOBJ );
+ }
+ if ( *path != '\0' ) {
+ *rbuflen = 0;
+ return( AFPERR_BITMAP );
+ }
+
+ if ((int)ibuf & 1 ) {
+ ibuf++;
+ }
+
+ vi.in_size = iend - ibuf;
+ vi.in = ibuf;
+ vi.out_size = 0;
+
+ if ( pioctl( ".", VIOCSETAL, &vi, 1 ) < 0 ) {
+ *rbuflen = 0;
+ return( AFPERR_PARAM );
+ }
+ pioctl( ".AppleDouble", VIOCSETAL, &vi, 1 );
+ if ( curdir->d_did == DIRDID_ROOT ) {
+ pioctl( ".AppleDesktop", VIOCSETAL, &vi, 1 );
+ }
+
+ return( AFP_OK );
+}
+
+
+#ifdef UAM_AFSKRB
+
+#include <krb.h>
+#include <des.h>
+#include <afs/kauth.h>
+#include <afs/kautils.h>
+
+extern C_Block seskey;
+extern Key_schedule seskeysched;
+
+afp_afschangepw(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ char name[ MAXKTCNAMELEN ], instance[ MAXKTCNAMELEN ];
+ char realm[ MAXKTCREALMLEN ];
+ char oldpw[ 9 ], newpw[ 9 ];
+ int len, rc;
+ u_int16_t clen;
+ struct ktc_encryptionKey oldkey, newkey;
+ struct ktc_token adtok;
+ struct ubik_client *conn;
+
+ *rbuflen = 0;
+ ++ibuf;
+ len = (unsigned char) *ibuf++;
+ ibuf[ len ] = '\0';
+ *name = *instance = *realm = '\0';
+ ka_ParseLoginName( ibuf, name, instance, realm );
+ ucase( realm );
+ if ( *realm == '\0' ) {
+ if ( krb_get_lrealm( realm, 1 ) != KSUCCESS ) {
+ syslog( LOG_ERR, "krb_get_lrealm failed" );
+ return( AFPERR_BADUAM );
+ }
+ }
+
+ if ( strlen( name ) < 2 || strlen( name ) > 18 ) {
+ return( AFPERR_PARAM );
+ }
+ ibuf += len;
+
+ memcpy( &clen, ibuf, sizeof( clen ));
+ clen = ntohs( clen );
+ if ( clen % 8 != 0 ) {
+ return( AFPERR_PARAM );
+ }
+
+ ibuf += sizeof( short );
+ pcbc_encrypt((C_Block *)ibuf, (C_Block *)ibuf,
+ clen, seskeysched, seskey, DES_DECRYPT );
+
+ len = (unsigned char) *ibuf++;
+ if ( len > 8 ) {
+ return( AFPERR_PARAM );
+ }
+ memset( oldpw, 0, sizeof( oldpw ));
+ memcpy( oldpw, ibuf, len );
+ ibuf += len;
+ oldpw[ len ] = '\0';
+
+ len = (unsigned char) *ibuf++;
+ if ( len > 8 ) {
+ return( AFPERR_PARAM );
+ }
+ memset( newpw, 0, sizeof( newpw ));
+ memcpy( newpw, ibuf, len );
+ ibuf += len;
+ newpw[ len ] = '\0';
+
+ syslog( LOG_INFO,
+ "changing password for <%s>.<%s>@<%s>", name, instance, realm );
+
+ ka_StringToKey( oldpw, realm, &oldkey );
+ memset( oldpw, 0, sizeof( oldpw ));
+ ka_StringToKey( newpw, realm, &newkey );
+ memset( newpw, 0, sizeof( newpw ));
+
+ rc = ka_GetAdminToken( name, instance, realm, &oldkey, 60, &adtok, 0 );
+ memset( &oldkey, 0, sizeof( oldkey ));
+ switch ( rc ) {
+ case 0:
+ break;
+ case KABADREQUEST:
+ memset( &newkey, 0, sizeof( newkey ));
+ return( AFPERR_NOTAUTH );
+ default:
+ memset( &newkey, 0, sizeof( newkey ));
+ return( AFPERR_BADUAM );
+ }
+ if ( ka_AuthServerConn( realm, KA_MAINTENANCE_SERVICE, &adtok, &conn )
+ != 0 ) {
+ memset( &newkey, 0, sizeof( newkey ));
+ return( AFPERR_BADUAM );
+ }
+
+ rc = ka_ChangePassword( name, instance, conn, 0, &newkey );
+ memset( &newkey, 0, sizeof( newkey ));
+ if ( rc != 0 ) {
+ return( AFPERR_BADUAM );
+ }
+
+ syslog( LOG_DEBUG, "password changed succeeded" );
+ return( AFP_OK );
+}
+
+#endif UAM_AFSKRB
+#endif AFS
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <errno.h>
+
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/afp.h>
+
+#include "volume.h"
+#include "globals.h"
+#include "directory.h"
+#include "file.h"
+#include "desktop.h"
+
+static struct savedt sa = { { 0, 0, 0, 0 }, -1, 0 };
+
+static __inline__ int pathcmp( p, plen, q, qlen )
+ char *p;
+ int plen;
+ char *q;
+ int qlen;
+{
+ return (( plen == qlen && memcmp( p, q, plen ) == 0 ) ? 0 : 1 );
+}
+
+static int applopen( vol, creator, flags, mode )
+ struct vol *vol;
+ u_char creator[ 4 ];
+{
+ char *dtf, *adt, *adts;
+
+ if ( sa.sdt_fd != -1 ) {
+ if ( !(flags & ( O_RDWR | O_WRONLY )) &&
+ memcmp( sa.sdt_creator, creator, sizeof( CreatorType )) == 0 &&
+ sa.sdt_vid == vol->v_vid ) {
+ return( AFP_OK );
+ }
+ close( sa.sdt_fd );
+ sa.sdt_fd = -1;
+ }
+
+ dtf = dtfile( vol, creator, ".appl" );
+
+ if (( sa.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
+ if ( errno == ENOENT && ( flags & O_CREAT )) {
+ if (( adts = strrchr( dtf, '/' )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+ *adts = '\0';
+ if (( adt = strrchr( dtf, '/' )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+ *adt = '\0';
+ (void) ad_mkdir( dtf, DIRBITS | 0777 );
+ *adt = '/';
+ (void) ad_mkdir( dtf, DIRBITS | 0777 );
+ *adts = '/';
+
+ if (( sa.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
+ return( AFPERR_PARAM );
+ }
+ } else {
+ return( AFPERR_PARAM );
+ }
+ }
+ memcpy( sa.sdt_creator, creator, sizeof( CreatorType ));
+ sa.sdt_vid = vol->v_vid;
+ sa.sdt_index = 0;
+ return( AFP_OK );
+}
+
+/*
+ * copy appls to new file, deleting any matching (old) appl entries
+ */
+static int copyapplfile( sfd, dfd, mpath, mplen )
+ int sfd;
+ int dfd;
+ char *mpath;
+ u_short mplen;
+{
+ int cc;
+ char *p;
+ u_int16_t len;
+ u_char appltag[ 4 ];
+ char buf[ MAXPATHLEN ];
+
+ while (( cc = read( sfd, buf, sizeof(appltag) + sizeof( u_short ))) > 0 ) {
+ p = buf + sizeof(appltag);
+ memcpy( &len, p, sizeof(len));
+ len = ntohs( len );
+ p += sizeof( len );
+ if (( cc = read( sa.sdt_fd, p, len )) < len ) {
+ break;
+ }
+ if ( pathcmp( mpath, mplen, p, len ) != 0 ) {
+ p += len;
+ if ( write( dfd, buf, p - buf ) != p - buf ) {
+ cc = -1;
+ break;
+ }
+ }
+ }
+ return( cc );
+}
+
+/*
+ * build mac. path (backwards) by traversing the directory tree
+ *
+ * The old way: dir and path refer to an app, path is a mac format
+ * pathname. makemacpath() builds something that looks like a cname,
+ * but uses upaths instead of mac format paths.
+ *
+ * The new way: dir and path refer to an app, path is a mac format
+ * pathname. makemacpath() builds a cname.
+ *
+ * See afp_getappl() for the backward compatiblity code.
+ */
+static char *
+makemacpath( mpath, mpathlen, dir, path )
+ char *mpath;
+ int mpathlen;
+ struct dir *dir;
+ char *path;
+{
+ char *p;
+
+ p = mpath + mpathlen;
+ p -= strlen( path );
+ strncpy( p, path, strlen( path ));
+
+ while ( dir->d_parent != NULL ) {
+ p -= strlen( dir->d_name ) + 1;
+ strcpy( p, dir->d_name );
+ dir = dir->d_parent;
+ }
+ return( p );
+}
+
+
+int afp_addappl(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct vol *vol;
+ struct dir *dir;
+ int tfd, cc;
+ u_int32_t did;
+ u_int16_t vid, mplen;
+ char *path, *dtf, *p, *mp;
+ u_char creator[ 4 ];
+ u_char appltag[ 4 ];
+ char *mpath, *tempfile;
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy( &did, ibuf, sizeof( did ));
+ ibuf += sizeof( did );
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ memcpy( creator, ibuf, sizeof( creator ));
+ ibuf += sizeof( creator );
+
+ memcpy( appltag, ibuf, sizeof( appltag ));
+ ibuf += sizeof( appltag );
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+ if ( *path == '\0' ) {
+ return( AFPERR_BADTYPE );
+ }
+
+ if ( applopen( vol, creator, O_RDWR|O_CREAT, 0666 ) != AFP_OK ) {
+ return( AFPERR_PARAM );
+ }
+ if ( lseek( sa.sdt_fd, 0L, SEEK_SET ) < 0 ) {
+ return( AFPERR_PARAM );
+ }
+ dtf = dtfile( vol, creator, ".appl.temp" );
+ tempfile = obj->oldtmp;
+ strcpy( tempfile, dtf );
+ if (( tfd = open( tempfile, O_RDWR|O_CREAT, 0666 )) < 0 ) {
+ return( AFPERR_PARAM );
+ }
+ mpath = obj->newtmp;
+ mp = makemacpath( mpath, AFPOBJ_TMPSIZ, curdir, path );
+ mplen = mpath + AFPOBJ_TMPSIZ - mp;
+
+ /* write the new appl entry at start of temporary file */
+ p = mp - sizeof( u_short );
+ mplen = htons( mplen );
+ memcpy( p, &mplen, sizeof( mplen ));
+ mplen = ntohs( mplen );
+ p -= sizeof( appltag );
+ memcpy(p, appltag, sizeof( appltag ));
+ cc = mpath + AFPOBJ_TMPSIZ - p;
+ if ( write( tfd, p, cc ) != cc ) {
+ unlink( tempfile );
+ return( AFPERR_PARAM );
+ }
+ cc = copyapplfile( sa.sdt_fd, tfd, mp, mplen );
+ close( tfd );
+ close( sa.sdt_fd );
+ sa.sdt_fd = -1;
+
+ if ( cc < 0 ) {
+ unlink( tempfile );
+ return( AFPERR_PARAM );
+ }
+ if ( rename( tempfile, dtfile( vol, creator, ".appl" )) < 0 ) {
+ return( AFPERR_PARAM );
+ }
+ return( AFP_OK );
+}
+
+int afp_rmvappl(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct vol *vol;
+ struct dir *dir;
+ int tfd, cc;
+ u_int32_t did;
+ u_int16_t vid, mplen;
+ char *path, *dtf, *mp;
+ u_char creator[ 4 ];
+ char *tempfile, *mpath;
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy( &did, ibuf, sizeof( did ));
+ ibuf += sizeof( did );
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ memcpy( creator, ibuf, sizeof( creator ));
+ ibuf += sizeof( creator );
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+ if ( *path == '.' ) {
+ return( AFPERR_BADTYPE );
+ }
+
+ if ( applopen( vol, creator, O_RDWR, 0666 ) != AFP_OK ) {
+ return( AFPERR_NOOBJ );
+ }
+ if ( lseek( sa.sdt_fd, 0L, SEEK_SET ) < 0 ) {
+ return( AFPERR_PARAM );
+ }
+ dtf = dtfile( vol, creator, ".appl.temp" );
+ tempfile = obj->oldtmp;
+ strcpy( tempfile, dtf );
+ if (( tfd = open( tempfile, O_RDWR|O_CREAT, 0666 )) < 0 ) {
+ return( AFPERR_PARAM );
+ }
+ mpath = obj->newtmp;
+ mp = makemacpath( mpath, AFPOBJ_TMPSIZ, curdir, path );
+ mplen = mpath + AFPOBJ_TMPSIZ - mp;
+ cc = copyapplfile( sa.sdt_fd, tfd, mp, mplen );
+ close( tfd );
+ close( sa.sdt_fd );
+ sa.sdt_fd = -1;
+
+ if ( cc < 0 ) {
+ unlink( tempfile );
+ return( AFPERR_PARAM );
+ }
+ if ( rename( tempfile, dtfile( vol, creator, ".appl" )) < 0 ) {
+ return( AFPERR_PARAM );
+ }
+ return( AFP_OK );
+}
+
+int afp_getappl(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct stat st;
+ struct vol *vol;
+ char *p, *q;
+ int cc, buflen;
+ u_int16_t vid, aindex, bitmap, len;
+ u_char creator[ 4 ];
+ u_char appltag[ 4 ];
+ char *buf, *cbuf;
+
+ ibuf += 2;
+
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_PARAM );
+ }
+
+ memcpy( creator, ibuf, sizeof( creator ));
+ ibuf += sizeof( creator );
+
+ memcpy( &aindex, ibuf, sizeof( aindex ));
+ ibuf += sizeof( aindex );
+ aindex = ntohs( aindex );
+ if ( aindex ) { /* index 0 == index 1 */
+ --aindex;
+ }
+
+ memcpy( &bitmap, ibuf, sizeof( bitmap ));
+ bitmap = ntohs( bitmap );
+ ibuf += sizeof( bitmap );
+
+ if ( applopen( vol, creator, O_RDONLY, 0666 ) != AFP_OK ) {
+ *rbuflen = 0;
+ return( AFPERR_NOITEM );
+ }
+ if ( aindex < sa.sdt_index ) {
+ if ( lseek( sa.sdt_fd, 0L, SEEK_SET ) < 0 ) {
+ *rbuflen = 0;
+ return( AFPERR_PARAM );
+ }
+ sa.sdt_index = 0;
+ }
+
+ /* position to correct spot within appl file */
+ buf = obj->oldtmp;
+ while (( cc = read( sa.sdt_fd, buf, sizeof( appltag )
+ + sizeof( u_short ))) > 0 ) {
+ p = buf + sizeof( appltag );
+ memcpy( &len, p, sizeof( len ));
+ len = ntohs( len );
+ p += sizeof( u_short );
+ if (( cc = read( sa.sdt_fd, p, len )) < len ) {
+ break;
+ }
+ if ( sa.sdt_index == aindex ) {
+ break;
+ }
+ sa.sdt_index++;
+ }
+ if ( cc <= 0 || sa.sdt_index != aindex ) {
+ *rbuflen = 0;
+ return( AFPERR_NOITEM );
+ }
+ sa.sdt_index++;
+
+#ifdef APPLCNAME
+ /*
+ * Check to see if this APPL mapping has an mpath or a upath. If
+ * there are any ':'s in the name, it is a upath and must be converted
+ * to an mpath. Hopefully, this code will go away.
+ */
+ {
+#define hextoint( c ) ( isdigit( c ) ? c - '0' : c + 10 - 'a' )
+#define islxdigit(x) (!isupper(x)&&isxdigit(x))
+
+ char utomname[ MAXPATHLEN + 1];
+ char *u, *m;
+ int i, h;
+
+ u = p;
+ m = utomname;
+ i = len;
+ while ( i ) {
+ if ( *u == ':' && *(u+1) != '\0' && islxdigit( *(u+1)) &&
+ *(u+2) != '\0' && islxdigit( *(u+2))) {
+ ++u, --i;
+ h = hextoint( *u ) << 4;
+ ++u, --i;
+ h |= hextoint( *u );
+ *m++ = h;
+ } else {
+ *m++ = *u;
+ }
+ ++u, --i;
+ }
+
+ len = m - utomname;
+ p = utomname;
+
+ if ( p[ len - 1 ] == '\0' ) {
+ len--;
+ }
+ }
+#endif APPLCNAME
+
+ /* fake up a cname */
+ cbuf = obj->newtmp;
+ q = cbuf;
+ *q++ = 2; /* long path type */
+ *q++ = (unsigned char)len;
+ memcpy( q, p, len );
+ q = cbuf;
+
+ if (( p = cname( vol, vol->v_dir, &q )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_NOITEM );
+ }
+
+ if ( stat( mtoupath(vol, p), &st ) < 0 ) {
+ *rbuflen = 0;
+ return( AFPERR_NOITEM );
+ }
+ buflen = *rbuflen - sizeof( bitmap ) - sizeof( appltag );
+ if ( getfilparams(vol, bitmap, p, curdir, &st, rbuf + sizeof( bitmap ) +
+ sizeof( appltag ), &buflen ) != AFP_OK ) {
+ *rbuflen = 0;
+ return( AFPERR_BITMAP );
+ }
+
+ *rbuflen = buflen + sizeof( bitmap ) + sizeof( appltag );
+ bitmap = htons( bitmap );
+ memcpy( rbuf, &bitmap, sizeof( bitmap ));
+ rbuf += sizeof( bitmap );
+ memcpy( rbuf, appltag, sizeof( appltag ));
+ rbuf += sizeof( appltag );
+ return( AFP_OK );
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <netatalk/endian.h>
+#include <atalk/afp.h>
+#include <atalk/compat.h>
+#include <atalk/util.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
+#include <syslog.h>
+
+#include "globals.h"
+#include "auth.h"
+#include "uam_auth.h"
+#include "switch.h"
+#include "status.h"
+
+int afp_version = 11;
+uid_t uuid;
+#if defined( __svr4__ ) && !defined( NGROUPS )
+#define NGROUPS NGROUPS_MAX
+#endif __svr4__ NGROUPS
+#if defined( sun ) && !defined( __svr4__ ) || defined( ultrix )
+int groups[ NGROUPS ];
+#else sun __svr4__ ultrix
+#if defined( __svr4__ ) && !defined( NGROUPS )
+#define NGROUPS NGROUPS_MAX
+#endif __svr4__ NGROUPS
+gid_t groups[ NGROUPS ];
+#endif sun ultrix
+int ngroups;
+
+/*
+ * These numbers are scattered throughout the code.
+ */
+static struct afp_versions afp_versions[] = {
+ { "AFPVersion 1.1", 11 },
+ { "AFPVersion 2.0", 20 },
+ { "AFPVersion 2.1", 21 },
+ { "AFP2.2", 22 }
+};
+
+static struct uam_mod uam_modules = {NULL, NULL, &uam_modules, &uam_modules};
+static struct uam_obj uam_login = {"", "", 0, {{NULL}}, &uam_login,
+ &uam_login};
+static struct uam_obj uam_changepw = {"", "", 0, {{NULL}}, &uam_changepw,
+ &uam_changepw};
+
+static struct uam_obj *afp_uam = NULL;
+
+void status_versions( data )
+ char *data;
+{
+ char *start = data;
+ u_int16_t status;
+ int len, num, i;
+
+ memcpy(&status, start + AFPSTATUS_VERSOFF, sizeof(status));
+ num = sizeof( afp_versions ) / sizeof( afp_versions[ 0 ] );
+ data += ntohs( status );
+ *data++ = num;
+ for ( i = 0; i < num; i++ ) {
+ len = strlen( afp_versions[ i ].av_name );
+ *data++ = len;
+ memcpy( data, afp_versions[ i ].av_name , len );
+ data += len;
+ }
+ status = htons( data - start );
+ memcpy(start + AFPSTATUS_UAMSOFF, &status, sizeof(status));
+}
+
+void status_uams(char *data, const char *authlist)
+{
+ char *start = data;
+ u_int16_t status;
+ struct uam_obj *uams;
+ int len, num = 0;
+
+ memcpy(&status, start + AFPSTATUS_UAMSOFF, sizeof(status));
+ uams = &uam_login;
+ while ((uams = uams->uam_prev) != &uam_login) {
+ if (strstr(authlist, uams->uam_path))
+ num++;
+ }
+
+ data += ntohs( status );
+ *data++ = num;
+ while ((uams = uams->uam_prev) != &uam_login) {
+ if (strstr(authlist, uams->uam_path)) {
+ syslog(LOG_INFO, "uam: \"%s\" available", uams->uam_name);
+ len = strlen( uams->uam_name);
+ *data++ = len;
+ memcpy( data, uams->uam_name, len );
+ data += len;
+ }
+ }
+
+ /* icon offset */
+ status = htons(data - start);
+ memcpy(start + AFPSTATUS_ICONOFF, &status, sizeof(status));
+}
+
+/* handle errors by closing the connection. this is only needed
+ * by the afp_* functions. */
+static int send_reply(const AFPObj *obj, const int err)
+{
+ if ((err == AFP_OK) || (err == AFPERR_AUTHCONT))
+ return err;
+
+ obj->reply(obj->handle, err);
+ obj->exit(0);
+}
+
+static int login(AFPObj *obj, struct passwd *pwd, void (*logout)(void))
+{
+ if ( pwd->pw_uid == 0 ) { /* don't allow root login */
+ syslog( LOG_ERR, "login: root login denied!" );
+ return AFPERR_NOTAUTH;
+ }
+
+ syslog( LOG_INFO, "login %s (uid %d, gid %d)", pwd->pw_name,
+ pwd->pw_uid, pwd->pw_gid );
+ if (initgroups( pwd->pw_name, pwd->pw_gid ) < 0) {
+#ifdef RUN_AS_USER
+ syslog(LOG_INFO, "running with uid %d", geteuid());
+#else
+ syslog(LOG_ERR, "login: %m");
+ return AFPERR_BADUAM;
+#endif
+ }
+
+ if (setegid( pwd->pw_gid ) < 0 || seteuid( pwd->pw_uid ) < 0) {
+ syslog( LOG_ERR, "login: %m" );
+ return AFPERR_BADUAM;
+ }
+
+ if (( ngroups = getgroups( NGROUPS, groups )) < 0 ) {
+ syslog( LOG_ERR, "login: getgroups: %m" );
+ return AFPERR_BADUAM;
+ }
+ uuid = pwd->pw_uid;
+
+ afp_switch = postauth_switch;
+ obj->logout = logout;
+ return( AFP_OK );
+}
+
+int afp_login(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct passwd *pwd = NULL;
+ int len, i, num;
+
+ *rbuflen = 0;
+
+ if ( nologin & 1)
+ return send_reply(obj, AFPERR_SHUTDOWN );
+
+ ibuf++;
+ len = (unsigned char) *ibuf++;
+ num = sizeof( afp_versions ) / sizeof( afp_versions[ 0 ]);
+ for ( i = 0; i < num; i++ ) {
+ if ( strncmp( ibuf, afp_versions[ i ].av_name , len ) == 0 ) {
+ afp_version = afp_versions[ i ].av_number;
+ break;
+ }
+ }
+ if ( i == num ) /* An inappropo version */
+ return send_reply(obj, AFPERR_BADVERS );
+ ibuf += len;
+
+ len = (unsigned char) *ibuf++;
+ if ((afp_uam = auth_uamfind(UAM_SERVER_LOGIN, ibuf, len)) == NULL)
+ return send_reply(obj, AFPERR_BADUAM);
+ ibuf += len;
+
+ i = afp_uam->u.uam_login.login(obj, &pwd, ibuf, ibuflen, rbuf, rbuflen);
+ if (i || !pwd)
+ return send_reply(obj, i);
+
+ return send_reply(obj, login(obj, pwd, afp_uam->u.uam_login.logout));
+}
+
+
+int afp_logincont(obj, ibuf, ibuflen, rbuf, rbuflen)
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct passwd *pwd = NULL;
+ int err;
+
+ if ( afp_uam == NULL || afp_uam->u.uam_login.logincont == NULL ) {
+ *rbuflen = 0;
+ return send_reply(obj, AFPERR_NOTAUTH );
+ }
+
+ ibuf += 2;
+ err = afp_uam->u.uam_login.logincont(obj, &pwd, ibuf, ibuflen,
+ rbuf, rbuflen);
+ if (err || !pwd)
+ return send_reply(obj, err);
+
+ return send_reply(obj, login(obj, pwd, afp_uam->u.uam_login.logout));
+}
+
+
+int afp_logout(obj, ibuf, ibuflen, rbuf, rbuflen)
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ syslog(LOG_INFO, "logout %s", obj->username);
+ obj->exit(0);
+}
+
+
+
+/* change password --
+ * NOTE: an FPLogin must already have completed successfully for this
+ * to work. this also does a little pre-processing before it hands
+ * it off to the uam.
+ */
+int afp_changepw(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ char username[MACFILELEN + 1], *start = ibuf;
+ struct uam_obj *uam;
+ struct passwd *pwd;
+ int len;
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ /* make sure we can deal w/ this uam */
+ len = (unsigned char) *ibuf++;
+ if ((uam = auth_uamfind(UAM_SERVER_CHANGEPW, ibuf, len)) == NULL)
+ return AFPERR_BADUAM;
+
+ ibuf += len;
+ if ((len + 1) & 1) /* pad byte */
+ ibuf++;
+
+ len = (unsigned char) *ibuf++;
+ if ( len > sizeof(username) - 1) {
+ return AFPERR_PARAM;
+ }
+ memcpy(username, ibuf, len);
+ username[ len ] = '\0';
+ ibuf += len;
+ if ((len + 1) & 1) /* pad byte */
+ ibuf++;
+
+ syslog(LOG_INFO, "changing password for <%s>", username);
+
+ if (( pwd = uam_getname( username, sizeof(username))) == NULL )
+ return AFPERR_PARAM;
+
+ /* send it off to the uam. we really don't use ibuflen right now. */
+ ibuflen -= (ibuf - start);
+ len = uam->u.uam_changepw(obj, username, pwd, ibuf, ibuflen,
+ rbuf, rbuflen);
+ syslog(LOG_INFO, "password change %s.",
+ (len == AFPERR_AUTHCONT) ? "continued" :
+ (len ? "failed" : "succeeded"));
+ return len;
+}
+
+
+/* FPGetUserInfo */
+int afp_getuserinfo(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ u_int8_t thisuser;
+ u_int32_t id;
+ u_int16_t bitmap;
+
+ *rbuflen = 0;
+ ibuf++;
+ thisuser = *ibuf++;
+ ibuf += sizeof(id); /* userid is not used in AFP 2.0 */
+ memcpy(&bitmap, ibuf, sizeof(bitmap));
+ bitmap = ntohs(bitmap);
+
+ /* deal with error cases. we don't have to worry about
+ * AFPERR_ACCESS or AFPERR_NOITEM as geteuid and getegid always
+ * succeed. */
+ if (!thisuser)
+ return AFPERR_PARAM;
+ if ((bitmap & USERIBIT_ALL) != bitmap)
+ return AFPERR_BITMAP;
+
+ /* copy the bitmap back to reply buffer */
+ memcpy(rbuf, ibuf, sizeof(bitmap));
+ rbuf += sizeof(bitmap);
+ *rbuflen = sizeof(bitmap);
+
+ /* copy the user/group info */
+ if (bitmap & USERIBIT_USER) {
+ id = htonl(geteuid());
+ memcpy(rbuf, &id, sizeof(id));
+ rbuf += sizeof(id);
+ *rbuflen += sizeof(id);
+ }
+
+ if (bitmap & USERIBIT_GROUP) {
+ id = htonl(getegid());
+ memcpy(rbuf, &id, sizeof(id));
+ rbuf += sizeof(id);
+ *rbuflen += sizeof(id);
+ }
+
+ return AFP_OK;
+}
+
+#define UAM_LIST(type) (((type) == UAM_SERVER_LOGIN) ? &uam_login : \
+ (((type) == UAM_SERVER_CHANGEPW) ? \
+ &uam_changepw : NULL))
+
+/* just do a linked list search. this could be sped up with a hashed
+ * list, but i doubt anyone's going to have enough uams to matter. */
+struct uam_obj *auth_uamfind(const int type, const char *name,
+ const int len)
+{
+ struct uam_obj *prev, *start;
+
+ if (!name || !(start = UAM_LIST(type)))
+ return NULL;
+
+ prev = start;
+ while ((prev = prev->uam_prev) != start)
+ if (strndiacasecmp(prev->uam_name, name, len) == 0)
+ return prev;
+
+ return NULL;
+}
+
+int auth_register(const int type, struct uam_obj *uam)
+{
+ struct uam_obj *start;
+
+ if (!uam || !uam->uam_name || (*uam->uam_name == '\0'))
+ return -1;
+
+ if (!(start = UAM_LIST(type)))
+ return 0; /* silently fail */
+
+ uam_attach(start, uam);
+ return 0;
+}
+
+/* load all of the modules */
+int auth_load(const char *path, const char *list)
+{
+ char name[MAXPATHLEN + 1], buf[MAXPATHLEN + 1], *p;
+ struct uam_mod *mod;
+ struct stat st;
+ int len;
+
+ if (!path || !list || (len = strlen(path)) > sizeof(name) - 2)
+ return -1;
+
+ strncpy(buf, list, sizeof(buf));
+ if ((p = strtok(buf, ",")) == NULL)
+ return -1;
+
+ strcpy(name, path);
+ if (name[len - 1] != '/') {
+ strcat(name, "/");
+ len++;
+ }
+
+ while (p) {
+ strncpy(name + len, p, sizeof(name) - len);
+ if ((stat(name, &st) == 0) && (mod = uam_load(name, p))) {
+ uam_attach(&uam_modules, mod);
+ syslog(LOG_INFO, "uam: %s loaded", p);
+ }
+ p = strtok(NULL, ",");
+ }
+}
+
+/* get rid of all of the uams */
+void auth_unload()
+{
+ struct uam_mod *mod, *prev, *start = &uam_modules;
+
+ prev = start->uam_prev;
+ while ((mod = prev) != start) {
+ prev = prev->uam_prev;
+ uam_detach(mod);
+ uam_unload(mod);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef AFPD_AUTH_H
+#define AFPD_AUTH_H 1
+
+#include <limits.h>
+#include <sys/cdefs.h>
+#include "globals.h"
+
+struct afp_versions {
+ char *av_name;
+ int av_number;
+};
+
+/* for GetUserInfo */
+#define USERIBIT_USER (1 << 0)
+#define USERIBIT_GROUP (1 << 1)
+#define USERIBIT_ALL (USERIBIT_USER | USERIBIT_GROUP)
+
+extern uid_t uuid;
+#if defined( __svr4__ ) && !defined( NGROUPS )
+#define NGROUPS NGROUPS_MAX
+#endif /*__svr4__ NGROUPS*/
+#if defined( sun ) && !defined( __svr4__ ) || defined( ultrix )
+extern int groups[ NGROUPS ];
+#else /*sun __svr4__ ultrix*/
+extern gid_t groups[ NGROUPS ];
+#endif /*sun __svr4__ ultrix*/
+extern int ngroups;
+
+/* FP functions */
+extern int afp_login __P((AFPObj *, char *, int, char *, int *));
+extern int afp_logincont __P((AFPObj *, char *, int, char *, int *));
+extern int afp_changepw __P((AFPObj *, char *, int, char *, int *));
+extern int afp_logout __P((AFPObj *, char *, int, char *, int *));
+extern int afp_getuserinfo __P((AFPObj *, char *, int, char *, int *));
+#endif /* auth.h */
--- /dev/null
+/*
+ * Copyright (c) 2000 Adrian Sun
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * codepage support (based initially on some code from
+ * julian@whistle.com)
+ *
+ * the strategy:
+ * for single-byte maps, the codepage support devolves to a lookup
+ * table for all possible 8-bit values. for double-byte maps,
+ * the first byte is used as a hash index followed by a linked list of
+ * values.
+ *
+ * the badumap specifies illegal characters. these are 8-bit values
+ * with an associated rule field. here are the rules:
+ *
+ *
+ * illegal values: 0 is the only illegal value. no translation will
+ * occur in those cases.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <syslog.h>
+
+#include <netatalk/endian.h>
+
+#include "globals.h"
+#include "volume.h"
+#include "codepage.h"
+
+#define MAPSIZE 256
+
+/* deal with linked lists */
+#define CP_INIT(a) (a)->next = (a)->prev = (a)
+#define CP_ADD(a, b) do { \
+ (b)->next = (a)->next; \
+ (b)->prev = (a); \
+ (a)->next = (b); \
+} while (0)
+#define CP_REMOVE(a) do { \
+ (a)->prev->next = (a)->next; \
+ (a)->next->prev = (a)->prev; \
+} while (0)
+
+/* search for stuff */
+#if 0
+static __inline__ unsigned char *codepage_find(struct codepage *page,
+ unsigned char *from)
+{
+}
+#endif
+
+static int add_code(struct codepage *page, unsigned char *from,
+ unsigned char *to)
+{
+ union codepage_val *ptr;
+
+ if (page->quantum < 1) /* no quantum given. don't do anything */
+ return 1;
+
+ if (page->quantum == 1) {
+ page->map[*from].value = *to;
+ return 0;
+ }
+
+#if 0
+ if (ptr = codepage_find(page->map, from)) {
+
+ } else {
+ unsigned char *space;
+ ptr = map[*from].hash;
+
+ space = (unsigned char *) malloc(sizeof(unsigned char)*quantum);
+ if (!ptr->from) {
+ } else {
+ }
+
+ map[*from].hash
+ }
+#endif
+}
+
+static struct codepage *init_codepage(const int quantum)
+{
+ struct codepage *cp;
+
+ cp = (struct codepage *) malloc(sizeof(struct codepage));
+ if (!cp)
+ return NULL;
+
+ if ((cp->map = (union codepage_val *)
+ calloc(MAPSIZE, sizeof(union codepage_val))) == NULL) {
+ free(cp);
+ return NULL;
+ }
+
+ cp->quantum = quantum;
+ return cp;
+}
+
+
+static void free_codepage(struct codepage *cp)
+{
+ int i;
+
+ if (!cp)
+ return;
+
+ if (cp->map) {
+ if (cp->quantum > 1) {
+ /* deal with any linked lists that may exist */
+ for (i = 0; i < MAPSIZE; i++) {
+ struct codepage_hash *ptr, *h;
+
+ h = &cp->map[i].hash; /* we don't free this one */
+ while ((ptr = h->prev) != h) {
+ CP_REMOVE(ptr);
+ free(ptr);
+ }
+ }
+ }
+ free(cp->map);
+ }
+ free(cp);
+}
+
+
+
+/* this is used by both codepages and generic mapping utilities. we
+ * allocate enough space to map all 8-bit characters if necessary.
+ * for double-byte mappings, we just use the table as a hash lookup.
+ * if we don't match, we don't convert.
+ */
+int codepage_init(struct vol *vol, const int rules,
+ const int quantum)
+{
+ if ((rules & CODEPAGE_RULE_MTOU) && !vol->v_utompage) {
+ vol->v_utompage = init_codepage(quantum);
+ if (!vol->v_utompage)
+ return -1;
+ }
+
+ if ((rules & CODEPAGE_RULE_UTOM) && !vol->v_mtoupage) {
+ vol->v_mtoupage = init_codepage(quantum);
+ if (!vol->v_mtoupage) {
+ goto err_utompage;
+ }
+ }
+
+ if ((rules & CODEPAGE_RULE_BADU) && !vol->v_badumap) {
+ vol->v_badumap = init_codepage(quantum);
+ if (!vol->v_badumap)
+ goto err_mtoupage;
+ }
+ return 0;
+
+err_mtoupage:
+ free_codepage(vol->v_mtoupage);
+ vol->v_mtoupage = NULL;
+
+err_utompage:
+ free_codepage(vol->v_utompage);
+ vol->v_utompage = NULL;
+ return -1;
+}
+
+void codepage_free(struct vol *vol)
+{
+ if (vol->v_utompage) {
+ free_codepage(vol->v_utompage);
+ vol->v_utompage = NULL;
+ }
+
+ if (vol->v_mtoupage) {
+ free_codepage(vol->v_mtoupage);
+ vol->v_mtoupage = NULL;
+ }
+
+ if (vol->v_badumap) {
+ free_codepage(vol->v_badumap);
+ vol->v_badumap = NULL;
+ }
+}
+
+
+int codepage_read(struct vol *vol, const char *path)
+{
+ unsigned char buf[CODEPAGE_FILE_HEADER_SIZE], *cur;
+ u_int16_t id;
+ int fd, i, quantum, rules;
+
+ if ((fd = open(path, O_RDONLY)) < 0) {
+ syslog(LOG_ERR, "%s: failed to open codepage", path);
+ return -1;
+ }
+
+ /* Read the codepage file header. */
+ if(read(fd, buf, sizeof(buf)) != sizeof(buf)) {
+ syslog( LOG_ERR, "%s: failed to read codepage header", path);
+ goto codepage_fail;
+ }
+
+ /* Check the file id */
+ cur = buf;
+ memcpy(&id, cur, sizeof(id));
+ cur += sizeof(id);
+ id = ntohs(id);
+ if (id != CODEPAGE_FILE_ID) {
+ syslog( LOG_ERR, "%s: not a codepage", path);
+ goto codepage_fail;
+ }
+
+ /* check the version number */
+ if (*cur++ != CODEPAGE_FILE_VERSION) {
+ syslog( LOG_ERR, "%s: codepage version not supported", path);
+ goto codepage_fail;
+ }
+
+ /* find out the data quantum size. default to 1 if nothing's given. */
+ quantum = *cur ? *cur : 1;
+ cur++;
+
+ /* rules used in this file. */
+ rules = *cur++;
+
+ if (codepage_init(vol, rules, quantum) < 0) {
+ syslog( LOG_ERR, "%s: Unable to allocate memory", path);
+ goto codepage_fail;
+ }
+
+ /* offset to data */
+
+ /* skip to the start of the data */
+ memcpy(&id, cur , sizeof(id));
+ id = ntohs(id);
+ lseek(fd, id, SEEK_SET);
+
+ /* mtoupage is the the equivalent of samba's unix2dos. utompage is
+ * the equivalent of dos2unix. it's a little confusing due to a
+ * desire to match up with mtoupath and utompath.
+ * NOTE: we allow codepages to specify 7-bit mappings if they want.
+ */
+ i = 1 + 2*quantum;
+ while (read(fd, buf, i) == i) {
+ if (*buf & CODEPAGE_RULE_MTOU) {
+ if (add_code(vol->v_mtoupage, buf + 1, buf + 1 + quantum) < 0) {
+ syslog(LOG_ERR, "unable to allocate memory for mtoupage");
+ break;
+ }
+ }
+
+ if (*buf & CODEPAGE_RULE_UTOM) {
+ if (add_code(vol->v_utompage, buf + 1 + quantum, buf + 1) < 0) {
+ syslog(LOG_ERR, "unable to allocate memory for utompage");
+ break;
+ }
+ }
+
+ /* we only look at the first character here. if we need to
+ * do so, we can always use the quantum to expand the
+ * available flags. */
+ if (*buf & CODEPAGE_RULE_BADU)
+ vol->v_badumap->map[*(buf + 1)].value = *(buf + 1 + quantum);
+ }
+ close(fd);
+ return 0;
+
+codepage_fail:
+ close(fd);
+ return -1;
+}
--- /dev/null
+#ifndef AFPD_CODEPAGE_H
+#define AFPD_CODEPAGE_H 1
+
+/*
+ * header of a codepage --
+ * file magic: 2 bytes
+ * file version: 1 byte
+ * size of name: 1 byte
+ * quantum: 1 byte
+ * rules: 1 byte
+ * offset to data: 2 bytes (network byte order)
+ * size of data: 2 bytes (network byte order)
+ * name: strlen(name) bytes (padded)
+ *
+ * data for a version 2 codepage --
+ * mapping rule: 1 byte
+ * mac/bad code: <quantum> bytes
+ * xlate/rule code: <quantum> bytes
+ * ...
+ */
+
+#define CODEPAGE_FILE_ID 0xCFAF
+#define CODEPAGE_FILE_VERSION 0x02
+#define CODEPAGE_FILE_HEADER_SIZE 10
+
+/* m->u and u->m mappings */
+#define CODEPAGE_RULE_MTOU (1 << 0)
+#define CODEPAGE_RULE_UTOM (1 << 1)
+#define CODEPAGE_RULE_BADU (1 << 2)
+
+/* rules for bad character assignment.
+ *
+ * bit assignments:
+ * bit 0 set: it's an illegal character
+ * unset: it's okay
+ *
+ * bit 1 set: bad anywhere
+ * unset: only check at the beginning of each quantum.
+ *
+ * bit 2 set: all quanta are the same length
+ * unset: quanta beginning with ascii characters are 1-byte long
+ *
+ */
+#define CODEPAGE_BADCHAR_SET (1 << 0)
+#define CODEPAGE_BADCHAR_ANYWHERE (1 << 1)
+#define CODEPAGE_BADCHAR_QUANTUM (1 << 2)
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <syslog.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <atalk/dsi.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+#include <atalk/nbp.h>
+#include <atalk/afp.h>
+#include <atalk/compat.h>
+#include <atalk/server_child.h>
+
+#include "globals.h"
+#include "config.h"
+#include "uam_auth.h"
+#include "status.h"
+
+#define LINESIZE 1024
+
+/* get rid of unneeded configurations. i use reference counts to deal
+ * w/ multiple configs sharing the same afp_options. oh, to dream of
+ * garbage collection ... */
+void configfree(AFPConfig *configs, const AFPConfig *config)
+{
+ AFPConfig *p, *q;
+
+ for (p = configs; p; p = q) {
+ q = p->next;
+ if (p == config)
+ continue;
+
+ /* do a little reference counting */
+ if (--(*p->optcount) < 1) {
+ afp_options_free(&p->obj.options, p->defoptions);
+ free(p->optcount);
+ }
+
+ switch (p->obj.proto) {
+#ifndef NO_DDP
+ case AFPPROTO_ASP:
+ free(p->obj.Obj);
+ free(p->obj.Type);
+ free(p->obj.Zone);
+ atp_close(((ASP) p->obj.handle)->asp_atp);
+ free(p->obj.handle);
+ break;
+#endif /* no afp/asp */
+ case AFPPROTO_DSI:
+ close(p->fd);
+ free(p->obj.handle);
+ break;
+ }
+ free(p);
+ }
+}
+
+#ifndef NO_DDP
+static void asp_cleanup(const AFPConfig *config)
+{
+ nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
+ &config->obj.options.ddpaddr);
+}
+
+/* these two are almost identical. it should be possible to collapse them
+ * into one with minimal junk. */
+static int asp_start(AFPConfig *config, AFPConfig *configs,
+ server_child *server_children)
+{
+ ASP asp;
+
+ if (!(asp = asp_getsession(config->obj.handle, server_children,
+ config->obj.options.tickleval))) {
+ syslog( LOG_ERR, "main: asp_getsession: %m" );
+ exit( 1 );
+ }
+
+ if (asp->child) {
+ configfree(configs, config); /* free a bunch of stuff */
+ afp_over_asp(&config->obj);
+ exit (0);
+ }
+}
+#endif /* no afp/asp */
+
+static int dsi_start(AFPConfig *config, AFPConfig *configs,
+ server_child *server_children)
+{
+ DSI *dsi;
+
+ if (!(dsi = dsi_getsession(config->obj.handle, server_children,
+ config->obj.options.tickleval))) {
+ syslog( LOG_ERR, "main: dsi_getsession: %m" );
+ exit( 1 );
+ }
+
+ /* we've forked. */
+ if (dsi->child) {
+ configfree(configs, config);
+ afp_over_dsi(&config->obj); /* start a session */
+ exit (0);
+ }
+}
+
+#ifndef NO_DDP
+static AFPConfig *ASPConfigInit(const struct afp_options *options,
+ unsigned char *refcount)
+{
+ AFPConfig *config;
+ ATP atp;
+ ASP asp;
+ char *Obj, *Type = "AFPServer", *Zone = "*";
+
+ if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
+ return NULL;
+
+ if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL) {
+ syslog( LOG_ERR, "main: atp_open: %m");
+ free(config);
+ return NULL;
+ }
+
+ if ((asp = asp_init( atp )) == NULL) {
+ syslog( LOG_ERR, "main: asp_init: %m" );
+ atp_close(atp);
+ free(config);
+ return NULL;
+ }
+
+ /* register asp server */
+ Obj = (char *) options->hostname;
+ if (nbp_name(options->server, &Obj, &Type, &Zone )) {
+ syslog( LOG_ERR, "main: can't parse %s", options->server );
+ goto serv_free_return;
+ }
+
+ /* dup Obj, Type and Zone as they get assigned to a single internal
+ * buffer by nbp_name */
+ if ((config->obj.Obj = strdup(Obj)) == NULL)
+ goto serv_free_return;
+
+ if ((config->obj.Type = strdup(Type)) == NULL) {
+ free(config->obj.Obj);
+ goto serv_free_return;
+ }
+
+ if ((config->obj.Zone = strdup(Zone)) == NULL) {
+ free(config->obj.Obj);
+ free(config->obj.Type);
+ goto serv_free_return;
+ }
+
+ /* make sure we're not registered */
+ nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
+ if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
+ syslog( LOG_ERR, "Can't register %s:%s@%s", Obj, Type, Zone );
+ free(config->obj.Obj);
+ free(config->obj.Type);
+ free(config->obj.Zone);
+ goto serv_free_return;
+ }
+
+ syslog( LOG_INFO, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
+ ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
+ atp_sockaddr( atp )->sat_addr.s_node,
+ atp_sockaddr( atp )->sat_port, VERSION );
+
+ config->fd = atp_fileno(atp);
+ config->obj.handle = asp;
+ config->obj.config = config;
+ config->obj.proto = AFPPROTO_ASP;
+
+ memcpy(&config->obj.options, options, sizeof(struct afp_options));
+ config->optcount = refcount;
+ (*refcount)++;
+
+ config->server_start = asp_start;
+ config->server_cleanup = asp_cleanup;
+
+ return config;
+
+serv_free_return:
+ asp_close(asp);
+ free(config);
+ return NULL;
+}
+#endif /* no afp/asp */
+
+
+static AFPConfig *DSIConfigInit(const struct afp_options *options,
+ unsigned char *refcount,
+ const dsi_proto protocol)
+{
+ AFPConfig *config;
+ DSI *dsi;
+ char *p, *q;
+
+ if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
+ syslog( LOG_ERR, "DSIConfigInit: malloc(config): %m" );
+ return NULL;
+ }
+
+ if ((dsi = dsi_init(protocol, "afpd", options->hostname,
+ options->ipaddr, options->port,
+ options->flags & OPTION_PROXY,
+ options->server_quantum)) == NULL) {
+ syslog( LOG_ERR, "main: dsi_init: %m" );
+ free(config);
+ return NULL;
+ }
+
+ if (options->flags & OPTION_PROXY) {
+ syslog(LOG_INFO, "ASIP proxy initialized for %s:%d (%s)",
+ inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
+ VERSION);
+ } else {
+ syslog(LOG_INFO, "ASIP started on %s:%d(%d) (%s)",
+ inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
+ dsi->serversock, VERSION);
+ }
+
+ config->fd = dsi->serversock;
+ config->obj.handle = dsi;
+ config->obj.config = config;
+ config->obj.proto = AFPPROTO_DSI;
+
+ memcpy(&config->obj.options, options, sizeof(struct afp_options));
+ /* get rid of any appletalk info. we use the fact that the DSI
+ * stuff is done after the ASP stuff. */
+ p = config->obj.options.server;
+ if (p && (q = strchr(p, ':')))
+ *q = '\0';
+
+ config->optcount = refcount;
+ (*refcount)++;
+
+ config->server_start = dsi_start;
+ return config;
+}
+
+/* allocate server configurations. this should really store the last
+ * entry in config->last or something like that. that would make
+ * supporting multiple dsi transports easier. */
+static AFPConfig *AFPConfigInit(const struct afp_options *options,
+ const struct afp_options *defoptions)
+{
+ AFPConfig *config = NULL, *next = NULL;
+ unsigned char *refcount;
+
+ if ((refcount = (unsigned char *)
+ calloc(1, sizeof(unsigned char))) == NULL) {
+ syslog( LOG_ERR, "AFPConfigInit: calloc(refcount): %m" );
+ return NULL;
+ }
+
+#ifndef NO_DDP
+ /* handle asp transports */
+ if ((options->transports & AFPTRANS_DDP) &&
+ (config = ASPConfigInit(options, refcount)))
+ config->defoptions = defoptions;
+#endif
+
+ /* handle dsi transports and dsi proxies. we only proxy
+ * for DSI connections. */
+
+ /* this should have something like the following:
+ * for (i=mindsi; i < maxdsi; i++)
+ * if (options->transports & (1 << i) &&
+ * (next = DSIConfigInit(options, refcount, i)))
+ * next->defoptions = defoptions;
+ */
+ if ((options->transports & AFPTRANS_TCP) &&
+ (((options->flags & OPTION_PROXY) == 0) ||
+ ((options->flags & OPTION_PROXY) && config))
+ && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
+ next->defoptions = defoptions;
+
+ /* load in all the authentication modules. we can load the same
+ things multiple times if necessary. however, loading different
+ things with the same names will cause complaints. by not loading
+ in any uams with proxies, we prevent ddp connections from succeeding.
+ */
+ auth_load(options->uampath, options->uamlist);
+
+ /* this should be able to accept multiple dsi transports. i think
+ * the only thing that gets affected is the net addresses. */
+ status_init(config, next, options);
+
+ /* attach dsi config to tail of asp config */
+ if (config) {
+ config->next = next;
+ return config;
+ }
+
+ return next;
+}
+
+/* fill in the appropriate bits for each interface */
+AFPConfig *configinit(struct afp_options *cmdline)
+{
+ FILE *fp;
+ char buf[LINESIZE + 1], *p, have_option = 0;
+ struct afp_options options;
+ AFPConfig *config, *first = NULL;
+
+ /* if config file doesn't exist, load defaults */
+ if ((fp = fopen(cmdline->configfile, "r")) == NULL)
+ return AFPConfigInit(cmdline, cmdline);
+
+ /* scan in the configuration file */
+ while (!feof(fp)) {
+ if (!fgets(buf, sizeof(buf), fp) || buf[0] == '#')
+ continue;
+
+ /* a little pre-processing to get rid of spaces and end-of-lines */
+ p = buf;
+ while (p && isspace(*p))
+ p++;
+ if (!p || (*p == '\0'))
+ continue;
+
+ have_option = 1;
+
+ memcpy(&options, cmdline, sizeof(options));
+ if (!afp_options_parseline(p, &options))
+ continue;
+
+ /* this should really get a head and a tail to simplify things. */
+ if (!first) {
+ if ((first = AFPConfigInit(&options, cmdline)))
+ config = first->next ? first->next : first;
+ } else if ((config->next = AFPConfigInit(&options, cmdline))) {
+ config = config->next->next ? config->next->next : config->next;
+ }
+ }
+
+ fclose(fp);
+
+ if (!have_option)
+ return AFPConfigInit(cmdline, cmdline);
+
+ return first;
+}
--- /dev/null
+#ifndef AFPD_CONFIG_H
+#define AFPD_CONFIG_H 1
+
+#include <sys/cdefs.h>
+#include <atalk/server_child.h>
+#include <atalk/atp.h>
+#include "globals.h"
+
+typedef struct AFPConfig {
+ AFPObj obj;
+ int fd, statuslen;
+ unsigned char *optcount;
+ char status[ATP_MAXDATA];
+ const void *defoptions, *signature;
+ int (*server_start) __P((struct AFPConfig *, struct AFPConfig *,
+ server_child *));
+ void (*server_cleanup) __P((const struct AFPConfig *));
+ struct AFPConfig *next;
+} AFPConfig;
+
+extern AFPConfig *configinit __P((struct afp_options *));
+extern void configfree __P((AFPConfig *, const AFPConfig *));
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/syslog.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <atalk/dsi.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+#include <atalk/afp.h>
+#include <atalk/adouble.h>
+#include <atalk/util.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include "volume.h"
+#include "directory.h"
+#include "fork.h"
+#include "globals.h"
+#include "desktop.h"
+
+int afp_opendt(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct vol *vol;
+ u_int16_t vid;
+
+ ibuf += 2;
+
+ memcpy( &vid, ibuf, sizeof(vid));
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_PARAM );
+ }
+
+ memcpy( rbuf, &vid, sizeof(vid));
+ *rbuflen = sizeof(vid);
+ return( AFP_OK );
+}
+
+int afp_closedt(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ *rbuflen = 0;
+ return( AFP_OK );
+}
+
+struct savedt si = { { 0, 0, 0, 0 }, -1, 0 };
+
+static int iconopen( vol, creator, flags, mode )
+ struct vol *vol;
+ u_char creator[ 4 ];
+{
+ char *dtf, *adt, *adts;
+
+ if ( si.sdt_fd != -1 ) {
+ if ( memcmp( si.sdt_creator, creator, sizeof( CreatorType )) == 0 &&
+ si.sdt_vid == vol->v_vid ) {
+ return 0;
+ }
+ close( si.sdt_fd );
+ si.sdt_fd = -1;
+ }
+
+ dtf = dtfile( vol, creator, ".icon" );
+
+ if (( si.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
+ if ( errno == ENOENT && ( flags & O_CREAT )) {
+ if (( adts = strrchr( dtf, '/' )) == NULL ) {
+ return -1;
+ }
+ *adts = '\0';
+ if (( adt = strrchr( dtf, '/' )) == NULL ) {
+ return -1;
+ }
+ *adt = '\0';
+ (void) ad_mkdir( dtf, DIRBITS | 0777 );
+ *adt = '/';
+ (void) ad_mkdir( dtf, DIRBITS | 0777 );
+ *adts = '/';
+
+ if (( si.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
+ syslog( LOG_ERR, "iconopen: open %s: %m", dtf );
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ }
+
+ memcpy( si.sdt_creator, creator, sizeof( CreatorType ));
+ si.sdt_vid = vol->v_vid;
+ si.sdt_index = 1;
+ return 0;
+}
+
+int afp_addicon(obj, ibuf, ibuflen, rbuf, rbuflen)
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct vol *vol;
+ struct iovec iov[ 2 ];
+ u_char fcreator[ 4 ], imh[ 12 ], irh[ 12 ], *p;
+ int itype, cc = AFP_OK, iovcnt = 0, buflen;
+ u_int32_t ftype, itag;
+ u_int16_t bsize, rsize, vid;
+
+ buflen = *rbuflen;
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ cc = AFPERR_PARAM;
+ goto addicon_err;
+ }
+
+ memcpy( fcreator, ibuf, sizeof( fcreator ));
+ ibuf += sizeof( fcreator );
+
+ memcpy( &ftype, ibuf, sizeof( ftype ));
+ ibuf += sizeof( ftype );
+
+ itype = (unsigned char) *ibuf;
+ ibuf += 2;
+
+ memcpy( &itag, ibuf, sizeof( itag ));
+ ibuf += sizeof( itag );
+
+ memcpy( &bsize, ibuf, sizeof( bsize ));
+ bsize = ntohs( bsize );
+
+ if ( si.sdt_fd != -1 ) {
+ (void)close( si.sdt_fd );
+ si.sdt_fd = -1;
+ }
+
+ if (iconopen( vol, fcreator, O_RDWR|O_CREAT, 0666 ) < 0) {
+ cc = AFPERR_NOITEM;
+ goto addicon_err;
+ }
+
+ if (lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0) {
+ close(si.sdt_fd);
+ si.sdt_fd = -1;
+ syslog( LOG_ERR, "afp_addicon: lseek: %m" );
+ cc = AFPERR_PARAM;
+ goto addicon_err;
+ }
+
+ /*
+ * Read icon elements until we find a match to replace, or
+ * we get to the end to insert.
+ */
+ p = imh;
+ memcpy( p, &itag, sizeof( itag ));
+ p += sizeof( itag );
+ memcpy( p, &ftype, sizeof( ftype ));
+ p += sizeof( ftype );
+ *p++ = itype;
+ *p++ = 0;
+ bsize = htons( bsize );
+ memcpy( p, &bsize, sizeof( bsize ));
+ bsize = ntohs( bsize );
+ while (( cc = read( si.sdt_fd, irh, sizeof( irh ))) > 0 ) {
+ memcpy( &rsize, irh + 10, sizeof( rsize ));
+ rsize = ntohs( rsize );
+ /*
+ * Is this our set of headers?
+ */
+ if ( memcmp( irh, imh, sizeof( irh ) - sizeof( u_short )) == 0 ) {
+ /*
+ * Is the size correct?
+ */
+ if ( bsize != rsize )
+ cc = AFPERR_ITYPE;
+ break;
+ }
+
+ if ( lseek( si.sdt_fd, (off_t) rsize, SEEK_CUR ) < 0 ) {
+ syslog( LOG_ERR, "afp_addicon: lseek: %m" );
+ cc = AFPERR_PARAM;
+ }
+ }
+
+ /*
+ * Some error occurred, return.
+ */
+addicon_err:
+ if ( cc < 0 ) {
+ syslog( LOG_ERR, "afp_addicon: %m" );
+ if (obj->proto == AFPPROTO_DSI) {
+ dsi_writeinit(obj->handle, rbuf, buflen);
+ dsi_writeflush(obj->handle);
+ }
+ return cc;
+ }
+
+
+ switch (obj->proto) {
+#ifndef NO_DDP
+ case AFPPROTO_ASP:
+ buflen = bsize;
+ if ((asp_wrtcont(obj->handle, rbuf, &buflen) < 0) || buflen != bsize)
+ return( AFPERR_PARAM );
+
+ if (obj->options.flags & OPTION_DEBUG) {
+ printf("(write) len: %d\n", buflen);
+ bprint(rbuf, buflen);
+ }
+
+ /*
+ * We're at the end of the file, add the headers, etc. */
+ if ( cc == 0 ) {
+ iov[ 0 ].iov_base = (caddr_t)imh;
+ iov[ 0 ].iov_len = sizeof( imh );
+ iov[ 1 ].iov_base = rbuf;
+ iov[ 1 ].iov_len = bsize;
+ iovcnt = 2;
+ }
+
+ /*
+ * We found an icon to replace.
+ */
+ if ( cc > 0 ) {
+ iov[ 0 ].iov_base = rbuf;
+ iov[ 0 ].iov_len = bsize;
+ iovcnt = 1;
+ }
+
+ if ( writev( si.sdt_fd, iov, iovcnt ) < 0 ) {
+ syslog( LOG_ERR, "afp_addicon: writev: %m" );
+ return( AFPERR_PARAM );
+ }
+ break;
+#endif /* no afp/asp */
+ case AFPPROTO_DSI:
+ {
+ DSI *dsi = obj->handle;
+
+ iovcnt = dsi_writeinit(dsi, rbuf, buflen);
+
+ /* add headers at end of file */
+ if ((cc == 0) && (write(si.sdt_fd, imh, sizeof(imh)) < 0)) {
+ syslog(LOG_ERR, "afp_addicon: write: %m");
+ dsi_writeflush(dsi);
+ return AFPERR_PARAM;
+ }
+
+ if ((cc = write(si.sdt_fd, rbuf, iovcnt)) < 0) {
+ syslog(LOG_ERR, "afp_addicon: write: %m");
+ dsi_writeflush(dsi);
+ return AFPERR_PARAM;
+ }
+
+ while ((iovcnt = dsi_write(dsi, rbuf, buflen))) {
+ if ( obj->options.flags & OPTION_DEBUG ) {
+ printf("(write) command cont'd: %d\n", iovcnt);
+ bprint(rbuf, iovcnt);
+ }
+
+ if ((cc = write(si.sdt_fd, rbuf, iovcnt)) < 0) {
+ syslog(LOG_ERR, "afp_addicon: write: %m");
+ dsi_writeflush(dsi);
+ return AFPERR_PARAM;
+ }
+ }
+ }
+ break;
+ }
+
+ close( si.sdt_fd );
+ si.sdt_fd = -1;
+ return( AFP_OK );
+}
+
+u_char utag[] = { 0, 0, 0, 0 };
+u_char ucreator[] = { 'U', 'N', 'I', 'X' };
+u_char utype[] = { 'T', 'E', 'X', 'T' };
+short usize = 256;
+u_char uicon[] = {
+0x1F, 0xFF, 0xFC, 0x00, 0x10, 0x00, 0x06, 0x00,
+0x10, 0x00, 0x05, 0x00, 0x10, 0x00, 0x04, 0x80,
+0x10, 0x00, 0x04, 0x40, 0x10, 0x00, 0x04, 0x20,
+0x10, 0x00, 0x07, 0xF0, 0x10, 0x00, 0x00, 0x10,
+0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10,
+0x10, 0x00, 0x00, 0x10, 0x10, 0x80, 0x02, 0x10,
+0x11, 0x80, 0x03, 0x10, 0x12, 0x80, 0x02, 0x90,
+0x12, 0x80, 0x02, 0x90, 0x14, 0x80, 0x02, 0x50,
+0x14, 0x87, 0xC2, 0x50, 0x14, 0x58, 0x34, 0x50,
+0x14, 0x20, 0x08, 0x50, 0x12, 0x16, 0xD0, 0x90,
+0x11, 0x01, 0x01, 0x10, 0x12, 0x80, 0x02, 0x90,
+0x12, 0x9C, 0x72, 0x90, 0x14, 0x22, 0x88, 0x50,
+0x14, 0x41, 0x04, 0x50, 0x14, 0x49, 0x24, 0x50,
+0x14, 0x55, 0x54, 0x50, 0x14, 0x5D, 0x74, 0x50,
+0x14, 0x5D, 0x74, 0x50, 0x12, 0x49, 0x24, 0x90,
+0x12, 0x22, 0x88, 0x90, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xFE, 0x00,
+0x1F, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0x80,
+0x1F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xE0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+};
+
+int afp_geticoninfo(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct vol *vol;
+ u_char fcreator[ 4 ], ih[ 12 ];
+ u_int16_t vid, iindex, bsize;
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy( fcreator, ibuf, sizeof( fcreator ));
+ ibuf += sizeof( fcreator );
+ memcpy( &iindex, ibuf, sizeof( iindex ));
+ iindex = ntohs( iindex );
+
+ if ( memcmp( fcreator, ucreator, sizeof( ucreator )) == 0 ) {
+ if ( iindex > 1 ) {
+ return( AFPERR_NOITEM );
+ }
+ memcpy( ih, utag, sizeof( utag ));
+ memcpy( ih + sizeof( utag ), utype, sizeof( utype ));
+ *( ih + sizeof( utag ) + sizeof( utype )) = 1;
+ *( ih + sizeof( utag ) + sizeof( utype ) + 1 ) = 0;
+ memcpy( ih + sizeof( utag ) + sizeof( utype ) + 2, &usize,
+ sizeof( usize ));
+ memcpy( rbuf, ih, sizeof( ih ));
+ *rbuflen = sizeof( ih );
+ return( AFP_OK );
+ }
+
+ if ( iconopen( vol, fcreator, O_RDONLY, 0 ) < 0) {
+ return( AFPERR_NOITEM );
+ }
+
+ if ( iindex < si.sdt_index ) {
+ if ( lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0 ) {
+ return( AFPERR_PARAM );
+ }
+ si.sdt_index = 1;
+ }
+
+ /*
+ * Position to the correct spot.
+ */
+ for (;;) {
+ if ( read( si.sdt_fd, ih, sizeof( ih )) != sizeof( ih )) {
+ close( si.sdt_fd );
+ si.sdt_fd = -1;
+ return( AFPERR_NOITEM );
+ }
+ memcpy( &bsize, ih + 10, sizeof( bsize ));
+ bsize = ntohs(bsize);
+ if ( lseek( si.sdt_fd, (off_t) bsize, SEEK_CUR ) < 0 ) {
+ syslog( LOG_ERR, "afp_iconinfo: lseek: %m" );
+ return( AFPERR_PARAM );
+ }
+ if ( si.sdt_index == iindex ) {
+ memcpy( rbuf, ih, sizeof( ih ));
+ *rbuflen = sizeof( ih );
+ return( AFP_OK );
+ }
+ si.sdt_index++;
+ }
+}
+
+
+int afp_geticon(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct vol *vol;
+ off_t offset;
+ int rc, buflen;
+ u_char fcreator[ 4 ], ftype[ 4 ], itype, ih[ 12 ];
+ u_int16_t vid, bsize, rsize;
+
+ buflen = *rbuflen;
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy( fcreator, ibuf, sizeof( fcreator ));
+ ibuf += sizeof( fcreator );
+ memcpy( ftype, ibuf, sizeof( ftype ));
+ ibuf += sizeof( ftype );
+ itype = (unsigned char) *ibuf++;
+ ibuf++;
+ memcpy( &bsize, ibuf, sizeof( bsize ));
+ bsize = ntohs( bsize );
+
+ if ( memcmp( fcreator, ucreator, sizeof( ucreator )) == 0 &&
+ memcmp( ftype, utype, sizeof( utype )) == 0 &&
+ itype == 1 &&
+ bsize <= usize ) {
+ memcpy( rbuf, uicon, bsize);
+ *rbuflen = bsize;
+ return( AFP_OK );
+ }
+
+ if ( iconopen( vol, fcreator, O_RDONLY, 0 ) < 0) {
+ return( AFPERR_NOITEM );
+ }
+
+ if ( lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0 ) {
+ close(si.sdt_fd);
+ si.sdt_fd = -1;
+ syslog(LOG_ERR, "afp_geticon: lseek: %m");
+ return( AFPERR_PARAM );
+ }
+
+ si.sdt_index = 1;
+ offset = 0;
+ while (( rc = read( si.sdt_fd, ih, sizeof( ih ))) > 0 ) {
+ si.sdt_index++;
+ offset += sizeof(ih);
+ if ( memcmp( ih + sizeof( int ), ftype, sizeof( ftype )) == 0 &&
+ *(ih + sizeof( int ) + sizeof( ftype )) == itype ) {
+ break;
+ }
+ memcpy( &rsize, ih + 10, sizeof( rsize ));
+ rsize = ntohs( rsize );
+ if ( lseek( si.sdt_fd, (off_t) rsize, SEEK_CUR ) < 0 ) {
+ syslog( LOG_ERR, "afp_geticon: lseek: %m" );
+ return( AFPERR_PARAM );
+ }
+ offset += rsize;
+ }
+
+ if ( rc < 0 ) {
+ syslog(LOG_ERR, "afp_geticon: read: %m");
+ return( AFPERR_PARAM );
+ }
+
+ if ( rc == 0 ) {
+ return( AFPERR_NOITEM );
+ }
+
+ memcpy( &rsize, ih + 10, sizeof( rsize ));
+ rsize = ntohs( rsize );
+#define min(a,b) ((a)<(b)?(a):(b))
+ rc = min( bsize, rsize );
+
+ if ((obj->proto == AFPPROTO_DSI) && (buflen < rc)) {
+ DSI *dsi = obj->handle;
+ struct stat st;
+ off_t size;
+
+ size = (fstat(si.sdt_fd, &st) < 0) ? 0 : st.st_size;
+ if (size < rc + offset) {
+ return AFPERR_PARAM;
+ }
+
+ if ((*rbuflen = dsi_readinit(dsi, rbuf, buflen, rc, AFP_OK)) < 0)
+ goto geticon_exit;
+
+ /* do to the streaming nature, we have to exit if we encounter
+ * a problem. much confusion results otherwise. */
+ while (*rbuflen > 0) {
+#if defined(SENDFILE_FLAVOR_LINUX) || defined(SENDFILE_FLAVOR_BSD)
+ if (!obj->options.flags & OPTION_DEBUG) {
+#ifdef SENDFILE_FLAVOR_LINUX
+ if (sendfile(dsi->socket, si.sdt_fd, &offset, dsi->datasize) < 0)
+ goto geticon_exit;
+#endif
+
+#ifdef SENDFILE_FLAVOR_BSD
+ if (sendfile(si.sdt_fd, dsi->socket, offset, rc, NULL, NULL, 0) < 0)
+ goto geticon_exit;
+#endif
+
+ goto geticon_done;
+ }
+#endif
+
+ buflen = read(si.sdt_fd, rbuf, *rbuflen);
+ if (buflen < 0)
+ goto geticon_exit;
+
+ if (obj->options.flags & OPTION_DEBUG) {
+ printf( "(read) reply: %d, %d\n", buflen, dsi->clientID);
+ bprint(rbuf, buflen);
+ }
+
+ /* dsi_read() also returns buffer size of next allocation */
+ buflen = dsi_read(dsi, rbuf, buflen); /* send it off */
+ if (buflen < 0)
+ goto geticon_exit;
+
+ *rbuflen = buflen;
+ }
+
+geticon_done:
+ dsi_readdone(dsi);
+ return AFP_OK;
+
+geticon_exit:
+ syslog(LOG_INFO, "afp_geticon: %m");
+ dsi_readdone(dsi);
+ obj->exit(1);
+
+ } else {
+ if ( read( si.sdt_fd, rbuf, rc ) < rc ) {
+ return( AFPERR_PARAM );
+ }
+ *rbuflen = rc;
+ return AFP_OK;
+ }
+}
+
+
+static char hexdig[] = "0123456789abcdef";
+char *dtfile(const struct vol *vol, u_char creator[], char *ext )
+{
+ static char path[ MAXPATHLEN + 1];
+ char *p;
+ int i;
+
+ strcpy( path, vol->v_path );
+ strcat( path, "/.AppleDesktop/" );
+ for ( p = path; *p != '\0'; p++ )
+ ;
+
+ if ( !isprint( creator[ 0 ] ) || creator[ 0 ] == '/' ) {
+ *p++ = hexdig[ ( creator[ 0 ] & 0xf0 ) >> 4 ];
+ *p++ = hexdig[ creator[ 0 ] & 0x0f ];
+ } else {
+ *p++ = creator[ 0 ];
+ }
+
+ *p++ = '/';
+
+ for ( i = 0; i < sizeof( CreatorType ); i++ ) {
+ if ( !isprint( creator[ i ] ) || creator[ i ] == '/' ) {
+ *p++ = hexdig[ ( creator[ i ] & 0xf0 ) >> 4 ];
+ *p++ = hexdig[ creator[ i ] & 0x0f ];
+ } else {
+ *p++ = creator[ i ];
+ }
+ }
+ *p = '\0';
+ strcat( path, ext );
+
+ return( path );
+}
+
+char *mtoupath(const struct vol *vol, char *mpath)
+{
+ static unsigned char upath[ MAXPATHLEN + 1];
+ unsigned char *m, *u;
+ int i = 0;
+
+ if ( *mpath == '\0' ) {
+ return( "." );
+ }
+
+ m = mpath;
+ u = upath;
+ while ( *m != '\0' ) {
+ /* handle case conversion first */
+ if (vol->v_casefold & AFPVOL_MTOUUPPER)
+ *m = diatoupper( *m );
+ else if (vol->v_casefold & AFPVOL_MTOULOWER)
+ *m = diatolower( *m );
+
+ /* we have a code page. we only use the ascii range
+ * if we have map ascii specified. */
+#if 0
+ if (vol->v_mtoupage && ((*m > 0x7F) ||
+ vol->v_flags & AFPVOL_MAPASCII)) {
+ *u = vol->v_mtoupage[*m];
+ } else
+#endif
+#if AD_VERSION == AD_VERSION1
+ if ((((vol->v_flags & AFPVOL_NOHEX) == 0) &&
+ (!isascii(*m) || *m == '/')) ||
+ (((vol->v_flags & AFPVOL_USEDOTS) == 0) &&
+ ( i == 0 && (*m == '.' )))) {
+#else
+ if ((((vol->v_flags & AFPVOL_NOHEX) == 0) &&
+ (!isprint(*m) || *m == '/')) ||
+ (((vol->v_flags & AFPVOL_USEDOTS) == 0) &&
+ ( i == 0 && (*m == '.' )))) {
+#endif
+ /* do hex conversion. */
+ *u++ = ':';
+ *u++ = hexdig[ ( *m & 0xf0 ) >> 4 ];
+ *u = hexdig[ *m & 0x0f ];
+ } else
+ *u = *m;
+ u++;
+ i++;
+ m++;
+ }
+ *u = '\0';
+
+ return( upath );
+}
+
+#define hextoint( c ) ( isdigit( c ) ? c - '0' : c + 10 - 'a' )
+#define islxdigit(x) (!isupper(x)&&isxdigit(x))
+
+char *utompath(const struct vol *vol, char *upath)
+{
+ static unsigned char mpath[ MAXPATHLEN + 1];
+ unsigned char *m, *u;
+ int h;
+
+ /* do the hex conversion */
+ u = upath;
+ m = mpath;
+ while ( *u != '\0' ) {
+ /* we have a code page */
+#if 0
+ if (vol->v_utompage && ((*u > 0x7F) ||
+ (vol->v_flags & AFPVOL_MAPASCII))) {
+ *m = vol->v_utompage[*u];
+ } else
+#endif
+ if ( *u == ':' && *(u+1) != '\0' && islxdigit( *(u+1)) &&
+ *(u+2) != '\0' && islxdigit( *(u+2))) {
+ ++u;
+ h = hextoint( *u ) << 4;
+ ++u;
+ h |= hextoint( *u );
+ *m = h;
+ } else
+ *m = *u;
+
+ /* handle case conversion */
+ if (vol->v_casefold & AFPVOL_UTOMLOWER)
+ *m = diatolower( *m );
+ else if (vol->v_casefold & AFPVOL_UTOMUPPER)
+ *m = diatoupper( *m );
+
+ u++;
+ m++;
+ }
+ *m = '\0';
+ return( mpath );
+}
+
+int afp_addcomment(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct adouble ad, *adp;
+ struct vol *vol;
+ struct dir *dir;
+ struct ofork *of;
+ char *path, *name;
+ int clen;
+ u_int32_t did;
+ u_int16_t vid;
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy( &did, ibuf, sizeof( did ));
+ ibuf += sizeof( did );
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if ((u_long)ibuf & 1 ) {
+ ibuf++;
+ }
+
+ clen = (u_char)*ibuf++;
+ clen = min( clen, 199 );
+
+ if ((*path == '\0') || !(of = of_findname(vol, curdir, path))) {
+ memset(&ad, 0, sizeof(ad));
+ adp = &ad;
+ } else
+ adp = of->of_ad;
+ if (ad_open( mtoupath( vol, path ), vol_noadouble(vol) |
+ (( *path == '\0' ) ? ADFLAGS_HF|ADFLAGS_DIR : ADFLAGS_HF),
+ O_RDWR|O_CREAT, 0666, adp) < 0 ) {
+ return( AFPERR_ACCESS );
+ }
+
+ if ( ad_getoflags( adp, ADFLAGS_HF ) & O_CREAT ) {
+ if ( *path == '\0' ) {
+ name = curdir->d_name;
+ } else {
+ name = path;
+ }
+ ad_setentrylen( adp, ADEID_NAME, strlen( name ));
+ memcpy( ad_entry( adp, ADEID_NAME ), name,
+ ad_getentrylen( adp, ADEID_NAME ));
+ }
+
+ ad_setentrylen( adp, ADEID_COMMENT, clen );
+ memcpy( ad_entry( adp, ADEID_COMMENT ), ibuf, clen );
+ ad_flush( adp, ADFLAGS_HF );
+ ad_close( adp, ADFLAGS_HF );
+ return( AFP_OK );
+}
+
+int afp_getcomment(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct adouble ad, *adp;
+ struct vol *vol;
+ struct dir *dir;
+ struct ofork *of;
+ char *path;
+ u_int32_t did;
+ u_int16_t vid;
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy( &did, ibuf, sizeof( did ));
+ ibuf += sizeof( did );
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if ((*path == '\0') || !(of = of_findname(vol, curdir, path))) {
+ memset(&ad, 0, sizeof(ad));
+ adp = &ad;
+ } else
+ adp = of->of_ad;
+ if ( ad_open( mtoupath( vol, path ),
+ (( *path == '\0' ) ? ADFLAGS_HF|ADFLAGS_DIR : ADFLAGS_HF),
+ O_RDONLY, 0666, adp) < 0 ) {
+ return( AFPERR_NOITEM );
+ }
+
+ /*
+ * Make sure the AD file is not bogus.
+ */
+ if ( ad_getentrylen( adp, ADEID_COMMENT ) < 0 ||
+ ad_getentrylen( adp, ADEID_COMMENT ) > 199 ) {
+ ad_close( adp, ADFLAGS_HF );
+ return( AFPERR_NOITEM );
+ }
+
+ *rbuf++ = ad_getentrylen( adp, ADEID_COMMENT );
+ memcpy( rbuf, ad_entry( adp, ADEID_COMMENT ),
+ ad_getentrylen( adp, ADEID_COMMENT ));
+ *rbuflen = ad_getentrylen( adp, ADEID_COMMENT ) + 1;
+ ad_close( adp, ADFLAGS_HF );
+ return( AFP_OK );
+}
+
+int afp_rmvcomment(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct adouble ad, *adp;
+ struct vol *vol;
+ struct dir *dir;
+ struct ofork *of;
+ char *path;
+ u_int32_t did;
+ u_int16_t vid;
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy( &did, ibuf, sizeof( did ));
+ ibuf += sizeof( did );
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if ((*path == '\0') || !(of = of_findname(vol, curdir, path))) {
+ memset(&ad, 0, sizeof(ad));
+ adp = &ad;
+ } else
+ adp = of->of_ad;
+ if ( ad_open( mtoupath( vol, path ),
+ (( *path == '\0' ) ? ADFLAGS_HF|ADFLAGS_DIR : ADFLAGS_HF),
+ O_RDWR, 0, adp) < 0 ) {
+ switch ( errno ) {
+ case ENOENT :
+ return( AFPERR_NOITEM );
+ case EACCES :
+ return( AFPERR_ACCESS );
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+
+ ad_setentrylen( adp, ADEID_COMMENT, 0 );
+ ad_flush( adp, ADFLAGS_HF );
+ ad_close( adp, ADFLAGS_HF );
+ return( AFP_OK );
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef AFPD_DESKTOP_H
+#define AFPD_DESKTOP_H 1
+
+#include <sys/cdefs.h>
+#include "globals.h"
+#include "volume.h"
+
+/* various finder info bits */
+#define FINDERINFO_FRFLAGOFF 8
+#define FINDERINFO_FRVIEWOFF 14
+#define FINDERINFO_INVISIBLE (1<<14)
+#define FINDERINFO_CLOSEDVIEW 0x100
+
+struct savedt {
+ u_char sdt_creator[ 4 ];
+ int sdt_fd;
+ int sdt_index;
+ short sdt_vid;
+};
+
+typedef unsigned char CreatorType[4];
+
+extern char *dtfile __P((const struct vol *, u_char [], char *));
+extern char *mtoupath __P((const struct vol *, char *));
+extern char *utompath __P((const struct vol *, char *));
+extern u_char ucreator[];
+
+#define validupath(vol, name) ((((vol)->v_flags & AFPVOL_USEDOTS) ? \
+ (strncasecmp((name),".Apple", 6) && \
+ strcasecmp((name), ".Parent")) : (name)[0] != '.'))
+
+/* FP functions */
+extern int afp_opendt __P((AFPObj *, char *, int, char *, int *));
+extern int afp_addcomment __P((AFPObj *, char *, int, char *, int *));
+extern int afp_getcomment __P((AFPObj *, char *, int, char *, int *));
+extern int afp_rmvcomment __P((AFPObj *, char *, int, char *, int *));
+extern int afp_addappl __P((AFPObj *, char *, int, char *, int *));
+extern int afp_rmvappl __P((AFPObj *, char *, int, char *, int *));
+extern int afp_getappl __P((AFPObj *, char *, int, char *, int *));
+extern int afp_closedt __P((AFPObj *, char *, int, char *, int *));
+extern int afp_addicon __P((AFPObj *, char *, int, char *, int *));
+extern int afp_geticoninfo __P((AFPObj *, char *, int, char *, int *));
+extern int afp_geticon __P((AFPObj *, char *, int, char *, int *));
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * 19 jan 2000 implemented red-black trees for directory lookups
+ * (asun@cobalt.com).
+ */
+
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/afp.h>
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <utime.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <string.h>
+
+#include "directory.h"
+#include "desktop.h"
+#include "volume.h"
+#include "fork.h"
+#include "file.h"
+#include "globals.h"
+#include "unix.h"
+
+struct dir *curdir;
+
+#define SENTINEL (&sentinel)
+static struct dir sentinel = { SENTINEL, SENTINEL, NULL, DIRTREE_COLOR_BLACK,
+ NULL, NULL, NULL, NULL, NULL, 0, 0, NULL };
+static struct dir rootpar = { SENTINEL, SENTINEL, NULL, 0,
+ NULL, NULL, NULL, NULL, NULL, 0, 0, NULL };
+
+/* (from IM: Toolbox Essentials)
+ * dirFinderInfo (DInfo) fields:
+ * field bytes
+ * frRect 8 folder's window rectangle
+ * frFlags 2 flags
+ * frLocation 4 folder's location in window
+ * frView 2 folder's view (default == closedView (256))
+ *
+ * extended dirFinderInfo (DXInfo) fields:
+ * frScroll 4 scroll position
+ * frOpenChain: 4 directory ID chain of open folders
+ * frScript: 1 script flag and code
+ * frXFlags: 1 reserved
+ * frComment: 2 comment ID
+ * frPutAway: 4 home directory ID
+ */
+
+/*
+ * redid did assignment for directories. now we use red-black trees.
+ * how exciting.
+ */
+ struct dir *
+dirsearch( vol, did )
+ const struct vol *vol;
+ u_int32_t did;
+{
+ struct dir *dir;
+
+
+ /* check for 0 did */
+ if (!did)
+ return NULL;
+
+ if ( did == DIRDID_ROOT_PARENT ) {
+ if (!rootpar.d_did)
+ rootpar.d_did = DIRDID_ROOT_PARENT;
+ rootpar.d_child = vol->v_dir;
+ return( &rootpar );
+ }
+
+ dir = vol->v_root;
+ while ( dir != SENTINEL ) {
+ if (dir->d_did == did)
+ return dir->d_name ? dir : NULL;
+ dir = (dir->d_did > did) ? dir->d_left : dir->d_right;
+ }
+ return NULL;
+}
+
+
+/* rotate the tree to the left */
+static void dir_leftrotate(vol, dir)
+ struct vol *vol;
+ struct dir *dir;
+{
+ struct dir *right = dir->d_right;
+
+ /* whee. move the right's left tree into dir's right tree */
+ dir->d_right = right->d_left;
+ if (right->d_left != SENTINEL)
+ right->d_left->d_back = dir;
+
+ if (right != SENTINEL) {
+ right->d_back = dir->d_back;
+ right->d_left = dir;
+ }
+
+ if (!dir->d_back) /* no parent. move the right tree to the top. */
+ vol->v_root = right;
+ else if (dir == dir->d_back->d_left) /* we were on the left */
+ dir->d_back->d_left = right;
+ else
+ dir->d_back->d_right = right; /* we were on the right */
+
+ /* re-insert dir on the left tree */
+ if (dir != SENTINEL)
+ dir->d_back = right;
+}
+
+
+
+/* rotate the tree to the right */
+static void dir_rightrotate(vol, dir)
+ struct vol *vol;
+ struct dir *dir;
+{
+ struct dir *left = dir->d_left;
+
+ /* whee. move the left's right tree into dir's left tree */
+ dir->d_left = left->d_right;
+ if (left->d_right != SENTINEL)
+ left->d_right->d_back = dir;
+
+ if (left != SENTINEL) {
+ left->d_back = dir->d_back;
+ left->d_right = dir;
+ }
+
+ if (!dir->d_back) /* no parent. move the left tree to the top. */
+ vol->v_root = left;
+ else if (dir == dir->d_back->d_right) /* we were on the right */
+ dir->d_back->d_right = left;
+ else
+ dir->d_back->d_left = left; /* we were on the left */
+
+ /* re-insert dir on the right tree */
+ if (dir != SENTINEL)
+ dir->d_back = left;
+}
+
+/* recolor after a removal */
+static struct dir *dir_rmrecolor(vol, dir)
+ struct vol *vol;
+ struct dir *dir;
+{
+ struct dir *leaf;
+
+ while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
+ /* are we on the left tree? */
+ if (dir == dir->d_back->d_left) {
+ leaf = dir->d_back->d_right; /* get right side */
+ if (leaf->d_color == DIRTREE_COLOR_RED) {
+ /* we're red. we need to change to black. */
+ leaf->d_color = DIRTREE_COLOR_BLACK;
+ dir->d_back->d_color = DIRTREE_COLOR_RED;
+ dir_leftrotate(vol, dir->d_back);
+ leaf = dir->d_back->d_right;
+ }
+
+ /* right leaf has black end nodes */
+ if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
+ (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
+ leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
+ dir = dir->d_back; /* ascend */
+ } else {
+ if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
+ leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
+ leaf->d_color = DIRTREE_COLOR_RED;
+ dir_rightrotate(vol, leaf);
+ leaf = dir->d_back->d_right;
+ }
+ leaf->d_color = dir->d_back->d_color;
+ dir->d_back->d_color = DIRTREE_COLOR_BLACK;
+ leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
+ dir_leftrotate(vol, dir->d_back);
+ dir = vol->v_root;
+ }
+ } else { /* right tree */
+ leaf = dir->d_back->d_left; /* left tree */
+ if (leaf->d_color == DIRTREE_COLOR_RED) {
+ leaf->d_color = DIRTREE_COLOR_BLACK;
+ dir->d_back->d_color = DIRTREE_COLOR_RED;
+ dir_rightrotate(vol, dir->d_back);
+ leaf = dir->d_back->d_left;
+ }
+
+ /* left leaf has black end nodes */
+ if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
+ (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
+ leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
+ dir = dir->d_back; /* ascend */
+ } else {
+ if (leaf->d_left->d_color == DIRTREE_COLOR_BLACK) {
+ leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
+ leaf->d_color = DIRTREE_COLOR_RED;
+ dir_leftrotate(vol, leaf);
+ leaf = dir->d_back->d_left;
+ }
+ leaf->d_color = dir->d_back->d_color;
+ dir->d_back->d_color = DIRTREE_COLOR_BLACK;
+ leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
+ dir_rightrotate(vol, dir->d_back);
+ dir = vol->v_root;
+ }
+ }
+ }
+ dir->d_color = DIRTREE_COLOR_BLACK;
+}
+
+
+/* remove the node from the tree. this is just like insertion, but
+ * different. actually, it has to worry about a bunch of things that
+ * insertion doesn't care about. */
+static void dir_remove( vol, dir )
+ struct vol *vol;
+ struct dir *dir;
+{
+#ifdef REMOVE_NODES
+ struct ofork *of, *last;
+ struct dir *node, *leaf;
+#endif
+
+ if (!dir || (dir == SENTINEL))
+ return;
+
+/* i'm not sure if it really helps to delete stuff. */
+#ifndef REMOVE_NODES
+ free(dir->d_name);
+ dir->d_name = NULL;
+#else
+
+ /* go searching for a node with at most one child */
+ if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
+ node = dir;
+ } else {
+ node = dir->d_right;
+ while (node->d_left != SENTINEL)
+ node = node->d_left;
+ }
+
+ /* get that child */
+ leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
+
+ /* detach node */
+ leaf->d_back = node->d_back;
+ if (!node->d_back) {
+ vol->v_root = leaf;
+ } else if (node == node->d_back->d_left) { /* left tree */
+ node->d_back->d_left = leaf;
+ } else {
+ node->d_back->d_right = leaf;
+ }
+
+ /* we want to free node, but we also want to free the data in dir.
+ * currently, that's d_name and the directory traversal bits.
+ * we just copy the necessary bits and then fix up all the
+ * various pointers to the directory. needless to say, there are
+ * a bunch of places that store the directory struct. */
+ if (node != dir) {
+ struct dir save, *tmp;
+
+ memcpy(&save, dir, sizeof(save));
+ memcpy(dir, node, sizeof(struct dir));
+
+ /* restore the red-black bits */
+ dir->d_left = save.d_left;
+ dir->d_right = save.d_right;
+ dir->d_back = save.d_back;
+ dir->d_color = save.d_color;
+
+ if (node == vol->v_dir) {/* we may need to fix up this pointer */
+ vol->v_dir = dir;
+ rootpar.d_child = vol->v_dir;
+ } else {
+ /* if we aren't the root directory, we have parents and
+ * siblings to worry about */
+ if (dir->d_parent->d_child == node)
+ dir->d_parent->d_child = dir;
+ dir->d_next->d_prev = dir;
+ dir->d_prev->d_next = dir;
+ }
+
+ /* fix up children. */
+ tmp = dir->d_child;
+ while (tmp) {
+ tmp->d_parent = dir;
+ tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
+ }
+
+ if (node == curdir) /* another pointer to fixup */
+ curdir = dir;
+
+ /* we also need to fix up oforks. bleah */
+ if ((of = dir->d_ofork)) {
+ last = of->of_d_prev;
+ while (of) {
+ of->of_dir = dir;
+ of = (last == of) ? NULL : of->of_d_next;
+ }
+ }
+
+ /* set the node's d_name */
+ node->d_name = save.d_name;
+ }
+
+ if (node->d_color == DIRTREE_COLOR_BLACK)
+ dir_rmrecolor(vol, leaf);
+ free(node->d_name);
+ free(node);
+#endif
+}
+
+
+static struct dir *dir_insert(vol, dir)
+ const struct vol *vol;
+ struct dir *dir;
+{
+ struct dir *pdir;
+
+ pdir = vol->v_root;
+ while (pdir->d_did != dir->d_did ) {
+ if ( pdir->d_did > dir->d_did ) {
+ if ( pdir->d_left == SENTINEL ) {
+ pdir->d_left = dir;
+ dir->d_back = pdir;
+ return NULL;
+ }
+ pdir = pdir->d_left;
+ } else {
+ if ( pdir->d_right == SENTINEL ) {
+ pdir->d_right = dir;
+ dir->d_back = pdir;
+ return NULL;
+ }
+ pdir = pdir->d_right;
+ }
+ }
+ return pdir;
+}
+
+
+/*
+ * attempt to extend the current dir. tree to include path
+ * as a side-effect, movecwd to that point and return the new dir
+ */
+
+static struct dir *
+extenddir( vol, dir, path )
+ struct vol *vol;
+ struct dir *dir;
+ char *path;
+{
+ char *p;
+ struct stat st;
+
+ p = mtoupath(vol, path );
+ if ( stat( p, &st ) != 0 ) {
+ return( NULL );
+ }
+ if (!S_ISDIR(st.st_mode)) {
+ return( NULL );
+ }
+
+ if (( dir = adddir( vol, dir, path, strlen( path ), p, strlen(p),
+ &st)) == NULL ) {
+ return( NULL );
+ }
+
+ if ( movecwd( vol, dir ) < 0 ) {
+ return( NULL );
+ }
+
+ return( dir );
+}
+
+static int deletedir(char *dir)
+{
+ char path[MAXPATHLEN + 1];
+ DIR *dp;
+ struct dirent *de;
+ struct stat st;
+ int len, err;
+
+ if ((len = strlen(dir)) > sizeof(path))
+ return AFPERR_PARAM;
+
+ /* already gone */
+ if ((dp = opendir(dir)) == NULL)
+ return AFP_OK;
+
+ strcpy(path, dir);
+ strcat(path, "/");
+ len++;
+ while ((de = readdir(dp))) {
+ /* skip this and previous directory */
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+
+ strncpy(path + len, de->d_name, sizeof(path) - len);
+ if (stat(path, &st) == 0) {
+ if (S_ISDIR(st.st_mode)) {
+ if ((err = deletedir(path)) < 0) {
+ closedir(dp);
+ return err;
+ }
+ } else if (unlink(path) < 0) {
+ switch (errno) {
+ case ENOENT :
+ continue; /* somebody went and deleted it behind our backs. */
+ case EROFS:
+ err = AFPERR_VLOCK;
+ case EPERM:
+ case EACCES :
+ err = AFPERR_ACCESS;
+ default :
+ err = AFPERR_PARAM;
+ }
+ closedir(dp);
+ return err;
+ }
+ }
+ }
+ closedir(dp);
+
+ /* okay. the directory is empty. delete it. note: we already got rid
+ of .AppleDouble. */
+ if (rmdir(dir) < 0) {
+ switch ( errno ) {
+ case ENOENT :
+ break;
+ case ENOTEMPTY : /* should never happen */
+ return( AFPERR_DIRNEMPT );
+ case EPERM:
+ case EACCES :
+ return( AFPERR_ACCESS );
+ case EROFS:
+ return AFPERR_VLOCK;
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+ return AFP_OK;
+}
+
+/* do a recursive copy. */
+static int copydir(char *src, char *dst, int noadouble)
+{
+ char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
+ DIR *dp;
+ struct dirent *de;
+ struct stat st;
+ struct utimbuf ut;
+ int slen, dlen, err;
+
+ /* doesn't exist or the path is too long. */
+ if (((slen = strlen(src)) > sizeof(spath) - 2) ||
+ ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
+ ((dp = opendir(src)) == NULL))
+ return AFPERR_PARAM;
+
+ /* try to create the destination directory */
+ if (ad_mkdir(dst, DIRBITS | 0777) < 0) {
+ closedir(dp);
+ switch ( errno ) {
+ case ENOENT :
+ return( AFPERR_NOOBJ );
+ case EROFS :
+ return( AFPERR_VLOCK );
+ case EPERM:
+ case EACCES :
+ return( AFPERR_ACCESS );
+ case EEXIST :
+ return( AFPERR_EXIST );
+ case ENOSPC :
+ case EDQUOT :
+ return( AFPERR_DFULL );
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+
+ /* set things up to copy */
+ strcpy(spath, src);
+ strcat(spath, "/");
+ slen++;
+ strcpy(dpath, dst);
+ strcat(dpath, "/");
+ dlen++;
+ err = AFP_OK;
+ while ((de = readdir(dp))) {
+ /* skip this and previous directory */
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+
+ strncpy(spath + slen, de->d_name, sizeof(spath) - slen);
+ if (stat(spath, &st) == 0) {
+ strncpy(dpath + dlen, de->d_name, sizeof(dpath) - dlen);
+
+ if (S_ISDIR(st.st_mode)) {
+ if ((err = copydir(spath, dpath, noadouble)) < 0)
+ goto copydir_done;
+ } else if ((err = copyfile(spath, dpath, NULL, noadouble)) < 0) {
+ goto copydir_done;
+
+ } else {
+ /* keep the same time stamp. */
+ ut.actime = ut.modtime = st.st_mtime;
+ utime(dpath, &ut);
+ }
+ }
+ }
+
+ /* keep the same time stamp. */
+ if (stat(src, &st) == 0) {
+ ut.actime = ut.modtime = st.st_mtime;
+ utime(dst, &ut);
+ }
+
+copydir_done:
+ closedir(dp);
+ return err;
+}
+
+
+/* --- public functions follow --- */
+
+/* NOTE: we start off with at least one node (the root directory). */
+struct dir *dirinsert( vol, dir )
+ struct vol *vol;
+ struct dir *dir;
+{
+ struct dir *node;
+
+ if ((node = dir_insert(vol, dir)))
+ return node;
+
+ /* recolor the tree. the current node is red. */
+ dir->d_color = DIRTREE_COLOR_RED;
+
+ /* parent of this node has to be black. if the parent node
+ * is red, then we have a grandparent. */
+ while ((dir != vol->v_root) &&
+ (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
+ /* are we on the left tree? */
+ if (dir->d_back == dir->d_back->d_back->d_left) {
+ node = dir->d_back->d_back->d_right; /* get the right node */
+ if (node->d_color == DIRTREE_COLOR_RED) {
+ /* we're red. we need to change to black. */
+ dir->d_back->d_color = DIRTREE_COLOR_BLACK;
+ node->d_color = DIRTREE_COLOR_BLACK;
+ dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
+ dir = dir->d_back->d_back; /* finished. go up. */
+ } else {
+ if (dir == dir->d_back->d_right) {
+ dir = dir->d_back;
+ dir_leftrotate(vol, dir);
+ }
+ dir->d_back->d_color = DIRTREE_COLOR_BLACK;
+ dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
+ dir_rightrotate(vol, dir->d_back->d_back);
+ }
+ } else {
+ node = dir->d_back->d_back->d_left;
+ if (node->d_color == DIRTREE_COLOR_RED) {
+ /* we're red. we need to change to black. */
+ dir->d_back->d_color = DIRTREE_COLOR_BLACK;
+ node->d_color = DIRTREE_COLOR_BLACK;
+ dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
+ dir = dir->d_back->d_back; /* finished. ascend */
+ } else {
+ if (dir == dir->d_back->d_left) {
+ dir = dir->d_back;
+ dir_rightrotate(vol, dir);
+ }
+ dir->d_back->d_color = DIRTREE_COLOR_BLACK;
+ dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
+ dir_leftrotate(vol, dir->d_back->d_back);
+ }
+ }
+ }
+
+ vol->v_root->d_color = DIRTREE_COLOR_BLACK;
+ return NULL;
+}
+
+/* free everything down. we don't bother to recolor as this is only
+ * called to free the entire tree */
+void dirfree( dir )
+ struct dir *dir;
+{
+ if (!dir || (dir == SENTINEL))
+ return;
+
+ if ( dir->d_left != SENTINEL ) {
+ dirfree( dir->d_left );
+ }
+ if ( dir->d_right != SENTINEL ) {
+ dirfree( dir->d_right );
+ }
+
+ if (dir != SENTINEL) {
+ free( dir->d_name );
+ free( dir );
+ }
+}
+
+
+struct dir *dirnew(const int len)
+{
+ struct dir *dir;
+
+ dir = (struct dir *) calloc(1, sizeof( struct dir ));
+ if (!dir)
+ return NULL;
+
+ if ((dir->d_name = (char *) malloc(sizeof(char)*len)) == NULL) {
+ free(dir);
+ return NULL;
+ }
+
+ dir->d_left = dir->d_right = SENTINEL;
+ dir->d_next = dir->d_prev = dir;
+ return dir;
+}
+
+
+/* XXX: this needs to be changed to handle path types */
+ char *
+cname( vol, dir, cpath )
+ const struct vol *vol;
+ struct dir *dir;
+ char **cpath;
+{
+ struct dir *cdir;
+ static char path[ MAXPATHLEN + 1];
+ char *data, *p;
+ int extend = 0;
+ int len;
+
+ data = *cpath;
+ if ( *data++ != 2 ) { /* path type */
+ return( NULL );
+ }
+ len = (unsigned char) *data++;
+ *cpath += len + 2;
+ *path = '\0';
+
+ for ( ;; ) {
+ if ( len == 0 ) {
+ if ( !extend && movecwd( vol, dir ) < 0 ) {
+ return( NULL );
+ }
+ return( path );
+ }
+
+ if ( *data == '\0' ) {
+ data++;
+ len--;
+ }
+
+ while ( *data == '\0' && len > 0 ) {
+ if ( dir->d_parent == NULL ) {
+ return( NULL );
+ }
+ dir = dir->d_parent;
+ data++;
+ len--;
+ }
+
+ /* would this be faster with strlen + strncpy? */
+ p = path;
+ while ( *data != '\0' && len > 0 ) {
+ *p++ = *data++;
+ len--;
+ }
+
+ /* short cut bits by chopping off a trailing \0. this also
+ makes the traversal happy w/ filenames at the end of the
+ cname. */
+ if (len == 1)
+ len--;
+
+#ifdef notdef
+ /*
+ * Dung Nguyen <ntd@adb.fr>
+ *
+ * AFPD cannot handle paths with "::" if the "::" notation is
+ * not at the beginning of the path. The following path will not
+ * be interpreted correctly:
+ *
+ * :a:b:::c: (directory c at the same level as directory a) */
+ if ( len > 0 ) {
+ data++;
+ len--;
+ }
+#endif notdef
+ *p = '\0';
+
+ if ( p != path ) { /* we got something */
+ if ( !extend ) {
+ cdir = dir->d_child;
+ while (cdir) {
+ if ( strcasecmp( cdir->d_name, path ) == 0 ) {
+ break;
+ }
+ cdir = (cdir == dir->d_child->d_prev) ? NULL :
+ cdir->d_next;
+ }
+ if ( cdir == NULL ) {
+ ++extend;
+ if ( movecwd( vol, dir ) < 0 ) {
+ return( NULL );
+ }
+ cdir = extenddir( vol, dir, path );
+ }
+
+ } else {
+ cdir = extenddir( vol, dir, path );
+ }
+
+ if ( cdir == NULL ) {
+ if ( len > 0 ) {
+ return( NULL );
+ }
+
+ } else {
+ dir = cdir;
+ *path = '\0';
+ }
+ }
+ }
+}
+
+/*
+ * Move curdir to dir, with a possible chdir()
+ */
+int movecwd( vol, dir)
+ const struct vol *vol;
+ struct dir *dir;
+{
+ char path[MAXPATHLEN + 1];
+ struct dir *d;
+ char *p, *u;
+ int n;
+
+ if ( dir == curdir ) {
+ return( 0 );
+ }
+ if ( dir->d_did == DIRDID_ROOT_PARENT) {
+ return( -1 );
+ }
+
+ p = path + sizeof(path) - 1;
+ *p-- = '\0';
+ *p = '.';
+ for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
+ *--p = '/';
+ u = mtoupath(vol, d->d_name );
+ n = strlen( u );
+ p -= n;
+ strncpy( p, u, n );
+ }
+ if ( d != curdir ) {
+ *--p = '/';
+ n = strlen( vol->v_path );
+ p -= n;
+ strncpy( p, vol->v_path, n );
+ }
+ if ( chdir( p ) < 0 ) {
+ return( -1 );
+ }
+ curdir = dir;
+ return( 0 );
+}
+
+int getdirparams(vol, bitmap, upath, dir, st, buf, buflen )
+ const struct vol *vol;
+ u_int16_t bitmap;
+ char *upath;
+ struct dir *dir;
+ struct stat *st;
+ char *buf;
+ int *buflen;
+{
+ struct maccess ma;
+ struct adouble ad;
+ char *data, *nameoff = NULL;
+ DIR *dp;
+ struct dirent *de;
+ int bit = 0, isad = 1;
+ u_int32_t aint;
+ u_int16_t ashort;
+
+ memset(&ad, 0, sizeof(ad));
+ if ( ad_open( upath, ADFLAGS_HF|ADFLAGS_DIR, O_RDONLY,
+ DIRBITS | 0777, &ad) < 0 ) {
+ isad = 0;
+ }
+
+ data = buf;
+ while ( bitmap != 0 ) {
+ while (( bitmap & 1 ) == 0 ) {
+ bitmap = bitmap>>1;
+ bit++;
+ }
+
+ switch ( bit ) {
+ case DIRPBIT_ATTR :
+ if ( isad ) {
+ ad_getattr(&ad, &ashort);
+ } else if (*upath == '.' && strcmp(upath, ".") &&
+ strcmp(upath, "..")) {
+ ashort = htons(ATTRBIT_INVISIBLE);
+ } else
+ ashort = 0;
+ memcpy( data, &ashort, sizeof( ashort ));
+ data += sizeof( ashort );
+ break;
+
+ case DIRPBIT_PDID :
+ if ( dir->d_did == DIRDID_ROOT) {
+ aint = DIRDID_ROOT_PARENT;
+ } else if (dir->d_did == DIRDID_ROOT_PARENT) {
+ aint = 0;
+ } else {
+ aint = dir->d_parent->d_did;
+ }
+ memcpy( data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case DIRPBIT_CDATE :
+ if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
+ aint = AD_DATE_FROM_UNIX(st->st_mtime);
+ memcpy( data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case DIRPBIT_MDATE :
+ aint = AD_DATE_FROM_UNIX(st->st_mtime);
+ memcpy( data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case DIRPBIT_BDATE :
+ if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
+ aint = AD_DATE_START;
+ memcpy( data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case DIRPBIT_FINFO :
+ if ( isad ) {
+ memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
+ } else { /* no appledouble */
+ memset( data, 0, 32 );
+ /* set default view -- this also gets done in ad_open() */
+ ashort = htons(FINDERINFO_CLOSEDVIEW);
+ memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
+
+ /* dot files are by default invisible */
+ if (*upath == '.' && strcmp(upath, ".") &&
+ strcmp(upath, "..")) {
+ ashort = htons(FINDERINFO_INVISIBLE);
+ memcpy(data + FINDERINFO_FRFLAGOFF,
+ &ashort, sizeof(ashort));
+ }
+ }
+ data += 32;
+ break;
+
+ case DIRPBIT_LNAME :
+ if (dir->d_name) /* root of parent can have a null name */
+ nameoff = data;
+ else
+ memset(data, 0, sizeof(u_int16_t));
+ data += sizeof( u_int16_t );
+ break;
+
+ case DIRPBIT_SNAME :
+ memset(data, 0, sizeof(u_int16_t));
+ data += sizeof( u_int16_t );
+ break;
+
+ case DIRPBIT_DID :
+ memcpy( data, &dir->d_did, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case DIRPBIT_OFFCNT :
+ ashort = 0;
+ /* this needs to handle current directory access rights */
+ if ((dp = opendir( upath ))) {
+ while (( de = readdir( dp )) != NULL ) {
+ if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, "."))
+ continue;
+
+ if (!validupath(vol, de->d_name))
+ continue;
+
+ /* now check against too long a filename */
+ if (strlen(utompath(vol, de->d_name)) > MACFILELEN)
+ continue;
+
+ ashort++;
+ }
+ closedir( dp );
+ }
+ ashort = htons( ashort );
+ memcpy( data, &ashort, sizeof( ashort ));
+ data += sizeof( ashort );
+ break;
+
+ case DIRPBIT_UID :
+ aint = st->st_uid;
+ memcpy( data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case DIRPBIT_GID :
+ aint = st->st_gid;
+ memcpy( data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case DIRPBIT_ACCESS :
+ utommode( st, &ma );
+#ifdef AFS
+ afsmode( upath, &ma, dir );
+#endif AFS
+ *data++ = ma.ma_user;
+ *data++ = ma.ma_world;
+ *data++ = ma.ma_group;
+ *data++ = ma.ma_owner;
+ break;
+
+ /* Client has requested the ProDOS information block.
+ Just pass back the same basic block for all
+ directories. <shirsch@ibm.net> */
+ case DIRPBIT_PDINFO : /* ProDOS Info Block */
+ *data++ = 0x0f;
+ *data++ = 0;
+ ashort = htons( 0x0200 );
+ memcpy( data, &ashort, sizeof( ashort ));
+ data += sizeof( ashort );
+ memset( data, 0, sizeof( ashort ));
+ data += sizeof( ashort );
+ break;
+
+ default :
+ if ( isad ) {
+ ad_close( &ad, ADFLAGS_HF );
+ }
+ return( AFPERR_BITMAP );
+ }
+ bitmap = bitmap>>1;
+ bit++;
+ }
+ if ( nameoff ) {
+ ashort = htons( data - buf );
+ memcpy( nameoff, &ashort, sizeof( ashort ));
+
+ if ((aint = strlen( dir->d_name )) > MACFILELEN)
+ aint = MACFILELEN;
+
+ *data++ = aint;
+ memcpy( data, dir->d_name, aint );
+ data += aint;
+ }
+ if ( isad ) {
+ ad_close( &ad, ADFLAGS_HF );
+ }
+ *buflen = data - buf;
+ return( AFP_OK );
+}
+
+int afp_setdirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct vol *vol;
+ struct dir *dir;
+ char *path;
+ u_int16_t vid, bitmap;
+ u_int32_t did;
+ int rc;
+
+ *rbuflen = 0;
+ ibuf += 2;
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (vol->v_flags & AFPVOL_RO)
+ return AFPERR_VLOCK;
+
+ memcpy( &did, ibuf, sizeof( did ));
+ ibuf += sizeof( int );
+
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ memcpy( &bitmap, ibuf, sizeof( bitmap ));
+ bitmap = ntohs( bitmap );
+ ibuf += sizeof( bitmap );
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ /*
+ * If ibuf is odd, make it even.
+ */
+ if ((u_long)ibuf & 1 ) {
+ ibuf++;
+ }
+
+ if (( rc = setdirparams(vol, path, bitmap, ibuf )) == AFP_OK ) {
+ setvoltime(obj, vol );
+ }
+ return( rc );
+}
+
+int setdirparams(vol, path, bitmap, buf )
+ const struct vol *vol;
+ char *path, *buf;
+ u_int16_t bitmap;
+{
+ struct maccess ma;
+ struct adouble ad;
+ struct utimbuf ut;
+ char *upath;
+ int bit = 0, aint, isad = 1;
+ u_int16_t ashort, bshort;
+ int err = AFP_OK;
+
+ upath = mtoupath(vol, path);
+ memset(&ad, 0, sizeof(ad));
+ if (ad_open( upath, vol_noadouble(vol)|ADFLAGS_HF|ADFLAGS_DIR,
+ O_RDWR|O_CREAT, 0666, &ad) < 0) {
+ /*
+ * Check to see what we're trying to set. If it's anything
+ * but ACCESS, UID, or GID, give an error. If it's any of those
+ * three, we don't need the ad to be open, so just continue.
+ *
+ * note: we also don't need to worry about mdate. also, be quiet
+ * if we're using the noadouble option.
+ */
+ if (!vol_noadouble(vol) && (bitmap &
+ ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
+ (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO))))
+ return AFPERR_ACCESS;
+
+ isad = 0;
+ } else {
+ /*
+ * Check to see if a create was necessary. If it was, we'll want
+ * to set our name, etc.
+ */
+ if ( ad_getoflags( &ad, ADFLAGS_HF ) & O_CREAT ) {
+ ad_setentrylen( &ad, ADEID_NAME, strlen( curdir->d_name ));
+ memcpy( ad_entry( &ad, ADEID_NAME ), curdir->d_name,
+ ad_getentrylen( &ad, ADEID_NAME ));
+ }
+ }
+
+ while ( bitmap != 0 ) {
+ while (( bitmap & 1 ) == 0 ) {
+ bitmap = bitmap>>1;
+ bit++;
+ }
+
+ switch( bit ) {
+ case DIRPBIT_ATTR :
+ if (isad) {
+ memcpy( &ashort, buf, sizeof( ashort ));
+ ad_getattr(&ad, &bshort);
+ if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
+ bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
+ } else {
+ bshort &= ~ashort;
+ }
+ ad_setattr(&ad, bshort);
+ }
+ buf += sizeof( ashort );
+ break;
+
+ case DIRPBIT_CDATE :
+ if (isad) {
+ memcpy(&aint, buf, sizeof(aint));
+ ad_setdate(&ad, AD_DATE_CREATE, aint);
+ }
+ buf += sizeof( aint );
+ break;
+
+ case DIRPBIT_MDATE :
+ memcpy(&aint, buf, sizeof(aint));
+ if (isad)
+ ad_setdate(&ad, AD_DATE_MODIFY, aint);
+ ut.actime = ut.modtime = AD_DATE_TO_UNIX(aint);
+ utime(upath, &ut);
+ buf += sizeof( aint );
+ break;
+
+ case DIRPBIT_BDATE :
+ if (isad) {
+ memcpy(&aint, buf, sizeof(aint));
+ ad_setdate(&ad, AD_DATE_BACKUP, aint);
+ }
+ buf += sizeof( aint );
+ break;
+
+ case DIRPBIT_FINFO :
+ /*
+ * Alright, we admit it, this is *really* sick!
+ * The 4 bytes that we don't copy, when we're dealing
+ * with the root of a volume, are the directory's
+ * location information. This eliminates that annoying
+ * behavior one sees when mounting above another mount
+ * point.
+ */
+ if (isad) {
+ if ( curdir->d_did == DIRDID_ROOT ) {
+ memcpy( ad_entry( &ad, ADEID_FINDERI ), buf, 10 );
+ memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, buf + 14, 18 );
+ } else {
+ memcpy( ad_entry( &ad, ADEID_FINDERI ), buf, 32 );
+ }
+ }
+ buf += 32;
+ break;
+
+ case DIRPBIT_UID : /* What kind of loser mounts as root? */
+ buf += sizeof( int );
+ break;
+
+ case DIRPBIT_GID :
+ memcpy( &aint, buf, sizeof( aint ));
+ buf += sizeof( aint );
+ if (curdir->d_did == DIRDID_ROOT)
+ setdeskowner( -1, aint );
+
+#if 0 /* don't error if we can't set the desktop owner. */
+ switch ( errno ) {
+ case EPERM :
+ case EACCES :
+ err = AFPERR_ACCESS;
+ goto setdirparam_done;
+ break;
+ case EROFS :
+ err = AFPERR_VLOCK;
+ goto setdirparam_done;
+ break;
+ default :
+ syslog( LOG_ERR, "setdirparam: setdeskowner: %m" );
+ if (!isad) {
+ err = AFPERR_PARAM;
+ goto setdirparam_done;
+ }
+ break;
+ }
+#endif
+
+ if ( setdirowner( -1, aint, vol_noadouble(vol) ) < 0 ) {
+ switch ( errno ) {
+ case EPERM :
+ case EACCES :
+ err = AFPERR_ACCESS;
+ goto setdirparam_done;
+ break;
+ case EROFS :
+ err = AFPERR_VLOCK;
+ goto setdirparam_done;
+ break;
+ default :
+ syslog( LOG_ERR, "setdirparam: setdirowner: %m" );
+ break;
+ }
+ }
+ break;
+
+ case DIRPBIT_ACCESS :
+ ma.ma_user = *buf++;
+ ma.ma_world = *buf++;
+ ma.ma_group = *buf++;
+ ma.ma_owner = *buf++;
+
+ if (curdir->d_did == DIRDID_ROOT)
+ setdeskmode(mtoumode( &ma ));
+#if 0 /* don't error if we can't set the desktop mode */
+ switch ( errno ) {
+ case EPERM :
+ case EACCES :
+ err = AFPERR_ACCESS;
+ goto setdirparam_done;
+ case EROFS :
+ err = AFPERR_VLOCK;
+ goto setdirparam_done;
+ default :
+ syslog( LOG_ERR, "setdirparam: setdeskmode: %m" );
+ break;
+ err = AFPERR_PARAM;
+ goto setdirparam_done;
+ }
+ }
+#endif
+
+ if ( setdirmode( mtoumode( &ma ), vol_noadouble(vol)) < 0 ) {
+ switch ( errno ) {
+ case EPERM :
+ case EACCES :
+ err = AFPERR_ACCESS;
+ goto setdirparam_done;
+ case EROFS :
+ err = AFPERR_VLOCK;
+ goto setdirparam_done;
+ default :
+ syslog( LOG_ERR, "setdirparam: setdirmode: %m" );
+ err = AFPERR_PARAM;
+ goto setdirparam_done;
+ }
+ }
+ break;
+
+ /* Ignore what the client thinks we should do to the
+ ProDOS information block. Skip over the data and
+ report nothing amiss. <shirsch@ibm.net> */
+ case DIRPBIT_PDINFO :
+ buf += 6;
+ break;
+
+ default :
+ err = AFPERR_BITMAP;
+ goto setdirparam_done;
+ break;
+ }
+
+ bitmap = bitmap>>1;
+ bit++;
+ }
+
+
+setdirparam_done:
+ if ( isad ) {
+ ad_flush( &ad, ADFLAGS_HF );
+ ad_close( &ad, ADFLAGS_HF );
+ }
+
+ return err;
+}
+
+int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct adouble ad;
+ struct stat st;
+ struct vol *vol;
+ struct dir *dir;
+ char *path, *upath;
+ u_int32_t did;
+ u_int16_t vid;
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (vol->v_flags & AFPVOL_RO)
+ return AFPERR_VLOCK;
+
+ memcpy( &did, ibuf, sizeof( did ));
+ ibuf += sizeof( did );
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ /* check for illegal bits */
+ if ((vol->v_flags & AFPVOL_MSWINDOWS) &&
+ strpbrk(path, MSWINDOWS_BADCHARS))
+ return AFPERR_PARAM;
+
+ upath = mtoupath(vol, path);
+
+ if ((vol->v_flags & AFPVOL_NOHEX) && strchr(upath, '/'))
+ return AFPERR_PARAM;
+
+ if (!validupath(vol, upath))
+ return AFPERR_EXIST;
+
+ if ( ad_mkdir( upath, DIRBITS | 0777 ) < 0 ) {
+ switch ( errno ) {
+ case ENOENT :
+ return( AFPERR_NOOBJ );
+ case EROFS :
+ return( AFPERR_VLOCK );
+ case EACCES :
+ return( AFPERR_ACCESS );
+ case EEXIST :
+ return( AFPERR_EXIST );
+ case ENOSPC :
+ case EDQUOT :
+ return( AFPERR_DFULL );
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+
+ if (stat(upath, &st) < 0)
+ return AFPERR_MISC;
+
+ if ((dir = adddir( vol, curdir, path, strlen( path ), upath,
+ strlen(upath), &st)) == NULL)
+ return AFPERR_MISC;
+
+ if ( movecwd( vol, dir ) < 0 ) {
+ return( AFPERR_PARAM );
+ }
+
+ memset(&ad, 0, sizeof(ad));
+ if (ad_open( "", vol_noadouble(vol)|ADFLAGS_HF|ADFLAGS_DIR,
+ O_RDWR|O_CREAT, 0666, &ad ) < 0) {
+ if (vol_noadouble(vol))
+ goto createdir_done;
+ return( AFPERR_ACCESS );
+ }
+
+ ad_setentrylen( &ad, ADEID_NAME, strlen( path ));
+ memcpy( ad_entry( &ad, ADEID_NAME ), path,
+ ad_getentrylen( &ad, ADEID_NAME ));
+ ad_flush( &ad, ADFLAGS_HF );
+ ad_close( &ad, ADFLAGS_HF );
+
+createdir_done:
+ memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
+ *rbuflen = sizeof( u_int32_t );
+ setvoltime(obj, vol );
+ return( AFP_OK );
+}
+
+
+int renamedir(src, dst, dir, newparent, newname, noadouble)
+ char *src, *dst, *newname;
+ struct dir *dir, *newparent;
+ const int noadouble;
+{
+ struct adouble ad;
+ struct dir *parent;
+ char *buf;
+ int len, err;
+
+ /* existence check moved to afp_moveandrename */
+ if ( rename( src, dst ) < 0 ) {
+ switch ( errno ) {
+ case ENOENT :
+ return( AFPERR_NOOBJ );
+ case EACCES :
+ return( AFPERR_ACCESS );
+ case EROFS:
+ return AFPERR_VLOCK;
+ case EINVAL:
+ /* tried to move directory into a subdirectory of itself */
+ return AFPERR_CANTMOVE;
+ case EXDEV:
+ /* this needs to copy and delete. bleah. that means we have
+ * to deal with entire directory hierarchies. */
+ if ((err = copydir(src, dst, noadouble)) < 0) {
+ deletedir(dst);
+ return err;
+ }
+ if ((err = deletedir(src)) < 0)
+ return err;
+ break;
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+
+ memset(&ad, 0, sizeof(ad));
+ if ( ad_open( dst, ADFLAGS_HF|ADFLAGS_DIR, O_RDWR, 0, &ad) < 0 ) {
+ switch ( errno ) {
+ case ENOENT :
+ if (noadouble) {
+ len = strlen(newname);
+ goto renamedir_done;
+ }
+ return( AFPERR_NOOBJ );
+ case EACCES :
+ return( AFPERR_ACCESS );
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+ len = strlen( newname );
+ ad_setentrylen( &ad, ADEID_NAME, len );
+ memcpy( ad_entry( &ad, ADEID_NAME ), newname, len );
+ ad_flush( &ad, ADFLAGS_HF );
+ ad_close( &ad, ADFLAGS_HF );
+
+renamedir_done:
+ if ((buf = (char *) realloc( dir->d_name, len + 1 )) == NULL ) {
+ syslog( LOG_ERR, "renamedir: realloc: %m" );
+ return AFPERR_MISC;
+ }
+ dir->d_name = buf;
+ strcpy( dir->d_name, newname );
+
+ if (( parent = dir->d_parent ) == NULL ) {
+ return( AFP_OK );
+ }
+ if ( parent == newparent ) {
+ return( AFP_OK );
+ }
+
+ /* detach from old parent and add to new one. */
+ dirchildremove(parent, dir);
+ dir->d_parent = newparent;
+ dirchildadd(newparent, dir);
+ return( AFP_OK );
+}
+
+#define DOT_APPLEDOUBLE_LEN 13
+/* delete an empty directory */
+int deletecurdir( vol, path, pathlen )
+ const struct vol *vol;
+ char *path;
+ int pathlen;
+{
+ struct dirent *de;
+ struct stat st;
+ struct dir *fdir;
+ DIR *dp;
+
+ if ( curdir->d_parent == NULL ) {
+ return( AFPERR_ACCESS );
+ }
+
+ if ( curdir->d_child != NULL ) {
+ return( AFPERR_DIRNEMPT );
+ }
+
+ fdir = curdir;
+
+ /* delete stray .AppleDouble files. this happens to get .Parent files
+ as well. */
+ if ((dp = opendir(".AppleDouble"))) {
+ strcpy(path, ".AppleDouble/");
+ while ((de = readdir(dp))) {
+ /* skip this and previous directory */
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+
+ /* bail if the file exists in the current directory.
+ * note: this will not fail with dangling symlinks */
+ if (stat(de->d_name, &st) == 0) {
+ closedir(dp);
+ return AFPERR_DIRNEMPT;
+ }
+
+ strcpy(path + DOT_APPLEDOUBLE_LEN, de->d_name);
+ if (unlink(path) < 0) {
+ closedir(dp);
+ switch (errno) {
+ case EPERM:
+ case EACCES :
+ return( AFPERR_ACCESS );
+ case EROFS:
+ return AFPERR_VLOCK;
+ case ENOENT :
+ continue;
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+ }
+ closedir(dp);
+ }
+
+ if ( rmdir( ".AppleDouble" ) < 0 ) {
+ switch ( errno ) {
+ case ENOENT :
+ break;
+ case ENOTEMPTY :
+ return( AFPERR_DIRNEMPT );
+ case EROFS:
+ return AFPERR_VLOCK;
+ case EPERM:
+ case EACCES :
+ return( AFPERR_ACCESS );
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+
+ /* now get rid of dangling symlinks */
+ if ((dp = opendir("."))) {
+ while ((de = readdir(dp))) {
+ /* skip this and previous directory */
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+
+ /* bail if it's not a symlink */
+ if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode))
+ return AFPERR_DIRNEMPT;
+
+ if (unlink(de->d_name) < 0) {
+ switch (errno) {
+ case EPERM:
+ case EACCES :
+ return( AFPERR_ACCESS );
+ case EROFS:
+ return AFPERR_VLOCK;
+ case ENOENT :
+ continue;
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+ }
+ closedir(dp);
+ }
+
+ if ( movecwd( vol, curdir->d_parent ) < 0 ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if ( rmdir(mtoupath(vol, fdir->d_name)) < 0 ) {
+ switch ( errno ) {
+ case ENOENT :
+ return( AFPERR_NOOBJ );
+ case ENOTEMPTY :
+ return( AFPERR_DIRNEMPT );
+ case EPERM:
+ case EACCES :
+ return( AFPERR_ACCESS );
+ case EROFS:
+ return AFPERR_VLOCK;
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+
+ dirchildremove(curdir, fdir);
+#if AD_VERSION > AD_VERSION1
+ cnid_delete(vol->v_db, fdir->d_did);
+#endif
+ dir_remove( vol, fdir );
+
+ return( AFP_OK );
+}
+
+int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct passwd *pw;
+ struct group *gr;
+ char *name;
+ u_int32_t id;
+ int len, sfunc;
+
+ ibuf++;
+ sfunc = (unsigned char) *ibuf++;
+ memcpy( &id, ibuf, sizeof( id ));
+
+ if ( id != 0 ) {
+ switch ( sfunc ) {
+ case 1 :
+ if (( pw = getpwuid( id )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_NOITEM );
+ }
+ name = pw->pw_name;
+ break;
+
+ case 2 :
+ if (( gr = (struct group *)getgrgid( id )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_NOITEM );
+ }
+ name = gr->gr_name;
+ break;
+
+ default :
+ *rbuflen = 0;
+ return( AFPERR_PARAM );
+ }
+
+ len = strlen( name );
+
+ } else {
+ len = 0;
+ name = NULL;
+ }
+
+ *rbuf++ = len;
+ if ( len > 0 ) {
+ memcpy( rbuf, name, len );
+ }
+ *rbuflen = len + 1;
+ return( AFP_OK );
+}
+
+int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct passwd *pw;
+ struct group *gr;
+ int len, sfunc;
+ u_int32_t id;
+
+ ibuf++;
+ sfunc = (unsigned char) *ibuf++;
+ len = (unsigned char) *ibuf++;
+ ibuf[ len ] = '\0';
+
+ if ( len != 0 ) {
+ switch ( sfunc ) {
+ case 3 :
+ if (( pw = (struct passwd *)getpwnam( ibuf )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_NOITEM );
+ }
+ id = pw->pw_uid;
+ break;
+
+ case 4 :
+ if (( gr = (struct group *)getgrnam( ibuf )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_NOITEM );
+ }
+ id = gr->gr_gid;
+ break;
+ default :
+ *rbuflen = 0;
+ return( AFPERR_PARAM );
+ }
+ } else {
+ id = 0;
+ }
+
+ memcpy( rbuf, &id, sizeof( id ));
+ *rbuflen = sizeof( id );
+ return( AFP_OK );
+}
+
+/* variable DID support */
+int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+#if 0
+ struct vol *vol;
+ struct dir *dir;
+ u_int16_t vid;
+ u_int32_t did;
+#endif
+
+ *rbuflen = 0;
+
+ /* do nothing as dids are static for the life of the process. */
+#if 0
+ ibuf += 2;
+
+ memcpy(&vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy( &did, ibuf, sizeof( did ));
+ ibuf += sizeof( did );
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ /* dir_remove -- deletedid */
+#endif
+
+ return AFP_OK;
+}
+
+/* did creation gets done automatically */
+int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct vol *vol;
+ struct dir *dir, *parentdir;
+ struct stat st;
+ char *path, *upath;
+ u_int32_t did;
+ u_int16_t vid;
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy(&vid, ibuf, sizeof(vid));
+ ibuf += sizeof( vid );
+
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy(&did, ibuf, sizeof(did));
+ ibuf += sizeof(did);
+
+ if (( parentdir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if (( path = cname( vol, parentdir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ /* see if we already have the directory. */
+ upath = mtoupath(vol, path);
+ if ( stat( upath, &st ) < 0 ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ dir = parentdir->d_child;
+ while (dir) {
+ if (strdiacasecmp(dir->d_name, path) == 0) {
+ memcpy(rbuf, &dir->d_did, sizeof(dir->d_did));
+ *rbuflen = sizeof(dir->d_did);
+ return AFP_OK;
+ }
+ dir = (dir == parentdir->d_child->d_prev) ? NULL : dir->d_next;
+ }
+
+ /* we don't already have a did. add one in. */
+ if ((dir = adddir(vol, parentdir, path, strlen(path),
+ upath, strlen(upath), &st)) == NULL)
+ return AFPERR_MISC;
+
+ memcpy(rbuf, &dir->d_did, sizeof(dir->d_did));
+ *rbuflen = sizeof(dir->d_did);
+ return AFP_OK;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef AFPD_DIRECTORY_H
+#define AFPD_DIRECTORY_H 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+/*#include <sys/stat.h>*/ /* including it here causes some confusion */
+#include <netatalk/endian.h>
+
+/* sys/types.h usually snarfs in major/minor macros. if they don't
+ * try this file. */
+#ifndef major
+#include <sys/sysmacros.h>
+#endif
+
+#include "globals.h"
+#include "volume.h"
+
+/* the did tree is now a red-black tree while the parent/child
+ * tree is a circular doubly-linked list. how exciting. */
+struct dir {
+ struct dir *d_left, *d_right, *d_back; /* for red-black tree */
+ int d_color;
+ struct dir *d_parent, *d_child; /* parent-child */
+ struct dir *d_prev, *d_next; /* siblings */
+ void *d_ofork; /* oforks using this directory. */
+ u_int32_t d_did;
+ int d_flags;
+ char *d_name;
+};
+
+/* child addition/removal macros */
+#define dirchildadd(a, b) do { \
+ if (!(a)->d_child) \
+ (a)->d_child = (b); \
+ else { \
+ (b)->d_next = (a)->d_child; \
+ (b)->d_prev = (b)->d_next->d_prev; \
+ (b)->d_next->d_prev = (b); \
+ (b)->d_prev->d_next = (b); \
+ } \
+} while (0)
+
+#define dirchildremove(a,b) do { \
+ if ((a)->d_child == (b)) \
+ (a)->d_child = ((b) == (b)->d_next) ? NULL : (b)->d_next; \
+ (b)->d_next->d_prev = (b)->d_prev; \
+ (b)->d_prev->d_next = (b)->d_next; \
+ (b)->d_next = (b)->d_prev = (b); \
+} while (0)
+
+#define DIRTREE_COLOR_RED 0
+#define DIRTREE_COLOR_BLACK 1
+
+/* setgid directories */
+#ifndef DIRBITS
+#define DIRBITS S_ISGID
+#endif
+
+#define DIRF_FSMASK (3<<0)
+#define DIRF_NOFS (0<<0)
+#define DIRF_AFS (1<<0)
+#define DIRF_UFS (2<<0)
+
+#define AFPDIR_READ (1<<0)
+
+/* directory bits */
+#define DIRPBIT_ATTR 0
+#define DIRPBIT_PDID 1
+#define DIRPBIT_CDATE 2
+#define DIRPBIT_MDATE 3
+#define DIRPBIT_BDATE 4
+#define DIRPBIT_FINFO 5
+#define DIRPBIT_LNAME 6
+#define DIRPBIT_SNAME 7
+#define DIRPBIT_DID 8
+#define DIRPBIT_OFFCNT 9
+#define DIRPBIT_UID 10
+#define DIRPBIT_GID 11
+#define DIRPBIT_ACCESS 12
+#define DIRPBIT_PDINFO 13 /* ProDOS Info */
+
+/* directory attribute bits (see file.h for other bits) */
+#define ATTRBIT_EXPFOLDER (1 << 1) /* shared point */
+#define ATTRBIT_MOUNTED (1 << 3) /* mounted share point by non-admin */
+#define ATTRBIT_INEXPFOLDER (1 << 4) /* folder in a shared area */
+
+#define FILDIRBIT_ISDIR (1 << 7) /* is a directory */
+#define FILDIRBIT_ISFILE (0) /* is a file */
+
+/* reserved directory id's */
+#define DIRDID_ROOT_PARENT htonl(1) /* parent directory of root */
+#define DIRDID_ROOT htonl(2) /* root directory */
+
+/* file/directory ids. what a mess. we scramble things in a vain attempt
+ * to get something meaningful */
+#ifndef AFS
+#define CNID_XOR(a) (((a) >> 16) ^ (a))
+#define CNID_DEV(a) ((((CNID_XOR(major((a)->st_dev)) & 0xf) << 3) | \
+ (CNID_XOR(minor((a)->st_dev)) & 0x7)) << 24)
+#define CNID_INODE(a) (((a)->st_ino ^ (((a)->st_ino & 0xff000000) >> 8)) \
+ & 0x00ffffff)
+#define CNID_FILE(a) (((a) & 0x1) << 31)
+#define CNID(a,b) (CNID_DEV(a) | CNID_INODE(a) | CNID_FILE(b))
+#else
+#define CNID(a,b) (((a)->st_ino & 0x7fffffff) | CNID_FILE(b))
+#endif
+
+
+struct maccess {
+ u_char ma_user;
+ u_char ma_world;
+ u_char ma_group;
+ u_char ma_owner;
+};
+
+#define AR_USEARCH (1<<0)
+#define AR_UREAD (1<<1)
+#define AR_UWRITE (1<<2)
+#define AR_UOWN (1<<7)
+
+extern struct dir *dirnew __P((const int));
+extern void dirfree __P((struct dir *));
+extern struct dir *dirsearch __P((const struct vol *, u_int32_t));
+extern struct dir *adddir __P((struct vol *, struct dir *, char *,
+ int, char *, int, struct stat *));
+extern struct dir *dirinsert __P((struct vol *, struct dir *));
+extern int movecwd __P((const struct vol *, struct dir *));
+extern int deletecurdir __P((const struct vol *, char *, int));
+extern char *cname __P((const struct vol *, struct dir *,
+ char **));
+extern mode_t mtoumode __P((struct maccess *));
+extern void utommode __P((struct stat *, struct maccess *));
+extern int getdirparams __P((const struct vol *, u_int16_t, char *,
+ struct dir *, struct stat *, char *, int *));
+extern int setdirparams __P((const struct vol *, char *, u_int16_t, char *));
+extern int renamedir __P((char *, char *, struct dir *,
+ struct dir *, char *, const int));
+
+
+/* FP functions */
+extern int afp_createdir __P((AFPObj *, char *, int, char *, int *));
+extern int afp_opendir __P((AFPObj *, char *, int, char *, int *));
+extern int afp_setdirparams __P((AFPObj *, char *, int, char *, int *));
+extern int afp_closedir __P((AFPObj *, char *, int, char *, int *));
+extern int afp_mapid __P((AFPObj *, char *, int, char *, int *));
+extern int afp_mapname __P((AFPObj *, char *, int, char *, int *));
+
+/* from enumerate.c */
+extern int afp_enumerate __P((AFPObj *, char *, int, char *, int *));
+extern int afp_catsearch __P((AFPObj *, char *, int, char *, int *));
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/param.h>
+
+#include <netatalk/endian.h>
+#include <atalk/afp.h>
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+
+#include "desktop.h"
+#include "directory.h"
+#include "volume.h"
+#include "globals.h"
+#include "file.h"
+
+
+struct dir *
+adddir( vol, dir, name, namlen, upath, upathlen, st )
+ struct vol *vol;
+ struct dir *dir;
+ char *name, *upath;
+ int namlen, upathlen;
+ struct stat *st;
+{
+ struct dir *cdir, *edir;
+#if AD_VERSION > AD_VERSION1
+ struct adouble ad;
+#endif
+
+#ifndef USE_LASTDID
+ struct stat lst, *lstp;
+#endif
+
+ if ((cdir = dirnew(namlen + 1)) == NULL) {
+ syslog( LOG_ERR, "adddir: malloc: %m" );
+ return NULL;
+ }
+ strcpy( cdir->d_name, name );
+ cdir->d_name[namlen] = '\0';
+
+#if AD_VERSION > AD_VERSION1
+ /* find out if we have a fixed did already */
+ if (!(cdir->d_did = cnid_lookup(vol->v_db, st, dir->d_did, upath,
+ upathlen))) {
+ memset(&ad, 0, sizeof(ad));
+ if (ad_open(upath, ADFLAGS_HF|ADFLAGS_DIR, O_RDONLY, 0, &ad) < 0)
+ cdir->d_did = 0;
+ else {
+ memcpy(&cdir->d_did, ad_entry(&ad, ADEID_DID), sizeof(cdir->d_did));
+ ad_close(&ad, ADFLAGS_HF);
+ }
+
+ if (!(cdir->d_did = cnid_add(vol->v_db, st, dir->d_did, upath,
+ upathlen, cdir->d_did))) {
+#ifdef USE_LASTDID
+ cdir->d_did = htonl( vol->v_lastdid++ );
+#else
+ lstp = lstat(upath, &lst) < 0 ? st : &lst;
+ cdir->d_did = htonl( CNID(lstp, 0) );
+#endif
+ }
+ }
+#else
+
+#ifdef USE_LASTDID
+ cdir->d_did = htonl( vol->v_lastdid++ );
+#else
+ lstp = lstat(upath, &lst) < 0 ? st : &lst;
+ cdir->d_did = htonl( CNID(lstp, 0) );
+#endif
+#endif
+
+ if (edir = dirinsert( vol, cdir )) {
+ if (edir->d_name) {
+ if (strcmp(edir->d_name, cdir->d_name)) {
+ syslog(LOG_INFO, "WARNING: DID conflict for '%s' and '%s'. Are these the same file?", edir->d_name, cdir->d_name);
+ }
+ free(cdir->d_name);
+ free(cdir);
+ return edir;
+ }
+ edir->d_name = cdir->d_name;
+ free(cdir);
+ cdir = edir;
+ }
+
+ /* parent/child directories */
+ cdir->d_parent = dir;
+ dirchildadd(dir, cdir);
+ return( cdir );
+}
+
+/*
+ * Struct to save directory reading context in. Used to prevent
+ * O(n^2) searches on a directory.
+ */
+struct savedir {
+ u_short sd_vid;
+ int sd_did;
+ int sd_buflen;
+ char *sd_buf;
+ char *sd_last;
+ int sd_sindex;
+};
+#define SDBUFBRK 1024
+
+int afp_enumerate(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct stat st;
+ static struct savedir sd = { 0, 0, 0, NULL, NULL, 0 };
+ struct vol *vol;
+ struct dir *dir;
+ struct dirent *de;
+ DIR *dp;
+ int did, ret, esz, len, first = 1;
+ char *path, *data, *end, *start;
+ u_int16_t vid, fbitmap, dbitmap, reqcnt, actcnt = 0;
+ u_int16_t sindex, maxsz, sz = 0;
+
+ if ( sd.sd_buflen == 0 ) {
+ if (( sd.sd_buf = (char *)malloc( SDBUFBRK )) == NULL ) {
+ syslog( LOG_ERR, "afp_enumerate: malloc: %m" );
+ *rbuflen = 0;
+ return AFPERR_MISC;
+ }
+ sd.sd_buflen = SDBUFBRK;
+ }
+
+ ibuf += 2;
+
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_PARAM );
+ }
+
+ memcpy( &did, ibuf, sizeof( did ));
+ ibuf += sizeof( did );
+
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_NODIR );
+ }
+
+ memcpy( &fbitmap, ibuf, sizeof( fbitmap ));
+ fbitmap = ntohs( fbitmap );
+ ibuf += sizeof( fbitmap );
+
+ memcpy( &dbitmap, ibuf, sizeof( dbitmap ));
+ dbitmap = ntohs( dbitmap );
+ ibuf += sizeof( dbitmap );
+
+ /* check for proper bitmaps -- the stuff in comments is for
+ * variable directory ids. */
+ if (!(fbitmap || dbitmap)
+ /*|| (fbitmap & (1 << FILPBIT_PDID)) ||
+ (dbitmap & (1 << DIRPBIT_PDID))*/) {
+ *rbuflen = 0;
+ return AFPERR_BITMAP;
+ }
+
+ memcpy( &reqcnt, ibuf, sizeof( reqcnt ));
+ reqcnt = ntohs( reqcnt );
+ ibuf += sizeof( reqcnt );
+
+ memcpy( &sindex, ibuf, sizeof( sindex ));
+ sindex = ntohs( sindex );
+ ibuf += sizeof( sindex );
+
+ memcpy( &maxsz, ibuf, sizeof( maxsz ));
+ maxsz = ntohs( maxsz );
+ ibuf += sizeof( maxsz );
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_NODIR );
+ }
+ data = rbuf + 3 * sizeof( u_int16_t );
+ sz = 3 * sizeof( u_int16_t );
+
+ /*
+ * Read the directory into a pre-malloced buffer, stored
+ * len <name> \0
+ * The end is indicated by a len of 0.
+ */
+ if ( sindex == 1 || curdir->d_did != sd.sd_did || vid != sd.sd_vid ) {
+ sd.sd_last = sd.sd_buf;
+
+ if (( dp = opendir( mtoupath(vol, path ))) == NULL ) {
+ *rbuflen = 0;
+ return (errno == ENOTDIR) ? AFPERR_BADTYPE : AFPERR_NODIR;
+ }
+
+ end = sd.sd_buf + sd.sd_buflen;
+ for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
+ if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, "."))
+ continue;
+
+ if (!(validupath(vol, de->d_name)))
+ continue;
+
+ /* now check against too big a file */
+ if (strlen(utompath(vol, de->d_name)) > MACFILELEN)
+ continue;
+
+ len = strlen(de->d_name);
+ *(sd.sd_last)++ = len;
+
+ if ( sd.sd_last + len + 2 > end ) {
+ char *buf;
+
+ start = sd.sd_buf;
+ if ((buf = (char *) realloc( sd.sd_buf, sd.sd_buflen +
+ SDBUFBRK )) == NULL ) {
+ syslog( LOG_ERR, "afp_enumerate: realloc: %m" );
+ closedir(dp);
+ *rbuflen = 0;
+ return AFPERR_MISC;
+ }
+ sd.sd_buf = buf;
+ sd.sd_buflen += SDBUFBRK;
+ sd.sd_last = ( sd.sd_last - start ) + sd.sd_buf;
+ end = sd.sd_buf + sd.sd_buflen;
+ }
+
+ memcpy( sd.sd_last, de->d_name, len + 1 );
+ sd.sd_last += len + 1;
+ }
+ *sd.sd_last = 0;
+
+ sd.sd_last = sd.sd_buf;
+ sd.sd_sindex = 1;
+
+ closedir( dp );
+ sd.sd_vid = vid;
+ sd.sd_did = did;
+ }
+
+ /*
+ * Position sd_last as dictated by sindex.
+ */
+ if ( sindex < sd.sd_sindex ) {
+ sd.sd_sindex = 1;
+ sd.sd_last = sd.sd_buf;
+ }
+ while ( sd.sd_sindex < sindex ) {
+ len = *(sd.sd_last)++;
+ if ( len == 0 ) {
+ sd.sd_did = -1; /* invalidate sd struct to force re-read */
+ *rbuflen = 0;
+ return( AFPERR_NOOBJ );
+ }
+ sd.sd_last += len + 1;
+ sd.sd_sindex++;
+ }
+
+ while (( len = *(sd.sd_last)) != 0 ) {
+ /*
+ * If we've got all we need, send it.
+ */
+ if ( actcnt == reqcnt ) {
+ break;
+ }
+
+ /*
+ * Save the start position, in case we exceed the buffer
+ * limitation, and have to back up one.
+ */
+ start = sd.sd_last;
+ sd.sd_last++;
+
+ if ( stat( sd.sd_last, &st ) < 0 ) {
+ syslog( LOG_DEBUG, "afp_enumerate: stat %s: %m", sd.sd_last );
+ sd.sd_last += len + 1;
+ continue;
+ }
+
+ /*
+ * If a fil/dir is not a dir, it's a file. This is slightly
+ * inaccurate, since that means /dev/null is a file, /dev/printer
+ * is a file, etc.
+ */
+ if ( S_ISDIR(st.st_mode)) {
+ if ( dbitmap == 0 ) {
+ sd.sd_last += len + 1;
+ continue;
+ }
+ path = utompath(vol, sd.sd_last);
+ dir = curdir->d_child;
+ while (dir) {
+ if ( strcmp( dir->d_name, path ) == 0 ) {
+ break;
+ }
+ dir = (dir == curdir->d_child->d_prev) ? NULL : dir->d_next;
+ }
+ if (!dir && ((dir = adddir( vol, curdir, path, strlen( path ),
+ sd.sd_last, len, &st)) == NULL)) {
+ *rbuflen = 0;
+ return AFPERR_MISC;
+ }
+
+
+ if (( ret = getdirparams(vol, dbitmap, sd.sd_last, dir,
+ &st, data + 2 * sizeof( u_char ), &esz )) != AFP_OK ) {
+ *rbuflen = 0;
+ return( ret );
+ }
+
+ } else {
+ if ( fbitmap == 0 ) {
+ sd.sd_last += len + 1;
+ continue;
+ }
+
+ if (( ret = getfilparams(vol, fbitmap, utompath(vol, sd.sd_last),
+ curdir, &st, data + 2 * sizeof( u_char ), &esz )) !=
+ AFP_OK ) {
+ *rbuflen = 0;
+ return( ret );
+ }
+ }
+
+ /*
+ * Make sure entry is an even length, possibly with a null
+ * byte on the end.
+ */
+ if ( esz & 1 ) {
+ *(data + 2 * sizeof( u_char ) + esz ) = '\0';
+ esz++;
+ }
+
+ /*
+ * Check if we've exceeded the size limit.
+ */
+ if ( maxsz < sz + esz + 2 * sizeof( u_char )) {
+ if (first) { /* maxsz can't hold a single reply */
+ *rbuflen = 0;
+ return AFPERR_PARAM;
+ }
+ sd.sd_last = start;
+ break;
+ }
+
+ if (first)
+ first = 0;
+
+ sz += esz + 2 * sizeof( u_char );
+ *data++ = esz + 2 * sizeof( u_char );
+ *data++ = S_ISDIR(st.st_mode) ? FILDIRBIT_ISDIR : FILDIRBIT_ISFILE;
+ data += esz;
+ actcnt++;
+ sd.sd_last += len + 1;
+ }
+
+ if ( actcnt == 0 ) {
+ *rbuflen = 0;
+ sd.sd_did = -1; /* invalidate sd struct to force re-read */
+ return( AFPERR_NOOBJ );
+ }
+ sd.sd_sindex = sindex + actcnt;
+
+ /*
+ * All done, fill in misc junk in rbuf
+ */
+ fbitmap = htons( fbitmap );
+ memcpy( rbuf, &fbitmap, sizeof( fbitmap ));
+ rbuf += sizeof( fbitmap );
+ dbitmap = htons( dbitmap );
+ memcpy( rbuf, &dbitmap, sizeof( dbitmap ));
+ rbuf += sizeof( dbitmap );
+ actcnt = htons( actcnt );
+ memcpy( rbuf, &actcnt, sizeof( actcnt ));
+ rbuf += sizeof( actcnt );
+ *rbuflen = sz;
+ return( AFP_OK );
+}
+
+
+/* why is this here? well, FPCatSearch is essentially an FPEnumerate
+ * with filters. */
+int afp_catsearch(AFPObj *obj, char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ struct vol *vol;
+ u_int16_t vid;
+
+ ibuf += 2;
+ memcpy(&vid, ibuf, sizeof(vid));
+ ibuf += sizeof(vid);
+
+ *rbuflen = 0;
+ if ((vol = getvolbyvid(vid)) == NULL)
+ return AFPERR_PARAM;
+
+ /* the ritual:
+ * do a breadth-first search of directories:
+ * lookup did/name info.
+ * add to result if match
+ * check to see if we've exceeded our timelimit
+ * if yes, return current position
+ * if not, continue
+ *
+ * we keep a copy of our current position in struct vol.
+ * if the next catsearch request for that volume isn't at
+ * at the current position, bail and return catchanged.
+ */
+
+ /* eof when done */
+ return AFPERR_EOF;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <utime.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/afp.h>
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+
+#include "directory.h"
+#include "desktop.h"
+#include "volume.h"
+#include "fork.h"
+#include "file.h"
+#include "filedir.h"
+#include "globals.h"
+
+/* the format for the finderinfo fields (from IM: Toolbox Essentials):
+ * field bytes subfield bytes
+ *
+ * files:
+ * ioFlFndrInfo 16 -> type 4 type field
+ * creator 4 creator field
+ * flags 2 finder flags:
+ * alias, bundle, etc.
+ * location 4 location in window
+ * folder 2 window that contains file
+ *
+ * ioFlXFndrInfo 16 -> iconID 2 icon id
+ * unused 6 reserved
+ * script 1 script system
+ * xflags 1 reserved
+ * commentID 2 comment id
+ * putawayID 4 home directory id
+ */
+
+const u_char ufinderi[] = {
+ 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X',
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+int getfilparams(vol, bitmap, path, dir, st, buf, buflen )
+ struct vol *vol;
+ u_int16_t bitmap;
+ char *path;
+ struct dir *dir;
+ struct stat *st;
+ char *buf;
+ int *buflen;
+{
+ struct stat hst, lst, *lstp;
+ struct adouble ad, *adp;
+ struct ofork *of;
+ struct extmap *em;
+ char *data, *nameoff = NULL, *upath;
+ int bit = 0, isad = 1, aint;
+ u_int16_t ashort;
+ u_char achar, fdType[4];
+
+ upath = mtoupath(vol, path);
+ if ((of = of_findname(vol, curdir, path))) {
+ adp = of->of_ad;
+ } else {
+ memset(&ad, 0, sizeof(ad));
+ adp = &ad;
+ }
+
+ if ( ad_open( upath, ADFLAGS_HF, O_RDONLY, 0, adp) < 0 ) {
+ isad = 0;
+ } else if ( fstat( ad_hfileno( adp ), &hst ) < 0 ) {
+ syslog( LOG_ERR, "getfilparams fstat: %m" );
+ }
+
+ data = buf;
+ while ( bitmap != 0 ) {
+ while (( bitmap & 1 ) == 0 ) {
+ bitmap = bitmap>>1;
+ bit++;
+ }
+
+ switch ( bit ) {
+ case FILPBIT_ATTR :
+ if ( isad ) {
+ ad_getattr(adp, &ashort);
+ } else if (*upath == '.') {
+ ashort = htons(ATTRBIT_INVISIBLE);
+ } else
+ ashort = 0;
+ memcpy(data, &ashort, sizeof( ashort ));
+ data += sizeof( u_short );
+ break;
+
+ case FILPBIT_PDID :
+ memcpy(data, &dir->d_did, sizeof( int ));
+ data += sizeof( int );
+ break;
+
+ case FILPBIT_CDATE :
+ if (!isad || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
+ aint = AD_DATE_FROM_UNIX(st->st_mtime);
+ memcpy(data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case FILPBIT_MDATE :
+ if ( isad && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
+ if ((st->st_mtime > AD_DATE_TO_UNIX(aint)) &&
+ (hst.st_mtime < st->st_mtime)) {
+ aint = AD_DATE_FROM_UNIX(st->st_mtime);
+ }
+ } else {
+ aint = AD_DATE_FROM_UNIX(st->st_mtime);
+ }
+ memcpy(data, &aint, sizeof( int ));
+ data += sizeof( int );
+ break;
+
+ case FILPBIT_BDATE :
+ if (!isad || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
+ aint = AD_DATE_START;
+ memcpy(data, &aint, sizeof( int ));
+ data += sizeof( int );
+ break;
+
+ case FILPBIT_FINFO :
+ if (isad)
+ memcpy(data, ad_entry(adp, ADEID_FINDERI), 32);
+ else {
+ memcpy(data, ufinderi, 32);
+ if (*upath == '.') { /* make it invisible */
+ ashort = htons(FINDERINFO_INVISIBLE);
+ memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
+ }
+ }
+
+ if ((!isad || (memcmp(ad_entry(adp, ADEID_FINDERI),
+ ufinderi, 8 ) == 0)) &&
+ (em = getextmap( path ))) {
+ memcpy(data, em->em_type, sizeof( em->em_type ));
+ memcpy(data + 4, em->em_creator, sizeof(em->em_creator));
+ }
+ data += 32;
+ break;
+
+ case FILPBIT_LNAME :
+ nameoff = data;
+ data += sizeof( u_int16_t );
+ break;
+
+ case FILPBIT_SNAME :
+ memset(data, 0, sizeof(u_int16_t));
+ data += sizeof( u_int16_t );
+ break;
+
+ case FILPBIT_FNUM :
+#if AD_VERSION > AD_VERSION1
+ /* use the CNID database if we're using AD v2 */
+ if (isad)
+ memcpy(&aint, ad_entry(adp, ADEID_DID), sizeof(aint));
+ else
+ aint = 0;
+
+ if (!(aint = cnid_add(vol->v_db, st, dir->d_did, upath,
+ strlen(upath), aint))) {
+#endif
+ /*
+ * What a fucking mess. First thing: DID and FNUMs are
+ * in the same space for purposes of enumerate (and several
+ * other wierd places). While we consider this Apple's bug,
+ * this is the work-around: In order to maintain constant and
+ * unique DIDs and FNUMs, we monotonically generate the DIDs
+ * during the session, and derive the FNUMs from the filesystem.
+ * Since the DIDs are small, we insure that the FNUMs are fairly
+ * large by setting thier high bits to the device number.
+ *
+ * AFS already does something very similar to this for the
+ * inode number, so we don't repeat the procedure.
+ *
+ * new algorithm:
+ * due to complaints over did's being non-persistent,
+ * here's the current hack to provide semi-persistent
+ * did's:
+ * 1) we reserve the first bit for file ids.
+ * 2) the next 7 bits are for the device.
+ * 3) the remaining 24 bits are for the inode.
+ *
+ * both the inode and device information are actually hashes
+ * that are then truncated to the requisite bit length.
+ *
+ * it should be okay to use lstat to deal with symlinks.
+ */
+ lstp = (lstat(upath, &lst) < 0) ? st : &lst;
+ aint = htonl(CNID(lstp, 1));
+#if AD_VERSION > AD_VERSION1
+ }
+#endif
+ memcpy(data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case FILPBIT_DFLEN :
+ aint = htonl( st->st_size );
+ memcpy(data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case FILPBIT_RFLEN :
+ if ( isad ) {
+ aint = htonl( ad_getentrylen( adp, ADEID_RFORK ));
+ } else {
+ aint = 0;
+ }
+ memcpy(data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ /* Current client needs ProDOS info block for this file.
+ Use simple heuristic and let the Mac "type" string tell
+ us what the PD file code should be. Everything gets a
+ subtype of 0x0000 unless the original value was hashed
+ to "pXYZ" when we created it. See IA, Ver 2.
+ <shirsch@ibm.net> */
+ case FILPBIT_PDINFO :
+ if ( isad ) {
+ memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
+
+ if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
+ achar = '\x04';
+ ashort = 0x0000;
+ }
+ else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
+ achar = '\xff';
+ ashort = 0x0000;
+ }
+ else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
+ achar = '\xb3';
+ ashort = 0x0000;
+ }
+ else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
+ achar = '\x00';
+ ashort = 0x0000;
+ }
+ else if ( fdType[0] == 'p' ) {
+ achar = fdType[1];
+ ashort = (fdType[2] * 256) + fdType[3];
+ }
+ else {
+ achar = '\x00';
+ ashort = 0x0000;
+ }
+ }
+ else {
+ achar = '\x00';
+ ashort = 0x0000;
+ }
+
+ *data++ = achar;
+ *data++ = 0;
+ memcpy(data, &ashort, sizeof( ashort ));
+ data += sizeof( ashort );
+ memset(data, 0, sizeof( ashort ));
+ data += sizeof( ashort );
+ break;
+
+ default :
+ if ( isad ) {
+ ad_close( adp, ADFLAGS_HF );
+ }
+ return( AFPERR_BITMAP );
+ }
+ bitmap = bitmap>>1;
+ bit++;
+ }
+ if ( nameoff ) {
+ ashort = htons( data - buf );
+ memcpy(nameoff, &ashort, sizeof( ashort ));
+ if ((aint = strlen( path )) > MACFILELEN)
+ aint = MACFILELEN;
+ *data++ = aint;
+ memcpy(data, path, aint );
+ data += aint;
+ }
+ if ( isad ) {
+ ad_close( adp, ADFLAGS_HF );
+ }
+ *buflen = data - buf;
+ return( AFP_OK );
+}
+
+int afp_createfile(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct stat st;
+ struct adouble ad, *adp;
+ struct vol *vol;
+ struct dir *dir;
+ struct ofork *of;
+ char *path, *upath;
+ int creatf, did, openf;
+ u_int16_t vid;
+
+ *rbuflen = 0;
+ ibuf++;
+ creatf = (unsigned char) *ibuf++;
+
+ memcpy(&vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (vol->v_flags & AFPVOL_RO)
+ return AFPERR_VLOCK;
+
+ memcpy(&did, ibuf, sizeof( did));
+ ibuf += sizeof( did );
+
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if ((vol->v_flags & AFPVOL_MSWINDOWS) &&
+ strpbrk(path, MSWINDOWS_BADCHARS))
+ return AFPERR_PARAM;
+
+ upath = mtoupath(vol, path);
+
+ if ((vol->v_flags & AFPVOL_NOHEX) && strchr(upath, '/'))
+ return AFPERR_PARAM;
+
+ if (!validupath(vol, upath))
+ return AFPERR_EXIST;
+
+ if ((of = of_findname(vol, curdir, path))) {
+ adp = of->of_ad;
+ } else {
+ memset(&ad, 0, sizeof(ad));
+ adp = &ad;
+ }
+ if ( creatf) {
+ /* on a hard create, fail if file exists and is open */
+ if ((stat(upath, &st) == 0) && of)
+ return AFPERR_BUSY;
+ openf = O_RDWR|O_CREAT|O_TRUNC;
+ } else {
+ openf = O_RDWR|O_CREAT|O_EXCL;
+ }
+
+ if ( ad_open( upath, vol_noadouble(vol)|ADFLAGS_DF|ADFLAGS_HF,
+ openf, 0666, adp) < 0 ) {
+ switch ( errno ) {
+ case EEXIST :
+ return( AFPERR_EXIST );
+ case EACCES :
+ return( AFPERR_ACCESS );
+ case ENOENT:
+ /* on noadouble volumes, just creating the data fork is ok */
+ if (vol_noadouble(vol) && (stat(upath, &st) == 0))
+ goto createfile_done;
+ /* fallthrough */
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+
+ ad_setentrylen( adp, ADEID_NAME, strlen( path ));
+ memcpy(ad_entry( adp, ADEID_NAME ), path,
+ ad_getentrylen( adp, ADEID_NAME ));
+ ad_flush( adp, ADFLAGS_DF|ADFLAGS_HF );
+ ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
+
+createfile_done:
+ setvoltime(obj, vol );
+ return AFP_OK;
+}
+
+int afp_setfilparams(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct vol *vol;
+ struct dir *dir;
+ char *path;
+ int did, rc;
+ u_int16_t vid, bitmap;
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy(&vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (vol->v_flags & AFPVOL_RO)
+ return AFPERR_VLOCK;
+
+ memcpy(&did, ibuf, sizeof( did ));
+ ibuf += sizeof( did );
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ memcpy(&bitmap, ibuf, sizeof( bitmap ));
+ bitmap = ntohs( bitmap );
+ ibuf += sizeof( bitmap );
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if ((u_long)ibuf & 1 ) {
+ ibuf++;
+ }
+
+ if (( rc = setfilparams(vol, path, bitmap, ibuf )) == AFP_OK ) {
+ setvoltime(obj, vol );
+ }
+
+ return( rc );
+}
+
+
+int setfilparams(vol, path, bitmap, buf )
+ struct vol *vol;
+ char *path, *buf;
+ u_int16_t bitmap;
+{
+ struct adouble ad, *adp;
+ struct ofork *of;
+ struct extmap *em;
+ int bit = 0, isad = 1, err = AFP_OK;
+ char *upath;
+ u_char achar, *fdType, xyy[4];
+ u_int16_t ashort, bshort;
+ u_int32_t aint;
+ struct utimbuf ut;
+
+ upath = mtoupath(vol, path);
+ if ((of = of_findname(vol, curdir, path))) {
+ adp = of->of_ad;
+ } else {
+ memset(&ad, 0, sizeof(ad));
+ adp = &ad;
+ }
+ if (ad_open( upath, vol_noadouble(vol) | ADFLAGS_HF,
+ O_RDWR|O_CREAT, 0666, adp) < 0) {
+ /* for some things, we don't need an adouble header */
+ if (bitmap & ~(1<<FILPBIT_MDATE)) {
+ return vol_noadouble(vol) ? AFP_OK : AFPERR_ACCESS;
+ }
+ isad = 0;
+ } else if ((ad_getoflags( adp, ADFLAGS_HF ) & O_CREAT) ) {
+ ad_setentrylen( adp, ADEID_NAME, strlen( path ));
+ memcpy(ad_entry( adp, ADEID_NAME ), path,
+ ad_getentrylen( adp, ADEID_NAME ));
+ }
+
+ while ( bitmap != 0 ) {
+ while (( bitmap & 1 ) == 0 ) {
+ bitmap = bitmap>>1;
+ bit++;
+ }
+
+ switch( bit ) {
+ case FILPBIT_ATTR :
+ memcpy(&ashort, buf, sizeof( ashort ));
+ ad_getattr(adp, &bshort);
+ if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
+ bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
+ } else {
+ bshort &= ~ashort;
+ }
+ ad_setattr(adp, bshort);
+ buf += sizeof( ashort );
+ break;
+
+ case FILPBIT_CDATE :
+ memcpy(&aint, buf, sizeof(aint));
+ ad_setdate(adp, AD_DATE_CREATE, aint);
+ buf += sizeof( aint );
+ break;
+
+ case FILPBIT_MDATE :
+ memcpy(&aint, buf, sizeof( aint ));
+ if (isad)
+ ad_setdate(adp, AD_DATE_MODIFY, aint);
+ ut.actime = ut.modtime = AD_DATE_TO_UNIX(aint);
+ utime(upath, &ut);
+ buf += sizeof( aint );
+ break;
+
+ case FILPBIT_BDATE :
+ memcpy(&aint, buf, sizeof(aint));
+ ad_setdate(adp, AD_DATE_BACKUP, aint);
+ buf += sizeof( aint );
+ break;
+
+ case FILPBIT_FINFO :
+ if ((memcmp( ad_entry( adp, ADEID_FINDERI ), ufinderi, 8 ) == 0)
+ && (em = getextmap( path )) &&
+ (memcmp(buf, em->em_type, sizeof( em->em_type )) == 0) &&
+ (memcmp(buf + 4, em->em_creator,
+ sizeof( em->em_creator )) == 0)) {
+ memcpy(buf, ufinderi, 8 );
+ }
+ memcpy(ad_entry( adp, ADEID_FINDERI ), buf, 32 );
+ buf += 32;
+ break;
+
+ /* Client needs to set the ProDOS file info for this file.
+ Use defined strings for the simple cases, and convert
+ all else into pXYY per Inside Appletalk. Always set
+ the creator as "pdos". <shirsch@ibm.net> */
+ case FILPBIT_PDINFO :
+ achar = *buf;
+ buf += 2;
+ memcpy(&ashort, buf, sizeof( ashort ));
+ ashort = ntohs( ashort );
+ buf += 2;
+
+ switch ( (unsigned int) achar )
+ {
+ case 0x04 :
+ fdType = ( u_char *) "TEXT";
+ break;
+
+ case 0xff :
+ fdType = ( u_char *) "PSYS";
+ break;
+
+ case 0xb3 :
+ fdType = ( u_char *) "PS16";
+ break;
+
+ case 0x00 :
+ fdType = ( u_char *) "BINA";
+ break;
+
+ default :
+ xyy[0] = ( u_char ) 'p';
+ xyy[1] = achar;
+ xyy[2] = ( u_char ) ( ashort >> 8 ) & 0xff;
+ xyy[3] = ( u_char ) ashort & 0xff;
+ fdType = xyy;
+ break;
+ }
+
+ memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
+ memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
+ break;
+
+
+ default :
+ err = AFPERR_BITMAP;
+ goto setfilparam_done;
+ }
+
+ bitmap = bitmap>>1;
+ bit++;
+ }
+
+setfilparam_done:
+ if (isad) {
+ ad_flush( adp, ADFLAGS_HF );
+ ad_close( adp, ADFLAGS_HF );
+ }
+ return err;
+}
+
+/*
+ * renamefile and copyfile take the old and new unix pathnames
+ * and the new mac name.
+ * NOTE: if we have to copy a file instead of renaming it, locks
+ * will break.
+ */
+int renamefile(src, dst, newname, noadouble )
+ char *src, *dst, *newname;
+ const int noadouble;
+{
+ struct adouble ad;
+ char adsrc[ MAXPATHLEN + 1];
+ int len, rc;
+
+ /*
+ * Note that this is only checking the existance of the data file,
+ * not the header file. The thinking is that if the data file doesn't
+ * exist, but the header file does, the right thing to do is remove
+ * the data file silently.
+ */
+
+ /* existence check moved to afp_moveandrename */
+
+ if ( rename( src, dst ) < 0 ) {
+ switch ( errno ) {
+ case ENOENT :
+ return( AFPERR_NOOBJ );
+ case EPERM:
+ case EACCES :
+ return( AFPERR_ACCESS );
+ case EROFS:
+ return AFPERR_VLOCK;
+ case EXDEV : /* Cross device move -- try copy */
+ if (( rc = copyfile(src, dst, newname, noadouble )) != AFP_OK ) {
+ deletefile( dst );
+ return( rc );
+ }
+ return deletefile( src );
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+
+ strcpy( adsrc, ad_path( src, 0 ));
+ rc = 0;
+rename_retry:
+ if (rename( adsrc, ad_path( dst, 0 )) < 0 ) {
+ struct stat st;
+
+ switch ( errno ) {
+ case ENOENT :
+ /* check for a source appledouble header. if it exists, make
+ * a dest appledouble directory and do the rename again. */
+ memset(&ad, 0, sizeof(ad));
+ if (rc || stat(adsrc, &st) ||
+ (ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad) < 0))
+ return AFP_OK;
+ rc++;
+ ad_close(&ad, ADFLAGS_HF);
+ goto rename_retry;
+ case EPERM:
+ case EACCES :
+ return( AFPERR_ACCESS );
+ case EROFS:
+ return AFPERR_VLOCK;
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+
+ memset(&ad, 0, sizeof(ad));
+ if ( ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, &ad) < 0 ) {
+ switch ( errno ) {
+ case ENOENT :
+ return( AFPERR_NOOBJ );
+ case EACCES :
+ return( AFPERR_ACCESS );
+ case EROFS:
+ return AFPERR_VLOCK;
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+
+ len = strlen( newname );
+ ad_setentrylen( &ad, ADEID_NAME, len );
+ memcpy(ad_entry( &ad, ADEID_NAME ), newname, len );
+ ad_flush( &ad, ADFLAGS_HF );
+ ad_close( &ad, ADFLAGS_HF );
+
+ return( AFP_OK );
+}
+
+int afp_copyfile(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct vol *vol;
+ struct dir *dir;
+ char *newname, *path, *p;
+ u_int32_t sdid, ddid;
+ int plen, err;
+ u_int16_t svid, dvid;
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy(&svid, ibuf, sizeof( svid ));
+ ibuf += sizeof( svid );
+ if (( vol = getvolbyvid( svid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy(&sdid, ibuf, sizeof( sdid ));
+ ibuf += sizeof( sdid );
+ if (( dir = dirsearch( vol, sdid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy(&dvid, ibuf, sizeof( dvid ));
+ ibuf += sizeof( dvid );
+ memcpy(&ddid, ibuf, sizeof( ddid ));
+ ibuf += sizeof( ddid );
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+ if ( *path == '\0' ) {
+ return( AFPERR_BADTYPE );
+ }
+
+ /* don't allow copies when the file is open.
+ * XXX: the spec only calls for read/deny write access.
+ * however, copyfile doesn't have any of that info,
+ * and locks need to stay coherent. as a result,
+ * we just balk if the file is opened already. */
+ if (of_findname(vol, curdir, path))
+ return AFPERR_DENYCONF;
+
+ newname = obj->newtmp;
+ strcpy( newname, path );
+
+ p = ctoupath( vol, curdir, newname );
+
+ if (( vol = getvolbyvid( dvid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (vol->v_flags & AFPVOL_RO)
+ return AFPERR_VLOCK;
+
+ if (( dir = dirsearch( vol, ddid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+ if ( *path != '\0' ) {
+ return( AFPERR_BADTYPE );
+ }
+
+ /* one of the handful of places that knows about the path type */
+ if ( *ibuf++ != 2 ) {
+ return( AFPERR_PARAM );
+ }
+ if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
+ strncpy( newname, ibuf, plen );
+ newname[ plen ] = '\0';
+ }
+
+ if ( (err = copyfile(p, mtoupath(vol, newname ), newname,
+ vol_noadouble(vol))) < 0 ) {
+ return err;
+ }
+
+ setvoltime(obj, vol );
+ return( AFP_OK );
+}
+
+
+static __inline__ int copy_all(const int dfd, const void *buf,
+ size_t buflen)
+{
+ ssize_t cc;
+
+ while (buflen > 0) {
+ if ((cc = write(dfd, buf, buflen)) < 0) {
+ switch (errno) {
+ case EINTR:
+ continue;
+ case EDQUOT:
+ case EFBIG:
+ case ENOSPC:
+ return AFPERR_DFULL;
+ case EROFS:
+ return AFPERR_VLOCK;
+ default:
+ return AFPERR_PARAM;
+ }
+ }
+ buflen -= cc;
+ }
+
+ return 0;
+}
+
+/* XXX: this needs to use ad_open and ad_lock. so, we need to
+ * pass in vol and path */
+int copyfile(src, dst, newname, noadouble )
+ char *src, *dst, *newname;
+ const int noadouble;
+{
+ struct adouble ad;
+ struct stat st;
+ char filebuf[8192];
+ int sfd, dfd, len, err = AFP_OK;
+ ssize_t cc;
+
+
+ if (newname) {
+ if ((sfd = open( ad_path( src, ADFLAGS_HF ), O_RDONLY, 0 )) < 0 ) {
+ switch ( errno ) {
+ case ENOENT :
+ break; /* just copy the data fork */
+ case EACCES :
+ return( AFPERR_ACCESS );
+ default :
+ return( AFPERR_PARAM );
+ }
+ } else {
+ if (( dfd = open( ad_path( dst, ADFLAGS_HF ), O_WRONLY|O_CREAT,
+ ad_mode( ad_path( dst, ADFLAGS_HF ), 0666 ))) < 0 ) {
+ close( sfd );
+ switch ( errno ) {
+ case ENOENT :
+ return( AFPERR_NOOBJ );
+ case EACCES :
+ return( AFPERR_ACCESS );
+ case EROFS:
+ return AFPERR_VLOCK;
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+
+ /* copy the file */
+#ifdef SENDFILE_FLAVOR_LINUX
+ if (fstat(sfd, &st) == 0) {
+ if ((cc = sendfile(dfd, sfd, NULL, st.st_size)) < 0) {
+ switch (errno) {
+ case EDQUOT:
+ case EFBIG:
+ case ENOSPC:
+ err = AFPERR_DFULL;
+ break;
+ case EROFS:
+ err = AFPERR_VLOCK;
+ break;
+ default:
+ err = AFPERR_PARAM;
+ }
+ }
+ goto copyheader_done;
+ }
+#endif
+ while (1) {
+ if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
+ if (errno == EINTR)
+ continue;
+ err = AFPERR_PARAM;
+ break;
+ }
+
+ if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0))
+ break;
+ }
+
+copyheader_done:
+ close(sfd);
+ close(dfd);
+ if (err < 0) {
+ unlink(ad_path(dst, ADFLAGS_HF));
+ return err;
+ }
+ }
+ }
+
+ /* data fork copying */
+ if (( sfd = open( src, O_RDONLY, 0 )) < 0 ) {
+ switch ( errno ) {
+ case ENOENT :
+ return( AFPERR_NOOBJ );
+ case EACCES :
+ return( AFPERR_ACCESS );
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+
+ if (( dfd = open( dst, O_WRONLY|O_CREAT, ad_mode( dst, 0666 ))) < 0 ) {
+ close( sfd );
+ switch ( errno ) {
+ case ENOENT :
+ return( AFPERR_NOOBJ );
+ case EACCES :
+ return( AFPERR_ACCESS );
+ case EROFS:
+ return AFPERR_VLOCK;
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+
+#ifdef SENDFILE_FLAVOR_LINUX
+ if (fstat(sfd, &st) == 0) {
+ if ((cc = sendfile(dfd, sfd, NULL, st.st_size)) < 0) {
+ switch (errno) {
+ case EDQUOT:
+ case EFBIG:
+ case ENOSPC:
+ err = AFPERR_DFULL;
+ break;
+ default:
+ err = AFPERR_PARAM;
+ }
+ }
+ goto copydata_done;
+ }
+#endif
+
+ while (1) {
+ if ((cc = read( sfd, filebuf, sizeof( filebuf ))) < 0) {
+ if (errno == EINTR)
+ continue;
+
+ err = AFPERR_PARAM;
+ break;
+ }
+
+ if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
+ break;
+ }
+ }
+
+copydata_done:
+ close(sfd);
+ close(dfd);
+ if (err < 0) {
+ unlink(ad_path(dst, ADFLAGS_HF));
+ unlink(dst);
+ return err;
+ }
+
+ if (newname) {
+ memset(&ad, 0, sizeof(ad));
+ if ( ad_open( dst, noadouble | ADFLAGS_HF, O_RDWR|O_CREAT,
+ 0666, &ad) < 0 ) {
+ switch ( errno ) {
+ case ENOENT :
+ return noadouble ? AFP_OK : AFPERR_NOOBJ;
+ case EACCES :
+ return( AFPERR_ACCESS );
+ case EROFS:
+ return AFPERR_VLOCK;
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+
+ len = strlen( newname );
+ ad_setentrylen( &ad, ADEID_NAME, len );
+ memcpy(ad_entry( &ad, ADEID_NAME ), newname, len );
+ ad_flush( &ad, ADFLAGS_HF );
+ ad_close( &ad, ADFLAGS_HF );
+ }
+
+ return( AFP_OK );
+}
+
+
+int deletefile( file )
+ char *file;
+{
+ struct adouble ad;
+ int adflags, err = AFP_OK;
+
+ /* try to open both at once */
+ adflags = ADFLAGS_DF|ADFLAGS_HF;
+ memset(&ad, 0, sizeof(ad));
+ if ( ad_open( file, adflags, O_RDWR, 0, &ad ) < 0 ) {
+ switch (errno) {
+ case ENOENT:
+ adflags = ADFLAGS_DF;
+ /* that failed. now try to open just the data fork */
+ memset(&ad, 0, sizeof(ad));
+ if ( ad_open( file, adflags, O_RDWR, 0, &ad ) < 0 ) {
+ switch (errno) {
+ case ENOENT:
+ return AFPERR_NOOBJ;
+ case EACCES:
+ return AFPERR_ACCESS;
+ default:
+ return AFPERR_PARAM;
+ }
+ }
+ break;
+
+ case EACCES:
+ return( AFPERR_ACCESS );
+ case EROFS:
+ return AFPERR_VLOCK;
+ default:
+ return( AFPERR_PARAM );
+ }
+ }
+
+ if ((adflags & ADFLAGS_HF) &&
+ (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR, 0, 0) < 0 )) {
+ ad_close( &ad, adflags );
+ return( AFPERR_BUSY );
+ }
+
+ if (ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0 ) < 0) {
+ err = AFPERR_BUSY;
+ goto delete_unlock;
+ }
+
+ if ( unlink( ad_path( file, ADFLAGS_HF )) < 0 ) {
+ switch ( errno ) {
+ case EPERM:
+ case EACCES :
+ err = AFPERR_ACCESS;
+ goto delete_unlock;
+ case EROFS:
+ err = AFPERR_VLOCK;
+ goto delete_unlock;
+ case ENOENT :
+ break;
+ default :
+ err = AFPERR_PARAM;
+ goto delete_unlock;
+ }
+ }
+
+ if ( unlink( file ) < 0 ) {
+ switch ( errno ) {
+ case EPERM:
+ case EACCES :
+ err = AFPERR_ACCESS;
+ break;
+ case EROFS:
+ err = AFPERR_VLOCK;
+ break;
+ case ENOENT :
+ break;
+ default :
+ err = AFPERR_PARAM;
+ break;
+ }
+ }
+
+delete_unlock:
+ if (adflags & ADFLAGS_HF)
+ ad_tmplock(&ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
+ ad_tmplock(&ad, ADEID_DFORK, ADLOCK_CLR, 0, 0);
+ ad_close( &ad, adflags );
+ return err;
+}
+
+
+#if AD_VERSION > AD_VERSION1
+/* return a file id */
+int afp_createid(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct stat st;
+ struct adouble ad;
+ struct vol *vol;
+ struct dir *dir;
+ char *path, *upath;
+ int len;
+ cnid_t did, id;
+ u_short vid;
+
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy(&vid, ibuf, sizeof(vid));
+ ibuf += sizeof(vid);
+
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM);
+ }
+
+ if (vol->v_flags & AFPVOL_RO)
+ return AFPERR_VLOCK;
+
+ memcpy(&did, ibuf, sizeof( did ));
+ ibuf += sizeof(did);
+
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if ( *path == '\0' ) {
+ return( AFPERR_BADTYPE );
+ }
+
+ upath = mtoupath(vol, path);
+ if (stat(upath, &st) < 0) {
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ return AFPERR_ACCESS;
+ case ENOENT:
+ return AFPERR_NOOBJ;
+ default:
+ return AFPERR_PARAM;
+ }
+ }
+
+ if (id = cnid_lookup(vol->v_db, &st, did, upath, len = strlen(upath))) {
+ memcpy(rbuf, &id, sizeof(id));
+ *rbuflen = sizeof(id);
+ return AFPERR_EXISTID;
+ }
+
+ memset(&ad, 0, sizeof(ad));
+ if (ad_open( upath, ADFLAGS_HF, O_RDONLY, 0, &ad ) < 0)
+ id = 0;
+ else {
+ memcpy(&id, ad_entry(&ad, ADEID_DID), sizeof(id));
+ ad_close(upath, ADFLAGS_HF);
+ }
+
+ if (id = cnid_add(vol->v_db, &st, did, upath, len, id)) {
+ memcpy(rbuf, &id, sizeof(id));
+ *rbuflen = sizeof(id);
+ return AFP_OK;
+ }
+
+ switch (errno) {
+ case EROFS:
+ return AFPERR_VLOCK;
+ break;
+ case EPERM:
+ case EACCES:
+ return AFPERR_ACCESS;
+ break;
+ default:
+ syslog(LOG_ERR, "afp_createid: cnid_add: %m");
+ return AFPERR_PARAM;
+ }
+}
+
+/* resolve a file id */
+int afp_resolveid(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct stat st;
+ struct vol *vol;
+ struct dir *dir;
+ char *upath;
+ int err, buflen;
+ cnid_t id;
+ u_int16_t vid, bitmap;
+
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy(&vid, ibuf, sizeof(vid));
+ ibuf += sizeof(vid);
+
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM);
+ }
+
+ memcpy(&id, ibuf, sizeof( id ));
+ ibuf += sizeof(id);
+
+ if ((upath = cnid_resolve(vol->v_db, &id)) == NULL) {
+ return AFPERR_BADID;
+ }
+
+ if (( dir = dirsearch( vol, id )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
+ switch (errno) {
+ case EACCES:
+ case EPERM:
+ return AFPERR_ACCESS;
+ case ENOENT:
+ return AFPERR_NOID;
+ default:
+ return AFPERR_PARAM;
+ }
+ }
+
+ /* directories are bad */
+ if (S_ISDIR(st.st_mode))
+ return AFPERR_BADTYPE;
+
+ memcpy(&bitmap, ibuf, sizeof(bitmap));
+ bitmap = ntohs( bitmap );
+
+ if ((err = getfilparams(vol, bitmap, utompath(vol, upath), curdir, &st,
+ rbuf + sizeof(bitmap), &buflen)) != AFP_OK)
+ return err;
+
+ *rbuflen = buflen + sizeof(bitmap);
+ memcpy(rbuf, ibuf, sizeof(bitmap));
+ return AFP_OK;
+}
+
+int afp_deleteid(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct stat st;
+ struct vol *vol;
+ struct dir *dir;
+ char *upath;
+ int err;
+ cnid_t id;
+ u_short vid;
+
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy(&vid, ibuf, sizeof(vid));
+ ibuf += sizeof(vid);
+
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM);
+ }
+
+ if (vol->v_flags & AFPVOL_RO)
+ return AFPERR_VLOCK;
+
+ memcpy(&id, ibuf, sizeof( id ));
+ ibuf += sizeof(id);
+
+ if ((upath = cnid_resolve(vol->v_db, &id)) == NULL) {
+ return AFPERR_NOID;
+ }
+
+ if (( dir = dirsearch( vol, id )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ err = AFP_OK;
+ if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
+ switch (errno) {
+ case EACCES:
+ case EPERM:
+ return AFPERR_ACCESS;
+ case ENOENT:
+ /* still try to delete the id */
+ err = AFPERR_NOOBJ;
+ break;
+ default:
+ return AFPERR_PARAM;
+ }
+ }
+
+ /* directories are bad */
+ if (S_ISDIR(st.st_mode))
+ return AFPERR_BADTYPE;
+
+ if (cnid_delete(vol->v_db, id)) {
+ switch (errno) {
+ case EROFS:
+ return AFPERR_VLOCK;
+ case EPERM:
+ case EACCES:
+ return AFPERR_ACCESS;
+ default:
+ return AFPERR_PARAM;
+ }
+ }
+
+ return err;
+}
+#endif
+
+#define APPLETEMP ".AppleTempXXXXXX"
+int afp_exchangefiles(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct stat srcst, destst;
+ struct vol *vol;
+ struct dir *dir, *sdir;
+ char *spath, temp[17], *path, *p;
+ char *supath, *upath;
+ int err;
+#if AD_VERSION > AD_VERSION1
+ int slen, dlen;
+#endif
+ cnid_t sid, did;
+ u_int16_t vid;
+
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy(&vid, ibuf, sizeof(vid));
+ ibuf += sizeof(vid);
+
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM);
+ }
+
+ if (vol->v_flags & AFPVOL_RO)
+ return AFPERR_VLOCK;
+
+ /* source and destination dids */
+ memcpy(&sid, ibuf, sizeof(sid));
+ ibuf += sizeof(sid);
+ memcpy(&did, ibuf, sizeof(did));
+ ibuf += sizeof(did);
+
+ /* source file */
+ if ((dir = dirsearch( vol, sid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if ( *path == '\0' ) {
+ return( AFPERR_BADTYPE );
+ }
+
+ upath = mtoupath(vol, path);
+ if (stat(upath, &srcst) < 0) {
+ switch (errno) {
+ case ENOENT:
+ return AFPERR_NOID;
+ case EPERM:
+ case EACCES:
+ return AFPERR_ACCESS;
+ default:
+ return AFPERR_PARAM;
+ }
+ }
+
+ /* save some stuff */
+ sdir = curdir;
+ spath = obj->oldtmp;
+ supath = obj->newtmp;
+ strcpy(spath, path);
+ strcpy(supath, upath); /* this is for the cnid changing */
+ p = ctoupath( vol, sdir, spath);
+
+ /* look for the source cnid. if it doesn't exist, don't worry about
+ * it. */
+#if AD_VERSION > AD_VERSION1
+ sid = cnid_lookup(vol->v_db, &srcst, sdir->d_did, supath,
+ slen = strlen(supath));
+#endif
+
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if ( *path == '\0' ) {
+ return( AFPERR_BADTYPE );
+ }
+
+ /* FPExchangeFiles is the only call that can return the SameObj
+ * error */
+ if ((curdir == sdir) && strcmp(spath, path) == 0)
+ return AFPERR_SAMEOBJ;
+
+ upath = mtoupath(vol, path);
+ if (stat(upath, &destst) < 0) {
+ switch (errno) {
+ case ENOENT:
+ return AFPERR_NOID;
+ case EPERM:
+ case EACCES:
+ return AFPERR_ACCESS;
+ default:
+ return AFPERR_PARAM;
+ }
+ }
+
+#if AD_VERSION > AD_VERSION1
+ /* look for destination id. */
+ did = cnid_lookup(vol->v_db, &destst, curdir->d_did, upath,
+ dlen = strlen(upath));
+#endif
+
+ /* construct a temp name.
+ * NOTE: the temp file will be in the dest file's directory. it
+ * will also be inaccessible from AFP. */
+ memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
+ if (!mktemp(temp))
+ return AFPERR_MISC;
+
+ /* now, quickly rename the file. we error if we can't. */
+ if ((err = renamefile(p, temp, temp, vol_noadouble(vol))) < 0)
+ goto err_exchangefile;
+ of_rename(vol, sdir, spath, curdir, temp);
+
+ /* rename destination to source */
+ if ((err = renamefile(path, p, spath, vol_noadouble(vol))) < 0)
+ goto err_src_to_tmp;
+ of_rename(vol, curdir, path, sdir, spath);
+
+ /* rename temp to destination */
+ if ((err = renamefile(temp, upath, path, vol_noadouble(vol))) < 0)
+ goto err_dest_to_src;
+ of_rename(vol, curdir, temp, curdir, path);
+
+#if AD_VERSION > AD_VERSION1
+ /* id's need switching. src -> dest and dest -> src. */
+ if (sid && (cnid_update(vol->v_db, sid, &destst, curdir->d_did,
+ upath, dlen) < 0)) {
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ err = AFPERR_ACCESS;
+ default:
+ err = AFPERR_PARAM;
+ }
+ goto err_temp_to_dest;
+ }
+
+ if (did && (cnid_update(vol->v_db, did, &srcst, sdir->d_did,
+ supath, slen) < 0)) {
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ err = AFPERR_ACCESS;
+ default:
+ err = AFPERR_PARAM;
+ }
+
+ if (sid)
+ cnid_update(vol->v_db, sid, &srcst, sdir->d_did, supath, slen);
+ goto err_temp_to_dest;
+ }
+#endif
+ return AFP_OK;
+
+
+ /* all this stuff is so that we can unwind a failed operation
+ * properly. */
+err_temp_to_dest:
+ /* rename dest to temp */
+ renamefile(upath, temp, temp, vol_noadouble(vol));
+ of_rename(vol, curdir, upath, curdir, temp);
+
+err_dest_to_src:
+ /* rename source back to dest */
+ renamefile(p, upath, path, vol_noadouble(vol));
+ of_rename(vol, sdir, spath, curdir, path);
+
+err_src_to_tmp:
+ /* rename temp back to source */
+ renamefile(temp, p, spath, vol_noadouble(vol));
+ of_rename(vol, curdir, temp, sdir, spath);
+
+err_exchangefile:
+ return err;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef AFPD_FILE_H
+#define AFPD_FILE_H 1
+
+/*#include <sys/stat.h>*/ /* including it here causes some confusion */
+#include <sys/param.h>
+#include <sys/cdefs.h>
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+
+#include "globals.h"
+#include "volume.h"
+#include "directory.h"
+
+extern const u_char ufinderi[];
+
+#define FILPBIT_ATTR 0
+#define FILPBIT_PDID 1
+#define FILPBIT_CDATE 2
+#define FILPBIT_MDATE 3
+#define FILPBIT_BDATE 4
+#define FILPBIT_FINFO 5
+#define FILPBIT_LNAME 6
+#define FILPBIT_SNAME 7
+#define FILPBIT_FNUM 8
+#define FILPBIT_DFLEN 9
+#define FILPBIT_RFLEN 10
+#define FILPBIT_PDINFO 13 /* ProDOS Info */
+
+/* attribute bits. (d) = directory attribute bit as well. */
+#define ATTRBIT_INVISIBLE (1<<0) /* invisible (d) */
+#define ATTRBIT_MULTIUSER (1<<1) /* multiuser */
+#define ATTRBIT_SYSTEM (1<<2) /* system (d) */
+#define ATTRBIT_DOPEN (1<<3) /* data fork already open */
+#define ATTRBIT_ROPEN (1<<4) /* resource fork already open */
+#define ATTRBIT_NOWRITE (1<<5) /* write inhibit(v2)/read-only(v1) bit */
+#define ATTRBIT_BACKUP (1<<6) /* backup needed (d) */
+#define ATTRBIT_NORENAME (1<<7) /* rename inhibit (d) */
+#define ATTRBIT_NODELETE (1<<8) /* delete inhibit (d) */
+#define ATTRBIT_NOCOPY (1<<10) /* copy protect */
+#define ATTRBIT_SETCLR (1<<15) /* set/clear bits (d) */
+
+struct extmap {
+ struct extmap *em_next;
+ char em_ext[ MAXPATHLEN + 1];
+ char em_creator[ 4 ];
+ char em_type[ 4 ];
+};
+
+extern struct extmap *extmap;
+extern struct extmap *getextmap __P((const char *));
+
+extern int getfilparams __P((struct vol *, u_int16_t, char *,
+ struct dir *, struct stat *, char *buf,
+ int *));
+extern int setfilparams __P((struct vol *, char *, u_int16_t, char *));
+extern int renamefile __P((char *, char *, char *, const int));
+extern int copyfile __P((char *, char *, char *, const int));
+extern int deletefile __P((char *));
+
+/* FP functions */
+extern int afp_exchangefiles __P((AFPObj *, char *, int, char *, int *));
+extern int afp_setfilparams __P((AFPObj *, char *, int, char *, int *));
+extern int afp_copyfile __P((AFPObj *, char *, int, char *, int *));
+extern int afp_createfile __P((AFPObj *, char *, int, char *, int *));
+#if AD_VERSION > AD_VERSION1
+extern int afp_createid __P((AFPObj *, char *, int, char *, int *));
+extern int afp_resolveid __P((AFPObj *, char *, int, char *, int *));
+extern int afp_deleteid __P((AFPObj *, char *, int, char *, int *));
+#else
+#define afp_createid afp_null
+#define afp_resolveid afp_null
+#define afp_deleteid afp_null
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <errno.h>
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/afp.h>
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+
+#include "directory.h"
+#include "desktop.h"
+#include "volume.h"
+#include "fork.h"
+#include "file.h"
+#include "globals.h"
+#include "filedir.h"
+
+int afp_getfildirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct stat st;
+ struct vol *vol;
+ struct dir *dir;
+ u_int32_t did;
+ int buflen, ret;
+ char *path;
+ u_int16_t fbitmap, dbitmap, vid;
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy( &did, ibuf, sizeof( did ));
+ ibuf += sizeof( did );
+
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ memcpy( &fbitmap, ibuf, sizeof( fbitmap ));
+ fbitmap = ntohs( fbitmap );
+ ibuf += sizeof( fbitmap );
+ memcpy( &dbitmap, ibuf, sizeof( dbitmap ));
+ dbitmap = ntohs( dbitmap );
+ ibuf += sizeof( dbitmap );
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if ( stat( mtoupath(vol, path ), &st ) < 0 ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ buflen = 0;
+ if (S_ISDIR(st.st_mode)) {
+ if (dbitmap && ( ret = getdirparams(vol, dbitmap, ".", curdir,
+ &st, rbuf + 3 * sizeof( u_int16_t ), &buflen )) != AFP_OK ) {
+ return( ret );
+ }
+ /* this is a directory */
+ *(rbuf + 2 * sizeof( u_int16_t )) = FILDIRBIT_ISDIR;
+ } else {
+ if (fbitmap && ( ret = getfilparams(vol, fbitmap, path, curdir, &st,
+ rbuf + 3 * sizeof( u_int16_t ), &buflen )) != AFP_OK ) {
+ return( ret );
+ }
+ /* this is a file */
+ *(rbuf + 2 * sizeof( u_int16_t )) = FILDIRBIT_ISFILE;
+ }
+ *rbuflen = buflen + 3 * sizeof( u_int16_t );
+ fbitmap = htons( fbitmap );
+ memcpy( rbuf, &fbitmap, sizeof( fbitmap ));
+ rbuf += sizeof( fbitmap );
+ dbitmap = htons( dbitmap );
+ memcpy( rbuf, &dbitmap, sizeof( dbitmap ));
+ rbuf += sizeof( dbitmap ) + sizeof( u_char );
+ *rbuf = 0;
+
+ return( AFP_OK );
+}
+
+int afp_setfildirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct stat st;
+ struct vol *vol;
+ struct dir *dir;
+ char *path;
+ u_int16_t vid, bitmap;
+ int did, rc;
+
+ *rbuflen = 0;
+ ibuf += 2;
+ memcpy( &vid, ibuf, sizeof(vid));
+ ibuf += sizeof( vid );
+
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (vol->v_flags & AFPVOL_RO)
+ return AFPERR_VLOCK;
+
+ memcpy( &did, ibuf, sizeof( did));
+ ibuf += sizeof( did);
+
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ memcpy( &bitmap, ibuf, sizeof( bitmap ));
+ bitmap = ntohs( bitmap );
+ ibuf += sizeof( bitmap );
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if ( stat( mtoupath(vol, path ), &st ) < 0 ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ /*
+ * If ibuf is odd, make it even.
+ */
+ if ((u_long)ibuf & 1 ) {
+ ibuf++;
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ rc = setdirparams(vol, path, bitmap, ibuf );
+ } else {
+ rc = setfilparams(vol, path, bitmap, ibuf );
+ }
+ if ( rc == AFP_OK ) {
+ setvoltime(obj, vol );
+ }
+ return( rc );
+}
+
+int afp_rename(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct adouble ad;
+ struct stat st;
+ struct vol *vol;
+ struct dir *dir, *odir = NULL;
+ char *path, *buf, *upath, *newpath;
+ char *newadpath;
+ u_int32_t did;
+ int plen;
+ u_int16_t vid;
+#if AD_VERSION > AD_VERSION1
+ cnid_t id;
+#endif
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (vol->v_flags & AFPVOL_RO)
+ return AFPERR_VLOCK;
+
+ memcpy( &did, ibuf, sizeof( did ));
+ ibuf += sizeof( did );
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ /* another place where we know about the path type */
+ if ( *ibuf++ != 2 ) {
+ return( AFPERR_PARAM );
+ }
+ plen = (unsigned char) *ibuf++;
+ *( ibuf + plen ) = '\0';
+
+ if ( *path == '\0' ) {
+ if ( curdir->d_parent == NULL ) { /* root directory */
+ return( AFPERR_NORENAME );
+ }
+ odir = curdir;
+ path = curdir->d_name;
+ if ( movecwd( vol, curdir->d_parent ) < 0 ) {
+ return( AFPERR_NOOBJ );
+ }
+ }
+
+#ifdef notdef
+ if ( strcasecmp( path, ibuf ) == 0 ) {
+ return( AFP_OK );
+ }
+#endif notdef
+
+ /* if a curdir/newname ofork exists, return busy */
+ if (of_findname(vol, curdir, ibuf))
+ return AFPERR_BUSY;
+
+ /* source == destination. just say okay. */
+ if (strcmp(path, ibuf) == 0)
+ return AFP_OK;
+
+ /* check for illegal characters */
+ if ((vol->v_flags & AFPVOL_MSWINDOWS) &&
+ strpbrk(ibuf, MSWINDOWS_BADCHARS))
+ return AFPERR_PARAM;
+
+ newpath = obj->oldtmp;
+ strcpy( newpath, mtoupath(vol, ibuf ));
+
+ if ((vol->v_flags & AFPVOL_NOHEX) && strchr(newpath, '/'))
+ return AFPERR_PARAM;
+
+ if (!validupath(vol, newpath))
+ return AFPERR_EXIST;
+
+ /* the strdiacasecmp deals with case-insensitive, case preserving
+ filesystems */
+ if (stat( newpath, &st ) == 0 && strdiacasecmp(path, ibuf))
+ return( AFPERR_EXIST );
+
+ upath = mtoupath(vol, path);
+
+#if AD_VERSION > AD_VERSION1
+ id = cnid_get(vol->v_db, curdir->d_did, upath, strlen(upath));
+#endif
+
+ if ( rename( upath, newpath ) < 0 ) {
+ switch ( errno ) {
+ case ENOENT :
+ return( AFPERR_NOOBJ );
+ case EACCES :
+ return( AFPERR_ACCESS );
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+
+#if AD_VERSION > AD_VERSION1
+ if (stat(newpath, &st) < 0) /* this shouldn't fail */
+ return AFPERR_MISC;
+ cnid_update(vol->v_db, id, &st, curdir->d_did, newpath, strlen(newpath));
+#endif
+
+ if ( !odir ) {
+ newadpath = obj->newtmp;
+ strcpy( newadpath, ad_path( newpath, 0 ));
+ if ( rename( ad_path( upath, 0 ), newadpath ) < 0 ) {
+ if ( errno == ENOENT ) { /* no adouble header file */
+ if (( unlink( newadpath ) < 0 ) && ( errno != ENOENT )) {
+ return( AFPERR_PARAM );
+ }
+ goto out;
+ }
+ return( AFPERR_PARAM );
+ }
+
+ memset(&ad, 0, sizeof(ad));
+ if ( ad_open( newpath, ADFLAGS_HF, O_RDWR|O_CREAT, 0666,
+ &ad) < 0 ) {
+ return( AFPERR_PARAM );
+ }
+ } else {
+ int isad = 1;
+
+ memset(&ad, 0, sizeof(ad));
+ if ( ad_open( newpath, vol_noadouble(vol)|ADFLAGS_HF|ADFLAGS_DIR,
+ O_RDWR|O_CREAT, 0666, &ad) < 0 ) {
+ if (!((errno == ENOENT) && vol_noadouble(vol)))
+ return( AFPERR_PARAM );
+ isad = 0;
+ }
+ if ((buf = realloc( odir->d_name, plen + 1 )) == NULL ) {
+ syslog( LOG_ERR, "afp_rename: realloc: %m" );
+ if (isad) {
+ ad_flush(&ad, ADFLAGS_HF); /* in case of create */
+ ad_close(&ad, ADFLAGS_HF);
+ }
+ return AFPERR_MISC;
+ }
+ odir->d_name = buf;
+ strcpy( odir->d_name, ibuf );
+ if (!isad)
+ goto out;
+ }
+
+ ad_setentrylen( &ad, ADEID_NAME, plen );
+ memcpy( ad_entry( &ad, ADEID_NAME ), ibuf, plen );
+ ad_flush( &ad, ADFLAGS_HF );
+ ad_close( &ad, ADFLAGS_HF );
+
+out:
+ setvoltime(obj, vol );
+
+ /* if it's still open, rename the ofork as well. */
+ if (of_rename(vol, curdir, path, curdir, ibuf) < 0)
+ return AFPERR_MISC;
+
+ return( AFP_OK );
+}
+
+
+int afp_delete(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct vol *vol;
+ struct dir *dir;
+ char *path, *upath;
+ int did, rc;
+ u_int16_t vid;
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (vol->v_flags & AFPVOL_RO)
+ return AFPERR_VLOCK;
+
+ memcpy( &did, ibuf, sizeof( did ));
+ ibuf += sizeof( int );
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if ( *path == '\0' ) {
+ rc = deletecurdir( vol, obj->oldtmp, AFPOBJ_TMPSIZ);
+ } else if (of_findname(vol, curdir, path)) {
+ rc = AFPERR_BUSY;
+ } else if ((rc = deletefile( upath = mtoupath(vol, path ))) == AFP_OK) {
+#if AD_VERSION > AD_VERSION1 /* get rid of entry */
+ cnid_t id = cnid_get(vol->v_db, curdir->d_did, upath, strlen(upath));
+ cnid_delete(vol->v_db, id);
+#endif
+ }
+ if ( rc == AFP_OK ) {
+ setvoltime(obj, vol );
+ }
+ return( rc );
+}
+
+char *ctoupath( vol, dir, name )
+ const struct vol *vol;
+ struct dir *dir;
+ char *name;
+{
+ struct dir *d;
+ static char path[ MAXPATHLEN + 1];
+ char *p, *u;
+ int len;
+
+ p = path + sizeof( path ) - 1;
+ *p = '\0';
+ u = mtoupath(vol, name );
+ len = strlen( u );
+ p -= len;
+ strncpy( p, u, len );
+ for ( d = dir; d->d_parent; d = d->d_parent ) {
+ *--p = '/';
+ u = mtoupath(vol, d->d_name );
+ len = strlen( u );
+ p -= len;
+ strncpy( p, u, len );
+ }
+ *--p = '/';
+ len = strlen( vol->v_path );
+ p -= len;
+ strncpy( p, vol->v_path, len );
+
+ return( p );
+}
+
+
+int afp_moveandrename(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct vol *vol;
+ struct dir *sdir, *ddir, *odir = NULL;
+ struct stat st;
+ char *oldname, *newname;
+ char *path, *p, *upath;
+ int did, rc;
+ int plen;
+ u_int16_t vid;
+#if AD_VERSION > AD_VERSION1
+ cnid_t id;
+#endif
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy( &vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (vol->v_flags & AFPVOL_RO)
+ return AFPERR_VLOCK;
+
+ /* source did followed by dest did */
+ memcpy( &did, ibuf, sizeof( did ));
+ ibuf += sizeof( int );
+ if (( sdir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy( &did, ibuf, sizeof( did ));
+ ibuf += sizeof( int );
+
+ /* source pathname */
+ if (( path = cname( vol, sdir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ sdir = curdir;
+ newname = obj->newtmp;
+ oldname = obj->oldtmp;
+ if ( *path != '\0' ) {
+ /* not a directory */
+ strcpy(newname, path);
+ strcpy(oldname, path); /* an extra copy for of_rename */
+#if AD_VERSION > AD_VERSION1
+ p = mtoupath(vol, path);
+ id = cnid_get(vol->v_db, sdir->d_did, p, strlen(p));
+#endif
+ p = ctoupath( vol, sdir, newname );
+ } else {
+ odir = curdir;
+ strcpy( newname, odir->d_name );
+ strcpy(oldname, odir->d_name);
+ p = ctoupath( vol, odir->d_parent, newname );
+#if AD_VERSION > AD_VERSION1
+ id = curdir->d_did; /* we already have the CNID */
+#endif
+ }
+ /*
+ * p now points to the full pathname of the source fs object.
+ */
+
+ /* get the destination directory */
+ if (( ddir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+ if (( path = cname( vol, ddir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+ if ( *path != '\0' ) {
+ return( AFPERR_BADTYPE );
+ }
+
+ /* one more place where we know about path type */
+ if ( *ibuf++ != 2 ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
+ strncpy( newname, ibuf, plen );
+ newname[ plen ] = '\0';
+ }
+
+ /* check for illegal characters */
+ if ((vol->v_flags & AFPVOL_MSWINDOWS) &&
+ strpbrk(newname, MSWINDOWS_BADCHARS))
+ return AFPERR_PARAM;
+
+ upath = mtoupath(vol, newname);
+
+ if ((vol->v_flags & AFPVOL_NOHEX) && strchr(upath, '/'))
+ return AFPERR_PARAM;
+
+ if (!validupath(vol, upath))
+ return AFPERR_EXIST;
+
+ /* source == destination. we just silently accept this. */
+ if (curdir == sdir) {
+ if (strcmp(oldname, newname) == 0)
+ return AFP_OK;
+
+ /* deal with case insensitive, case-preserving filesystems. */
+ if ((stat(upath, &st) == 0) && strdiacasecmp(oldname, newname))
+ return AFPERR_EXIST;
+
+ } else if (stat(upath, &st ) == 0)
+ return( AFPERR_EXIST );
+
+ if ( !odir ) {
+ if (of_findname(vol, curdir, newname)) {
+ rc = AFPERR_BUSY;
+ } else if ((rc = renamefile( p, upath, newname,
+ vol_noadouble(vol) )) == AFP_OK) {
+ /* if it's still open, rename the ofork as well. */
+ rc = of_rename(vol, sdir, oldname, curdir, newname);
+ }
+ } else {
+ rc = renamedir(p, upath, odir, curdir, newname, vol_noadouble(vol));
+ }
+
+ if ( rc == AFP_OK ) {
+#if AD_VERSION > AD_VERSION1
+ /* renaming may have moved the file/dir across a filesystem */
+ if (stat(upath, &st) < 0)
+ return AFPERR_MISC;
+
+ /* fix up the catalog entry */
+ cnid_update(vol->v_db, id, &st, curdir->d_did, upath, strlen(upath));
+#endif
+ setvoltime(obj, vol );
+ }
+ return( rc );
+}
+
--- /dev/null
+#ifndef AFPD_FILEDIR_H
+#define AFPD_FILEDIR_H 1
+
+#include <sys/cdefs.h>
+#include "globals.h"
+#include "volume.h"
+
+extern char *ctoupath __P((const struct vol *, struct dir *,
+ char *));
+
+/* FP functions */
+extern int afp_moveandrename __P((AFPObj *, char *, int, char *, int *));
+extern int afp_rename __P((AFPObj *, char *, int, char *, int *));
+extern int afp_delete __P((AFPObj *, char *, int, char *, int *));
+extern int afp_getfildirparams __P((AFPObj *, char *, int, char *,
+ int *));
+extern int afp_setfildirparams __P((AFPObj *, char *, int, char *,
+ int *));
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+
+#include <atalk/dsi.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+#include <atalk/afp.h>
+#include <atalk/adouble.h>
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+
+#include "fork.h"
+#include "file.h"
+#include "globals.h"
+#include "directory.h"
+#include "desktop.h"
+#include "volume.h"
+
+#define BYTELOCK_MAX 0x7FFFFFFFU
+
+struct ofork *writtenfork;
+
+static int getforkparams(ofork, bitmap, buf, buflen, attrbits )
+ struct ofork *ofork;
+ u_int16_t bitmap;
+ char *buf;
+ int *buflen;
+ const u_int16_t attrbits;
+{
+ struct stat st;
+ struct extmap *em;
+ char *data, *nameoff = NULL, *upath;
+ int bit = 0, isad = 1;
+ u_int32_t aint;
+ u_int16_t ashort;
+
+ if ( ad_hfileno( ofork->of_ad ) == -1 ) {
+ isad = 0;
+ } else {
+ aint = ad_getentrylen( ofork->of_ad, ADEID_RFORK );
+ if ( ad_refresh( ofork->of_ad ) < 0 ) {
+ syslog( LOG_ERR, "getforkparams: ad_refresh: %m");
+ return( AFPERR_PARAM );
+ }
+ /* See afp_closefork() for why this is bad */
+ ad_setentrylen( ofork->of_ad, ADEID_RFORK, aint );
+ }
+
+ /* can only get the length of the opened fork */
+ if (((bitmap & (1<<FILPBIT_DFLEN)) && (ofork->of_flags & AFPFORK_RSRC)) ||
+ ((bitmap & (1<<FILPBIT_RFLEN)) && (ofork->of_flags & AFPFORK_DATA))) {
+ return( AFPERR_BITMAP );
+ }
+
+ if ( bitmap & ( 1<<FILPBIT_DFLEN | 1<<FILPBIT_FNUM |
+ (1 << FILPBIT_CDATE) | (1 << FILPBIT_MDATE) |
+ (1 << FILPBIT_BDATE))) {
+ upath = mtoupath(ofork->of_vol, ofork->of_name);
+ if ( ad_dfileno( ofork->of_ad ) == -1 ) {
+ if ( stat( upath, &st ) < 0 )
+ return( AFPERR_NOOBJ );
+ } else {
+ if ( fstat( ad_dfileno( ofork->of_ad ), &st ) < 0 ) {
+ return( AFPERR_BITMAP );
+ }
+ }
+ }
+
+ data = buf;
+ while ( bitmap != 0 ) {
+ while (( bitmap & 1 ) == 0 ) {
+ bitmap = bitmap>>1;
+ bit++;
+ }
+
+ switch ( bit ) {
+ case FILPBIT_ATTR :
+ if ( isad ) {
+ ad_getattr(ofork->of_ad, &ashort);
+ } else {
+ ashort = 0;
+ }
+ if (attrbits)
+ ashort = htons(ntohs(ashort) | attrbits);
+ memcpy(data, &ashort, sizeof( ashort ));
+ data += sizeof( ashort );
+ break;
+
+ case FILPBIT_PDID :
+ memcpy(data, &ofork->of_dir->d_did, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case FILPBIT_CDATE :
+ if (!isad ||
+ (ad_getdate(ofork->of_ad, AD_DATE_CREATE, &aint) < 0))
+ aint = AD_DATE_FROM_UNIX(st.st_mtime);
+ memcpy(data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case FILPBIT_MDATE :
+ if (!isad ||
+ (ad_getdate(ofork->of_ad, AD_DATE_MODIFY, &aint) < 0) ||
+ (AD_DATE_TO_UNIX(aint) < st.st_mtime))
+ aint = AD_DATE_FROM_UNIX(st.st_mtime);
+ memcpy(data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case FILPBIT_BDATE :
+ if (!isad ||
+ (ad_getdate(ofork->of_ad, AD_DATE_BACKUP, &aint) < 0))
+ aint = AD_DATE_START;
+ memcpy(data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case FILPBIT_FINFO :
+ memcpy(data, isad ?
+ (void *) ad_entry(ofork->of_ad, ADEID_FINDERI) :
+ (void *) ufinderi, 32);
+ if ( !isad ||
+ memcmp( ad_entry( ofork->of_ad, ADEID_FINDERI ),
+ ufinderi, 8 ) == 0 ) {
+ memcpy(data, ufinderi, 8 );
+ if (( em = getextmap( ofork->of_name )) != NULL ) {
+ memcpy(data, em->em_type, sizeof( em->em_type ));
+ memcpy(data + 4, em->em_creator,
+ sizeof( em->em_creator ));
+ }
+ }
+ data += 32;
+ break;
+
+ case FILPBIT_LNAME :
+ nameoff = data;
+ data += sizeof(u_int16_t);
+ break;
+
+ case FILPBIT_SNAME :
+ memset(data, 0, sizeof(u_int16_t));
+ data += sizeof(u_int16_t);
+ break;
+
+ case FILPBIT_FNUM :
+ /*
+ * See file.c getfilparams() for why this is done this
+ * way.
+ */
+#if AD_VERSION > AD_VERSION1
+ if (isad)
+ memcpy(&aint, ad_entry(ofork->of_ad, ADEID_DID), sizeof(aint));
+ else
+ aint = 0;
+
+ if (!(aint = cnid_add(ofork->of_vol->v_db, &st,
+ ofork->of_dir->d_did,
+ upath, strlen(upath), aint))) {
+#endif
+#ifdef AFS
+ aint = st.st_ino;
+#else AFS
+ aint = ( st.st_dev << 16 ) | ( st.st_ino & 0x0000ffff );
+#endif AFS
+#if AD_VERSION > AD_VERSION1
+ }
+#endif
+ memcpy(data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case FILPBIT_DFLEN :
+ aint = htonl( st.st_size );
+ memcpy(data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case FILPBIT_RFLEN :
+ if ( isad ) {
+ aint = htonl( ad_getentrylen( ofork->of_ad, ADEID_RFORK ));
+ } else {
+ aint = 0;
+ }
+ memcpy(data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ default :
+ return( AFPERR_BITMAP );
+ }
+ bitmap = bitmap>>1;
+ bit++;
+ }
+
+ if ( nameoff ) {
+ ashort = htons( data - buf );
+ memcpy(nameoff, &ashort, sizeof( ashort ));
+ aint = strlen( ofork->of_name );
+ aint = ( aint > MACFILELEN ) ? MACFILELEN : aint;
+ *data++ = aint;
+ memcpy(data, ofork->of_name, aint );
+ data += aint;
+ }
+
+ *buflen = data - buf;
+ return( AFP_OK );
+}
+
+int afp_openfork(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct vol *vol;
+ struct dir *dir;
+ struct ofork *ofork, *opened;
+ struct adouble *adsame = NULL;
+ int buflen, ret, adflags, eid, lockop;
+ u_int32_t did;
+ u_int16_t vid, bitmap, access, ofrefnum, attrbits = 0;
+ char fork, *path, *upath;
+
+ ibuf++;
+ fork = *ibuf++;
+ memcpy(&vid, ibuf, sizeof( vid ));
+ ibuf += sizeof(vid);
+
+ *rbuflen = 0;
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy(&did, ibuf, sizeof( did ));
+ ibuf += sizeof( int );
+
+ if (( dir = dirsearch( vol, did )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ memcpy(&bitmap, ibuf, sizeof( bitmap ));
+ bitmap = ntohs( bitmap );
+ ibuf += sizeof( bitmap );
+ memcpy(&access, ibuf, sizeof( access ));
+ access = ntohs( access );
+ ibuf += sizeof( access );
+
+ if ((vol->v_flags & AFPVOL_RO) && (access & OPENACC_WR)) {
+ return AFPERR_VLOCK;
+ }
+
+ if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+ return( AFPERR_NOOBJ );
+ }
+
+ if ( fork == OPENFORK_DATA ) {
+ eid = ADEID_DFORK;
+ adflags = ADFLAGS_DF|ADFLAGS_HF;
+ } else {
+ eid = ADEID_RFORK;
+ adflags = ADFLAGS_HF;
+ }
+
+ /* XXX: this probably isn't the best way to do this. the already
+ open bits should really be set if the fork is opened by any
+ program, not just this one. however, that's problematic to do
+ if we can't write lock files somewhere. opened is also passed to
+ ad_open so that we can keep file locks together. */
+ if ((opened = of_findname(vol, curdir, path))) {
+ attrbits = ((opened->of_flags & AFPFORK_RSRC) ? ATTRBIT_ROPEN : 0) |
+ ((opened->of_flags & AFPFORK_DATA) ? ATTRBIT_DOPEN : 0);
+ adsame = opened->of_ad;
+ }
+
+ if (( ofork = of_alloc(vol, curdir, path, &ofrefnum, eid,
+ adsame)) == NULL ) {
+ return( AFPERR_NFILE );
+ }
+
+ /* try opening in read-write mode with a fallback to read-only
+ * if we don't need write access. */
+ upath = mtoupath(vol, path);
+ ret = AFPERR_NOOBJ;
+ if (ad_open(upath, adflags, O_RDWR, 0, ofork->of_ad) < 0) {
+ switch ( errno ) {
+ case EROFS:
+ ret = AFPERR_VLOCK;
+ case EACCES:
+ if (access & OPENACC_WR)
+ goto openfork_err;
+
+ if (ad_open(upath, adflags, O_RDONLY, 0, ofork->of_ad) < 0) {
+ /* check for a read-only data fork */
+ if ((adflags != ADFLAGS_HF) &&
+ (ad_open(upath, ADFLAGS_DF, O_RDONLY, 0, ofork->of_ad) < 0))
+ goto openfork_err;
+
+ adflags = ADFLAGS_DF;
+ }
+ break;
+ case ENOENT:
+ {
+ struct stat st;
+
+ /* see if client asked for the data fork */
+ if (fork == OPENFORK_DATA) {
+ if (((access & OPENACC_WR) &&
+ (ad_open(upath, ADFLAGS_DF, O_RDWR, 0, ofork->of_ad) < 0))
+ || (ad_open(upath, ADFLAGS_DF, O_RDONLY, 0,
+ ofork->of_ad) < 0)) {
+ goto openfork_err;
+ }
+ adflags = ADFLAGS_DF;
+
+ } else if (stat(upath, &st) == 0) {
+ /* here's the deal. we only try to create the resource
+ * fork if the user wants to open it for write access. */
+ if ((access & OPENACC_WR) &&
+ (ad_open(upath, adflags, O_RDWR | O_CREAT,
+ 0666, ofork->of_ad) < 0))
+ goto openfork_err;
+ } else
+ goto openfork_err;
+ }
+ break;
+ case EMFILE :
+ case ENFILE :
+ ret = AFPERR_NFILE;
+ goto openfork_err;
+ break;
+ case EISDIR :
+ ret = AFPERR_BADTYPE;
+ goto openfork_err;
+ break;
+ default:
+ syslog( LOG_ERR, "afp_openfork: ad_open: %m" );
+ ret = AFPERR_PARAM;
+ goto openfork_err;
+ break;
+ }
+ }
+
+ if ((adflags & ADFLAGS_HF) &&
+ (ad_getoflags( ofork->of_ad, ADFLAGS_HF ) & O_CREAT)) {
+ ad_setentrylen( ofork->of_ad, ADEID_NAME, strlen( path ));
+ memcpy(ad_entry( ofork->of_ad, ADEID_NAME ), path,
+ ad_getentrylen( ofork->of_ad, ADEID_NAME ));
+ ad_flush( ofork->of_ad, adflags );
+ }
+
+ if (( ret = getforkparams(ofork, bitmap, rbuf + 2 * sizeof( u_int16_t ),
+ &buflen, attrbits )) != AFP_OK ) {
+ ad_close( ofork->of_ad, adflags );
+ goto openfork_err;
+ }
+
+ *rbuflen = buflen + 2 * sizeof( u_int16_t );
+ bitmap = htons( bitmap );
+ memcpy(rbuf, &bitmap, sizeof( u_int16_t ));
+ rbuf += sizeof( u_int16_t );
+
+ /*
+ * synchronization locks:
+ *
+ * here's the ritual:
+ * 1) attempt a read lock to see if we have read or write
+ * privileges.
+ * 2) if that succeeds, set a write lock to correspond to the
+ * deny mode requested.
+ * 3) whenever a file is read/written, locks get set which
+ * prevent conflicts.
+ */
+
+ /* don't try to lock non-existent rforks. */
+ if ((eid == ADEID_DFORK) || (ad_hfileno(ofork->of_ad) != -1)) {
+
+ /* try to see if we have access. */
+ ret = 0;
+ if (access & OPENACC_WR) {
+ ofork->of_flags |= AFPFORK_ACCWR;
+ ret = ad_lock(ofork->of_ad, eid, ADLOCK_RD | ADLOCK_FILELOCK,
+ AD_FILELOCK_WR, 1, ofrefnum);
+ }
+
+ if (!ret && (access & OPENACC_RD)) {
+ ofork->of_flags |= AFPFORK_ACCRD;
+ ret = ad_lock(ofork->of_ad, eid, ADLOCK_RD | ADLOCK_FILELOCK,
+ AD_FILELOCK_RD, 1, ofrefnum);
+ }
+
+ /* can we access the fork? */
+ if (ret < 0) {
+ ad_close( ofork->of_ad, adflags );
+ of_dealloc( ofork );
+ ofrefnum = 0;
+ memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
+ return (AFPERR_DENYCONF);
+ }
+
+ /* now try to set the deny lock. if the fork is open for read or
+ * write, a read lock will already have been set. otherwise, we upgrade
+ * our lock to a write lock.
+ *
+ * NOTE: we can't write lock a read-only file. on those, we just
+ * make sure that we have a read lock set. that way, we at least prevent
+ * someone else from really setting a deny read/write on the file. */
+ lockop = (ad_getoflags(ofork->of_ad, eid) & O_RDWR) ?
+ ADLOCK_WR : ADLOCK_RD;
+ ret = (access & OPENACC_DWR) ? ad_lock(ofork->of_ad, eid,
+ lockop | ADLOCK_FILELOCK |
+ ADLOCK_UPGRADE, AD_FILELOCK_WR, 1,
+ ofrefnum) : 0;
+ if (!ret && (access & OPENACC_DRD))
+ ret = ad_lock(ofork->of_ad, eid, lockop | ADLOCK_FILELOCK |
+ ADLOCK_UPGRADE, AD_FILELOCK_RD, 1, ofrefnum);
+
+ if (ret < 0) {
+ ret = errno;
+ ad_close( ofork->of_ad, adflags );
+ of_dealloc( ofork );
+ switch (ret) {
+ case EAGAIN: /* return data anyway */
+ case EACCES:
+ case EINVAL:
+ ofrefnum = 0;
+ memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
+ return( AFPERR_DENYCONF );
+ break;
+ default:
+ *rbuflen = 0;
+ syslog( LOG_ERR, "afp_openfork: ad_lock: %m" );
+ return( AFPERR_PARAM );
+ }
+ }
+ }
+
+ memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
+ return( AFP_OK );
+
+openfork_err:
+ of_dealloc( ofork );
+ if (errno == EACCES)
+ return (access & OPENACC_WR) ? AFPERR_LOCK : AFPERR_ACCESS;
+ return ret;
+}
+
+int afp_setforkparams(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct ofork *ofork;
+ int32_t size;
+ u_int16_t ofrefnum, bitmap;
+ int err;
+
+ ibuf += 2;
+ memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
+ ibuf += sizeof( ofrefnum );
+ memcpy(&bitmap, ibuf, sizeof(bitmap));
+ bitmap = ntohs(bitmap);
+ ibuf += sizeof( bitmap );
+ memcpy(&size, ibuf, sizeof( size ));
+ size = ntohl( size );
+
+ *rbuflen = 0;
+ if (( ofork = of_find( ofrefnum )) == NULL ) {
+ syslog( LOG_ERR, "afp_setforkparams: of_find: %m" );
+ return( AFPERR_PARAM );
+ }
+
+ if (ofork->of_vol->v_flags & AFPVOL_RO)
+ return AFPERR_VLOCK;
+
+ if ((ofork->of_flags & AFPFORK_ACCWR) == 0)
+ return AFPERR_ACCESS;
+
+ if (size < 0)
+ return AFPERR_PARAM;
+
+ if ((bitmap == (1<<FILPBIT_DFLEN)) && (ofork->of_flags & AFPFORK_DATA)) {
+ err = ad_dtruncate( ofork->of_ad, size );
+ if (err < 0)
+ goto afp_setfork_err;
+ } else if ((bitmap == (1<<FILPBIT_RFLEN)) &&
+ (ofork->of_flags & AFPFORK_RSRC)) {
+ ad_refresh( ofork->of_ad );
+ err = ad_rtruncate(ofork->of_ad, size);
+ if (err < 0)
+ goto afp_setfork_err;
+
+ if (ad_flush( ofork->of_ad, ADFLAGS_HF ) < 0) {
+ syslog( LOG_ERR, "afp_setforkparams: ad_flush: %m" );
+ return( AFPERR_PARAM );
+ }
+ } else
+ return AFPERR_BITMAP;
+
+#ifdef AFS
+ if ( flushfork( ofork ) < 0 ) {
+ syslog( LOG_ERR, "afp_setforkparams: flushfork: %m" );
+ }
+#endif AFS
+
+ return( AFP_OK );
+
+afp_setfork_err:
+ if (err == -2)
+ return AFPERR_LOCK;
+ else {
+ switch (errno) {
+ case EROFS:
+ return AFPERR_VLOCK;
+ case EPERM:
+ case EACCES:
+ return AFPERR_ACCESS;
+ case EDQUOT:
+ case EFBIG:
+ case ENOSPC:
+ return AFPERR_DFULL;
+ default:
+ return AFPERR_PARAM;
+ }
+ }
+}
+
+/* for this to work correctly, we need to check for locks before each
+ * read and write. that's most easily handled by always doing an
+ * appropriate check before each ad_read/ad_write. other things
+ * that can change files like truncate are handled internally to those
+ * functions.
+ */
+#define ENDBIT(a) ((a) & 0x80)
+#define UNLOCKBIT(a) ((a) & 0x01)
+int afp_bytelock(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct ofork *ofork;
+ int32_t offset, length;
+ int eid;
+ u_int16_t ofrefnum;
+ u_int8_t flags;
+
+ *rbuflen = 0;
+
+ /* figure out parameters */
+ ibuf++;
+ flags = *ibuf; /* first bit = endflag, lastbit = lockflag */
+ ibuf++;
+ memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
+ ibuf += sizeof(ofrefnum);
+
+ if (( ofork = of_find( ofrefnum )) == NULL ) {
+ syslog( LOG_ERR, "afp_bytelock: of_find: %m" );
+ return( AFPERR_PARAM );
+ }
+
+ if ( ofork->of_flags & AFPFORK_DATA) {
+ eid = ADEID_DFORK;
+ } else if (ofork->of_flags & AFPFORK_RSRC) {
+ eid = ADEID_RFORK;
+ } else
+ return AFPERR_PARAM;
+
+ memcpy(&offset, ibuf, sizeof( offset ));
+ offset = ntohl(offset);
+ ibuf += sizeof(offset);
+
+ memcpy(&length, ibuf, sizeof( length ));
+ length = ntohl(length);
+ if (length == 0xFFFFFFFF)
+ length = BYTELOCK_MAX;
+ else if (length <= 0) {
+ return AFPERR_PARAM;
+ } else if ((length >= AD_FILELOCK_BASE) &&
+ (ad_hfileno(ofork->of_ad) == -1))
+ return AFPERR_LOCK;
+
+ if (ENDBIT(flags))
+ offset += ad_size(ofork->of_ad, eid);
+
+ if (offset < 0) /* error if we have a negative offset */
+ return AFPERR_PARAM;
+
+ /* if the file is a read-only file, we use read locks instead of
+ * write locks. that way, we can prevent anyone from initiating
+ * a write lock. */
+ if (ad_lock(ofork->of_ad, eid, UNLOCKBIT(flags) ? ADLOCK_CLR :
+ ((ad_getoflags(ofork->of_ad, eid) & O_RDWR) ?
+ ADLOCK_WR : ADLOCK_RD), offset, length,
+ ofork->of_refnum) < 0) {
+ switch (errno) {
+ case EACCES:
+ case EAGAIN:
+ return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_LOCK;
+ break;
+ case ENOLCK:
+ return AFPERR_NLOCK;
+ break;
+ case EINVAL:
+ return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_RANGEOVR;
+ break;
+ case EBADF:
+ default:
+ return AFPERR_PARAM;
+ break;
+ }
+ }
+
+ offset = htonl(offset);
+ memcpy(rbuf, &offset, sizeof( offset ));
+ *rbuflen = sizeof( offset );
+ return( AFP_OK );
+}
+#undef UNLOCKBIT
+
+
+static __inline__ int crlf( of )
+ struct ofork *of;
+{
+ struct extmap *em;
+
+ if ( ad_hfileno( of->of_ad ) == -1 ||
+ memcmp( ufinderi, ad_entry( of->of_ad, ADEID_FINDERI ),
+ 8) == 0 ) {
+ if (( em = getextmap( of->of_name )) == NULL ||
+ memcmp( "TEXT", em->em_type, sizeof( em->em_type )) == 0 ) {
+ return( 1 );
+ } else {
+ return( 0 );
+ }
+ } else {
+ if ( memcmp( ufinderi,
+ ad_entry( of->of_ad, ADEID_FINDERI ), 4 ) == 0 ) {
+ return( 1 );
+ } else {
+ return( 0 );
+ }
+ }
+}
+
+
+static __inline__ ssize_t read_file(struct ofork *ofork, int eid,
+ int offset, u_char nlmask,
+ u_char nlchar, char *rbuf,
+ int *rbuflen, const int xlate)
+{
+ ssize_t cc;
+ int eof = 0;
+ char *p, *q;
+
+ cc = ad_read(ofork->of_ad, eid, offset, rbuf, *rbuflen);
+ if ( cc < 0 ) {
+ syslog( LOG_ERR, "afp_read: ad_read: %m" );
+ *rbuflen = 0;
+ return( AFPERR_PARAM );
+ }
+ if ( cc < *rbuflen ) {
+ eof = 1;
+ }
+
+ /*
+ * Do Newline check.
+ */
+ if ( nlmask != 0 ) {
+ for ( p = rbuf, q = p + cc; p < q; ) {
+ if (( *p++ & nlmask ) == nlchar ) {
+ break;
+ }
+ }
+ if ( p != q ) {
+ cc = p - rbuf;
+ eof = 0;
+ }
+ }
+
+ /*
+ * If this file is of type TEXT, then swap \012 to \015.
+ */
+ if (xlate) {
+ for ( p = rbuf, q = p + cc; p < q; p++ ) {
+ if ( *p == '\012' ) {
+ *p = '\015';
+ } else if ( *p == '\015' ) {
+ *p = '\012';
+ }
+
+ }
+ }
+
+ *rbuflen = cc;
+ if ( eof ) {
+ return( AFPERR_EOF );
+ }
+ return AFP_OK;
+}
+
+int afp_read(obj, ibuf, ibuflen, rbuf, rbuflen)
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct ofork *ofork;
+ off_t size;
+ int32_t offset, saveoff, reqcount;
+ int cc, err, eid, xlate = 0;
+ u_int16_t ofrefnum;
+ u_char nlmask, nlchar;
+
+ ibuf += 2;
+ memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
+ ibuf += sizeof( u_short );
+
+ if (( ofork = of_find( ofrefnum )) == NULL ) {
+ syslog( LOG_ERR, "afp_read: of_find: %m" );
+ err = AFPERR_PARAM;
+ goto afp_read_err;
+ }
+
+ if ((ofork->of_flags & AFPFORK_ACCRD) == 0) {
+ err = AFPERR_ACCESS;
+ goto afp_read_err;
+ }
+
+ memcpy(&offset, ibuf, sizeof( offset ));
+ offset = ntohl( offset );
+ ibuf += sizeof( offset );
+ memcpy(&reqcount, ibuf, sizeof( reqcount ));
+ reqcount = ntohl( reqcount );
+ ibuf += sizeof( reqcount );
+
+ nlmask = *ibuf++;
+ nlchar = *ibuf++;
+
+ /* if we wanted to be picky, we could add in the following
+ * bit: if (afp_version == 11 && !(nlmask == 0xFF || !nlmask))
+ */
+ if (reqcount < 0 || offset < 0) {
+ err = AFPERR_PARAM;
+ goto afp_read_err;
+ }
+
+ if ( ofork->of_flags & AFPFORK_DATA) {
+ eid = ADEID_DFORK;
+ xlate = (ofork->of_vol->v_flags & AFPVOL_CRLF) ? crlf(ofork) : 0;
+ } else if (ofork->of_flags & AFPFORK_RSRC) {
+ eid = ADEID_RFORK;
+ } else { /* fork wasn't opened. this should never really happen. */
+ err = AFPERR_ACCESS;
+ goto afp_read_err;
+ }
+
+ /* zero request count */
+ if (!reqcount) {
+ err = AFP_OK;
+ goto afp_read_err;
+ }
+
+ /* reqcount isn't always truthful. we need to deal with that. */
+ if ((size = ad_size(ofork->of_ad, eid)) == 0) {
+ err = AFPERR_EOF;
+ goto afp_read_err;
+ }
+
+ saveoff = offset;
+ if (ad_tmplock(ofork->of_ad, eid, ADLOCK_RD, saveoff, reqcount) < 0) {
+ err = AFPERR_LOCK;
+ goto afp_read_err;
+ }
+
+#define min(a,b) ((a)<(b)?(a):(b))
+ *rbuflen = min( reqcount, *rbuflen );
+ err = read_file(ofork, eid, offset, nlmask, nlchar, rbuf, rbuflen,
+ xlate);
+ if (err < 0)
+ goto afp_read_done;
+
+ /* dsi can stream requests. we can only do this if we're not checking
+ * for an end-of-line character. oh well. */
+ if ((obj->proto == AFPPROTO_DSI) && (*rbuflen < reqcount) && !nlmask) {
+ DSI *dsi = obj->handle;
+
+ /* subtract off the offset */
+ size -= offset;
+ if (reqcount > size) {
+ reqcount = size;
+ err = AFPERR_EOF;
+ }
+
+ if (obj->options.flags & OPTION_DEBUG) {
+ printf( "(read) reply: %d/%d, %d\n", *rbuflen,
+ reqcount, dsi->clientID);
+ bprint(rbuf, *rbuflen);
+ }
+
+ offset += *rbuflen;
+
+ /* dsi_readinit() returns size of next read buffer. by this point,
+ * we know that we're sending some data. if we fail, something
+ * horrible happened. */
+ if ((*rbuflen = dsi_readinit(dsi, rbuf, *rbuflen, reqcount, err)) < 0)
+ goto afp_read_exit;
+
+ /* due to the nature of afp packets, we have to exit if we get
+ an error. we can't do this with translation on. */
+#ifdef HAVE_SENDFILE_READ
+ if (!(xlate || (obj->options.flags & OPTION_DEBUG))) {
+ if (ad_readfile(ofork->of_ad, eid, dsi->socket, offset,
+ dsi->datasize) < 0) {
+ if (errno == EINVAL)
+ goto afp_read_loop;
+ else {
+ syslog(LOG_ERR, "afp_read: ad_readfile: %m");
+ goto afp_read_exit;
+ }
+ }
+
+ dsi_readdone(dsi);
+ goto afp_read_done;
+ }
+
+afp_read_loop:
+#endif
+
+ /* fill up our buffer. */
+ while (*rbuflen > 0) {
+ cc = read_file(ofork, eid, offset, nlmask, nlchar, rbuf,
+ rbuflen, xlate);
+ if (cc < 0)
+ goto afp_read_exit;
+
+ offset += *rbuflen;
+ if (obj->options.flags & OPTION_DEBUG) {
+ printf( "(read) reply: %d, %d\n", *rbuflen, dsi->clientID);
+ bprint(rbuf, *rbuflen);
+ }
+
+ /* dsi_read() also returns buffer size of next allocation */
+ cc = dsi_read(dsi, rbuf, *rbuflen); /* send it off */
+ if (cc < 0)
+ goto afp_read_exit;
+ *rbuflen = cc;
+ }
+ dsi_readdone(dsi);
+ goto afp_read_done;
+
+afp_read_exit:
+ syslog(LOG_ERR, "afp_read: %m");
+ dsi_readdone(dsi);
+ ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount);
+ obj->exit(1);
+ }
+
+afp_read_done:
+ ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount);
+ return err;
+
+afp_read_err:
+ *rbuflen = 0;
+ return err;
+}
+
+int afp_flush(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct vol *vol;
+ u_int16_t vid;
+
+ *rbuflen = 0;
+ ibuf += 2;
+
+ memcpy(&vid, ibuf, sizeof(vid));
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ of_flush(vol);
+ return( AFP_OK );
+}
+
+int afp_flushfork(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct ofork *ofork;
+ u_int16_t ofrefnum;
+
+ *rbuflen = 0;
+ ibuf += 2;
+ memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
+
+ if (( ofork = of_find( ofrefnum )) == NULL ) {
+ syslog( LOG_ERR, "afp_flushfork: of_find: %m" );
+ return( AFPERR_PARAM );
+ }
+
+ if ( flushfork( ofork ) < 0 ) {
+ syslog( LOG_ERR, "afp_flushfork: %m" );
+ }
+
+ return( AFP_OK );
+}
+
+/* this is very similar to closefork */
+int flushfork( ofork )
+ struct ofork *ofork;
+{
+ struct timeval tv;
+ int len, err = 0, doflush = 0;
+
+ if ( ad_dfileno( ofork->of_ad ) != -1 &&
+ fsync( ad_dfileno( ofork->of_ad )) < 0 ) {
+ syslog( LOG_ERR, "flushfork: dfile(%d) %m",
+ ad_dfileno(ofork->of_ad) );
+ err = -1;
+ }
+
+ if ( ad_hfileno( ofork->of_ad ) != -1 ) {
+ if (ofork->of_flags & AFPFORK_RSRC) {
+ len = ad_getentrylen(ofork->of_ad, ADEID_RFORK);
+ ad_refresh(ofork->of_ad);
+ if (len != ad_getentrylen(ofork->of_ad, ADEID_RFORK)) {
+ ad_setentrylen(ofork->of_ad, ADEID_RFORK, len);
+ doflush++;
+ }
+ }
+
+ if ((ofork->of_flags & AFPFORK_DIRTY) &&
+ (gettimeofday(&tv, NULL) == 0)) {
+ ad_setdate(ofork->of_ad, AD_DATE_MODIFY|AD_DATE_UNIX, tv.tv_sec);
+ ofork->of_flags &= ~AFPFORK_DIRTY;
+ doflush++;
+ }
+
+ /* flush the header. */
+ if (doflush && (ad_flush(ofork->of_ad, ADFLAGS_HF) < 0))
+ err = -1;
+
+ if (fsync( ad_hfileno( ofork->of_ad )) < 0)
+ err = -1;
+
+ if (err < 0)
+ syslog( LOG_ERR, "flushfork: hfile(%d) %m",
+ ad_hfileno(ofork->of_ad) );
+ }
+
+ return( err );
+}
+
+int afp_closefork(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct ofork *ofork;
+ struct timeval tv;
+ int adflags, aint, doflush = 0;
+ u_int16_t ofrefnum;
+
+ *rbuflen = 0;
+ ibuf += 2;
+ memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
+
+ if (( ofork = of_find( ofrefnum )) == NULL ) {
+ syslog( LOG_ERR, "afp_closefork: of_find: %m" );
+ return( AFPERR_PARAM );
+ }
+
+ adflags = 0;
+ if ((ofork->of_flags & AFPFORK_DATA) &&
+ (ad_dfileno( ofork->of_ad ) != -1)) {
+ adflags |= ADFLAGS_DF;
+ }
+
+ if ( ad_hfileno( ofork->of_ad ) != -1 ) {
+ adflags |= ADFLAGS_HF;
+
+ aint = ad_getentrylen( ofork->of_ad, ADEID_RFORK );
+ ad_refresh( ofork->of_ad );
+ if ((ofork->of_flags & AFPFORK_DIRTY) &&
+ (gettimeofday(&tv, NULL) == 0)) {
+ ad_setdate(ofork->of_ad, AD_DATE_MODIFY | AD_DATE_UNIX,
+ tv.tv_sec);
+ doflush++;
+ }
+
+ /*
+ * Only set the rfork's length if we're closing the rfork.
+ */
+ if ((ofork->of_flags & AFPFORK_RSRC) && aint !=
+ ad_getentrylen( ofork->of_ad, ADEID_RFORK )) {
+ ad_setentrylen( ofork->of_ad, ADEID_RFORK, aint );
+ doflush++;
+ }
+ if ( doflush ) {
+ ad_flush( ofork->of_ad, adflags );
+ }
+ }
+
+ if ( ad_close( ofork->of_ad, adflags ) < 0 ) {
+ syslog( LOG_ERR, "afp_closefork: ad_close: %m" );
+ return( AFPERR_PARAM );
+ }
+
+ of_dealloc( ofork );
+ return( AFP_OK );
+}
+
+
+static __inline__ ssize_t write_file(struct ofork *ofork, int eid,
+ off_t offset, char *rbuf,
+ size_t rbuflen, const int xlate)
+{
+ char *p, *q;
+ ssize_t cc;
+
+ /*
+ * If this file is of type TEXT, swap \015 to \012.
+ */
+ if (xlate) {
+ for ( p = rbuf, q = p + rbuflen; p < q; p++ ) {
+ if ( *p == '\015' ) {
+ *p = '\012';
+ } else if ( *p == '\012' ) {
+ *p = '\015';
+ }
+ }
+ }
+
+ if (( cc = ad_write(ofork->of_ad, eid, offset, 0,
+ rbuf, rbuflen)) < 0 ) {
+ switch ( errno ) {
+ case EDQUOT :
+ case EFBIG :
+ case ENOSPC :
+ return( AFPERR_DFULL );
+ default :
+ syslog( LOG_ERR, "afp_write: ad_write: %m" );
+ return( AFPERR_PARAM );
+ }
+ }
+
+ return cc;
+}
+
+/* FPWrite. NOTE: on an error, we always use afp_write_err as
+ * the client may have sent us a bunch of data that's not reflected
+ * in reqcount et al. */
+int afp_write(obj, ibuf, ibuflen, rbuf, rbuflen)
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct ofork *ofork;
+ int32_t offset, saveoff, reqcount;
+ int endflag, eid, xlate = 0, err = AFP_OK;
+ u_int16_t ofrefnum;
+ ssize_t cc;
+
+ /* figure out parameters */
+ ibuf++;
+ endflag = ENDBIT(*ibuf);
+ ibuf++;
+ memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
+ ibuf += sizeof( ofrefnum );
+ memcpy(&offset, ibuf, sizeof( offset ));
+ offset = ntohl( offset );
+ ibuf += sizeof( offset );
+ memcpy(&reqcount, ibuf, sizeof( reqcount ));
+ reqcount = ntohl( reqcount );
+ ibuf += sizeof( reqcount );
+
+ if (( ofork = of_find( ofrefnum )) == NULL ) {
+ syslog( LOG_ERR, "afp_write: of_find: %m" );
+ err = AFPERR_PARAM;
+ goto afp_write_err;
+ }
+
+ if ((ofork->of_flags & AFPFORK_ACCWR) == 0) {
+ err = AFPERR_ACCESS;
+ goto afp_write_err;
+ }
+
+#ifdef AFS
+ writtenfork = ofork;
+#endif AFS
+
+ if ( ofork->of_flags & AFPFORK_DATA) {
+ eid = ADEID_DFORK;
+ xlate = (ofork->of_vol->v_flags & AFPVOL_CRLF) ? crlf(ofork) : 0;
+ } else if (ofork->of_flags & AFPFORK_RSRC) {
+ eid = ADEID_RFORK;
+ } else {
+ err = AFPERR_ACCESS; /* should never happen */
+ goto afp_write_err;
+ }
+
+ if (endflag)
+ offset += ad_size(ofork->of_ad, eid);
+
+ /* handle bogus parameters */
+ if (reqcount < 0 || offset < 0) {
+ err = AFPERR_PARAM;
+ goto afp_write_err;
+ }
+
+ /* offset can overflow on 64-bit capable filesystems.
+ * report disk full if that's going to happen. */
+ if (offset + reqcount < 0) {
+ err = AFPERR_DFULL;
+ goto afp_write_err;
+ }
+
+ if (!reqcount) { /* handle request counts of 0 */
+ err = AFP_OK;
+ offset = htonl(offset);
+ memcpy(rbuf, &offset, sizeof(offset));
+ goto afp_write_err;
+ }
+
+ saveoff = offset;
+ if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff,
+ reqcount) < 0) {
+ err = AFPERR_LOCK;
+ goto afp_write_err;
+ }
+
+ /* this is yucky, but dsi can stream i/o and asp can't */
+ switch (obj->proto) {
+#ifndef NO_DDP
+ case AFPPROTO_ASP:
+ if (asp_wrtcont(obj->handle, rbuf, rbuflen) < 0) {
+ *rbuflen = 0;
+ syslog( LOG_ERR, "afp_write: asp_wrtcont: %m" );
+ return( AFPERR_PARAM );
+ }
+
+ if (obj->options.flags & OPTION_DEBUG) {
+ printf("(write) len: %d\n", *rbuflen);
+ bprint(rbuf, *rbuflen);
+ }
+
+ if ((cc = write_file(ofork, eid, offset, rbuf, *rbuflen,
+ xlate)) < 0) {
+ *rbuflen = 0;
+ ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount);
+ return cc;
+ }
+ offset += cc;
+ break;
+#endif /* no afp/asp */
+
+ case AFPPROTO_DSI:
+ {
+ DSI *dsi = obj->handle;
+
+ /* find out what we have already and write it out. */
+ cc = dsi_writeinit(dsi, rbuf, *rbuflen);
+ if (!cc ||
+ (cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
+ dsi_writeflush(dsi);
+ *rbuflen = 0;
+ ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount);
+ return cc;
+ }
+ offset += cc;
+
+#if 0 /*def HAVE_SENDFILE_WRITE*/
+ if (!(xlate || obj->options.flags & OPTION_DEBUG)) {
+ if ((cc = ad_writefile(ofork->of_ad, eid, dsi->socket,
+ offset, dsi->datasize)) < 0) {
+ switch (errno) {
+ case EDQUOT :
+ case EFBIG :
+ case ENOSPC :
+ cc = AFPERR_DFULL;
+ break;
+ default :
+ syslog( LOG_ERR, "afp_write: ad_writefile: %m" );
+ goto afp_write_loop;
+ }
+ dsi_writeflush(dsi);
+ *rbuflen = 0;
+ ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
+ reqcount);
+ return cc;
+ }
+
+ offset += cc;
+ goto afp_write_done;
+ }
+#endif
+
+ /* loop until everything gets written. currently
+ * dsi_write handles the end case by itself. */
+afp_write_loop:
+ while ((cc = dsi_write(dsi, rbuf, *rbuflen))) {
+ if ( obj->options.flags & OPTION_DEBUG ) {
+ printf("(write) command cont'd: %d\n", cc);
+ bprint(rbuf, cc);
+ }
+
+ if ((cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
+ dsi_writeflush(dsi);
+ *rbuflen = 0;
+ ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
+ reqcount);
+ return cc;
+ }
+ offset += cc;
+ }
+ }
+ break;
+ }
+
+afp_write_done:
+ ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount);
+ if ( ad_hfileno( ofork->of_ad ) != -1 )
+ ofork->of_flags |= AFPFORK_DIRTY;
+
+ offset = htonl( offset );
+#if defined(__GNUC__) && defined(HAVE_GCC_MEMCPY_BUG)
+ bcopy(&offset, rbuf, sizeof(offset));
+#else
+ memcpy(rbuf, &offset, sizeof(offset));
+#endif
+ *rbuflen = sizeof(offset);
+ return( AFP_OK );
+
+afp_write_err:
+ if (obj->proto == AFPPROTO_DSI) {
+ dsi_writeinit(obj->handle, rbuf, *rbuflen);
+ dsi_writeflush(obj->handle);
+ }
+
+ *rbuflen = (err == AFP_OK) ? sizeof(offset) : 0;
+ return err;
+}
+
+
+int afp_getforkparams(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct ofork *ofork;
+ int buflen, ret;
+ u_int16_t ofrefnum, bitmap;
+
+ ibuf += 2;
+ memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
+ ibuf += sizeof( ofrefnum );
+ memcpy(&bitmap, ibuf, sizeof( bitmap ));
+ bitmap = ntohs( bitmap );
+ ibuf += sizeof( bitmap );
+
+ *rbuflen = 0;
+ if (( ofork = of_find( ofrefnum )) == NULL ) {
+ syslog( LOG_ERR, "afp_getforkparams: of_find: %m" );
+ return( AFPERR_PARAM );
+ }
+
+ if (( ret = getforkparams( ofork, bitmap,
+ rbuf + sizeof( u_short ), &buflen, 0 )) != AFP_OK ) {
+ return( ret );
+ }
+
+ *rbuflen = buflen + sizeof( u_short );
+ bitmap = htons( bitmap );
+ memcpy(rbuf, &bitmap, sizeof( bitmap ));
+ return( AFP_OK );
+}
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef AFPD_FORK_H
+#define AFPD_FORK_H 1
+
+#include <stdio.h>
+#include <sys/cdefs.h>
+
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include "volume.h"
+#include "directory.h"
+
+struct ofork {
+ struct adouble *of_ad;
+ struct vol *of_vol;
+ struct dir *of_dir;
+ char *of_name;
+ int of_namelen;
+ u_int16_t of_refnum;
+ int of_flags;
+ struct ofork **prevp, *next;
+ struct ofork *of_d_prev, *of_d_next;
+};
+
+#define OPENFORK_DATA (0)
+#define OPENFORK_RSCS (1<<7)
+
+#define OPENACC_RD (1<<0)
+#define OPENACC_WR (1<<1)
+#define OPENACC_DRD (1<<4)
+#define OPENACC_DWR (1<<5)
+
+#define AFPFORK_OPEN (1<<0)
+#define AFPFORK_RSRC (1<<1)
+#define AFPFORK_DATA (1<<2)
+#define AFPFORK_DIRTY (1<<3)
+#define AFPFORK_ACCRD (1<<4)
+#define AFPFORK_ACCWR (1<<5)
+#define AFPFORK_ACCMASK (AFPFORK_ACCRD | AFPFORK_ACCWR)
+
+/* in ofork.c */
+extern struct ofork *of_alloc __P((struct vol *, struct dir *,
+ char *, u_int16_t *, const int,
+ struct adouble *));
+extern void of_dealloc __P((struct ofork *));
+extern struct ofork *of_find __P((const u_int16_t));
+extern struct ofork *of_findname __P((const struct vol *, const struct dir *,
+ const char *));
+extern int of_rename __P((const struct vol *,
+ struct dir *, const char *,
+ struct dir *, const char *));
+extern int of_flush __P((const struct vol *));
+extern void of_pforkdesc __P((FILE *));
+
+/* in fork.c */
+extern int flushfork __P((struct ofork *));
+
+/* FP functions */
+extern int afp_openfork __P((AFPObj *, char *, int, char *, int *));
+extern int afp_bytelock __P((AFPObj *, char *, int, char *, int *));
+extern int afp_getforkparams __P((AFPObj *, char *, int, char *, int *));
+extern int afp_setforkparams __P((AFPObj *, char *, int, char *, int *));
+extern int afp_read __P((AFPObj *, char *, int, char *, int *));
+extern int afp_write __P((AFPObj *, char *, int, char *, int *));
+extern int afp_flushfork __P((AFPObj *, char *, int, char *, int *));
+extern int afp_flush __P((AFPObj *, char *, int, char *, int *));
+extern int afp_closefork __P((AFPObj *, char *, int, char *, int *));
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/param.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+
+#include "globals.h"
+
+static char *l_curr;
+static char *l_end;
+
+void initline( len, line )
+ int len;
+ char *line;
+{
+ l_curr = line;
+ l_end = line + len;
+}
+
+#define ST_QUOTE 0
+#define ST_WORD 1
+#define ST_BEGIN 2
+
+ int
+parseline( len, token )
+ int len;
+ char *token;
+{
+ char *p, *e;
+ int state;
+
+ state = ST_BEGIN;
+ p = token;
+ e = token + len;
+
+ for (;;) {
+ if ( l_curr > l_end ) { /* end of line */
+ *token = '\0';
+ return( -1 );
+ }
+
+ switch ( *l_curr ) {
+ case '"' :
+ if ( state == ST_QUOTE ) {
+ state = ST_WORD;
+ } else {
+ state = ST_QUOTE;
+ }
+ break;
+
+ case '\0' :
+ case '\t' :
+ case '\n' :
+ case ' ' :
+ if ( state == ST_WORD ) {
+ *p = '\0';
+ return( p - token );
+ }
+ if ( state != ST_QUOTE ) {
+ break;
+ }
+ /* FALL THROUGH */
+
+ default :
+ if ( state == ST_BEGIN ) {
+ state = ST_WORD;
+ }
+ if ( p > e ) { /* end of token */
+ *token = '\0';
+ return( -1 );
+ }
+ *p++ = *l_curr;
+ break;
+ }
+
+ l_curr++;
+ }
+}
+
+#ifdef notdef
+void parseline( token, user )
+ char *token, *user;
+{
+ char *p = pos, *t = token, *u, *q, buf[ MAXPATHLEN ];
+ struct passwd *pwent;
+ int quoted = 0;
+
+ while ( isspace( *p )) {
+ p++;
+ }
+
+ /*
+ * If we've reached the end of the line, or a comment,
+ * don't return any more tokens.
+ */
+ if ( *p == '\0' || *p == '#' ) {
+ *token = '\0';
+ return;
+ }
+
+ if ( *p == '"' ) {
+ p++;
+ quoted = 1;
+ }
+ while ( *p != '\0' && ( quoted || !isspace( *p ))) {
+ if ( *p == '"' ) {
+ if ( quoted ) {
+ *t = '\0';
+ break;
+ }
+ quoted = 1;
+ p++;
+ } else {
+ *t++ = *p++;
+ }
+ }
+ pos = p;
+ *t = '\0';
+
+ /*
+ * We got to the end of the line without closing an open quote
+ */
+ if ( *p == '\0' && quoted ) {
+ *token = '\0';
+ return;
+ }
+
+ t = token;
+ if ( *t == '~' ) {
+ t++;
+ if ( *t == '\0' || *t == '/' ) {
+ u = user;
+ if ( *t == '/' ) {
+ t++;
+ }
+ } else {
+ u = t;
+ if (( q = strchr( t, '/' )) == NULL ) {
+ t = "";
+ } else {
+ *q = '\0';
+ t = q + 1;
+ }
+ }
+ if ( u == NULL || ( pwent = getpwnam( u )) == NULL ) {
+ *token = '\0';
+ return;
+ }
+ strcpy( buf, pwent->pw_dir );
+ if ( *t != '\0' ) {
+ strcat( buf, "/" );
+ strcat( buf, t );
+ }
+ strcpy( token, buf );
+ }
+ return;
+}
+#endif notdef
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef AFPD_GLOBALS_H
+#define AFPD_GLOBALS_H 1
+
+#include <sys/param.h>
+#include <sys/cdefs.h>
+#include <netdb.h> /* this isn't header-protected under ultrix */
+#include <netatalk/at.h>
+#include <atalk/afp.h>
+#include <atalk/compat.h>
+
+/* test for inline */
+#ifndef __inline__
+#define __inline__
+#endif
+
+#define MACFILELEN 31
+
+
+#define OPTION_DEBUG (1 << 0)
+#define OPTION_USERVOLFIRST (1 << 1)
+#define OPTION_NOUSERVOL (1 << 2)
+#define OPTION_PROXY (1 << 3)
+#define OPTION_CUSTOMICON (1 << 4)
+
+/* a couple of these options could get stuck in unions to save
+ * space. */
+struct afp_options {
+ int connections, port, transports, tickleval, flags;
+ unsigned char passwdbits, passwdminlen, loginmaxfail;
+ u_int32_t server_quantum;
+ char hostname[MAXHOSTNAMELEN + 1], *server, *ipaddr, *configfile;
+ struct at_addr ddpaddr;
+ char *uampath, *nlspath, *fqdn;
+ char *pidfile, *defaultvol, *systemvol;
+ char *guest, *loginmesg, *keyfile, *passwdfile;
+ char *uamlist;
+};
+
+#define AFPOBJ_TMPSIZ (MAXPATHLEN)
+typedef struct AFPObj {
+ int proto;
+ unsigned long servernum;
+ void *handle, *config;
+ struct afp_options options;
+ char *Obj, *Type, *Zone;
+ char username[MACFILELEN + 1];
+ void (*logout)(void), (*exit)(int);
+ int (*reply)(void *, int);
+ int (*attention)(void *, AFPUserBytes);
+ /* to prevent confusion, only use these in afp_* calls */
+ char oldtmp[AFPOBJ_TMPSIZ + 1], newtmp[AFPOBJ_TMPSIZ + 1];
+ void *uam_cookie; /* cookie for uams */
+} AFPObj;
+
+extern int afp_version;
+extern unsigned char nologin;
+extern struct vol *volumes;
+extern struct dir *curdir;
+extern char getwdbuf[];
+
+extern void afp_options_init __P((struct afp_options *));
+extern int afp_options_parse __P((int, char **, struct afp_options *));
+extern int afp_options_parseline __P((char *, struct afp_options *));
+extern void afp_options_free __P((struct afp_options *,
+ const struct afp_options *));
+extern void setmessage __P((const char *));
+
+/* gettok.c */
+extern void initline __P((int, char *));
+extern int parseline __P((int, char *));
+
+#ifndef NO_DDP
+extern void afp_over_asp __P((AFPObj *));
+#endif
+extern void afp_over_dsi __P((AFPObj *));
+
+#endif /* globals.h */
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef AFPD_ICON_H
+#define AFPD_ICON_H 1
+
+#include <sys/cdefs.h>
+#include "globals.h"
+
+const unsigned char apple_atalk_icon[] = { /* default appletalk icon */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x9F, 0xE0,
+ 0x00, 0x04, 0x50, 0x30, 0x00, 0x08, 0x30, 0x28,
+ 0x00, 0x10, 0x10, 0x3C, 0x07, 0xA0, 0x08, 0x04,
+ 0x18, 0x7F, 0x04, 0x04, 0x10, 0x00, 0x82, 0x04,
+ 0x10, 0x00, 0x81, 0x04, 0x10, 0x00, 0x82, 0x04,
+ 0x10, 0x00, 0x84, 0x04, 0x10, 0x00, 0x88, 0x04,
+ 0x10, 0x00, 0x90, 0x04, 0x10, 0x00, 0xB0, 0x04,
+ 0x10, 0x00, 0xD0, 0x04, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x40, 0x00, 0x00, 0x02, 0x3F, 0xFF, 0xFF, 0xFC,
+ 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x05, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00,
+ 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x08, 0x80,
+ 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x0F, 0x80,
+ 0x00, 0x00, 0x0A, 0x80, 0xBF, 0xFF, 0xF2, 0x74,
+ 0x00, 0x00, 0x05, 0x00, 0xBF, 0xFF, 0xF8, 0xF4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x9F, 0xE0,
+ 0x00, 0x07, 0xDF, 0xF0, 0x00, 0x0F, 0xFF, 0xF8,
+ 0x00, 0x1F, 0xFF, 0xFC, 0x07, 0xBF, 0xFF, 0xFC,
+ 0x1F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFC,
+ 0x1F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFC,
+ 0x1F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFC,
+ 0x1F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFC,
+ 0x1F, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x7F, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xFC,
+ 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00,
+ 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00,
+ 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x0F, 0x80,
+ 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x0F, 0x80,
+ 0x00, 0x00, 0x0F, 0x80, 0xBF, 0xFF, 0xFF, 0xF4,
+ 0xBF, 0xFF, 0xFD, 0xF4, 0xBF, 0xFF, 0xF8, 0xF4
+};
+
+const unsigned char apple_tcp_icon[] = { /* default asip icon */
+ 0x30, 0x00, 0x8f, 0xf8, 0xcc, 0x01, 0x48, 0x0c,
+ 0xb3, 0x32, 0x28, 0x0a, 0x8c, 0xcc, 0x7c, 0x0f,
+ 0x83, 0x02, 0xff, 0x01, 0x80, 0xc3, 0xc3, 0x81,
+ 0x80, 0x33, 0xe3, 0xc1, 0x80, 0x0b, 0xd3, 0xc1,
+ 0x80, 0x0b, 0xb1, 0x61, 0x80, 0x0b, 0xe0, 0xe1,
+ 0x80, 0x0b, 0xe1, 0xe1, 0x80, 0x0b, 0xd1, 0xe1,
+ 0xc0, 0x0a, 0xc0, 0xe1, 0x70, 0x0b, 0x78, 0xc1,
+ 0x1c, 0x0b, 0x79, 0xc1, 0x17, 0x0b, 0x33, 0xff,
+ 0x21, 0xcb, 0xff, 0xc4, 0x40, 0x7f, 0xff, 0x02,
+ 0x80, 0x1e, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff,
+ 0x80, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00,
+ 0x00, 0x07, 0xc0, 0x00, 0x00, 0x04, 0x40, 0x00,
+ 0x00, 0x04, 0x40, 0x00, 0x00, 0x07, 0xc0, 0x00,
+ 0x00, 0x05, 0x40, 0x00, 0x0f, 0xf9, 0x3f, 0xfc,
+ 0x00, 0x02, 0x80, 0x00, 0x0f, 0xfc, 0x7f, 0xfc,
+ 0x30, 0x00, 0x8f, 0xf8, 0xfc, 0x01, 0xcf, 0xfc,
+ 0xff, 0x33, 0xef, 0xfe, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff,
+ 0x1f, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff,
+ 0x3f, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xfe,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x03, 0x80, 0x00, 0x00, 0x03, 0x80, 0x00,
+ 0x00, 0x07, 0xc0, 0x00, 0x00, 0x07, 0xc0, 0x00,
+ 0x00, 0x07, 0xc0, 0x00, 0x00, 0x07, 0xc0, 0x00,
+ 0x00, 0x07, 0xc0, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x3f, 0xfe, 0xff, 0xff, 0xff, 0xfc, 0x7f, 0xff
+};
+
+#if defined( ultrix )
+const u_char icon[] = { /* declogo */
+ 0x0, 0x80, 0x0, 0x0, 0x1, 0xC0, 0x0, 0x0,
+ 0x3, 0xE0, 0x0, 0x0, 0x7, 0xF0, 0x0, 0x0,
+ 0xF, 0xB0, 0x0, 0x0, 0x13, 0x6C, 0x0, 0x0,
+ 0x2C, 0xDE, 0x0, 0x0, 0x6D, 0xBB, 0x0, 0x0,
+ 0xF3, 0x7F, 0x80, 0x0, 0x7E, 0xEF, 0x40, 0x0,
+ 0x3D, 0xDE, 0xE0, 0x0, 0x1B, 0xBD, 0xF0, 0x0,
+ 0x7, 0x7B, 0xF8, 0x0, 0x7, 0xF7, 0xF8, 0x0,
+ 0x3, 0xEC, 0xF6, 0x0, 0x1, 0xDB, 0x6F, 0x0,
+ 0x0, 0xBB, 0x5D, 0x80, 0x0, 0x6C, 0xBF, 0xC0,
+ 0x0, 0x2D, 0x77, 0xA0, 0x0, 0x12, 0xEF, 0x70,
+ 0x0, 0xD, 0xDE, 0xF8, 0x7F, 0xFF, 0xFF, 0xFE,
+ 0x20, 0x0, 0x0, 0x4, 0x1F, 0xFF, 0xFF, 0xF8,
+ 0x0, 0x4, 0x40, 0x0, 0x0, 0x4, 0x40, 0x0,
+ 0x0, 0x3, 0x80, 0x0, 0x0, 0x4, 0x40, 0x0,
+ 0xAF, 0xF9, 0x3F, 0xF5, 0x0, 0x2, 0x80, 0x0,
+ 0xAF, 0xFC, 0x7F, 0xF5, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x80, 0x0, 0x0, 0x1, 0xC0, 0x0, 0x0,
+ 0x3, 0xE0, 0x0, 0x0, 0x7, 0xF0, 0x0, 0x0,
+ 0xF, 0xF8, 0x0, 0x0, 0x1F, 0xFC, 0x0, 0x0,
+ 0x3F, 0xFE, 0x0, 0x0, 0x7F, 0xFF, 0x0, 0x0,
+ 0xFF, 0xFF, 0x80, 0x0, 0x7F, 0xFF, 0xC0, 0x0,
+ 0x3F, 0xFF, 0xE0, 0x0, 0x1F, 0xFF, 0xF0, 0x0,
+ 0xF, 0xFF, 0xF8, 0x0, 0x7, 0xFF, 0xFC, 0x0,
+ 0x3, 0xFF, 0xFE, 0x0, 0x1, 0xFF, 0xFF, 0x0,
+ 0x0, 0xFF, 0xFF, 0x80, 0x0, 0x7F, 0xFF, 0xC0,
+ 0x0, 0x3F, 0xFF, 0xE0, 0x0, 0x1F, 0xFF, 0xF0,
+ 0x0, 0xF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFE,
+ 0x3F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8,
+ 0x0, 0x7, 0xC0, 0x0, 0x0, 0x7, 0xC0, 0x0,
+ 0x0, 0x3, 0x80, 0x0, 0x0, 0x7, 0xC0, 0x0,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
+ 0xFF, 0xFC, 0x7F, 0xFF, 0x0, 0x0, 0x0, 0x0
+};
+
+#else
+#if defined( vax )
+const u_char icon[] = { /* daemon */
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x2, 0x0,
+ 0x1, 0x80, 0x3, 0x0, 0x2, 0x80, 0x2, 0x80,
+ 0x2, 0x80, 0x2, 0x80, 0x4, 0x80, 0x2, 0x40,
+ 0x4, 0x87, 0xC2, 0x40, 0x4, 0x58, 0x34, 0x40,
+ 0x4, 0x20, 0x8, 0x40, 0x2, 0x16, 0xD0, 0x80,
+ 0x1, 0x1, 0x1, 0x0, 0x2, 0x80, 0x2, 0x80,
+ 0x2, 0x9C, 0x72, 0x80, 0x4, 0x22, 0x88, 0x40,
+ 0x4, 0x41, 0x4, 0x40, 0x4, 0x41, 0x4, 0x40,
+ 0x4, 0x41, 0x4, 0x40, 0x4, 0x49, 0x24, 0x40,
+ 0xE, 0x55, 0x54, 0xE0, 0x10, 0x5D, 0x74, 0x10,
+ 0x10, 0x3E, 0xF8, 0x10, 0x7F, 0xFC, 0x7F, 0xFE,
+ 0x20, 0x4, 0x40, 0x4, 0x1F, 0xFC, 0x7F, 0xF8,
+ 0x0, 0x7, 0xC0, 0x0, 0x0, 0x4, 0x40, 0x0,
+ 0x0, 0x3, 0x80, 0x0, 0x0, 0x4, 0x40, 0x0,
+ 0xAF, 0xF9, 0x3F, 0xF5, 0x0, 0x2, 0x80, 0x0,
+ 0xAF, 0xFC, 0x7F, 0xF5, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x2, 0x0,
+ 0x1, 0x80, 0x3, 0x0, 0x3, 0x80, 0x3, 0x80,
+ 0x3, 0x80, 0x3, 0x80, 0x7, 0x80, 0x3, 0xC0,
+ 0x7, 0x87, 0xC3, 0xC0, 0x7, 0xDF, 0xF7, 0xC0,
+ 0x7, 0xFF, 0xFF, 0xC0, 0x3, 0xFF, 0xFF, 0x80,
+ 0x1, 0xFF, 0xFF, 0x0, 0x3, 0xFF, 0xFF, 0x80,
+ 0x3, 0xFF, 0xFF, 0x80, 0x7, 0xFF, 0xFF, 0xC0,
+ 0x7, 0xFF, 0xFF, 0xC0, 0x7, 0xFF, 0xFF, 0xC0,
+ 0x7, 0xFF, 0xFF, 0xC0, 0x7, 0xFF, 0xFF, 0xC0,
+ 0xF, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xF0,
+ 0x1F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xFE,
+ 0x3F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8,
+ 0x0, 0x7, 0xC0, 0x0, 0x0, 0x7, 0xC0, 0x0,
+ 0x0, 0x3, 0x80, 0x0, 0x0, 0x4, 0x40, 0x0,
+ 0xAF, 0xF9, 0x3F, 0xF5, 0x0, 0x2, 0x80, 0x0,
+ 0xAF, 0xFC, 0x7F, 0xF5, 0x0, 0x0, 0x0, 0x0
+};
+
+#else
+#if defined( sun )
+const u_char icon[] = { /* sunlogo */
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x80, 0x0,
+ 0x0, 0x2, 0x40, 0x0, 0x0, 0x2, 0x20, 0x0,
+ 0x0, 0x9, 0x10, 0x0, 0x0, 0x4, 0x88, 0x0,
+ 0x0, 0x22, 0x44, 0x0, 0x0, 0x11, 0x20, 0x0,
+ 0x0, 0x88, 0x91, 0x0, 0x1, 0x4, 0x42, 0x0,
+ 0x2, 0x22, 0x44, 0x40, 0x4, 0x41, 0x88, 0x80,
+ 0x8, 0x98, 0x11, 0x30, 0x11, 0x24, 0x22, 0x48,
+ 0x12, 0x44, 0x24, 0x88, 0xC, 0x88, 0x19, 0x10,
+ 0x1, 0x11, 0x82, 0x20, 0x2, 0x22, 0x44, 0x40,
+ 0x0, 0x42, 0x20, 0x80, 0x0, 0x89, 0x11, 0x0,
+ 0x0, 0x4, 0x88, 0x0, 0x7F, 0xFF, 0xFF, 0xFE,
+ 0x20, 0x0, 0x0, 0x4, 0x1F, 0xFF, 0xFF, 0xF8,
+ 0x0, 0x4, 0x40, 0x0, 0x0, 0x4, 0x40, 0x0,
+ 0x0, 0x3, 0x80, 0x0, 0x0, 0x4, 0x40, 0x0,
+ 0xAF, 0xF9, 0x3F, 0xF5, 0x0, 0x2, 0x80, 0x0,
+ 0xAF, 0xFC, 0x7F, 0xF5, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x1, 0x80, 0x0, 0x0, 0x3, 0xC0, 0x0,
+ 0x0, 0x7, 0xE0, 0x0, 0x0, 0xF, 0xF0, 0x0,
+ 0x0, 0x1F, 0xF8, 0x0, 0x0, 0x3F, 0xFC, 0x0,
+ 0x0, 0x7F, 0xFE, 0x0, 0x0, 0xFF, 0xFF, 0x0,
+ 0x1, 0xFF, 0xFF, 0x80, 0x3, 0xFF, 0xFF, 0xC0,
+ 0x7, 0xFF, 0xFF, 0xE0, 0xF, 0xFF, 0xFF, 0xF0,
+ 0x1F, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xFC,
+ 0x3F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8,
+ 0xF, 0xFF, 0xFF, 0xF0, 0x7, 0xFF, 0xFF, 0xE0,
+ 0x3, 0xFF, 0xFF, 0xC0, 0x1, 0xFF, 0xFF, 0x80,
+ 0x0, 0xFF, 0xFF, 0x0, 0x7F, 0xFF, 0xFF, 0xFE,
+ 0x3F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8,
+ 0x0, 0x7, 0xC0, 0x0, 0x0, 0x7, 0xC0, 0x0,
+ 0x0, 0x3, 0x80, 0x0, 0x0, 0x7, 0xC0, 0x0,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFC, 0x7F, 0xFF, 0x0, 0x0, 0x0, 0x0
+};
+
+#else
+#if defined( _IBMR2 )
+const u_char icon[] = { /* hagar */
+ 0x0, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0, 0x18,
+ 0x24, 0x0, 0x0, 0x24, 0x44, 0x3, 0xC0, 0x22,
+ 0x44, 0xC, 0x30, 0x22, 0x42, 0x30, 0xC, 0x42,
+ 0x41, 0x40, 0x2, 0x82, 0x41, 0x80, 0x1, 0x82,
+ 0x21, 0x0, 0x0, 0x84, 0x11, 0x41, 0x2, 0x88,
+ 0xE, 0xA2, 0x85, 0x70, 0x4, 0x41, 0x2, 0x20,
+ 0x4, 0x0, 0x0, 0x20, 0x3, 0xFF, 0xFF, 0xC0,
+ 0x3, 0xC0, 0x3, 0xC0, 0x3, 0x82, 0x41, 0xC0,
+ 0x7, 0x81, 0x81, 0xE0, 0x7, 0xC2, 0x43, 0xE0,
+ 0x7, 0xFC, 0x3F, 0xE0, 0x7, 0xFC, 0x3F, 0xE0,
+ 0x7F, 0xFC, 0x3F, 0xFE, 0x20, 0x3, 0xC0, 0x4,
+ 0x1F, 0xFF, 0xFF, 0xF8, 0x0, 0x2, 0x40, 0x0,
+ 0x0, 0x2, 0x40, 0x0, 0x0, 0x3, 0xC0, 0x0,
+ 0x0, 0x4, 0x20, 0x0, 0xAF, 0xF9, 0x9F, 0xF5,
+ 0x0, 0x2, 0x40, 0x0, 0xAF, 0xFC, 0x3F, 0xF5,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0, 0x18,
+ 0x3C, 0x0, 0x0, 0x3C, 0x7C, 0x3, 0xC0, 0x3E,
+ 0x7C, 0xF, 0xF0, 0x3E, 0x7E, 0x3F, 0xFC, 0x7E,
+ 0x7F, 0x7F, 0xFE, 0xFE, 0x7F, 0xFF, 0xFF, 0xFE,
+ 0x3F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8,
+ 0xF, 0xFF, 0xFF, 0xF0, 0x7, 0xFF, 0xFF, 0xE0,
+ 0x7, 0xFF, 0xFF, 0xE0, 0x3, 0xFF, 0xFF, 0xC0,
+ 0x3, 0xFF, 0xFF, 0xC0, 0x3, 0xFF, 0xFF, 0xC0,
+ 0x7, 0xFF, 0xFF, 0xE0, 0x7, 0xFF, 0xFF, 0xE0,
+ 0x7, 0xFF, 0xFF, 0xE0, 0x7, 0xFF, 0xFF, 0xE0,
+ 0x7F, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xFC,
+ 0x1F, 0xFF, 0xFF, 0xF8, 0x0, 0x3, 0xC0, 0x0,
+ 0x0, 0x3, 0xC0, 0x0, 0x0, 0x3, 0xC0, 0x0,
+ 0x0, 0x7, 0xE0, 0x0, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
+};
+
+#else
+const u_char icon[] = { /* globe */
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF0, 0x0,
+ 0x0, 0x30, 0xC, 0x0, 0x0, 0xC0, 0x3, 0x0,
+ 0x1, 0x80, 0x3, 0x80, 0x3, 0xE2, 0x1F, 0xC0,
+ 0x7, 0xC0, 0x1D, 0xE0, 0xF, 0x80, 0x87, 0xF0,
+ 0xF, 0x1, 0x8F, 0xF0, 0xE, 0x0, 0x7F, 0x30,
+ 0x1E, 0x0, 0xFD, 0x78, 0x12, 0x0, 0xE4, 0xF8,
+ 0x10, 0x0, 0x40, 0xF8, 0x10, 0x0, 0x3E, 0x78,
+ 0x1F, 0x0, 0x7F, 0xF8, 0x1F, 0x80, 0x7F, 0xF8,
+ 0xF, 0x80, 0x3F, 0x30, 0xF, 0x80, 0xF, 0x30,
+ 0xF, 0x80, 0xE, 0x90, 0x7, 0x80, 0xE, 0xA0,
+ 0x3, 0xC0, 0xE, 0x40, 0x7F, 0xFF, 0xFF, 0xFE,
+ 0x20, 0x0, 0x0, 0x4, 0x1F, 0xFF, 0xFF, 0xF8,
+ 0x0, 0x4, 0x40, 0x0, 0x0, 0x4, 0x40, 0x0,
+ 0x0, 0x3, 0x80, 0x0, 0x0, 0x4, 0x40, 0x0,
+ 0xAF, 0xF9, 0x3F, 0xF5, 0x0, 0x2, 0x80, 0x0,
+ 0xAF, 0xFC, 0x7F, 0xF5, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF0, 0x0,
+ 0x0, 0x3F, 0xFC, 0x0, 0x0, 0xFF, 0xFF, 0x0,
+ 0x1, 0xFF, 0xFF, 0x80, 0x3, 0xFF, 0xFF, 0xC0,
+ 0x7, 0xFF, 0xFF, 0xE0, 0xF, 0xFF, 0xFF, 0xF0,
+ 0xF, 0xFF, 0xFF, 0xF0, 0xF, 0xFF, 0xFF, 0xF0,
+ 0x1F, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xF8,
+ 0x1F, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xF8,
+ 0x1F, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xF8,
+ 0xF, 0xFF, 0xFF, 0xF0, 0xF, 0xFF, 0xFF, 0xF0,
+ 0xF, 0xFF, 0xFF, 0xF0, 0x7, 0xFF, 0xFF, 0xE0,
+ 0x3, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFE,
+ 0x3F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8,
+ 0x0, 0x7, 0xC0, 0x0, 0x0, 0x7, 0xC0, 0x0,
+ 0x0, 0x3, 0x80, 0x0, 0x0, 0x7, 0xC0, 0x0,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
+ 0xFF, 0xFC, 0x7F, 0xFF, 0x0, 0x0, 0x0, 0x0
+};
+#endif /*_IBMR2*/
+#endif /*sun*/
+#endif /*vax*/
+#endif /*ultrix*/
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/syslog.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <errno.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/compat.h>
+#include <atalk/dsi.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+#include <atalk/afp.h>
+#include <atalk/adouble.h>
+#include <atalk/paths.h>
+#include <atalk/util.h>
+#include <atalk/server_child.h>
+
+#include "globals.h"
+#include "config.h"
+#include "status.h"
+#include "fork.h"
+#include "uam_auth.h"
+
+unsigned char nologin = 0;
+
+static struct afp_options default_options;
+static AFPConfig *configs;
+static server_child *server_children;
+static fd_set save_rfds;
+
+static void afp_exit(const int i)
+{
+ server_unlock(default_options.pidfile);
+ exit(i);
+}
+
+static void afp_goaway(int sig)
+{
+#ifndef NO_DDP
+ asp_kill(sig);
+#endif
+ dsi_kill(sig);
+ switch( sig ) {
+ case SIGTERM :
+ syslog( LOG_INFO, "shutting down on signal %d", sig );
+ break;
+ case SIGHUP :
+ /* w/ a configuration file, we can force a re-read if we want */
+ nologin++;
+ if ((nologin + 1) & 1) {
+ AFPConfig *config;
+
+ syslog(LOG_INFO, "re-reading configuration file");
+ for (config = configs; config; config = config->next)
+ if (config->server_cleanup)
+ config->server_cleanup(config);
+
+ configfree(configs, NULL);
+ if (!(configs = configinit(&default_options))) {
+ syslog(LOG_ERR, "config re-read: no servers configured");
+ afp_exit(1);
+ }
+ FD_ZERO(&save_rfds);
+ for (config = configs; config; config = config->next) {
+ if (config->fd < 0)
+ continue;
+ FD_SET(config->fd, &save_rfds);
+ }
+ } else {
+ syslog(LOG_INFO, "disallowing logins");
+ auth_unload();
+ }
+ break;
+ default :
+ syslog( LOG_ERR, "afp_goaway: bad signal" );
+ }
+ if ( sig == SIGTERM ) {
+ AFPConfig *config;
+
+ for (config = configs; config; config = config->next)
+ if (config->server_cleanup)
+ config->server_cleanup(config);
+
+ afp_exit(0);
+ }
+ return;
+}
+
+static void child_handler()
+{
+ server_child_handler(server_children);
+}
+
+int main( ac, av )
+ int ac;
+ char **av;
+{
+ AFPConfig *config;
+ fd_set rfds;
+ struct sigaction sv;
+ sigset_t sigs;
+
+ umask( 0 ); /* so inherited file permissions work right */
+
+ afp_options_init(&default_options);
+ if (!afp_options_parse(ac, av, &default_options))
+ exit(1);
+
+ switch(server_lock("afpd", default_options.pidfile,
+ default_options.flags & OPTION_DEBUG)) {
+ case -1: /* error */
+ exit(1);
+ case 0: /* child */
+ break;
+ default: /* server */
+ exit(0);
+ }
+
+ /* install child handler for asp and dsi. we do this before afp_goaway
+ * as afp_goaway references stuff from here.
+ * XXX: this should really be setup after the initial connections. */
+ if (!(server_children = server_child_alloc(default_options.connections,
+ CHILD_NFORKS))) {
+ syslog(LOG_ERR, "main: server_child alloc: %m");
+ afp_exit(1);
+ }
+
+ memset(&sv, 0, sizeof(sv));
+ sv.sa_handler = child_handler;
+ sigemptyset( &sv.sa_mask );
+ sv.sa_flags = SA_RESTART;
+ if ( sigaction( SIGCHLD, &sv, 0 ) < 0 ) {
+ syslog( LOG_ERR, "main: sigaction: %m" );
+ afp_exit(1);
+ }
+
+ sv.sa_handler = afp_goaway;
+ sigemptyset( &sv.sa_mask );
+ sigaddset(&sv.sa_mask, SIGHUP);
+ sigaddset(&sv.sa_mask, SIGTERM);
+ sv.sa_flags = SA_RESTART;
+ if ( sigaction( SIGHUP, &sv, 0 ) < 0 ) {
+ syslog( LOG_ERR, "main: sigaction: %m" );
+ afp_exit(1);
+ }
+ if ( sigaction( SIGTERM, &sv, 0 ) < 0 ) {
+ syslog( LOG_ERR, "main: sigaction: %m" );
+ afp_exit(1);
+ }
+
+ /* afpd.conf: not in config file: lockfile, connections, configfile
+ * preference: command-line provides defaults.
+ * config file over-writes defaults.
+ *
+ * we also need to make sure that killing afpd during startup
+ * won't leave any lingering registered names around.
+ */
+
+ sigemptyset(&sigs);
+ sigaddset(&sigs, SIGHUP);
+ sigaddset(&sigs, SIGTERM);
+ sigprocmask(SIG_BLOCK, &sigs, NULL);
+ if (!(configs = configinit(&default_options))) {
+ syslog(LOG_ERR, "main: no servers configured: %m\n");
+ afp_exit(1);
+ }
+ sigprocmask(SIG_UNBLOCK, &sigs, NULL);
+
+ /* watch atp and dsi sockets. */
+ FD_ZERO(&save_rfds);
+ for (config = configs; config; config = config->next) {
+ if (config->fd < 0) /* for proxies */
+ continue;
+ FD_SET(config->fd, &save_rfds);
+ }
+
+ /* wait for an appleshare connection. parent remains in the loop
+ * while the children get handled by afp_over_{asp,dsi}. this is
+ * currently vulnerable to a denial-of-service attack if a
+ * connection is made without an actual login attempt being made
+ * afterwards. establishing timeouts for logins is a possible
+ * solution. */
+ while (1) {
+ rfds = save_rfds;
+ if (select(FD_SETSIZE, &rfds, NULL, NULL, NULL) < 0) {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "main: can't wait for input: %m");
+ break;
+ }
+
+ for (config = configs; config; config = config->next) {
+ if (config->fd < 0)
+ continue;
+ if (FD_ISSET(config->fd, &rfds))
+ config->server_start(config, configs, server_children);
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <atalk/afp.h>
+#include "globals.h"
+#include "misc.h"
+
+#define MAXMESGSIZE 199
+
+/* this is only used by afpd children, so it's okay. */
+static char servermesg[MAXMESGSIZE] = "";
+
+void setmessage(const char *message)
+{
+ strncpy(servermesg, message, MAXMESGSIZE);
+}
+
+int afp_getsrvrmesg(obj, ibuf, ibuflen, rbuf, rbuflen)
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ char *message;
+ u_int16_t type, bitmap;
+
+ memcpy(&type, ibuf + 2, sizeof(type));
+ memcpy(&bitmap, ibuf + 4, sizeof(bitmap));
+
+ switch (ntohs(type)) {
+ case AFPMESG_LOGIN: /* login */
+ message = obj->options.loginmesg;
+ break;
+ case AFPMESG_SERVER: /* server */
+ message = servermesg;
+ break;
+ default:
+ *rbuflen = 0;
+ return AFPERR_BITMAP;
+ }
+
+ /* output format:
+ * message type: 2 bytes
+ * bitmap: 2 bytes
+ * message length: 1 byte
+ * message: up to 199 bytes
+ */
+ memcpy(rbuf, &type, sizeof(type));
+ rbuf += sizeof(type);
+ memcpy(rbuf, &bitmap, sizeof(bitmap));
+ rbuf += sizeof(bitmap);
+ *rbuflen = strlen(message);
+ if (*rbuflen > MAXMESGSIZE)
+ *rbuflen = MAXMESGSIZE;
+ *rbuf++ = *rbuflen;
+ memcpy(rbuf, message, *rbuflen);
+
+ *rbuflen += 5;
+
+ return AFP_OK;
+}
--- /dev/null
+#ifndef AFPD_MISC_H
+#define AFPD_MISC_H 1
+
+#include <sys/cdefs.h>
+#include "globals.h"
+
+/* FP functions */
+/* messages.c */
+extern int afp_getsrvrmesg __P((AFPObj *, char *, int, char *, int *));
+
+/* afs.c */
+# ifdef AFS
+extern int afp_getdiracl __P((AFPObj *, char *, int, char *, int *));
+extern int afp_setdiracl __P((AFPObj *, char *, int, char *, int *));
+# else AFS
+#define afp_getdiracl NULL
+#define afp_setdiracl NULL
+# endif AFS
+
+# if defined( AFS ) && defined( UAM_AFSKRB )
+extern int afp_afschangepw __P((AFPObj *, char *, int, char *, int *));
+# else AFS UAM_AFSKRB
+#define afp_afschangepw NULL
+# endif AFS UAM_AFSKRB
+
+#endif
--- /dev/null
+/* parts of this are lifted from the bsd quota program and are
+ * therefore under the following copyright:
+ *
+ * Copyright (c) 1980, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * Ported for AIX (jfs) by Joerg Schumacher (J.Schumacher@tu-bs.de) at the
+ * Technische Universitaet Braunschweig, FRG
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h> /* for DEV_BSIZE */
+#include <sys/time.h> /* <rpc/rpc.h> on ultrix doesn't include this */
+#include <syslog.h>
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpcsvc/rquota.h>
+
+#include <atalk/afp.h>
+
+#include "unix.h"
+
+#ifndef NO_QUOTA_SUPPORT
+/* lifted (with modifications) from the bsd quota program */
+static int
+callaurpc(vol, prognum, versnum, procnum, inproc, in, outproc, out)
+ struct vol *vol;
+ u_long prognum, versnum, procnum;
+ xdrproc_t inproc, outproc;
+ char *in, *out;
+{
+ enum clnt_stat clnt_stat;
+ struct timeval tottimeout;
+
+ if (!vol->v_nfsclient) {
+ struct hostent *hp;
+ struct sockaddr_in server_addr;
+ struct timeval timeout;
+ int socket = RPC_ANYSOCK;
+
+ if ((hp = gethostbyname(vol->v_gvs)) == NULL)
+ return ((int) RPC_UNKNOWNHOST);
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 6;
+ memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = 0;
+
+ if ((vol->v_nfsclient = (void *)
+ clntudp_create(&server_addr, prognum, versnum,
+ timeout, &socket)) == NULL)
+ return ((int) rpc_createerr.cf_stat);
+
+ ((CLIENT *) vol->v_nfsclient)->cl_auth = authunix_create_default();
+ }
+
+ tottimeout.tv_sec = 10;
+ tottimeout.tv_usec = 0;
+ clnt_stat = clnt_call((CLIENT *) vol->v_nfsclient, procnum,
+ inproc, in, outproc, out, tottimeout);
+ return ((int) clnt_stat);
+}
+
+
+/* sunos 4 machines structure things a little differently. */
+#ifdef USE_OLD_RQUOTA
+#define GQR_STATUS gqr_status
+#define GQR_RQUOTA gqr_rquota
+#else
+#define GQR_STATUS status
+#define GQR_RQUOTA getquota_rslt_u.gqr_rquota
+#endif
+
+int getnfsquota(const struct vol *vol, const int uid, const u_int32_t bsize,
+ struct dqblk *dqp)
+{
+
+ struct getquota_args gq_args;
+ struct getquota_rslt gq_rslt;
+ struct timeval tv;
+ char *hostpath;
+
+ /* figure out the host and path */
+ if ((hostpath = strchr(vol->v_gvs, ':')) == NULL) {
+ syslog(LOG_ERR, "can't find hostname for %s", vol->v_gvs);
+ return AFPERR_PARAM;
+ }
+
+ if (*(hostpath + 1) != '/')
+ return AFPERR_PARAM;
+
+ /* separate host from hostpath */
+ *hostpath = '\0';
+
+ gq_args.gqa_pathp = hostpath + 1;
+ gq_args.gqa_uid = uid;
+
+ if(callaurpc(vol, RQUOTAPROG, RQUOTAVERS, RQUOTAPROC_GETQUOTA,
+ (xdrproc_t) xdr_getquota_args, (char *) &gq_args,
+ (xdrproc_t) xdr_getquota_rslt, (char *) &gq_rslt) != 0) {
+ syslog(LOG_INFO, "nfsquota: can't retrieve nfs quota information. \
+make sure that rpc.rquotad is running on %s.", vol->v_gvs);
+ *hostpath = ':';
+ return AFPERR_PARAM;
+ }
+
+ switch (gq_rslt.GQR_STATUS) {
+ case Q_NOQUOTA:
+ break;
+
+ case Q_EPERM:
+ syslog(LOG_ERR, "nfsquota: quota permission error, host: %s\n",
+ vol->v_gvs);
+ break;
+
+ case Q_OK: /* we only copy the bits that we need. */
+ gettimeofday(&tv, NULL);
+
+#ifdef __svr4__
+ /* why doesn't using bsize work? */
+#define NFS_BSIZE (gq_rslt.GQR_RQUOTA.rq_bsize / DEV_BSIZE)
+#else
+ /* NOTE: linux' rquotad program doesn't currently report the
+ * correct rq_bsize. */
+#define NFS_BSIZE (gq_rslt.GQR_RQUOTA.rq_bsize / bsize)
+#endif
+
+ dqp->dqb_bhardlimit =
+ gq_rslt.GQR_RQUOTA.rq_bhardlimit*NFS_BSIZE;
+ dqp->dqb_bsoftlimit =
+ gq_rslt.GQR_RQUOTA.rq_bsoftlimit*NFS_BSIZE;
+ dqp->dqb_curblocks =
+ gq_rslt.GQR_RQUOTA.rq_curblocks*NFS_BSIZE;
+
+#ifdef ultrix
+ dqp->dqb_bwarn = gq_rslt.GQR_RQUOTA.rq_btimeleft;
+#else
+ dqp->dqb_btimelimit =
+ tv.tv_sec + gq_rslt.GQR_RQUOTA.rq_btimeleft;
+#endif
+
+ *hostpath = ':';
+ return AFP_OK;
+ break;
+
+ default:
+ syslog(LOG_INFO, "bad rpc result, host: %s\n", vol->v_gvs);
+ break;
+ }
+
+ *hostpath = ':';
+ return AFPERR_PARAM;
+}
+#endif
--- /dev/null
+SRC = makecode.c parsecode.c
+OBJ = makecode.o parsecode.o
+
+INCPATH= -I../../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH}
+
+all:: codepage.h parsecode makecode
+
+codepage.h:
+ -ln -s ../codepage.h .
+
+parsecode: parsecode.o
+ ${CC} ${CFLAGS} ${LDFLAGS} parsecode.o -o parsecode
+
+makecode: makecode.o
+ ${CC} ${CFLAGS} ${LDFLAGS} makecode.o -o makecode
+
+install:
+ -mkdir ${RESDIR}/nls
+ -./makecode
+ -${INSTALL} -m 644 maccode.* ${RESDIR}/nls
+
+clean:
+ rm -f *.o
+ rm -f makecode parsecode maccode.*
--- /dev/null
+/* quick-and-dirty way of creating code pages */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <netatalk/endian.h>
+#include "codepage.h"
+
+/* map of cp10000 (mac roman) to iso-latin-1 */
+static const unsigned char mac_to_isolatin1_map[] = {
+0xC4, 0xC5, 0xC7, 0xC9, 0xD1, 0xD6, 0xDC, 0xE1,
+0xE0, 0xE2, 0xE4, 0xE3, 0xE5, 0xE7, 0xE9, 0xE8,
+0xEA, 0xEB, 0xED, 0xEC, 0xEE, 0xEF, 0xF1, 0xF3,
+0xF2, 0xF4, 0xF6, 0xF5, 0xFA, 0xF9, 0xFB, 0xFC,
+0x00, 0xB0, 0xA2, 0xA3, 0xA7, 0xB7, 0xB6, 0xDF,
+0xAE, 0xA9, 0x00, 0xB4, 0xA8, 0x00, 0xC6, 0xD8,
+0x00, 0xB1, 0x00, 0x00, 0xA5, 0xB5, 0xF0, 0x00,
+0x00, 0x00, 0x00, 0xAA, 0xBA, 0x00, 0xE6, 0xF8,
+0xBF, 0xA1, 0xAC, 0x00, 0x00, 0x00, 0x00, 0xAB,
+0xBB, 0x00, 0xA0, 0xC0, 0xC3, 0xD5, 0x00, 0x00,
+0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x00,
+0xFF, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xB8, 0x00, 0x00, 0xC2, 0xCA, 0xC1,
+0xCB, 0xC8, 0xCD, 0xCE, 0xCF, 0xCC, 0xD3, 0xD4,
+0x00, 0xD2, 0xDA, 0xDB, 0xD9
+/*, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00 */
+};
+
+/* Map of CP10000 (Mac Roman) to MSDOS CP 437 */
+static const unsigned char mac_to_cp437_map[] = {
+0x8e, 0x8f, 0x80, 0x90, 0xa5, 0x99, 0x9a, 0xa0,
+0x85, 0x83, 0x84, 0xe0, 0x86, 0x87, 0x82, 0x8a,
+0x88, 0x89, 0xa1, 0x8d, 0x8c, 0x8b, 0xa4, 0xa2,
+0x95, 0x93, 0x94, 0xe5, 0xa3, 0x97, 0x96, 0x81,
+0xc5, 0xf8, 0x9b, 0x9c, 0xb6, 0xf9, 0xb8, 0xe1,
+0xc9, 0xe9, 0xcb, 0xd9, 0xc0, 0xd8, 0x92, 0xe8,
+0xec, 0xf1, 0xf3, 0xf2, 0x9d, 0xe6, 0xeb, 0xe4,
+0xef, 0xe3, 0xf4, 0xa6, 0xa7, 0xea, 0x91, 0xed,
+0xa8, 0xad, 0xaa, 0xfb, 0x9f, 0xf7, 0xdd, 0xae,
+0xaf, 0xb0, 0xff, 0xd6, 0xd2, 0xb1, 0xb2, 0xb7,
+0xb4, 0xc4, 0xb5, 0xb9, 0xba, 0xbb, 0xf6, 0xbc,
+0x98, 0xbd, 0xf5, 0xfe, 0xbe, 0xbf, 0xc1, 0xc2,
+0xce, 0xfa, 0xc3, 0xc6, 0xc7, 0xc8, 0xee, 0xca,
+0xcc, 0xcd, 0xb3, 0xd7, 0xcf, 0xd0, 0xd1, 0xd3,
+0xdb, 0xda, 0xa9, 0xab, 0xac, 0xd4, 0xd5, 0xdc,
+0xde, 0xdf, 0xfc, 0xfd, 0xe7, 0xe2, 0xf0, 0x9e
+};
+
+/* Map of CP10000 (Mac Roman) to MSDOS CP 850 */
+static const unsigned char mac_to_cp850_map[] = {
+0x8e, 0x8f, 0x80, 0x90, 0xa5, 0x99, 0x9a, 0xa0,
+0x85, 0x83, 0x84, 0xc6, 0x86, 0x87, 0x82, 0x8a,
+0x88, 0x89, 0xa1, 0x8d, 0x8c, 0x8b, 0xa4, 0xa2,
+0x95, 0x93, 0x94, 0xe4, 0xa3, 0x97, 0x96, 0x81,
+0xc5, 0xf8, 0xbd, 0x9c, 0xf5, 0xb0, 0xf4, 0xe1,
+0xa9, 0xb8, 0xb1, 0xef, 0xf9, 0xca, 0x92, 0x9d,
+0xb2, 0xf1, 0xb3, 0xb4, 0xbe, 0xe6, 0xd0, 0xba,
+0xbb, 0xcb, 0xdd, 0xa6, 0xa7, 0xbc, 0x91, 0x9b,
+0xa8, 0xad, 0xaa, 0xd9, 0x9f, 0xf2, 0xfe, 0xae,
+0xaf, 0xdc, 0xff, 0xb7, 0xc7, 0xe5, 0xc0, 0xc1,
+0xc2, 0xc4, 0xc3, 0x9e, 0xab, 0xac, 0xf6, 0xbf,
+0x98, 0xed, 0xc8, 0xcf, 0xc9, 0xcc, 0xcd, 0xce,
+0xd1, 0xfa, 0xd5, 0xda, 0xdb, 0xb6, 0xd2, 0xb5,
+0xd3, 0xd4, 0xd6, 0xd7, 0xd8, 0xde, 0xe0, 0xe2,
+0xf0, 0xe3, 0xe9, 0xea, 0xeb, 0xfb, 0xdf, 0xe7,
+0xee, 0xe8, 0xec, 0xf3, 0xf7, 0xfc, 0xfd, 0xb9
+};
+
+struct mac_code {
+ char *m_name;
+ const unsigned char *m_map;
+ u_int16_t m_len;
+ const char *m_id;
+};
+
+static struct mac_code names[] = {
+ {"maccode.437", mac_to_cp437_map, sizeof(mac_to_cp437_map), "cp437"},
+ {"maccode.850", mac_to_cp850_map, sizeof(mac_to_cp850_map), "cp850"},
+ {"maccode.iso8859-1", mac_to_isolatin1_map,
+ sizeof(mac_to_isolatin1_map), "iso8859-1"},
+ {NULL, NULL, 0, NULL}
+};
+
+int main(int argc, char **argv)
+{
+ unsigned char buf[CODEPAGE_FILE_HEADER_SIZE];
+ u_int16_t id;
+ int i, j;
+ FILE *fp;
+
+ for (i = 0; names[i].m_name; i++) {
+ if ((fp = fopen(names[i].m_name, "w")) == NULL) {
+ fprintf(stderr, "can't open %s\n", names[i].m_name);
+ continue;
+ }
+
+ id = htons(CODEPAGE_FILE_ID); /* file id */
+ memcpy(buf, &id, sizeof(id));
+ *(buf + 2) = CODEPAGE_FILE_VERSION; /* version */
+ if ((j = strlen(names[i].m_id)) & 1) /* pad to even boundary */
+ j++;
+ *(buf + 3) = j; /* length of name */
+
+ *(buf + 4) = 1; /* default quantum. this should be modified to
+ * deal with multibyte characters */
+
+ /* rules */
+ *(buf + 5) = CODEPAGE_RULE_MTOU | CODEPAGE_RULE_UTOM;
+
+ /* offset to data */
+ id = htons(CODEPAGE_FILE_HEADER_SIZE + j);
+ memcpy(buf + 6, &id, sizeof(id));
+
+ /* size of data */
+ id = htons(names[i].m_len);
+ memcpy(buf + 7, &id, sizeof(id));
+
+ /* write it out */
+ fwrite(buf, CODEPAGE_FILE_HEADER_SIZE, 1, fp);
+
+ /* we either keep or drop the null byte to keep the name on an
+ even boundary */
+ fwrite(names[i].m_id, j, 1, fp);
+
+ /* we typically only map characters > 0x7F */
+ for (j = 0; j < names[i].m_len; j++) {
+ buf[0] = CODEPAGE_RULE_MTOU | CODEPAGE_RULE_UTOM;
+ buf[1] = j + 0x80;
+ buf[2] = names[i].m_map[j];
+ fwrite(buf, 3, 1, fp);
+ }
+ fclose(fp);
+ }
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <netatalk/endian.h>
+#include "codepage.h"
+
+int main(int argc, char **argv)
+{
+ unsigned char name[255 + 1], buf[CODEPAGE_FILE_HEADER_SIZE];
+ unsigned char quantum, rules;
+ u_int16_t id;
+ FILE *fp;
+
+ if (argc != 2) {
+ fprintf(stderr, "%s <codepage>\n", *argv);
+ return -1;
+ }
+
+ if ((fp = fopen(argv[1], "r")) == NULL) {
+ fprintf(stderr, "%s: can't open file.\n", *argv);
+ return -1;
+ }
+
+ if (fread(buf, CODEPAGE_FILE_HEADER_SIZE, 1, fp) != 1) {
+ fprintf(stderr, "%s: can't get header.\n", *argv);
+ goto fail_end;
+ }
+
+ /* id */
+ memcpy(&id, buf, sizeof(id));
+ id = ntohs(id);
+ if (id != CODEPAGE_FILE_ID) {
+ fprintf(stderr, "%s: wrong file type.\n", *argv);
+ goto fail_end;
+ }
+
+ /* version */
+ if (*(buf + 2) != CODEPAGE_FILE_VERSION) {
+ fprintf(stderr, "%s: wrong file version.\n", *argv);
+ goto fail_end;
+ }
+
+ /* size of name */
+ id = *(buf + 3);
+
+ /* quantum and rules */
+ quantum = *(buf + 4);
+ rules = *(buf + 5);
+
+ fread(name, id, 1, fp);
+ if (name[id - 1] != '\0') /* name isn't null-padded */
+ name[id] = '\0';
+ printf("codepage: %s [", name);
+
+ /* move to the data */
+ memcpy(&id, buf + 6, sizeof(id));
+ id = ntohs(id);
+ fseek(fp, id, SEEK_SET);
+
+ /* size of data */
+ memcpy(&id, buf + 8, sizeof(id));
+ id = ntohs(id);
+ printf("size=%d]\n", id);
+ printf("---------\n");
+
+ while (fread(buf, 1 + 2*quantum, 1, fp) == 1) {
+ printf("0x%02x: 0x%02X 0x%02X\n", buf[0], buf[1], buf[1 + quantum]);
+ }
+ fclose(fp);
+ return 0;
+
+fail_end:
+ fclose(fp);
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (c) 1996 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h> /* works around a bug */
+#include <sys/param.h>
+#include <syslog.h>
+
+#include <atalk/adouble.h>
+
+#include "globals.h"
+#include "volume.h"
+#include "directory.h"
+#include "fork.h"
+
+/* we need to have a hashed list of oforks (by name). just hash
+ * by first letter. */
+#define OFORK_HASHSIZE 64
+static struct ofork *ofork_table[OFORK_HASHSIZE];
+
+static struct ofork **oforks = NULL;
+static int nforks = 0;
+static u_short lastrefnum = 0;
+
+
+/* OR some of each character for the hash*/
+static __inline__ unsigned long hashfn(const char *name)
+{
+ unsigned long i = 0;
+
+ while (*name) {
+ i = ((i << 4) | (8*sizeof(i) - 4)) ^ *name++;
+ }
+ return i & (OFORK_HASHSIZE - 1);
+}
+
+static __inline__ void of_hash(struct ofork *of)
+{
+ struct ofork **table;
+
+ table = &ofork_table[hashfn(of->of_name)];
+ if ((of->next = *table) != NULL)
+ (*table)->prevp = &of->next;
+ *table = of;
+ of->prevp = table;
+}
+
+static __inline__ void of_unhash(struct ofork *of)
+{
+ if (of->prevp) {
+ if (of->next)
+ of->next->prevp = of->prevp;
+ *(of->prevp) = of->next;
+ }
+}
+
+void of_pforkdesc( f )
+ FILE *f;
+{
+ u_short ofrefnum;
+
+ if (!oforks)
+ return;
+
+ for ( ofrefnum = 0; ofrefnum < nforks; ofrefnum++ ) {
+ if ( oforks[ ofrefnum ] != NULL ) {
+ fprintf( f, "%hu <%s>\n", ofrefnum, oforks[ ofrefnum ]->of_name);
+ }
+ }
+}
+
+int of_flush(const struct vol *vol)
+{
+ u_int16_t refnum;
+
+ if (!oforks)
+ return 0;
+
+ for ( refnum = 0; refnum < nforks; refnum++ ) {
+ if (oforks[ refnum ] != NULL && (oforks[refnum]->of_vol == vol) &&
+ flushfork( oforks[ refnum ] ) < 0 ) {
+ syslog( LOG_ERR, "of_flush: %m" );
+ }
+ }
+ return( 0 );
+}
+
+
+int of_rename(vol, olddir, oldpath, newdir, newpath)
+ const struct vol *vol;
+ struct dir *olddir, *newdir;
+ const char *oldpath, *newpath;
+{
+ struct ofork *of, *next, *d_ofork;
+
+ next = ofork_table[hashfn(oldpath)];
+ while ((of = next)) {
+ next = next->next; /* so we can unhash and still be all right. */
+
+ if ((vol == of->of_vol) && (olddir == of->of_dir) &&
+ (strcmp(of->of_name, oldpath) == 0)) {
+ of_unhash(of);
+ strncpy( of->of_name, newpath, of->of_namelen);
+ of->of_d_prev->of_d_next = of->of_d_prev;
+ of->of_d_next->of_d_prev = of->of_d_next;
+ of->of_dir = newdir;
+ if (!(d_ofork = newdir->d_ofork)) {
+ newdir->d_ofork = of;
+ of->of_d_next = of->of_d_prev = of;
+ } else {
+ of->of_d_next = d_ofork;
+ of->of_d_prev = d_ofork->of_d_prev;
+ of->of_d_prev->of_d_next = of;
+ d_ofork->of_d_prev = of;
+ }
+ of_hash(of);
+ }
+ }
+
+ return AFP_OK;
+}
+
+ struct ofork *
+of_alloc(vol, dir, path, ofrefnum, eid, ad)
+ struct vol *vol;
+ struct dir *dir;
+ char *path;
+ u_int16_t *ofrefnum;
+ const int eid;
+ struct adouble *ad;
+{
+ struct ofork *of, *d_ofork;
+ u_int16_t refnum, of_refnum;
+
+ int i;
+
+ if (!oforks) {
+ nforks = (getdtablesize() - 10) / 2;
+ oforks = (struct ofork **) calloc(nforks, sizeof(struct ofork *));
+ if (!oforks)
+ return NULL;
+ }
+
+ for ( refnum = lastrefnum++, i = 0; i < nforks; i++, refnum++ ) {
+ if ( oforks[ refnum % nforks ] == NULL ) {
+ break;
+ }
+ }
+ if ( i == nforks ) {
+ syslog(LOG_ERR, "of_alloc: maximum number of forks exceeded.");
+ return( NULL );
+ }
+
+ of_refnum = refnum % nforks;
+ if (( oforks[ of_refnum ] =
+ (struct ofork *)malloc( sizeof( struct ofork ))) == NULL ) {
+ syslog( LOG_ERR, "of_alloc: malloc: %m" );
+ return NULL;
+ }
+ of = oforks[of_refnum];
+
+ /* see if we need to allocate space for the adouble struct */
+ if ((of->of_ad = ad ? ad :
+ calloc(1, sizeof(struct adouble))) == NULL) {
+ syslog( LOG_ERR, "of_alloc: malloc: %m" );
+ return NULL;
+ }
+
+ of->of_vol = vol;
+ of->of_dir = dir;
+
+ if (!(d_ofork = dir->d_ofork)) {
+ dir->d_ofork = of;
+ of->of_d_next = of->of_d_prev = of;
+ } else {
+ of->of_d_next = d_ofork;
+ of->of_d_prev = d_ofork->of_d_prev;
+ d_ofork->of_d_prev->of_d_next = of;
+ d_ofork->of_d_prev = of;
+ }
+
+ /* here's the deal: we allocate enough for the standard mac file length.
+ * in the future, we'll reallocate in fairly large jumps in case
+ * of long unicode names */
+ if (( of->of_name =(char *)malloc(MACFILELEN + 1)) ==
+ NULL ) {
+ syslog( LOG_ERR, "of_alloc: malloc: %m" );
+ if (!ad)
+ free(of->of_ad);
+ free(of);
+ oforks[ of_refnum ] = NULL;
+ return NULL;
+ }
+ strncpy( of->of_name, path, of->of_namelen = MACFILELEN + 1);
+ *ofrefnum = refnum;
+ of->of_refnum = of_refnum;
+ of_hash(of);
+
+ if (eid == ADEID_DFORK)
+ of->of_flags = AFPFORK_DATA;
+ else
+ of->of_flags = AFPFORK_RSRC;
+
+ return( of );
+}
+
+struct ofork *of_find(const u_int16_t ofrefnum )
+{
+ if (!oforks)
+ return NULL;
+
+ return( oforks[ ofrefnum % nforks ] );
+}
+
+struct ofork *
+of_findname(const struct vol *vol, const struct dir *dir, const char *name)
+{
+ struct ofork *of;
+
+ for (of = ofork_table[hashfn(name)]; of; of = of->next) {
+ if ((vol == of->of_vol) && (dir == of->of_dir) &&
+ (strcmp(of->of_name, name) == 0))
+ return of;
+ }
+
+ return NULL;
+}
+
+
+void of_dealloc( of )
+ struct ofork *of;
+{
+ if (!oforks)
+ return;
+
+ of_unhash(of);
+
+ /* detach ofork */
+ of->of_d_prev->of_d_next = of->of_d_next;
+ of->of_d_next->of_d_prev = of->of_d_prev;
+ if (of->of_dir->d_ofork == of) {
+ of->of_dir->d_ofork = (of == of->of_d_next) ? NULL : of->of_d_next;
+ }
+
+ oforks[ of->of_refnum ] = NULL;
+ free( of->of_name );
+ /* free of_ad */
+ if ((of->of_ad->ad_hf.adf_fd == -1) &&
+ (of->of_ad->ad_df.adf_fd == -1)) {
+ free( of->of_ad);
+ } else {/* someone's still using it. just free this user's locks */
+ ad_unlock(of->of_ad, of->of_refnum);
+ }
+
+ free( of );
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <itc.h>
+#include <stdio.h>
+#include <string.h>
+#include <r/xdr.h>
+#include <r/r.h>
+#include <afs/comauth.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <afs/cellconfig.h>
+
+#define DB_PASSWD 0
+
+extern int r_errno;
+
+afs_changepw( ibuf, ibuflen, rbuf, rbuflen )
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ char cell[ MAXCELLCHARS ], name[ 20 ], oldpw[ 10 ], newpw[ 10 ];
+ char *p;
+ int len;
+ u_int16_t clen;
+
+ len = (unsigned char )*ibuf++;
+ ibuf[ len ] = '\0';
+ if (( p = strchr( ibuf, '@' )) != NULL ) {
+ *p++ = '\0';
+ strcpy( cell, p );
+ ucase( cell );
+ } else {
+ if ( GetLocalCellName() != CCONF_SUCCESS ) {
+ *rbuflen = 0;
+ return( AFPERR_BADUAM );
+ }
+ strcpy( cell, LclCellName );
+ }
+
+ if ( strlen( ibuf ) > 20 ) {
+ *rbuflen = 0;
+ return( AFPERR_PARAM );
+ }
+ strcpy( name, ibuf );
+ ibuf += len;
+
+
+ if (U_InitRPC() != 0) {
+ *rbuflen = 0;
+ return( AFPERR_BADUAM );
+ }
+
+ memcpy( &clen, ibuf, sizeof( clen ));
+ ibuf += sizeof( short );
+ pcbc_encrypt((C_Block *)ibuf, (C_Block *)ibuf,
+ clen, seskeysched, seskey, 0 );
+
+ len = (unsigned char) *ibuf++;
+ if ( len > 9 ) {
+ *rbuflen = 0;
+ return( AFPERR_PARAM );
+ }
+ memcpy( oldpw, ibuf, len );
+ oldpw[ len ] = '\0';
+
+ len = (unsigned char) *ibuf++;
+ if ( len > 9 ) {
+ *rbuflen = 0;
+ return( AFPERR_PARAM );
+ }
+ memcpy( newpw, ibuf, len );
+ newpw[ len ] = '\0';
+
+ rc = U_CellChangePassword( name, newpw, name, oldpw, cell ) != 0 ) {
+
+ if ( rc != 0 ) {
+ *rbuflen = 0;
+ if ( rc < 0 && r_errno = R_ERROR ) {
+ return( AFPERR_NOTAUTH );
+ } else {
+ return( AFPERR_BADUAM );
+ }
+ }
+
+ return( AFP_OK );
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <syslog.h>
+
+#include <atalk/afp.h>
+
+#include "auth.h"
+#include "volume.h"
+#include "unix.h"
+
+#ifndef NO_QUOTA_SUPPORT
+#ifdef NEED_QUOTACTL_WRAPPER
+int quotactl(int cmd, const char *special, int id, caddr_t addr)
+{
+ return syscall(__NR_quotactl, cmd, special, id, addr);
+}
+#endif
+
+
+#if defined(USE_MNTTAB_H) || defined(__svr4__)
+/*
+ * Return the mount point associated with the filesystem
+ * on which "file" resides. Returns NULL on failure.
+ */
+static char *
+mountp( file, nfs)
+ char *file;
+ int *nfs;
+{
+ struct stat sb;
+ FILE *mtab;
+ dev_t devno;
+ static struct mnttab mnt;
+
+ if ( stat( file, &sb ) < 0 ) {
+ return( NULL );
+ }
+ devno = sb.st_dev;
+
+ if (( mtab = fopen( "/etc/mnttab", "r" )) == NULL ) {
+ return( NULL );
+ }
+
+ while ( getmntent( mtab, &mnt ) == 0 ) {
+ /* local fs */
+ if ( (stat( mnt.mnt_special, &sb ) == 0) && (devno == sb.st_rdev)) {
+ fclose( mtab );
+ return mnt.mnt_mountp;
+ }
+
+ /* check for nfs. we probably should use
+ * strcmp(mnt.mnt_fstype, MNTTYPE_NFS), but that's not as fast. */
+ if ((stat(mnt.mnt_mountp, &sb) == 0) && (devno == sb.st_dev) &&
+ strchr(mnt.mnt_special, ':')) {
+ *nfs = 1;
+ fclose( mtab );
+ return mnt.mnt_special;
+ }
+ }
+
+ fclose( mtab );
+ return( NULL );
+}
+
+#else /* __svr4__ */
+#ifdef ultrix
+/*
+ * Return the block-special device name associated with the filesystem
+ * on which "file" resides. Returns NULL on failure.
+ */
+
+static char *
+special( file, nfs )
+ char *file;
+ int *nfs;
+{
+ static struct fs_data fsd;
+
+ if ( getmnt(0, &fsd, 0, STAT_ONE, file ) < 0 ) {
+ syslog(LOG_INFO, "special: getmnt %s: %m", file );
+ return( NULL );
+ }
+
+ /* XXX: does this really detect an nfs mounted fs? */
+ if (strchr(fsd.fd_req.devname, ':'))
+ *nfs = 1;
+ return( fsd.fd_req.devname );
+}
+
+#else /* ultrix */
+#if defined(USE_MOUNT_H) || defined(BSD4_4) || defined(_IBMR2)
+
+static char *
+special( file, nfs )
+ char *file;
+ int *nfs;
+{
+ static struct statfs sfs;
+
+ if ( statfs( file, &sfs ) < 0 ) {
+ return( NULL );
+ }
+
+ /* XXX: make sure this really detects an nfs mounted fs */
+ if (strchr(sfs.f_mntfromname, ':'))
+ *nfs = 1;
+ return( sfs.f_mntfromname );
+}
+
+#else /* BSD4_4 */
+
+static char *
+special( file, nfs )
+ char *file;
+ int *nfs;
+{
+ struct stat sb;
+ FILE *mtab;
+ dev_t devno;
+ struct mntent *mnt;
+
+ if ( stat( file, &sb ) < 0 ) {
+ return( NULL );
+ }
+ devno = sb.st_dev;
+
+ if (( mtab = setmntent( "/etc/mtab", "r" )) == NULL ) {
+ return( NULL );
+ }
+
+ while (( mnt = getmntent( mtab )) != NULL ) {
+ /* check for local fs */
+ if ( (stat( mnt->mnt_fsname, &sb ) == 0) && devno == sb.st_rdev) {
+ endmntent( mtab );
+ return( mnt->mnt_fsname );
+ }
+
+ /* check for an nfs mount entry. the alternative is to use
+ * strcmp(mnt->mnt_type, MNTTYPE_NFS) instead of the strchr. */
+ if ((stat(mnt->mnt_dir, &sb) == 0) && (devno == sb.st_dev) &&
+ strchr(mnt->mnt_fsname, ':')) {
+ *nfs = 1;
+ endmntent( mtab );
+ return( mnt->mnt_fsname );
+ }
+ }
+
+ endmntent( mtab );
+ return( NULL );
+}
+
+#endif /* BSD4_4 */
+#endif /* ultrix */
+#endif /* __svr4__ */
+
+
+static int getfsquota(vol, uid, dq)
+ struct vol *vol;
+ const int uid;
+ struct dqblk *dq;
+{
+#ifdef __svr4__
+ struct quotctl qc;
+
+ qc.op = Q_GETQUOTA;
+ qc.uid = uid;
+ qc.addr = (caddr_t)dq;
+ if ( ioctl( vol->v_qfd, Q_QUOTACTL, &qc ) < 0 ) {
+ return( AFPERR_PARAM );
+ }
+
+#else /* __svr4__ */
+#ifdef ultrix
+ if ( quota( Q_GETDLIM, uid, vol->v_gvs, dq ) != 0 ) {
+ return( AFPERR_PARAM );
+ }
+#else ultrix
+
+#ifndef USRQUOTA
+#define USRQUOTA 0
+#endif
+
+#ifndef QCMD
+#define QCMD(a,b) (a)
+#endif
+
+ /* for group quotas. we only use these if the user belongs
+ * to one group. */
+ struct dqblk dqg;
+
+ memset(&dqg, 0, sizeof(dqg));
+
+#ifdef BSD4_4
+ if ( quotactl( vol->v_gvs, QCMD(Q_GETQUOTA,USRQUOTA),
+ uid, (char *)dq ) != 0 ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (ngroups == 1)
+ quotactl(vol->v_gvs, QCMD(Q_GETQUOTA, GRPQUOTA),
+ groups[0], (char *) &dqg);
+
+#else /* BSD4_4 */
+ if ( quotactl(QCMD(Q_GETQUOTA, USRQUOTA), vol->v_gvs, uid,
+ (caddr_t) dq ) != 0 ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (ngroups == 1)
+ quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), vol->v_gvs,
+ groups[0], (char *) &dqg);
+#endif /* BSD4_4 */
+
+ /* set stuff up for group quotas if necessary */
+
+ /* max(user blocks, group blocks) */
+ if (dqg.dqb_curblocks && (dq->dqb_curblocks < dqg.dqb_curblocks))
+ dq->dqb_curblocks = dqg.dqb_curblocks;
+
+ /* min(user limit, group limit) */
+ if (dqg.dqb_bhardlimit && (!dq->dqb_bhardlimit ||
+ (dq->dqb_bhardlimit > dqg.dqb_bhardlimit)))
+ dq->dqb_bhardlimit = dqg.dqb_bhardlimit;
+
+ /* ditto */
+ if (dqg.dqb_bsoftlimit && (!dq->dqb_bsoftlimit ||
+ (dq->dqb_bsoftlimit > dqg.dqb_bsoftlimit)))
+ dq->dqb_bsoftlimit = dqg.dqb_bsoftlimit;
+
+ /* ditto */
+ if (dqg.dqb_btimelimit && (!dq->dqb_btimelimit ||
+ (dq->dqb_btimelimit > dqg.dqb_btimelimit)))
+ dq->dqb_btimelimit = dqg.dqb_btimelimit;
+
+#endif /* ultrix */
+#endif /* __svr4__ */
+
+ return AFP_OK;
+}
+
+
+static int getquota( vol, dq, bsize)
+ struct vol *vol;
+ struct dqblk *dq;
+ const u_int32_t bsize;
+{
+ char *p;
+
+#ifdef __svr4__
+ char buf[ MAXPATHLEN + 1];
+
+ if ( vol->v_qfd == -1 && vol->v_gvs == NULL) {
+ if (( p = mountp( vol->v_path, &vol->v_nfs)) == NULL ) {
+ syslog( LOG_INFO, "getquota: mountp %s fails", vol->v_path );
+ return( AFPERR_PARAM );
+ }
+
+ if (vol->v_nfs) {
+ if (( vol->v_gvs = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
+ syslog( LOG_ERR, "getquota: malloc: %m" );
+ return AFPERR_MISC;
+ }
+ strcpy( vol->v_gvs, p );
+
+ } else {
+ sprintf( buf, "%s/quotas", p );
+ if (( vol->v_qfd = open( buf, O_RDONLY, 0 )) < 0 ) {
+ syslog( LOG_INFO, "open %s: %m", buf );
+ return( AFPERR_PARAM );
+ }
+ }
+
+ }
+#else
+ if ( vol->v_gvs == NULL ) {
+ if (( p = special( vol->v_path, &vol->v_nfs )) == NULL ) {
+ syslog( LOG_INFO, "getquota: special %s fails", vol->v_path );
+ return( AFPERR_PARAM );
+ }
+
+ if (( vol->v_gvs = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
+ syslog( LOG_ERR, "getquota: malloc: %m" );
+ return AFPERR_MISC;
+ }
+ strcpy( vol->v_gvs, p );
+ }
+#endif
+
+ return vol->v_nfs ? getnfsquota(vol, uuid, bsize, dq) :
+ getfsquota(vol, uuid, dq);
+}
+
+static int overquota( dqblk )
+ struct dqblk *dqblk;
+{
+ struct timeval tv;
+
+ if ( dqblk->dqb_curblocks < dqblk->dqb_bsoftlimit ) {
+ return( 0 );
+ }
+#ifdef ultrix
+ if ( dqblk->dqb_bwarn ) {
+ return( 0 );
+ }
+#else /* ultrix */
+ if ( gettimeofday( &tv, 0 ) < 0 ) {
+ syslog( LOG_ERR, "overquota: gettimeofday: %m" );
+ return( AFPERR_PARAM );
+ }
+ if ( !dqblk->dqb_btimelimit || dqblk->dqb_btimelimit > tv.tv_sec ) {
+ return( 0 );
+ }
+#endif /* ultrix */
+ return( 1 );
+}
+
+
+#ifndef dbtob
+#define dbtob(a) ((a) << 10)
+#endif
+
+/* i do the cast to VolSpace here to make sure that 64-bit shifts
+ work */
+#ifdef HAVE_2ARG_DBTOB
+#define tobytes(a, b) dbtob((VolSpace) (a), (VolSpace) (b))
+#else
+#define tobytes(a, b) dbtob((VolSpace) (a))
+#endif
+int uquota_getvolspace( vol, bfree, btotal, bsize)
+ const struct vol *vol;
+ VolSpace *bfree, *btotal;
+ const u_int32_t bsize;
+{
+ struct dqblk dqblk;
+
+ if (getquota( vol, &dqblk, bsize) != 0 ) {
+ return( AFPERR_PARAM );
+ }
+
+ /* no limit set for this user. it might be set in the future. */
+ if (dqblk.dqb_bsoftlimit == 0 && dqblk.dqb_bhardlimit == 0) {
+ *btotal = *bfree = ~((VolSpace) 0);
+ } else if ( overquota( &dqblk )) {
+ *btotal = tobytes( dqblk.dqb_bhardlimit, bsize );
+ *bfree = tobytes( dqblk.dqb_bhardlimit, bsize ) -
+ tobytes( dqblk.dqb_curblocks, bsize );
+ } else {
+ *btotal = tobytes( dqblk.dqb_bsoftlimit, bsize );
+ *bfree = tobytes( dqblk.dqb_bsoftlimit, bsize ) -
+ tobytes( dqblk.dqb_curblocks, bsize );
+ }
+
+ return( AFP_OK );
+}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+
+#ifdef BSD4_4
+#include <sys/param.h>
+#ifndef USE_GETHOSTID
+#include <sys/sysctl.h>
+#endif
+#endif
+
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <atalk/dsi.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+#include <atalk/nbp.h>
+
+#include "globals.h" /* includes <netdb.h> */
+#include "status.h"
+#include "config.h"
+#include "icon.h"
+
+static void status_flags(char *data, const int ipok,
+ const unsigned char passwdbits)
+{
+ u_int16_t status;
+
+ status = AFPSRVRINFO_COPY;
+ if (passwdbits & PASSWD_SET) /* some uams may not allow this. */
+ status |= AFPSRVRINFO_PASSWD;
+ if (passwdbits & PASSWD_NOSAVE)
+ status |= AFPSRVRINFO_NOSAVEPASSWD;
+ status |= AFPSRVRINFO_SRVSIGNATURE;
+ /* only advertise tcp/ip if we have a valid address */
+ if (ipok)
+ status |= AFPSRVRINFO_TCPIP;
+ status |= AFPSRVRINFO_SRVMSGS;
+ status |= AFPSRVRINFO_SRVNOTIFY;
+ status |= AFPSRVRINFO_FASTBOZO;
+ status = htons(status);
+ memcpy(data + AFPSTATUS_FLAGOFF, &status, sizeof(status));
+}
+
+static int status_server(char *data, const char *server)
+{
+ char *start = data;
+ char *Obj, *Type, *Zone;
+ u_int16_t status;
+ int len;
+
+ /* make room for all offsets before server name */
+ data += AFPSTATUS_PRELEN;
+
+ /* extract the obj part of the server */
+ Obj = (char *) server;
+ nbp_name(server, &Obj, &Type, &Zone);
+ len = strlen(Obj);
+ *data++ = len;
+ memcpy( data, Obj, len );
+ if ((len + 1) & 1) /* pad server name and length byte to even boundary */
+ len++;
+ data += len;
+
+ /* make room for signature and net address offset. save location of
+ * signature offset.
+ *
+ * NOTE: technically, we don't need to reserve space for the
+ * signature and net address offsets if they're not going to be
+ * used. as there are no offsets after them, it doesn't hurt to
+ * have them specified though. so, we just do that to simplify
+ * things.
+ */
+ len = data - start;
+ status = htons(len + AFPSTATUS_POSTLEN);
+ memcpy(start + AFPSTATUS_MACHOFF, &status, sizeof(status));
+ return len; /* return the offset to beginning of signature offset */
+}
+
+static void status_machine(char *data)
+{
+ char *start = data;
+ u_int16_t status;
+ int len;
+#ifdef AFS
+ const char *machine = "afs";
+#else !AFS
+ const char *machine = "unix";
+#endif
+
+ memcpy(&status, start + AFPSTATUS_MACHOFF, sizeof(status));
+ data += ntohs( status );
+ len = strlen( machine );
+ *data++ = len;
+ memcpy( data, machine, len );
+ data += len;
+ status = htons(data - start);
+ memcpy(start + AFPSTATUS_VERSOFF, &status, sizeof(status));
+}
+
+
+/* server signature is a 16-byte quantity */
+static u_int16_t status_signature(char *data, int *servoffset, DSI *dsi,
+ const char *hostname)
+{
+ char *status;
+ int i;
+ u_int16_t offset, sigoff;
+ long hostid;
+ static int id = 0;
+#ifdef BSD4_4
+ int mib[2];
+ size_t len;
+#endif
+
+ status = data;
+
+ /* get server signature offset */
+ memcpy(&offset, data + *servoffset, sizeof(offset));
+ sigoff = offset = ntohs(offset);
+
+ /* jump to server signature offset */
+ data += offset;
+
+ /* 16-byte signature consists of copies of the hostid */
+#if defined(BSD4_4) && defined(USE_GETHOSTID)
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_HOSTID;
+ len = sizeof(hostid);
+ sysctl(mib, 2, &hostid, &len, NULL, 0);
+#else
+ hostid = gethostid();
+#endif
+ if (!hostid) {
+ if (dsi)
+ hostid = dsi->server.sin_addr.s_addr;
+ else {
+ struct hostent *host;
+
+ if ((host = gethostbyname(hostname)))
+ hostid = ((struct in_addr *) host->h_addr)->s_addr;
+ }
+ }
+
+ /* it turns out that a server signature screws up separate
+ * servers running on the same machine. to work around that,
+ * i add in an increment */
+ hostid += id;
+ id++;
+ for (i = 0; i < 16; i += sizeof(hostid)) {
+ memcpy(data, &hostid, sizeof(hostid));
+ data += sizeof(hostid);
+ }
+
+ /* calculate net address offset */
+ *servoffset += sizeof(offset);
+ offset = htons(data - status);
+ memcpy(status + *servoffset, &offset, sizeof(offset));
+ return sigoff;
+}
+
+static int status_netaddress(char *data, const int servoffset,
+ const ASP asp, const DSI *dsi,
+ const char *fqdn)
+{
+ char *begin;
+ u_int16_t offset;
+
+ begin = data;
+
+ /* get net address offset */
+ memcpy(&offset, data + servoffset, sizeof(offset));
+ data += ntohs(offset);
+
+ /* format:
+ Address count (byte)
+ len (byte = sizeof(length + address type + address)
+ address type (byte, ip address = 0x01, ip + port = 0x02,
+ ddp address = 0x03, fqdn = 0x04)
+ address (up to 254 bytes, ip = 4, ip + port = 6, ddp = 4)
+ */
+
+ /* number of addresses. this currently screws up if we have a dsi
+ connection, but we don't have the ip address. to get around this,
+ we turn off the status flag for tcp/ip. */
+ *data++ = (fqdn ? 1 : 0) + (dsi ? 1 : 0) + (asp ? 1 : 0);
+
+ /* handle DNS names */
+ if (fqdn) {
+ int len = strlen(fqdn) + 2;
+ *data++ = len;
+ *data++ = 0x04;
+ memcpy(data, fqdn, len);
+ data += len;
+ }
+
+ /* ip address */
+ if (dsi) {
+ const struct sockaddr_in *inaddr = &dsi->server;
+
+ if (inaddr->sin_port == htons(DSI_AFPOVERTCP_PORT)) {
+ *data++ = 6; /* length */
+ *data++ = 0x01; /* basic ip address */
+ memcpy(data, &inaddr->sin_addr.s_addr,
+ sizeof(inaddr->sin_addr.s_addr));
+ data += sizeof(inaddr->sin_addr.s_addr);
+ } else {
+ /* ip address + port */
+ *data++ = 8;
+ *data++ = 0x02; /* ip address with port */
+ memcpy(data, &inaddr->sin_addr.s_addr,
+ sizeof(inaddr->sin_addr.s_addr));
+ data += sizeof(inaddr->sin_addr.s_addr);
+ memcpy(data, &inaddr->sin_port, sizeof(inaddr->sin_port));
+ data += sizeof(inaddr->sin_port);
+ }
+ }
+
+#ifndef NO_DDP
+ if (asp) {
+ const struct sockaddr_at *ddpaddr = atp_sockaddr(asp->asp_atp);
+
+ /* ddp address */
+ *data++ = 6;
+ *data++ = 0x03; /* ddp address */
+ memcpy(data, &ddpaddr->sat_addr.s_net, sizeof(ddpaddr->sat_addr.s_net));
+ data += sizeof(ddpaddr->sat_addr.s_net);
+ memcpy(data, &ddpaddr->sat_addr.s_node,
+ sizeof(ddpaddr->sat_addr.s_node));
+ data += sizeof(ddpaddr->sat_addr.s_node);
+ memcpy(data, &ddpaddr->sat_port, sizeof(ddpaddr->sat_port));
+ data += sizeof(ddpaddr->sat_port);
+ }
+#endif
+
+ /* return length of buffer */
+ return (data - begin);
+}
+
+/* returns actual offset to signature */
+static void status_icon(char *data, const char *icondata,
+ const int iconlen, const int sigoffset)
+{
+ char *start = data;
+ char *sigdata = data + sigoffset;
+ u_int16_t ret, status;
+
+ memcpy(&status, start + AFPSTATUS_ICONOFF, sizeof(status));
+ if ( icondata == NULL ) {
+ ret = status;
+ memset(start + AFPSTATUS_ICONOFF, 0, sizeof(status));
+ } else {
+ data += ntohs( status );
+ memcpy( data, icondata, iconlen);
+ data += iconlen;
+ ret = htons(data - start);
+ }
+
+ /* put in signature offset */
+ if (sigoffset)
+ memcpy(sigdata, &ret, sizeof(ret));
+}
+
+const void status_init(AFPConfig *aspconfig, AFPConfig *dsiconfig,
+ const struct afp_options *options)
+{
+ ASP asp;
+ DSI *dsi;
+ char *status;
+ int c, sigoff;
+
+ if (!(aspconfig || dsiconfig) || !options)
+ return;
+
+ if (aspconfig) {
+ asp = aspconfig->obj.handle;
+ status = aspconfig->status;
+ } else
+ asp = NULL;
+
+ if (dsiconfig) {
+ dsi = dsiconfig->obj.handle;
+ if (!aspconfig)
+ status = dsiconfig->status;
+ } else
+ dsi = NULL;
+
+ /*
+ * These routines must be called in order -- earlier calls
+ * set the offsets for later calls.
+ *
+ * using structs is a bad idea, but that's what the original code
+ * does. solaris, in fact, will segfault with them. so, now
+ * we just use the powers of #defines and memcpy.
+ *
+ * reply block layout (offsets are 16-bit quantities):
+ * machine type offset -> AFP version count offset ->
+ * UAM count offset -> vol icon/mask offset -> flags ->
+ *
+ * server name [padded to even boundary] -> signature offset ->
+ * network address offset ->
+ *
+ * at the appropriate offsets:
+ * machine type, afp versions, uams, server signature
+ * (16-bytes), network addresses, volume icon/mask
+ */
+
+ status_flags(status, options->fqdn ||
+ (dsiconfig && dsi->server.sin_addr.s_addr),
+ options->passwdbits);
+ /* returns offset to signature offset */
+ c = status_server(status, options->server ? options->server :
+ options->hostname);
+ status_machine(status);
+ status_versions(status);
+ status_uams(status, options->uamlist);
+ if (options->flags & OPTION_CUSTOMICON)
+ status_icon(status, icon, sizeof(icon), c);
+ else
+ status_icon(status, apple_atalk_icon, sizeof(apple_atalk_icon), c);
+
+ sigoff = status_signature(status, &c, dsi, options->hostname);
+
+ /* returns length */
+ c = status_netaddress(status, c, asp, dsi, options->fqdn);
+
+
+#ifndef NO_DDP
+ if (aspconfig) {
+ asp_setstatus(asp, status, c);
+ aspconfig->signature = status + sigoff;
+ aspconfig->statuslen = c;
+ }
+#endif
+
+ if (dsiconfig) {
+ if (aspconfig) { /* copy to dsiconfig */
+ memcpy(dsiconfig->status, status, ATP_MAXDATA);
+ status = dsiconfig->status;
+ }
+
+ if ((options->flags & OPTION_CUSTOMICON) == 0) {
+ status_icon(status, apple_tcp_icon, sizeof(apple_tcp_icon), 0);
+ }
+ dsi_setstatus(dsi, status, c);
+ dsiconfig->signature = status + sigoff;
+ dsiconfig->statuslen = c;
+ }
+}
+
+/* this is the same as asp/dsi_getstatus */
+int afp_getsrvrinfo(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ AFPConfig *config = obj->config;
+
+ memcpy(rbuf, config->status, config->statuslen);
+ *rbuflen = config->statuslen;
+ return AFP_OK;
+}
--- /dev/null
+#ifndef AFPD_STATUS_H
+#define AFPD_STATUS_H 1
+
+#include <sys/cdefs.h>
+#include <atalk/dsi.h>
+#include <atalk/asp.h>
+#include "globals.h"
+#include "config.h"
+
+/* we use these to prevent whacky alignment problems */
+#define AFPSTATUS_MACHOFF 0
+#define AFPSTATUS_VERSOFF 2
+#define AFPSTATUS_UAMSOFF 4
+#define AFPSTATUS_ICONOFF 6
+#define AFPSTATUS_FLAGOFF 8
+#define AFPSTATUS_PRELEN 10
+#define AFPSTATUS_POSTLEN 4
+#define AFPSTATUS_LEN (AFPSTATUS_PRELEN + AFPSTATUS_POSTLEN)
+
+
+#define PASSWD_NONE 0
+#define PASSWD_SET (1 << 0)
+#define PASSWD_NOSAVE (1 << 1)
+#define PASSWD_ALL (PASSWD_SET | PASSWD_NOSAVE)
+
+extern void status_versions __P((char * /*status*/));
+extern void status_uams __P((char * /*status*/, const char * /*authlist*/));
+extern const void status_init __P((AFPConfig *, AFPConfig *,
+ const struct afp_options *));
+extern int afp_getsrvrinfo __P((AFPObj *, char *, int, char *, int *));
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <stdio.h> /* to pick up NULL */
+#include <sys/stat.h> /* works around a bug */
+#include <syslog.h>
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+#include "globals.h"
+
+/* grab the FP functions */
+#include "auth.h"
+#include "desktop.h"
+#include "switch.h"
+#include "fork.h"
+#include "file.h"
+#include "directory.h"
+#include "filedir.h"
+#include "status.h"
+#include "misc.h"
+
+static int afp_null(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ syslog( LOG_INFO, "afp_null handle %d", *ibuf );
+ *rbuflen = 0;
+ return( AFPERR_NOOP );
+}
+
+/*
+ * Routines marked "NULL" are not AFP functions.
+ * Routines marked "afp_null" are AFP functions
+ * which are not yet implemented. A fine line...
+ */
+int (*preauth_switch[])() = {
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 0 - 7 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 8 - 15 */
+ NULL, NULL, afp_login, afp_logincont,
+ afp_logout, NULL, NULL, NULL, /* 16 - 23 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 24 - 31 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 32 - 39 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 40 - 47 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 48 - 55 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 56 - 63 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 64 - 71 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 72 - 79 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 80 - 87 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 88 - 95 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 96 - 103 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 104 - 111 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 112 - 119 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 120 - 127 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 128 - 135 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 136 - 143 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 144 - 151 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 152 - 159 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 160 - 167 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 168 - 175 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 176 - 183 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 184 - 191 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 192 - 199 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 200 - 207 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 208 - 215 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 216 - 223 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 224 - 231 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 232 - 239 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 240 - 247 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 248 - 255 */
+};
+
+int (**afp_switch)() = preauth_switch;
+
+int (*postauth_switch[])() = {
+ NULL, afp_bytelock, afp_closevol, afp_closedir,
+ afp_closefork, afp_copyfile, afp_createdir, afp_createfile, /* 0 - 7 */
+ afp_delete, afp_enumerate, afp_flush, afp_flushfork,
+ afp_null, afp_null, afp_getforkparams, afp_getsrvrinfo, /* 8 - 15 */
+ afp_getsrvrparms, afp_getvolparams, afp_login, afp_logincont,
+ afp_logout, afp_mapid, afp_mapname, afp_moveandrename, /* 16 - 23 */
+ afp_openvol, afp_opendir, afp_openfork, afp_read,
+ afp_rename, afp_setdirparams, afp_setfilparams, afp_setforkparams,
+ /* 24 - 31 */
+ afp_setvolparams, afp_write, afp_getfildirparams, afp_setfildirparams,
+ afp_changepw, afp_getuserinfo, afp_getsrvrmesg, afp_createid, /* 32 - 39 */
+ afp_deleteid, afp_resolveid, afp_exchangefiles, afp_null /*afp_catsearch*/,
+ afp_null, afp_null, afp_null, afp_null, /* 40 - 47 */
+ afp_opendt, afp_closedt, afp_null, afp_geticon,
+ afp_geticoninfo, afp_addappl, afp_rmvappl, afp_getappl, /* 48 - 55 */
+ afp_addcomment, afp_rmvcomment, afp_getcomment, NULL,
+ NULL, NULL, NULL, NULL, /* 56 - 63 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 64 - 71 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 72 - 79 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 80 - 87 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 88 - 95 */
+ NULL, NULL, NULL, NULL,
+ afp_getdiracl, afp_setdiracl, afp_afschangepw, NULL, /* 96 - 103 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 104 - 111 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 112 - 119 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 120 - 127 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 128 - 135 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 136 - 143 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 144 - 151 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 152 - 159 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 160 - 167 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 168 - 175 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 176 - 183 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 184 - 191 */
+ afp_addicon, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 192 - 199 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 200 - 207 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 208 - 215 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 216 - 223 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 224 - 231 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 232 - 239 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 240 - 247 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, /* 248 - 255 */
+};
+
+
+/* add a new function if it's specified. return the old function in
+ * "old" if there's a pointer there. */
+int uam_afpserver_action(const int id, const int which,
+ int (**new)(), int (**old)())
+{
+ switch (which) {
+ case UAM_AFPSERVER_PREAUTH:
+ if (old)
+ *old = preauth_switch[id];
+ if (new)
+ preauth_switch[id] = *new;
+ break;
+ case UAM_AFPSERVER_POSTAUTH:
+ if (old)
+ *old = postauth_switch[id];
+ if (new)
+ postauth_switch[id] = *new;
+ break;
+ default:
+ syslog(LOG_DEBUG, "uam_afpserver_action: unknown switch %d[%d]",
+ which, id);
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef AFPD_SWITCH_H
+#define AFPD_SWITCH_H 1
+
+extern int (**afp_switch)();
+extern int (*postauth_switch[])();
+#endif
--- /dev/null
+/* Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <netatalk/endian.h>
+#include <atalk/asp.h>
+#include <atalk/dsi.h>
+#include <atalk/afp.h>
+#include <atalk/util.h>
+
+#include "globals.h"
+#include "config.h"
+#include "auth.h"
+#include "uam_auth.h"
+
+/* --- server uam functions -- */
+
+/* uam_load. uams must have a uam_setup function. */
+struct uam_mod *uam_load(const char *path, const char *name)
+{
+ char buf[MAXPATHLEN + 1], *p;
+ struct uam_mod *mod;
+ void *module;
+
+ if ((module = mod_open(path)) == NULL) {
+ syslog(LOG_ERR, "uam_load(%s): failed to load: %s", name, mod_error());
+ return NULL;
+ }
+
+ if ((mod = (struct uam_mod *) malloc(sizeof(struct uam_mod))) == NULL) {
+ syslog(LOG_ERR, "uam_load(%s): malloc failed", name);
+ goto uam_load_fail;
+ }
+
+ strncpy(buf, name, sizeof(buf));
+ if ((p = strchr(buf, '.')))
+ *p = '\0';
+ if ((mod->uam_fcn = mod_symbol(module, buf)) == NULL) {
+ goto uam_load_err;
+ }
+
+ if (mod->uam_fcn->uam_type != UAM_MODULE_SERVER) {
+ syslog(LOG_ERR, "uam_load(%s): attempted to load a non-server module",
+ name);
+ goto uam_load_err;
+ }
+
+ /* version check would go here */
+
+ if (!mod->uam_fcn->uam_setup ||
+ ((*mod->uam_fcn->uam_setup)(name) < 0)) {
+ syslog(LOG_ERR, "uam_load(%s): uam_setup failed", name);
+ goto uam_load_err;
+ }
+
+ mod->uam_module = module;
+ return mod;
+
+uam_load_err:
+ free(mod);
+uam_load_fail:
+ mod_close(module);
+ return NULL;
+}
+
+/* unload the module. we check for a cleanup function, but we don't
+ * die if one doesn't exist. however, things are likely to leak without one.
+ */
+void uam_unload(struct uam_mod *mod)
+{
+ if (mod->uam_fcn->uam_cleanup)
+ (*mod->uam_fcn->uam_cleanup)();
+ mod_close(mod->uam_module);
+ free(mod);
+}
+
+/* -- client-side uam functions -- */
+
+/* set up stuff for this uam. */
+int uam_register(const int type, const char *path, const char *name, ...)
+{
+ va_list ap;
+ struct uam_obj *uam;
+
+ if (!name)
+ return -1;
+
+ /* see if it already exists. */
+ if ((uam = auth_uamfind(type, name, strlen(name)))) {
+ if (strcmp(uam->uam_path, path)) {
+ /* it exists, but it's not the same module. */
+ syslog(LOG_ERR, "uam_register: \"%s\" already loaded by %s",
+ name, path);
+ return -1;
+ }
+ uam->uam_count++;
+ return 0;
+ }
+
+ /* allocate space for uam */
+ if ((uam = calloc(1, sizeof(struct uam_obj))) == NULL)
+ return -1;
+
+ uam->uam_name = name;
+ uam->uam_path = strdup(path);
+ uam->uam_count++;
+
+ va_start(ap, name);
+ switch (type) {
+ case UAM_SERVER_LOGIN: /* expect three arguments */
+ uam->u.uam_login.login = va_arg(ap, void *);
+ uam->u.uam_login.logincont = va_arg(ap, void *);
+ uam->u.uam_login.logout = va_arg(ap, void *);
+ break;
+ case UAM_SERVER_CHANGEPW: /* one argument */
+ uam->u.uam_changepw = va_arg(ap, void *);
+ break;
+ case UAM_SERVER_PRINTAUTH: /* x arguments */
+ default:
+ break;
+ }
+ va_end(ap);
+
+ /* attach to other uams */
+ if (auth_register(type, uam) < 0) {
+ free(uam->uam_path);
+ free(uam);
+ return -1;
+ }
+
+ return 0;
+}
+
+void uam_unregister(const int type, const char *name)
+{
+ struct uam_obj *uam;
+
+ if (!name)
+ return;
+
+ uam = auth_uamfind(type, name, strlen(name));
+ if (!uam || --uam->uam_count > 0)
+ return;
+
+ auth_unregister(uam);
+ free(uam->uam_path);
+ free(uam);
+}
+
+/* --- helper functions for plugin uams --- */
+
+struct passwd *uam_getname(char *name, const int len)
+{
+ struct passwd *pwent;
+ char *user;
+ int i;
+
+ if ((pwent = getpwnam(name)))
+ return pwent;
+
+#ifndef NO_REAL_USER_NAME
+ for (i = 0; i < len; i++)
+ name[i] = tolower(name[i]);
+
+ setpwent();
+ while ((pwent = getpwent())) {
+ if (user = strchr(pwent->pw_gecos, ','))
+ *user = '\0';
+ user = pwent->pw_gecos;
+
+ /* check against both the gecos and the name fields. the user
+ * might have just used a different capitalization. */
+ if ((strncasecmp(user, name, len) == 0) ||
+ (strncasecmp(pwent->pw_name, name, len) == 0)) {
+ strncpy(name, pwent->pw_name, len);
+ break;
+ }
+ }
+ endpwent();
+#endif
+
+ /* os x server doesn't keep anything useful if we do getpwent */
+ return pwent ? getpwnam(name) : NULL;
+}
+
+int uam_checkuser(const struct passwd *pwd)
+{
+ char *p;
+
+ if (!pwd || !pwd->pw_shell || (*pwd->pw_shell == '\0'))
+ return -1;
+
+ while ((p = getusershell())) {
+ if ( strcmp( p, pwd->pw_shell ) == 0 )
+ break;
+ }
+ endusershell();
+
+ if (!p) {
+ syslog( LOG_INFO, "illegal shell %s for %s", pwd->pw_shell, pwd->pw_name);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* afp-specific functions */
+int uam_afpserver_option(void *private, const int what, void *option,
+ int *len)
+{
+ AFPObj *obj = private;
+ char **buf = (char **) option; /* most of the options are this */
+ int32_t result;
+ int fd;
+
+ if (!obj || !option)
+ return -1;
+
+ switch (what) {
+ case UAM_OPTION_USERNAME:
+ *buf = (void *) obj->username;
+ if (len)
+ *len = sizeof(obj->username) - 1;
+ break;
+
+ case UAM_OPTION_GUEST:
+ *buf = (void *) obj->options.guest;
+ if (len)
+ *len = strlen(obj->options.guest);
+ break;
+
+ case UAM_OPTION_PASSWDOPT:
+ if (!len)
+ return -1;
+
+ switch (*len) {
+ case UAM_PASSWD_FILENAME:
+ *buf = (void *) obj->options.passwdfile;
+ *len = strlen(obj->options.passwdfile);
+ break;
+
+ case UAM_PASSWD_MINLENGTH:
+ *((int *) option) = obj->options.passwdminlen;
+ *len = sizeof(obj->options.passwdminlen);
+ break;
+
+ case UAM_PASSWD_MAXFAIL:
+ *((int *) option) = obj->options.loginmaxfail;
+ *len = sizeof(obj->options.loginmaxfail);
+ break;
+
+ case UAM_PASSWD_EXPIRETIME: /* not implemented */
+ default:
+ return -1;
+ break;
+ }
+ break;
+
+ case UAM_OPTION_SIGNATURE:
+ *buf = (void *) (((AFPConfig *)obj->config)->signature);
+ if (len)
+ *len = 16;
+ break;
+
+ case UAM_OPTION_RANDNUM: /* returns a random number in 4-byte units. */
+ if (!len || (*len < 0) || (*len % sizeof(result)))
+ return -1;
+
+ /* construct a random number */
+ if ((fd = open("/dev/urandom", O_RDONLY)) < 0) {
+ struct timeval tv;
+ struct timezone tz;
+ char *randnum = (char *) option;
+ int i;
+
+ if (gettimeofday(&tv, &tz) < 0)
+ return -1;
+ srandom(tv.tv_sec + (unsigned long) obj + (unsigned long) obj->handle);
+ for (i = 0; i < *len; i += sizeof(result)) {
+ result = random();
+ memcpy(randnum + i, &result, sizeof(result));
+ }
+ } else {
+ result = read(fd, option, *len);
+ close(fd);
+ if (result < 0)
+ return -1;
+ }
+ break;
+
+ case UAM_OPTION_HOSTNAME:
+ *buf = (void *) obj->options.hostname;
+ if (len)
+ *len = strlen(obj->options.hostname);
+ break;
+
+ case UAM_OPTION_COOKIE:
+ /* it's up to the uam to actually store something useful here.
+ * this just passes back a handle to the cookie. the uam side
+ * needs to do something like **buf = (void *) cookie to store
+ * the cookie. */
+ *buf = (void *) &obj->uam_cookie;
+ break;
+
+ default:
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+/* if we need to maintain a connection, this is how we do it.
+ * because an action pointer gets passed in, we can stream
+ * DSI connections */
+int uam_afp_read(void *handle, char *buf, int *buflen,
+ int (*action)(void *, void *, const int))
+{
+ AFPObj *obj = handle;
+ int len;
+
+ if (!obj)
+ return AFPERR_PARAM;
+
+ switch (obj->proto) {
+ case AFPPROTO_ASP:
+ if ((len = asp_wrtcont(obj->handle, buf, buflen )) < 0)
+ goto uam_afp_read_err;
+ return action(handle, buf, *buflen);
+ break;
+
+ case AFPPROTO_DSI:
+ len = dsi_writeinit(obj->handle, buf, *buflen);
+ if (!len || ((len = action(handle, buf, len)) < 0)) {
+ dsi_writeflush(obj->handle);
+ goto uam_afp_read_err;
+ }
+
+ while (len = (dsi_write(obj->handle, buf, *buflen))) {
+ if ((len = action(handle, buf, len)) < 0) {
+ dsi_writeflush(obj->handle);
+ goto uam_afp_read_err;
+ }
+ }
+ break;
+ }
+ return 0;
+
+uam_afp_read_err:
+ *buflen = 0;
+ return len;
+}
--- /dev/null
+/* Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * interface between uam.c and auth.c
+ */
+
+#ifndef AFPD_UAM_AUTH_H
+#define AFPD_UAM_AUTH_H 1
+
+#include <sys/cdefs.h>
+#include <pwd.h>
+
+#include <atalk/uam.h>
+#include "globals.h"
+
+struct uam_mod {
+ void *uam_module;
+ struct uam_export *uam_fcn;
+ struct uam_mod *uam_prev, *uam_next;
+};
+
+struct uam_obj {
+ const char *uam_name; /* authentication method */
+ char *uam_path; /* where it's located */
+ int uam_count;
+ union {
+ struct {
+ int (*login) __P((void *, struct passwd **,
+ char *, int, char *, int *));
+ int (*logincont) __P((void *, struct passwd **, char *,
+ int, char *, int *));
+ void (*logout) __P((void));
+ } uam_login;
+ int (*uam_changepw) __P((void *, char *, struct passwd *, char *,
+ int, char *, int *));
+ } u;
+ struct uam_obj *uam_prev, *uam_next;
+};
+
+#define uam_attach(a, b) do { \
+ (a)->uam_prev->uam_next = (b); \
+ (b)->uam_prev = (a)->uam_prev; \
+ (b)->uam_next = (a); \
+ (a)->uam_prev = (b); \
+} while (0)
+
+#define uam_detach(a) do { \
+ (a)->uam_prev->uam_next = (a)->uam_next; \
+ (a)->uam_next->uam_prev = (a)->uam_prev; \
+} while (0)
+
+
+extern struct uam_mod *uam_load __P((const char *, const char *));
+extern void uam_unload __P((struct uam_mod *));
+
+/* auth.c */
+int auth_load __P((const char *, const char *));
+int auth_register __P((const int, struct uam_obj *));
+#define auth_unregister(a) uam_detach(a)
+struct uam_obj *auth_uamfind __P((const int, const char *, const int));
+void auth_unload __P((void));
+
+#endif /* uam_auth.h */
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
+#include <netatalk/endian.h>
+#include <dirent.h>
+#include <limits.h>
+#include <atalk/afp.h>
+#include <string.h>
+#include <fcntl.h>
+#include "auth.h"
+#include "directory.h"
+#include "volume.h"
+#include "unix.h"
+
+/*
+ * Get the free space on a partition.
+ */
+int ustatfs_getvolspace( vol, bfree, btotal, bsize )
+ const struct vol *vol;
+ VolSpace *bfree, *btotal;
+ u_int32_t *bsize;
+{
+#ifdef ultrix
+ struct fs_data sfs;
+#else /*ultrix*/
+ struct statfs sfs;
+#endif /*ultrix*/
+
+ if ( statfs( vol->v_path, &sfs ) < 0 ) {
+ return( AFPERR_PARAM );
+ }
+
+#ifdef ultrix
+ *bfree = (VolSpace) sfs.fd_req.bfreen * 1024;
+ *bsize = 1024;
+#else
+ *bfree = (VolSpace) sfs.f_bavail * sfs.f_frsize;
+ *bsize = sfs.f_frsize;
+#endif ultrix
+
+#ifdef ultrix
+ *btotal = (VolSpace)
+ ( sfs.fd_req.btot - ( sfs.fd_req.bfree - sfs.fd_req.bfreen )) * 1024;
+#else ultrix
+ *btotal = (VolSpace)
+ ( sfs.f_blocks - ( sfs.f_bfree - sfs.f_bavail )) * sfs.f_frsize;
+#endif ultrix
+ return( AFP_OK );
+}
+
+static __inline__ int utombits( bits )
+ mode_t bits;
+{
+ int mbits;
+
+ mbits = 0;
+
+ mbits |= ( bits & ( S_IREAD >> 6 )) ? AR_UREAD : 0;
+ mbits |= ( bits & ( S_IWRITE >> 6 )) ? AR_UWRITE : 0;
+ mbits |= ( bits & ( S_IEXEC >> 6) ) ? AR_USEARCH : 0;
+
+ return( mbits );
+}
+
+void utommode( stat, ma )
+ struct stat *stat;
+ struct maccess *ma;
+{
+ mode_t mode;
+
+ mode = stat->st_mode;
+
+ ma->ma_world = utombits( mode );
+ mode = mode >> 3;
+
+ ma->ma_group = utombits( mode );
+ mode = mode >> 3;
+
+ ma->ma_owner = utombits( mode );
+
+ if ( uuid == stat->st_uid ) {
+ ma->ma_user = ma->ma_owner | AR_UOWN;
+ } else if ( gmem( stat->st_gid )) {
+ ma->ma_user = ma->ma_group;
+ } else {
+ ma->ma_user = ma->ma_world;
+ }
+
+ /*
+ * There are certain things the mac won't try if you don't have
+ * the "owner" bit set, even tho you can do these things on unix wiht
+ * only write permission. What were the things?
+ */
+ if ( ma->ma_user & AR_UWRITE ) {
+ ma->ma_user |= AR_UOWN;
+ }
+}
+
+
+int gmem( gid )
+ const gid_t gid;
+{
+ int i;
+
+ for ( i = 0; i < ngroups; i++ ) {
+ if ( groups[ i ] == gid ) {
+ return( 1 );
+ }
+ }
+ return( 0 );
+}
+
+static __inline__ mode_t mtoubits( bits )
+ u_char bits;
+{
+ mode_t mode;
+
+ mode = 0;
+
+ mode |= ( bits & AR_UREAD ) ? ( S_IREAD >> 6 ) : 0;
+ mode |= ( bits & AR_UWRITE ) ? ( S_IWRITE >> 6 ) : 0;
+ mode |= ( bits & AR_USEARCH ) ? ( S_IEXEC >> 6 ) : 0;
+
+ return( mode );
+}
+
+mode_t mtoumode( ma )
+ struct maccess *ma;
+{
+ mode_t mode;
+
+ mode = 0;
+ mode |= mtoubits( ma->ma_owner );
+ mode = mode << 3;
+
+ mode |= mtoubits( ma->ma_group );
+ mode = mode << 3;
+
+ mode |= mtoubits( ma->ma_world );
+
+ return( mode );
+}
+
+
+int setdeskmode( mode )
+ const mode_t mode;
+{
+ char wd[ MAXPATHLEN + 1];
+ struct stat st;
+ char modbuf[ 12 + 1], *m;
+ struct dirent *deskp, *subp;
+ DIR *desk, *sub;
+
+ if ( getcwd( wd , MAXPATHLEN) == NULL ) {
+ return( -1 );
+ }
+ if ( chdir( ".AppleDesktop" ) < 0 ) {
+ return( -1 );
+ }
+ if (( desk = opendir( "." )) == NULL ) {
+ if ( chdir( wd ) < 0 ) {
+ syslog( LOG_ERR, "setdeskmode: chdir %s: %m", wd );
+ }
+ return( -1 );
+ }
+ for ( deskp = readdir( desk ); deskp != NULL; deskp = readdir( desk )) {
+ if ( strcmp( deskp->d_name, "." ) == 0 ||
+ strcmp( deskp->d_name, ".." ) == 0 || strlen( deskp->d_name ) > 2 ) {
+ continue;
+ }
+ strcpy( modbuf, deskp->d_name );
+ strcat( modbuf, "/" );
+ m = strchr( modbuf, '\0' );
+ if (( sub = opendir( deskp->d_name )) == NULL ) {
+ continue;
+ }
+ for ( subp = readdir( sub ); subp != NULL; subp = readdir( sub )) {
+ if ( strcmp( subp->d_name, "." ) == 0 ||
+ strcmp( subp->d_name, ".." ) == 0 ) {
+ continue;
+ }
+ *m = '\0';
+ strcat( modbuf, subp->d_name );
+ /* XXX: need to preserve special modes */
+ if (stat(modbuf, &st) < 0) {
+ syslog( LOG_DEBUG, "setdeskmode: stat %s: %m", modbuf );
+ continue;
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ if ( chmod( modbuf, DIRBITS | mode ) < 0 ) {
+ syslog( LOG_DEBUG, "setdeskmode: chmod %s: %m", modbuf );
+ }
+ } else if ( chmod( modbuf, mode ) < 0 ) {
+ syslog( LOG_DEBUG, "setdeskmode: chmod %s: %m", modbuf );
+ }
+
+ }
+ closedir( sub );
+ /* XXX: need to preserve special modes */
+ if ( chmod( deskp->d_name, DIRBITS | mode ) < 0 ) {
+ syslog( LOG_DEBUG, "setdeskmode: chmod %s: %m", deskp->d_name );
+ }
+ }
+ closedir( desk );
+ if ( chdir( wd ) < 0 ) {
+ syslog( LOG_ERR, "setdeskmode: chdir %s: %m", wd );
+ return -1;
+ }
+ /* XXX: need to preserve special modes */
+ if ( chmod( ".AppleDesktop", DIRBITS | mode ) < 0 ) {
+ syslog( LOG_DEBUG, "setdeskmode: chmod .AppleDesktop: %m" );
+ }
+ return( 0 );
+}
+
+int setdirmode( mode, noadouble )
+ const mode_t mode;
+ const int noadouble;
+{
+ char buf[ MAXPATHLEN + 1];
+ struct stat st;
+ char *m;
+ struct dirent *dirp;
+ DIR *dir;
+
+ if (( dir = opendir( "." )) == NULL ) {
+ syslog( LOG_ERR, "setdirmode: opendir .: %m" );
+ return( -1 );
+ }
+
+ for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
+ if ( *dirp->d_name == '.' ) {
+ continue;
+ }
+ if ( stat( dirp->d_name, &st ) < 0 ) {
+ syslog( LOG_DEBUG, "setdirmode: stat %s: %m", dirp->d_name );
+ continue;
+ }
+
+ if (S_ISREG(st.st_mode)) {
+ /* XXX: need to preserve special modes */
+ if (S_ISDIR(st.st_mode)) {
+ if ( chmod( dirp->d_name, DIRBITS | mode ) < 0 ) {
+ syslog( LOG_DEBUG, "setdirmode: chmod %s: %m", dirp->d_name );
+ }
+ } else if ( chmod( dirp->d_name, mode ) < 0 ) {
+ syslog( LOG_DEBUG, "setdirmode: chmod %s: %m", dirp->d_name );
+ }
+ }
+ }
+ closedir( dir );
+ if (( dir = opendir( ".AppleDouble" )) == NULL ) {
+ if (noadouble)
+ goto setdirmode_noadouble;
+ syslog( LOG_ERR, "setdirmode: opendir .AppleDouble: %m" );
+ return( -1 );
+ }
+ strcpy( buf, ".AppleDouble" );
+ strcat( buf, "/" );
+ m = strchr( buf, '\0' );
+ for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
+ if ( strcmp( dirp->d_name, "." ) == 0 ||
+ strcmp( dirp->d_name, ".." ) == 0 ) {
+ continue;
+ }
+ *m = '\0';
+ strcat( buf, dirp->d_name );
+
+ if ( stat( buf, &st ) < 0 ) {
+ syslog( LOG_DEBUG, "setdirmode: stat %s: %m", buf );
+ continue;
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ if ( chmod(buf, DIRBITS | mode) < 0 ) {
+ syslog( LOG_DEBUG, "setdirmode: chmod %s: %m", buf );
+ }
+ } else if ( chmod(buf, mode) < 0 ) {
+ syslog( LOG_DEBUG, "setdirmode: chmod %s: %m", buf );
+ }
+ }
+ closedir( dir );
+
+ /* XXX: use special bits to tag directory permissions */
+
+ /* XXX: need to preserve special modes */
+ if ( chmod( ".AppleDouble", DIRBITS | mode ) < 0 ) {
+ syslog( LOG_ERR, "setdirmode: chmod .AppleDouble: %m" );
+ return( -1 );
+ }
+
+setdirmode_noadouble:
+ /* XXX: need to preserve special modes */
+ if ( chmod( ".", DIRBITS | mode ) < 0 ) {
+ syslog( LOG_ERR, "setdirmode: chmod .: %m" );
+ return( -1 );
+ }
+ return( 0 );
+}
+
+int setdeskowner( uid, gid )
+ const uid_t uid;
+ const gid_t gid;
+{
+ char wd[ MAXPATHLEN + 1];
+ char modbuf[12 + 1], *m;
+ struct dirent *deskp, *subp;
+ DIR *desk, *sub;
+
+ if ( getcwd( wd, MAXPATHLEN ) == NULL ) {
+ return( -1 );
+ }
+ if ( chdir( ".AppleDesktop" ) < 0 ) {
+ return( -1 );
+ }
+ if (( desk = opendir( "." )) == NULL ) {
+ if ( chdir( wd ) < 0 ) {
+ syslog( LOG_ERR, "setdeskowner: chdir %s: %m", wd );
+ }
+ return( -1 );
+ }
+ for ( deskp = readdir( desk ); deskp != NULL; deskp = readdir( desk )) {
+ if ( strcmp( deskp->d_name, "." ) == 0 ||
+ strcmp( deskp->d_name, ".." ) == 0 ||
+ strlen( deskp->d_name ) > 2 ) {
+ continue;
+ }
+ strcpy( modbuf, deskp->d_name );
+ strcat( modbuf, "/" );
+ m = strchr( modbuf, '\0' );
+ if (( sub = opendir( deskp->d_name )) == NULL ) {
+ continue;
+ }
+ for ( subp = readdir( sub ); subp != NULL; subp = readdir( sub )) {
+ if ( strcmp( subp->d_name, "." ) == 0 ||
+ strcmp( subp->d_name, ".." ) == 0 ) {
+ continue;
+ }
+ *m = '\0';
+ strcat( modbuf, subp->d_name );
+ /* XXX: add special any uid, ignore group bits */
+ if ( chown( modbuf, uid, gid ) < 0 ) {
+ syslog( LOG_DEBUG, "setdeskown: chown %s: %m", modbuf );
+ }
+ }
+ closedir( sub );
+ /* XXX: add special any uid, ignore group bits */
+ if ( chown( deskp->d_name, uid, gid ) < 0 ) {
+ syslog( LOG_DEBUG, "setdeskowner: chown %s: %m", deskp->d_name );
+ }
+ }
+ closedir( desk );
+ if ( chdir( wd ) < 0 ) {
+ syslog( LOG_ERR, "setdeskowner: chdir %s: %m", wd );
+ return -1;
+ }
+ if ( chown( ".AppleDesktop", uid, gid ) < 0 ) {
+ syslog( LOG_ERR, "setdeskowner: chown .AppleDesktop: %m" );
+ }
+ return( 0 );
+}
+
+
+/* uid/gid == 0 need to be handled as special cases. they really mean
+ * that user/group should inherit from other, but that doesn't fit
+ * into the unix permission scheme. we can get around this by
+ * co-opting some bits. */
+int setdirowner( uid, gid, noadouble )
+ const uid_t uid;
+ const gid_t gid;
+ const int noadouble;
+{
+ char buf[ MAXPATHLEN + 1];
+ struct stat st;
+ char *m;
+ struct dirent *dirp;
+ DIR *dir;
+
+ if (( dir = opendir( "." )) == NULL ) {
+ return( -1 );
+ }
+ for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
+ if ( *dirp->d_name == '.' ) {
+ continue;
+ };
+ if ( stat( dirp->d_name, &st ) < 0 ) {
+ syslog( LOG_DEBUG, "setdirowner: stat %s: %m", dirp->d_name );
+ continue;
+ }
+ if (( st.st_mode & S_IFMT ) == S_IFREG ) {
+ if ( chown( dirp->d_name, uid, gid ) < 0 ) {
+ syslog( LOG_DEBUG, "setdirowner: chown %s: %m", dirp->d_name );
+ }
+ }
+ }
+ closedir( dir );
+ if (( dir = opendir( ".AppleDouble" )) == NULL ) {
+ if (noadouble)
+ goto setdirowner_noadouble;
+ return( -1 );
+ }
+ strcpy( buf, ".AppleDouble" );
+ strcat( buf, "/" );
+ m = strchr( buf, '\0' );
+ for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
+ if ( strcmp( dirp->d_name, "." ) == 0 ||
+ strcmp( dirp->d_name, ".." ) == 0 ) {
+ continue;
+ }
+ *m = '\0';
+ strcat( buf, dirp->d_name );
+ if ( chown( buf, uid, gid ) < 0 ) {
+ syslog( LOG_DEBUG, "setdirowner: chown %d/%d %s: %m",
+ uid, gid, buf );
+ }
+ }
+ closedir( dir );
+
+ /*
+ * We cheat: we know that chown doesn't do anything.
+ */
+ if ( stat( ".AppleDouble", &st ) < 0 ) {
+ syslog( LOG_ERR, "setdirowner: stat .AppleDouble: %m" );
+ return( -1 );
+ }
+ if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 ) {
+ syslog( LOG_DEBUG, "setdirowner: chown %d/%d .AppleDouble: %m",
+ uid, gid);
+ }
+
+setdirowner_noadouble:
+ if ( stat( ".", &st ) < 0 ) {
+ return( -1 );
+ }
+ if ( gid && gid != st.st_gid && chown( ".", uid, gid ) < 0 ) {
+ syslog( LOG_DEBUG, "setdirowner: chown %d/%d .: %m",
+ uid, gid);
+ }
+
+ return( 0 );
+}
--- /dev/null
+#ifndef AFPD_UNIX_H
+#define AFPD_UNIX_H
+
+#include <sys/cdefs.h>
+#include <netatalk/endian.h>
+#include "volume.h"
+
+#if defined( sun ) && !defined( __svr4__ )
+#ifdef i386
+typedef int mode_t;
+#endif /*i386*/
+#endif /*sun __svr4__*/
+
+
+/* some GLIBC/old-libc-isms */
+#if defined(__GNU_LIBRARY__)
+#if __GNU_LIBRARY__ < 6
+#define USE_VFS_H
+#else
+#define USE_STATFS_H
+#endif
+#endif
+
+#if defined(USE_VFS_H) || defined( sun ) || defined( ibm032 )
+#include <sys/vfs.h>
+#endif
+
+#if defined(_IBMR2) || defined(USE_STATFS_H)
+#include <sys/statfs.h>
+/* this might not be right. */
+#define f_mntfromname f_fname
+#endif
+
+#if defined(USE_STATVFS_H) || defined(__svr4__)
+#include <sys/statvfs.h>
+#define statfs statvfs
+#else
+#define f_frsize f_bsize
+#endif
+
+#if defined(__svr4__) || defined(USE_MNTTAB_H)
+#include <sys/mnttab.h>
+#endif
+
+#if defined(USE_MOUNT_H) || defined(BSD4_4) || \
+ defined(linux) || defined(ultrix)
+#include <sys/mount.h>
+#endif
+
+#if defined(linux) || defined(USE_MNTENT_H)
+#include <mntent.h>
+#endif
+
+
+#ifndef NO_QUOTA_SUPPORT
+
+#if !(defined(__svr4__) || defined(HAVE_DQB_BTIMELIMIT))
+#define dqb_btimelimit dqb_btime
+#endif
+
+#if defined(linux) || defined(ultrix) || defined(USE_QUOTA_H)
+#ifndef NEED_QUOTACTL_WRAPPER
+#include <sys/quota.h>
+#else
+#include <asm/types.h>
+#include <asm/unistd.h>
+#include <linux/quota.h>
+#endif
+#endif
+
+#ifdef __svr4__
+#include <sys/fs/ufs_quota.h>
+#endif
+
+#ifdef BSD4_4
+#include <ufs/ufs/quota.h>
+#endif
+
+#ifdef USE_UFS_QUOTA_H
+#include <ufs/quota.h>
+#endif
+
+#ifdef _IBMR2
+#include <jfs/quota.h>
+#endif
+
+extern int getnfsquota __P((const struct vol *, const int, const u_int32_t,
+ struct dqblk *));
+
+extern int uquota_getvolspace __P((const struct vol *, VolSpace *, VolSpace *,
+ const u_int32_t));
+#endif /* NO_QUOTA_SUPPORT */
+
+extern int gmem __P((const gid_t));
+extern int setdeskmode __P((const mode_t));
+extern int setdirmode __P((const mode_t, const int));
+extern int setdeskowner __P((const uid_t, const gid_t));
+extern int setdirowner __P((const uid_t, const gid_t, const int));
+
+#endif /* UNIX_H */
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/time.h>
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <netatalk/endian.h>
+#include <atalk/asp.h>
+#include <atalk/dsi.h>
+#include <atalk/adouble.h>
+#include <atalk/afp.h>
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <utime.h>
+#include <errno.h>
+
+#include "directory.h"
+#include "file.h"
+#include "volume.h"
+#include "globals.h"
+#include "unix.h"
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef NO_LARGE_VOL_SUPPORT
+#if BYTE_ORDER == BIG_ENDIAN
+#define hton64(x) (x)
+#define ntoh64(x) (x)
+#else
+#define hton64(x) ((u_int64_t) (htonl(((x) >> 32) & 0xffffffffLL)) | \
+ (u_int64_t) ((htonl(x) & 0xffffffffLL) << 32))
+#define ntoh64(x) (hton64(x))
+#endif
+#endif
+
+static struct vol *volumes = NULL;
+static int lastvid = 0;
+#if AD_VERSION == AD_VERSION1
+static char *Trash = "\02\024Network Trash Folder";
+#endif
+static struct extmap *extmap = NULL, *defextmap = NULL;
+
+#define VOLOPT_ALLOW 0 /* user allow list */
+#define VOLOPT_DENY 1 /* user deny list */
+#define VOLOPT_RWLIST 2 /* user rw list */
+#define VOLOPT_ROLIST 3 /* user ro list */
+#define VOLOPT_CODEPAGE 4 /* codepage */
+#define VOLOPT_PASSWORD 5 /* volume password */
+#define VOLOPT_CASEFOLD 6 /* character case mangling */
+#define VOLOPT_FLAGS 7 /* various flags */
+#define VOLOPT_DBPATH 8 /* path to database */
+#define VOLOPT_MAPCHARS 9 /* does mtou and utom mappings. syntax:
+ m and u can be double-byte hex
+ strings if necessary.
+ m=u -> map both ways
+ m>u -> map m to u
+ m<u -> map u to m
+ !u -> make u illegal always
+ ~u -> make u illegal only as the first
+ part of a double-byte character.
+ */
+#define VOLOPT_MAX 9
+#define VOLOPT_NUM (VOLOPT_MAX + 1)
+
+#define VOLPASSLEN 8
+#define VOLOPT_DEFAULT ":DEFAULT:"
+#define VOLOPT_DEFAULT_LEN 9
+struct vol_option {
+ char *c_value;
+ int i_value;
+};
+
+static __inline__ void volfree(struct vol_option *options,
+ const struct vol_option *save)
+{
+ int i;
+
+ if (save) {
+ for (i = 0; i < VOLOPT_MAX; i++) {
+ if (options[i].c_value && (options[i].c_value != save[i].c_value))
+ free(options[i].c_value);
+ }
+ } else {
+ for (i = 0; i < VOLOPT_MAX; i++) {
+ if (options[i].c_value)
+ free(options[i].c_value);
+ }
+ }
+}
+
+
+/* handle variable substitutions. here's what we understand:
+ * $c -> client ip/appletalk address
+ * $f -> full name (whatever's in the gecos field)
+ * $g -> group
+ * $h -> hostname
+ * $s -> server name (hostname if it doesn't exist)
+ * $u -> username (guest is usually nobody)
+ * $v -> volume name (ADEID_NAME or basename)
+ * $z -> zone (may not exist)
+ * $$ -> $
+ */
+#define is_var(a, b) (strncmp((a), (b), 2) == 0)
+static void volxlate(AFPObj *obj, char *dest, int destlen,
+ char *src, struct passwd *pwd, char *path)
+{
+ char *p, *q;
+ int len;
+
+ strncpy(dest, src, destlen);
+ if ((p = strchr(src, '$')) == NULL) /* nothing to do */
+ return;
+
+ /* first part of the path. just forward to the next variable. */
+ len = MIN(p - src, destlen);
+ if (len > 0) {
+ destlen -= len;
+ dest += len;
+ }
+
+ while (p && destlen > 0) {
+ /* now figure out what the variable is */
+ q = NULL;
+ if (is_var(p, "$c")) {
+ if (obj->proto == AFPPROTO_ASP) {
+ ASP asp = obj->handle;
+
+ len = sprintf(dest, "%u.%u", ntohs(asp->asp_sat.sat_addr.s_net),
+ asp->asp_sat.sat_addr.s_node);
+ dest += len;
+ destlen -= len;
+
+ } else if (obj->proto == AFPPROTO_DSI) {
+ DSI *dsi = obj->handle;
+
+ len = sprintf(dest, "%s:%u", inet_ntoa(dsi->client.sin_addr),
+ ntohs(dsi->client.sin_port));
+ dest += len;
+ destlen -= len;
+ }
+ } else if (is_var(p, "$f")) {
+ if (q = strchr(pwd->pw_gecos, ','))
+ *q = '\0';
+ q = pwd->pw_gecos;
+ } else if (is_var(p, "$g")) {
+ struct group *grp = getgrgid(pwd->pw_gid);
+ if (grp)
+ q = grp->gr_name;
+ } else if (is_var(p, "$h")) {
+ q = obj->options.hostname;
+ } else if (is_var(p, "$s")) {
+ if (obj->Obj)
+ q = obj->Obj;
+ else if (obj->options.server) {
+ q = obj->options.server;
+ } else
+ q = obj->options.hostname;
+ } else if (is_var(p, "$u")) {
+ q = obj->username;
+ } else if (is_var(p, "$v")) {
+ if (path) {
+ struct adouble ad;
+
+ memset(&ad, 0, sizeof(ad));
+ if (ad_open(path, ADFLAGS_HF, O_RDONLY, 0, &ad) < 0)
+ goto no_volname;
+
+ if ((len = MIN(ad_getentrylen(&ad, ADEID_NAME), destlen)) > 0) {
+ memcpy(dest, ad_entry(&ad, ADEID_NAME), len);
+ ad_close(&ad, ADFLAGS_HF);
+ dest += len;
+ destlen -= len;
+ } else {
+ ad_close(&ad, ADFLAGS_HF);
+no_volname: /* simple basename */
+ if ((q = strrchr(path, '/')) == NULL)
+ q = path;
+ else if (*(q + 1) != '\0')
+ q++;
+ }
+ }
+ } else if (is_var(p, "$z")) {
+ q = obj->Zone;
+ } else if (is_var(p, "$$")) {
+ q = "$";
+ } else
+ q = p;
+
+ /* copy the stuff over. if we don't understand something that we
+ * should, just skip it over. */
+ if (q) {
+ len = MIN(p == q ? 2 : strlen(q), destlen);
+ strncpy(dest, q, len);
+ dest += len;
+ destlen -= len;
+ }
+
+ /* stuff up to next $ */
+ src = p + 2;
+ p = strchr(src, '$');
+ len = p ? MIN(p - src, destlen) : destlen;
+ if (len > 0) {
+ strncpy(dest, src, len);
+ dest += len;
+ destlen -= len;
+ }
+ }
+}
+
+/* to make sure that val is valid, make sure to select an opt that
+ includes val */
+#define optionok(buf,opt,val) (strstr((buf),(opt)) && ((val)[1] != '\0'))
+
+static __inline__ char *get_codepage_path(const char *path, const char *name)
+{
+ char *page;
+ int len;
+
+ if (path) {
+ page = (char *) malloc((len = strlen(path)) + strlen(name) + 2);
+ if (page) {
+ strcpy(page, path);
+ if (path[len - 1] != '/') /* add a / */
+ strcat(page, "/");
+ strcat(page, name);
+ }
+ } else {
+ page = strdup(name);
+ }
+
+ return page;
+}
+
+/* handle all the options. tmp can't be NULL. */
+static void volset(struct vol_option *options, char *volname, int vlen,
+ const char *nlspath, const char *tmp)
+{
+ char *val;
+
+ val = strchr(tmp, ':');
+ if (optionok(tmp, "allow:", val)) {
+ if (options[VOLOPT_ALLOW].c_value)
+ free(options[VOLOPT_ALLOW].c_value);
+ options[VOLOPT_ALLOW].c_value = strdup(val + 1);
+
+ } else if (optionok(tmp, "deny:", val)) {
+ if (options[VOLOPT_DENY].c_value)
+ free(options[VOLOPT_DENY].c_value);
+ options[VOLOPT_DENY].c_value = strdup(val + 1);
+
+ } else if (optionok(tmp, "rwlist:", val)) {
+ if (options[VOLOPT_RWLIST].c_value)
+ free(options[VOLOPT_RWLIST].c_value);
+ options[VOLOPT_RWLIST].c_value = strdup(val + 1);
+
+ } else if (optionok(tmp, "rolist:", val)) {
+ if (options[VOLOPT_ROLIST].c_value)
+ free(options[VOLOPT_ROLIST].c_value);
+ options[VOLOPT_ROLIST].c_value = strdup(val + 1);
+
+ } else if (optionok(tmp, "codepage:", val)) {
+ if (options[VOLOPT_CODEPAGE].c_value)
+ free(options[VOLOPT_CODEPAGE].c_value);
+ options[VOLOPT_CODEPAGE].c_value = get_codepage_path(nlspath, val + 1);
+
+ } else if (optionok(tmp, "casefold:", val)) {
+ if (strcasecmp(val + 1, "tolower") == 0)
+ options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMLOWER;
+ else if (strcasecmp(val + 1, "toupper") == 0)
+ options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMUPPER;
+ else if (strcasecmp(val + 1, "xlatelower") == 0)
+ options[VOLOPT_CASEFOLD].i_value = AFPVOL_UUPPERMLOWER;
+ else if (strcasecmp(val + 1, "xlateupper") == 0)
+ options[VOLOPT_CASEFOLD].i_value = AFPVOL_ULOWERMUPPER;
+
+ } else if (optionok(tmp, "options:", val)) {
+ char *p;
+
+ if ((p = strtok(val + 1, ",")) == NULL) /* nothing */
+ return;
+
+ while (p) {
+ if (strcasecmp(p, "prodos") == 0)
+ options[VOLOPT_FLAGS].i_value |= AFPVOL_A2VOL;
+ else if (strcasecmp(p, "mswindows") == 0) {
+ options[VOLOPT_FLAGS].i_value |= AFPVOL_MSWINDOWS;
+ if (!options[VOLOPT_CODEPAGE].c_value)
+ options[VOLOPT_CODEPAGE].c_value =
+ get_codepage_path(nlspath, MSWINDOWS_CODEPAGE);
+
+ } else if (strcasecmp(p, "crlf") == 0)
+ options[VOLOPT_FLAGS].i_value |= AFPVOL_CRLF;
+ else if (strcasecmp(p, "noadouble") == 0)
+ options[VOLOPT_FLAGS].i_value |= AFPVOL_NOADOUBLE;
+ else if (strcasecmp(p, "ro") == 0)
+ options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
+ else if (strcasecmp(p, "nohex") == 0)
+ options[VOLOPT_FLAGS].i_value |= AFPVOL_NOHEX;
+ else if (strcasecmp(p, "usedots") == 0)
+ options[VOLOPT_FLAGS].i_value |= AFPVOL_USEDOTS;
+ else if (strcasecmp(p, "limitsize") == 0)
+ options[VOLOPT_FLAGS].i_value |= AFPVOL_LIMITSIZE;
+
+ p = strtok(NULL, ",");
+ }
+
+#if AD_VERSION > AD_VERSION1
+ } else if (optionok(tmp, "dbpath:", val)) {
+ if (options[VOLOPT_DBPATH].c_value)
+ free(options[VOLOPT_DBPATH].c_value);
+
+ options[VOLOPT_DBPATH].c_value = strdup(val + 1);
+#endif
+ } else if (optionok(tmp, "mapchars:",val)) {
+ if (options[VOLOPT_MAPCHARS].c_value)
+ free(options[VOLOPT_MAPCHARS].c_value);
+ options[VOLOPT_MAPCHARS].c_value = strdup(val + 1);
+
+ } else if (optionok(tmp, "password:", val)) {
+ if (options[VOLOPT_PASSWORD].c_value)
+ free(options[VOLOPT_PASSWORD].c_value);
+ options[VOLOPT_PASSWORD].c_value = strdup(val + 1);
+ } else if (val) {
+ /* ignore unknown options */
+ syslog(LOG_DEBUG, "ignoring unknown volume option: %s", tmp);
+
+ } else {
+ /* we'll assume it's a volume name. */
+ strncpy(volname, tmp, vlen);
+ }
+}
+
+static int creatvol(const char *path, char *name, struct vol_option *options)
+{
+ struct vol *volume;
+ int vlen;
+
+ if ( name == NULL || *name == '\0' ) {
+ if ((name = strrchr( path, '/' )) == NULL) {
+ return -1; /* Obviously not a fully qualified path */
+ }
+
+ /* if you wish to share /, you need to specify a name. */
+ if (*++name == '\0')
+ return -1;
+ }
+
+ for ( volume = volumes; volume; volume = volume->v_next ) {
+ if ( strcasecmp( volume->v_name, name ) == 0 ) {
+ return -1; /* Won't be able to access it, anyway... */
+ }
+ }
+
+ vlen = strlen( name );
+ if ( vlen > AFPVOL_NAMELEN ) {
+ vlen = AFPVOL_NAMELEN;
+ name[AFPVOL_NAMELEN] = '\0';
+ }
+
+ if (( volume =
+ (struct vol *)calloc(1, sizeof( struct vol ))) == NULL ) {
+ syslog( LOG_ERR, "creatvol: malloc: %m" );
+ return -1;
+ }
+ if (( volume->v_name =
+ (char *)malloc( vlen + 1 )) == NULL ) {
+ syslog( LOG_ERR, "creatvol: malloc: %m" );
+ free(volume);
+ return -1;
+ }
+ if (( volume->v_path =
+ (char *)malloc( strlen( path ) + 1 )) == NULL ) {
+ syslog( LOG_ERR, "creatvol: malloc: %m" );
+ free(volume->v_name);
+ free(volume);
+ return -1;
+ }
+
+ strcpy( volume->v_name, name);
+ strcpy( volume->v_path, path );
+
+#ifdef __svr4__
+ volume->v_qfd = -1;
+#endif
+ volume->v_vid = lastvid++;
+ volume->v_lastdid = 3;
+
+ /* handle options */
+ if (options) {
+ /* should we casefold? */
+ volume->v_casefold = options[VOLOPT_CASEFOLD].i_value;
+
+ /* shift in some flags */
+ volume->v_flags = options[VOLOPT_FLAGS].i_value;
+
+ /* read in the code pages */
+ if (options[VOLOPT_CODEPAGE].c_value)
+ codepage_read(volume, options[VOLOPT_CODEPAGE].c_value);
+
+ if (options[VOLOPT_PASSWORD].c_value)
+ volume->v_password = strdup(options[VOLOPT_PASSWORD].c_value);
+
+#if AD_VERSION > AD_VERSION1
+ if (options[VOLOPT_DBPATH].c_value)
+ volume->v_dbpath = strdup(options[VOLOPT_DBPATH].c_value);
+#endif
+ }
+
+ volume->v_next = volumes;
+ volumes = volume;
+ return 0;
+}
+
+static char *myfgets( buf, size, fp )
+ char *buf;
+ int size;
+ FILE *fp;
+{
+ char *p;
+ int c;
+
+ p = buf;
+ while ((( c = getc( fp )) != EOF ) && ( size > 0 )) {
+ if ( c == '\n' || c == '\r' ) {
+ *p++ = '\n';
+ break;
+ } else {
+ *p++ = c;
+ }
+ size--;
+ }
+
+ if ( p == buf ) {
+ return( NULL );
+ } else {
+ *p = '\0';
+ return( buf );
+ }
+}
+
+
+/* check access list. this function wants something of the following
+ * form:
+ * @group,name,name2,@group2,name3
+ *
+ * a NULL argument allows everybody to have access.
+ * we return three things:
+ * -1: no list
+ * 0: list exists, but name isn't in it
+ * 1: in list
+ */
+static int accessvol(args, name)
+ const char *args;
+ const char *name;
+{
+ char buf[MAXPATHLEN + 1], *p;
+ struct group *gr;
+
+ if (!args)
+ return -1;
+
+ strncpy(buf, args, sizeof(buf));
+ if ((p = strtok(buf, ",")) == NULL) /* nothing, return okay */
+ return -1;
+
+ while (p) {
+ if (*p == '@') { /* it's a group */
+ if ((gr = getgrnam(p + 1)) && gmem(gr->gr_gid))
+ return 1;
+ } else if (strcmp(p, name) == 0) /* it's a user name */
+ return 1;
+ p = strtok(NULL, ",");
+ }
+
+ return 0;
+}
+
+static void setextmap( ext, type, creator, user)
+ char *ext, *type, *creator;
+ int user;
+{
+ struct extmap *em;
+
+ for ( em = extmap; em; em = em->em_next ) {
+ if ( strdiacasecmp( em->em_ext, ext ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( em == NULL ) {
+ if (( em =
+ (struct extmap *)malloc( sizeof( struct extmap ))) == NULL ) {
+ syslog( LOG_ERR, "setextmap: malloc: %m" );
+ return;
+ }
+ em->em_next = extmap;
+ extmap = em;
+ } else if ( !user ) {
+ return;
+ }
+
+ strcpy( em->em_ext, ext );
+
+ if ( *type == '\0' ) {
+ memcpy(em->em_type, "????", sizeof( em->em_type ));
+ } else {
+ memcpy(em->em_type, type, sizeof( em->em_type ));
+ }
+ if ( *creator == '\0' ) {
+ memcpy(em->em_creator, "UNIX", sizeof( em->em_creator ));
+ } else {
+ memcpy(em->em_creator, creator, sizeof( em->em_creator ));
+ }
+
+ if ( strcmp( ext, "." ) == 0 ) {
+ defextmap = em;
+ }
+}
+
+/*
+ * Read a volume configuration file and add the volumes contained within to
+ * the global volume list. If p2 is non-NULL, the file that is opened is
+ * p1/p2
+ *
+ * Lines that begin with # and blank lines are ignored.
+ * Volume lines are of the form:
+ * <unix path> [<volume name>] [allow:<user>,<@group>,...] \
+ * [codepage:<file>] [casefold:<num>]
+ * <extension> TYPE [CREATOR]
+ */
+static int readvolfile(obj, p1, p2, user, pwent)
+ AFPObj *obj;
+ char *p1, *p2;
+ int user;
+ struct passwd *pwent;
+{
+ FILE *fp;
+ char path[ MAXPATHLEN + 1], tmp[ MAXPATHLEN + 1],
+ volname[ AFPVOL_NAMELEN + 1 ], buf[ BUFSIZ ],
+ type[ 5 ], creator[ 5 ];
+ char *u, *p;
+ struct passwd *pw;
+ struct vol_option options[VOLOPT_NUM], save_options[VOLOPT_NUM];
+ int i;
+
+ if (!p1)
+ return -1;
+
+ strcpy( path, p1 );
+ if ( p2 != NULL ) {
+ strcat( path, "/" );
+ strcat( path, p2 );
+ }
+
+ if (( fp = fopen( path, "r" )) == NULL ) {
+ return( -1 );
+ }
+
+ memset(save_options, 0, sizeof(save_options));
+ while ( myfgets( buf, sizeof( buf ), fp ) != NULL ) {
+ initline( strlen( buf ), buf );
+ parseline( sizeof( path ) - 1, path );
+ switch ( *path ) {
+ case '\0' :
+ case '#' :
+ continue;
+
+ case ':':
+ /* change the default options for this file */
+ if (strncmp(path, VOLOPT_DEFAULT, VOLOPT_DEFAULT_LEN) == 0) {
+ *tmp = '\0';
+ for (i = 0; i < VOLOPT_NUM; i++) {
+ if (parseline( sizeof( path ) - VOLOPT_DEFAULT_LEN - 1,
+ path + VOLOPT_DEFAULT_LEN) < 0)
+ break;
+ volset(save_options, tmp, sizeof(tmp) - 1,
+ obj->options.nlspath, path + VOLOPT_DEFAULT_LEN);
+ }
+ }
+ break;
+
+ case '~' :
+ if (( p = strchr( path, '/' )) != NULL ) {
+ *p++ = '\0';
+ }
+ u = path;
+ u++;
+ if ( *u == '\0' ) {
+ u = obj->username;
+ }
+ if ( u == NULL || *u == '\0' || ( pw = getpwnam( u )) == NULL ) {
+ continue;
+ }
+ strcpy( tmp, pw->pw_dir );
+ if ( p != NULL && *p != '\0' ) {
+ strcat( tmp, "/" );
+ strcat( tmp, p );
+ }
+ /* fall through */
+
+ case '/' :
+ /* send path through variable substitution */
+ if (*path != '~') /* need to copy path to tmp */
+ strcpy(tmp, path);
+ if (!pwent)
+ pwent = getpwnam(obj->username);
+ volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL);
+
+ /* this is sort of braindead. basically, i want to be
+ * able to specify things in any order, but i don't want to
+ * re-write everything.
+ *
+ * currently we have 11 options:
+ * volname
+ * codepage:x
+ * casefold:x
+ * allow:x,y,@z
+ * deny:x,y,@z
+ * rwlist:x,y,@z
+ * rolist:x,y,@z
+ * options:prodos,crlf,noadouble,ro
+ * dbpath:x
+ * password:x
+ * namemask:x,y,!z (not implemented yet)
+ */
+ memcpy(options, save_options, sizeof(options));
+ *volname = '\0';
+
+ /* read in up to 11 possible options */
+ for (i = 0; i < VOLOPT_NUM; i++) {
+ if (parseline( sizeof( tmp ) - 1, tmp ) < 0)
+ break;
+
+ volset(options, volname, sizeof(volname) - 1,
+ obj->options.nlspath, tmp);
+ }
+
+ /* check allow/deny lists:
+ allow -> either no list (-1), or in list (1)
+ deny -> either no list (-1), or not in list (0) */
+ if (accessvol(options[VOLOPT_ALLOW].c_value, obj->username) &&
+ (accessvol(options[VOLOPT_DENY].c_value, obj->username) < 1)) {
+
+ /* handle read-only behaviour. semantics:
+ * 1) neither the rolist nor the rwlist exist -> rw
+ * 2) rolist exists -> ro if user is in it.
+ * 3) rwlist exists -> ro unless user is in it. */
+ if (((options[VOLOPT_FLAGS].i_value & AFPVOL_RO) == 0) &&
+ ((accessvol(options[VOLOPT_ROLIST].c_value,
+ obj->username) == 1) ||
+ !accessvol(options[VOLOPT_RWLIST].c_value,
+ obj->username)))
+ options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
+
+ /* do variable substitution */
+ volxlate(obj, tmp, sizeof(tmp) - 1, volname, pwent, path);
+ creatvol(path, tmp, options);
+ }
+ volfree(options, save_options);
+ break;
+
+ case '.' :
+ parseline( sizeof( type ) - 1, type );
+ parseline( sizeof( creator ) - 1, creator );
+ setextmap( path, type, creator, user);
+ break;
+
+ default :
+ break;
+ }
+ }
+ volfree(save_options, NULL);
+ if ( fclose( fp ) != 0 ) {
+ syslog( LOG_ERR, "readvolfile: fclose: %m" );
+ }
+ return( 0 );
+}
+
+
+static void load_volumes(AFPObj *obj)
+{
+ struct passwd *pwent = getpwnam(obj->username);
+
+ if ( (obj->options.flags & OPTION_USERVOLFIRST) == 0 ) {
+ readvolfile(obj, obj->options.systemvol, 0, pwent);
+ }
+
+ if ((*obj->username == '\0') || (obj->options.flags & OPTION_NOUSERVOL)) {
+ readvolfile(obj, obj->options.defaultvol, NULL, 1, pwent);
+ } else if (pwent) {
+ /*
+ * Read user's AppleVolumes or .AppleVolumes file
+ * If neither are readable, read the default volumes file. if
+ * that doesn't work, create a user share.
+ */
+ if ( readvolfile(obj, pwent->pw_dir, "AppleVolumes", 1, pwent) < 0 &&
+ readvolfile(obj, pwent->pw_dir, ".AppleVolumes", 1, pwent) < 0 &&
+ readvolfile(obj, pwent->pw_dir, "applevolumes", 1, pwent) < 0 &&
+ readvolfile(obj, pwent->pw_dir, ".applevolumes", 1, pwent) < 0 &&
+ obj->options.defaultvol != NULL ) {
+ if (readvolfile(obj, obj->options.defaultvol, NULL, 1, pwent) < 0)
+ creatvol(pwent->pw_dir, NULL, NULL);
+ }
+ }
+ if ( obj->options.flags & OPTION_USERVOLFIRST ) {
+ readvolfile(obj, obj->options.systemvol, NULL, 0, pwent );
+ }
+}
+
+static int getvolspace( vol, bfree, btotal, xbfree, xbtotal, bsize )
+ struct vol *vol;
+ u_int32_t *bfree, *btotal, *bsize;
+ VolSpace *xbfree, *xbtotal;
+{
+ int spaceflag, rc;
+ u_int32_t maxsize;
+#ifndef NO_QUOTA_SUPPORT
+ VolSpace qfree, qtotal;
+#endif
+
+ spaceflag = AFPVOL_GVSMASK & vol->v_flags;
+ /* report up to 2GB if afp version is < 2.2 (4GB if not) */
+ maxsize = (vol->v_flags & AFPVOL_A2VOL) ? 0x01fffe00 :
+ (((afp_version < 22) || (vol->v_flags & AFPVOL_LIMITSIZE))
+ ? 0x7fffffffL : 0xffffffffL);
+
+#ifdef AFS
+ if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_AFSGVS ) {
+ if ( afs_getvolspace( vol, xbfree, xbtotal, bsize ) == AFP_OK ) {
+ vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_AFSGVS;
+ goto getvolspace_done;
+ }
+ }
+#endif AFS
+
+ if (( rc = ustatfs_getvolspace( vol, xbfree, xbtotal,
+ bsize)) != AFP_OK ) {
+ return( rc );
+ }
+
+#define min(a,b) ((a)<(b)?(a):(b))
+#ifndef NO_QUOTA_SUPPORT
+ if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_UQUOTA ) {
+ if ( uquota_getvolspace( vol, &qfree, &qtotal, *bsize ) == AFP_OK ) {
+ vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_UQUOTA;
+ *xbfree = min(*xbfree, qfree);
+ *xbtotal = min( *xbtotal, qtotal);
+ goto getvolspace_done;
+ }
+ }
+#endif
+ vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_USTATFS;
+
+getvolspace_done:
+ *bfree = min( *xbfree, maxsize);
+ *btotal = min( *xbtotal, maxsize);
+ return( AFP_OK );
+}
+
+static int getvolparams( bitmap, vol, st, buf, buflen )
+ u_int16_t bitmap;
+ struct vol *vol;
+ struct stat *st;
+ char *buf;
+ int *buflen;
+{
+ struct adouble ad;
+ int bit = 0, aint, isad = 1;
+ u_short ashort;
+ u_int32_t bfree, btotal, bsize;
+ VolSpace xbfree, xbtotal; /* extended bytes */
+ char *data, *nameoff = NULL;
+ char *slash;
+
+ /* courtesy of jallison@whistle.com:
+ * For MacOS8.x support we need to create the
+ * .Parent file here if it doesn't exist. */
+
+ memset(&ad, 0, sizeof(ad));
+ if ( ad_open( vol->v_path, vol_noadouble(vol) |
+ ADFLAGS_HF|ADFLAGS_DIR, O_RDWR | O_CREAT,
+ 0666, &ad) < 0 ) {
+ isad = 0;
+
+ } else if (ad_getoflags( &ad, ADFLAGS_HF ) & O_CREAT) {
+ slash = strrchr( vol->v_path, '/' );
+ if(slash)
+ slash++;
+ else
+ slash = vol->v_path;
+
+ ad_setentrylen( &ad, ADEID_NAME, strlen( slash ));
+ memcpy(ad_entry( &ad, ADEID_NAME ), slash,
+ ad_getentrylen( &ad, ADEID_NAME ));
+ ad_flush(&ad, ADFLAGS_HF);
+ }
+
+ if (( bitmap & ( (1<<VOLPBIT_BFREE)|(1<<VOLPBIT_BTOTAL) |
+ (1<<VOLPBIT_XBFREE)|(1<<VOLPBIT_XBTOTAL) |
+ (1<<VOLPBIT_BSIZE)) ) != 0 ) {
+ if ( getvolspace( vol, &bfree, &btotal, &xbfree, &xbtotal,
+ &bsize) < 0 ) {
+ if ( isad ) {
+ ad_close( &ad, ADFLAGS_HF );
+ }
+ return( AFPERR_PARAM );
+ }
+ }
+
+ data = buf;
+ while ( bitmap != 0 ) {
+ while (( bitmap & 1 ) == 0 ) {
+ bitmap = bitmap>>1;
+ bit++;
+ }
+
+ switch ( bit ) {
+ case VOLPBIT_ATTR :
+#if AD_VERSION > AD_VERSION1
+ ashort = VOLPBIT_ATTR_FILEID;
+#else
+ ashort = 0;
+#endif
+ /* check for read-only.
+ * NOTE: we don't actually set the read-only flag unless
+ * it's passed in that way as it's possible to mount
+ * a read-write filesystem under a read-only one. */
+ if ((vol->v_flags & AFPVOL_RO) ||
+ ((utime(vol->v_path, NULL) < 0) && (errno == EROFS)))
+ ashort |= VOLPBIT_ATTR_RO;
+ ashort = htons(ashort);
+ memcpy(data, &ashort, sizeof( ashort ));
+ data += sizeof( ashort );
+ break;
+
+ case VOLPBIT_SIG :
+ ashort = htons( AFPVOLSIG_DEFAULT );
+ memcpy(data, &ashort, sizeof( ashort ));
+ data += sizeof( ashort );
+ break;
+
+ case VOLPBIT_CDATE :
+ if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
+ aint = AD_DATE_FROM_UNIX(st->st_mtime);
+ memcpy(data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case VOLPBIT_MDATE :
+ if ( st->st_mtime > vol->v_time ) {
+ vol->v_time = st->st_mtime;
+ aint = AD_DATE_FROM_UNIX(st->st_mtime);
+ } else {
+ aint = AD_DATE_FROM_UNIX(vol->v_time);
+ }
+ memcpy(data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case VOLPBIT_BDATE :
+ if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
+ aint = AD_DATE_START;
+ memcpy(data, &aint, sizeof( aint ));
+ data += sizeof( aint );
+ break;
+
+ case VOLPBIT_VID :
+ memcpy(data, &vol->v_vid, sizeof( vol->v_vid ));
+ data += sizeof( vol->v_vid );
+ break;
+
+ case VOLPBIT_BFREE :
+ bfree = htonl( bfree );
+ memcpy(data, &bfree, sizeof( bfree ));
+ data += sizeof( bfree );
+ break;
+
+ case VOLPBIT_BTOTAL :
+ btotal = htonl( btotal );
+ memcpy(data, &btotal, sizeof( btotal ));
+ data += sizeof( btotal );
+ break;
+
+#ifndef NO_LARGE_VOL_SUPPORT
+ case VOLPBIT_XBFREE :
+ xbfree = hton64( xbfree );
+#if defined(__GNUC__) && defined(HAVE_GCC_MEMCPY_BUG)
+ bcopy(&xbfree, data, sizeof(xbfree));
+#else
+ memcpy(data, &xbfree, sizeof( xbfree ));
+#endif
+ data += sizeof( xbfree );
+ break;
+
+ case VOLPBIT_XBTOTAL :
+ xbtotal = hton64( xbtotal );
+#if defined(__GNUC__) && defined(HAVE_GCC_MEMCPY_BUG)
+ bcopy(&xbtotal, data, sizeof(xbtotal));
+#else
+ memcpy(data, &xbtotal, sizeof( xbtotal ));
+#endif
+ data += sizeof( xbfree );
+ break;
+#endif
+
+ case VOLPBIT_NAME :
+ nameoff = data;
+ data += sizeof( u_int16_t );
+ break;
+
+ case VOLPBIT_BSIZE: /* block size */
+ bsize = htonl(bsize);
+ memcpy(data, &bsize, sizeof(bsize));
+ data += sizeof(bsize);
+ break;
+
+ default :
+ if ( isad ) {
+ ad_close( &ad, ADFLAGS_HF );
+ }
+ return( AFPERR_BITMAP );
+ }
+ bitmap = bitmap>>1;
+ bit++;
+ }
+ if ( nameoff ) {
+ ashort = htons( data - buf );
+ memcpy(nameoff, &ashort, sizeof( ashort ));
+ aint = strlen( vol->v_name );
+ *data++ = aint;
+ memcpy(data, vol->v_name, aint );
+ data += aint;
+ }
+ if ( isad ) {
+ ad_close( &ad, ADFLAGS_HF );
+ }
+ *buflen = data - buf;
+ return( AFP_OK );
+}
+
+
+
+int afp_getsrvrparms(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct timeval tv;
+ struct stat st;
+ struct vol *volume;
+ char *data;
+ int vcnt, len;
+
+
+ if (!volumes)
+ load_volumes(obj);
+
+ data = rbuf + 5;
+ for ( vcnt = 0, volume = volumes; volume; volume = volume->v_next ) {
+ if ( stat( volume->v_path, &st ) < 0 ) {
+ syslog( LOG_INFO, "afp_getsrvrparms: stat %s: %m",
+ volume->v_path );
+ continue; /* can't access directory */
+ }
+ if (!S_ISDIR(st.st_mode)) {
+ continue; /* not a dir */
+ }
+
+ /* set password bit if there's a volume password */
+ *data = (volume->v_password) ? AFPSRVR_PASSWD : 0;
+
+ /* Apple 2 clients running ProDOS-8 expect one volume to have
+ bit 0 of this byte set. They will not recognize anything
+ on the server unless this is the case. I have not
+ completely worked this out, but it's related to booting
+ from the server. Support for that function is a ways
+ off.. <shirsch@ibm.net> */
+ *data++ |= (volume->v_flags & AFPVOL_A2VOL) ? AFPSRVR_CONFIGINFO : 0;
+ len = strlen( volume->v_name );
+ *data++ = len;
+ memcpy(data, volume->v_name, len );
+ data += len;
+ vcnt++;
+ }
+
+ *rbuflen = data - rbuf;
+ data = rbuf;
+ if ( gettimeofday( &tv, 0 ) < 0 ) {
+ syslog( LOG_ERR, "afp_getsrvrparms: gettimeofday: %m" );
+ *rbuflen = 0;
+ return AFPERR_PARAM;
+ }
+ tv.tv_sec = AD_DATE_FROM_UNIX(tv.tv_sec);
+ memcpy(data, &tv.tv_sec, sizeof( u_int32_t));
+ data += sizeof( u_int32_t);
+ *data = vcnt;
+ return( AFP_OK );
+}
+
+int afp_openvol(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct stat st;
+ char *volname;
+#if AD_VERSION == AD_VERSION1
+ char *p;
+#endif
+ struct vol *volume;
+ struct dir *dir;
+ int len, ret, buflen;
+ u_int16_t bitmap;
+
+ ibuf += 2;
+ memcpy(&bitmap, ibuf, sizeof( bitmap ));
+ bitmap = ntohs( bitmap );
+ ibuf += sizeof( bitmap );
+ if (( bitmap & (1<<VOLPBIT_VID)) == 0 ) {
+ ret = AFPERR_BITMAP;
+ goto openvol_err;
+ }
+
+ len = (unsigned char)*ibuf++;
+ volname = obj->oldtmp;
+ memcpy(volname, ibuf, len );
+ *(volname + len) = '\0';
+ ibuf += len;
+ if ((len + 1) & 1) /* pad to an even boundary */
+ ibuf++;
+
+ if (!volumes)
+ load_volumes(obj);
+
+ for ( volume = volumes; volume; volume = volume->v_next ) {
+ if ( strcasecmp( volname, volume->v_name ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( volume == NULL ) {
+ ret = AFPERR_PARAM;
+ goto openvol_err;
+ }
+
+ /* check for a volume password */
+ if (volume->v_password &&
+ strncmp(ibuf, volume->v_password, VOLPASSLEN)) {
+ ret = AFPERR_ACCESS;
+ goto openvol_err;
+ }
+
+ if (( volume->v_flags & AFPVOL_OPEN ) == 0 ) {
+ if ((dir = dirnew(strlen(volume->v_name) + 1)) == NULL) {
+ syslog( LOG_ERR, "afp_openvol: malloc: %m" );
+ ret = AFPERR_MISC;
+ goto openvol_err;
+ }
+ dir->d_did = DIRDID_ROOT;
+ dir->d_color = DIRTREE_COLOR_BLACK; /* root node is black */
+ strcpy( dir->d_name, volume->v_name );
+ volume->v_dir = volume->v_root = dir;
+ volume->v_flags |= AFPVOL_OPEN;
+ }
+
+ if ( stat( volume->v_path, &st ) < 0 ) {
+ ret = AFPERR_PARAM;
+ goto openvol_err;
+ }
+
+ buflen = *rbuflen - sizeof( bitmap );
+ if (( ret = getvolparams( bitmap, volume, &st,
+ rbuf + sizeof(bitmap), &buflen )) != AFP_OK ) {
+ goto openvol_err;
+ }
+ *rbuflen = buflen + sizeof( bitmap );
+ bitmap = htons( bitmap );
+ memcpy(rbuf, &bitmap, sizeof( bitmap ));
+
+ curdir = volume->v_dir;
+ if ( chdir( volume->v_path ) < 0 ) {
+ ret = AFPERR_PARAM;
+ goto openvol_err;
+ }
+#if AD_VERSION == AD_VERSION1
+ /*
+ * If you mount a volume twice, the second time the trash appears on
+ * the desk-top. That's because the Mac remembers the DID for the
+ * trash (even for volumes in different zones, on different servers).
+ * Just so this works better, we prime the DID cache with the trash,
+ * fixing the trash at DID 3.
+ */
+ p = Trash;
+ cname( volume, volume->v_dir, &p );
+#endif
+
+ return( AFP_OK );
+
+openvol_err:
+ *rbuflen = 0;
+ return ret;
+}
+
+int afp_closevol(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct vol *vol, *ovol;
+ u_int16_t vid;
+
+ *rbuflen = 0;
+ ibuf += 2;
+ memcpy(&vid, ibuf, sizeof( vid ));
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ vol->v_flags &= ~AFPVOL_OPEN;
+ for ( ovol = volumes; ovol; ovol = ovol->v_next ) {
+ if ( ovol->v_flags & AFPVOL_OPEN ) {
+ break;
+ }
+ }
+ if ( ovol != NULL ) {
+ curdir = ovol->v_dir;
+ if ( chdir( ovol->v_path ) < 0 ) {
+ return( AFPERR_PARAM );
+ }
+ }
+
+ dirfree( vol->v_root );
+ vol->v_dir = NULL;
+#if AD_VERSION > AD_VERSION1
+ cnid_close(vol->v_db);
+ vol->v_db = NULL;
+#endif
+ return( AFP_OK );
+}
+
+struct vol *getvolbyvid(const u_int16_t vid )
+{
+ struct vol *vol;
+
+ for ( vol = volumes; vol; vol = vol->v_next ) {
+ if ( vid == vol->v_vid ) {
+ break;
+ }
+ }
+ if ( vol == NULL || ( vol->v_flags & AFPVOL_OPEN ) == 0 ) {
+ return( NULL );
+ }
+
+ return( vol );
+}
+
+struct extmap *getextmap(const char *path)
+{
+ char *p;
+ struct extmap *em;
+
+ if (( p = strrchr( path, '.' )) == NULL ) {
+ return( defextmap );
+ }
+
+ for ( em = extmap; em; em = em->em_next ) {
+ if ( strdiacasecmp( em->em_ext, p ) == 0 ) {
+ break;
+ }
+ }
+ if ( em == NULL ) {
+ return( defextmap );
+ } else {
+ return( em );
+ }
+}
+
+void setvoltime(obj, vol )
+ AFPObj *obj;
+ struct vol *vol;
+{
+ struct timeval tv;
+
+ /* fail w/out dying */
+ if ( gettimeofday( &tv, 0 ) < 0 ) {
+ syslog( LOG_ERR, "setvoltime: gettimeofday: %m" );
+ return;
+ }
+
+ /* a little granularity */
+ if (vol->v_time < tv.tv_sec) {
+ vol->v_time = tv.tv_sec;
+ obj->attention(obj->handle, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED);
+ }
+}
+
+int afp_getvolparams(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct stat st;
+ struct vol *vol;
+ int buflen, ret;
+ u_int16_t vid, bitmap;
+
+ ibuf += 2;
+ memcpy(&vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ memcpy(&bitmap, ibuf, sizeof( bitmap ));
+ bitmap = ntohs( bitmap );
+
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ *rbuflen = 0;
+ return( AFPERR_PARAM );
+ }
+
+ if ( stat( vol->v_path, &st ) < 0 ) {
+ *rbuflen = 0;
+ return( AFPERR_PARAM );
+ }
+
+ buflen = *rbuflen - sizeof( bitmap );
+ if (( ret = getvolparams( bitmap, vol, &st,
+ rbuf + sizeof( bitmap ), &buflen )) != AFP_OK ) {
+ *rbuflen = 0;
+ return( ret );
+ }
+ *rbuflen = buflen + sizeof( bitmap );
+ bitmap = htons( bitmap );
+ memcpy(rbuf, &bitmap, sizeof( bitmap ));
+ return( AFP_OK );
+}
+
+int afp_setvolparams(obj, ibuf, ibuflen, rbuf, rbuflen )
+ AFPObj *obj;
+ char *ibuf, *rbuf;
+ int ibuflen, *rbuflen;
+{
+ struct adouble ad;
+ struct vol *vol;
+ u_int16_t vid, bitmap;
+ u_int32_t aint;
+
+ ibuf += 2;
+ *rbuflen = 0;
+
+ memcpy(&vid, ibuf, sizeof( vid ));
+ ibuf += sizeof( vid );
+ memcpy(&bitmap, ibuf, sizeof( bitmap ));
+ bitmap = ntohs( bitmap );
+ ibuf += sizeof(bitmap);
+
+ if (( vol = getvolbyvid( vid )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+
+ if (vol->v_flags & AFPVOL_RO)
+ return AFPERR_VLOCK;
+
+ /* we can only set the backup date. */
+ if (bitmap != VOLPBIT_BDATE)
+ return AFPERR_BITMAP;
+
+ memset(&ad, 0, sizeof(ad));
+ if ( ad_open( vol->v_path, ADFLAGS_HF|ADFLAGS_DIR, O_RDWR,
+ 0666, &ad) < 0 ) {
+ if (errno == EROFS)
+ return AFPERR_VLOCK;
+
+ return AFPERR_ACCESS;
+ }
+
+ memcpy(&aint, ibuf, sizeof(aint));
+ ad_setdate(&ad, AD_DATE_BACKUP, aint);
+ ad_flush(&ad, ADFLAGS_HF);
+ ad_close(&ad, ADFLAGS_HF);
+ return( AFP_OK );
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef AFPD_VOLUME_H
+#define AFPD_VOLUME_H 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <netatalk/endian.h>
+#include "globals.h"
+
+#define AFPVOL_NAMELEN 27
+
+struct codepage_hash {
+ unsigned char *from, *to;
+ struct codepage_hash *next, *prev;
+};
+
+union codepage_val {
+ struct codepage_hash hash; /* hash for multibyte values */
+ unsigned char value; /* single byte value/rule */
+};
+
+struct codepage {
+ union codepage_val *map;
+ int quantum;
+};
+
+#define CP_HASH(a) (*(a))
+
+struct vol {
+ struct vol *v_next;
+ char *v_name;
+ char *v_path;
+ struct dir *v_dir, *v_root;
+ int v_flags;
+#ifdef __svr4__
+ int v_qfd;
+#endif /*__svr4__*/
+ void *v_gvs;
+ u_int32_t v_time;
+ int v_lastdid;
+ u_int16_t v_vid;
+ void *v_nfsclient;
+ int v_nfs, v_casefold;
+ struct codepage *v_mtoupage, *v_utompage, *v_badumap;
+ char *v_password;
+#if AD_VERSION > AD_VERSION1
+ void *v_db;
+ char *v_dbpath;
+#endif
+};
+
+#ifdef NO_LARGE_VOL_SUPPORT
+typedef u_int32_t VolSpace;
+#else
+typedef u_int64_t VolSpace;
+#endif
+
+#define AFPVOL_OPEN (1<<0)
+#define AFPVOL_DT (1<<1)
+
+#define AFPVOL_GVSMASK (7<<2)
+#define AFPVOL_NONE (0<<2)
+#define AFPVOL_AFSGVS (1<<2)
+#define AFPVOL_USTATFS (2<<2)
+#define AFPVOL_UQUOTA (4<<2)
+
+/* flags that alter volume behaviour. */
+#define AFPVOL_A2VOL (1 << 5) /* prodos volume */
+#define AFPVOL_CRLF (1 << 6) /* cr/lf translation */
+#define AFPVOL_NOADOUBLE (1 << 7) /* don't create .AppleDouble by default */
+#define AFPVOL_RO (1 << 8) /* read-only volume */
+#define AFPVOL_MSWINDOWS (1 << 9) /* deal with ms-windows yuckiness.
+ this is going away. */
+#define AFPVOL_NOHEX (1 << 10) /* don't do :hex translation */
+#define AFPVOL_USEDOTS (1 << 11) /* use real dots */
+#define AFPVOL_LIMITSIZE (1 << 12) /* limit size for older macs */
+#define AFPVOL_MAPASCII (1 << 13) /* map the ascii range as well */
+
+/* FPGetSrvrParms options */
+#define AFPSRVR_CONFIGINFO (1 << 0)
+#define AFPSRVR_PASSWD (1 << 7)
+
+/* handle casefolding */
+#define AFPVOL_MTOUUPPER (1 << 0)
+#define AFPVOL_MTOULOWER (1 << 1)
+#define AFPVOL_UTOMUPPER (1 << 2)
+#define AFPVOL_UTOMLOWER (1 << 3)
+#define AFPVOL_UMLOWER (AFPVOL_MTOULOWER | AFPVOL_UTOMLOWER)
+#define AFPVOL_UMUPPER (AFPVOL_MTOUUPPER | AFPVOL_UTOMUPPER)
+#define AFPVOL_UUPPERMLOWER (AFPVOL_MTOUUPPER | AFPVOL_UTOMLOWER)
+#define AFPVOL_ULOWERMUPPER (AFPVOL_MTOULOWER | AFPVOL_UTOMUPPER)
+
+#define MSWINDOWS_BADCHARS "\\/<>*?|\""
+#define MSWINDOWS_CODEPAGE "maccode.iso8859-1"
+
+#define AFPVOLSIG_FLAT 0x0001 /* flat fs */
+#define AFPVOLSIG_FIX 0x0002 /* fixed ids */
+#define AFPVOLSIG_VAR 0x0003 /* variable ids */
+#define AFPVOLSIG_DEFAULT AFPVOLSIG_FIX
+
+/* volume attributes */
+#define VOLPBIT_ATTR_RO (1 << 0)
+#define VOLPBIT_ATTR_PASSWD (1 << 1)
+#define VOLPBIT_ATTR_FILEID (1 << 2)
+#define VOLPBIT_ATTR_CATSEARCH (1 << 3)
+#define VOLPBIT_ATTR_BLANKACCESS (1 << 4)
+
+#define VOLPBIT_ATTR 0
+#define VOLPBIT_SIG 1
+#define VOLPBIT_CDATE 2
+#define VOLPBIT_MDATE 3
+#define VOLPBIT_BDATE 4
+#define VOLPBIT_VID 5
+#define VOLPBIT_BFREE 6
+#define VOLPBIT_BTOTAL 7
+#define VOLPBIT_NAME 8
+/* handle > 4GB volumes */
+#define VOLPBIT_XBFREE 9
+#define VOLPBIT_XBTOTAL 10
+#define VOLPBIT_BSIZE 11 /* block size */
+
+
+#define vol_noadouble(vol) (((vol)->v_flags & AFPVOL_NOADOUBLE) ? \
+ ADFLAGS_NOADOUBLE : 0)
+
+extern struct vol *getvolbyvid __P((const u_int16_t));
+extern int ustatfs_getvolspace __P((const struct vol *,
+ VolSpace *, VolSpace *,
+ u_int32_t *));
+extern int codepage_init __P((struct vol *, const int,
+ const int));
+extern int codepage_read __P((struct vol *, const char *));
+extern union codepage_val codepage_find __P(());
+extern void setvoltime __P((AFPObj *, struct vol *));
+
+/* FP functions */
+extern int afp_openvol __P((AFPObj *, char *, int, char *, int *));
+extern int afp_getvolparams __P((AFPObj *, char *, int, char *, int *));
+extern int afp_setvolparams __P((AFPObj *, char *, int, char *, int *));
+extern int afp_getsrvrparms __P((AFPObj *, char *, int, char *, int *));
+extern int afp_closevol __P((AFPObj *, char *, int, char *, int *));
+#endif
--- /dev/null
+SRC = main.c config.c zip.c nbp.c aep.c rtmp.c route.c multicast.c
+OBJ = main.o config.o zip.o nbp.o aep.o rtmp.o route.o multicast.o
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH} -DPHASE1NET
+LIBS= -latalk ${ADDLIBS}
+TAGSFILE= tags
+CC= cc
+INSTALL= install
+LIBDIRS= -L../../libatalk
+
+all : atalkd
+
+atalkd : ${OBJ} ../../libatalk/libatalk.a
+ ${CC} ${CFLAGS} -o atalkd ${OBJ} ${LIBDIRS} ${LIBS}
+
+main.o : main.c
+ ${CC} ${CFLAGS} -DVERSION=\"`cat ../../VERSION`\" \
+ ${CPPFLAGS} -c main.c
+
+config.o : config.c
+ ${CC} ${CFLAGS} -D_PATH_ATALKDCONF=\"${ETCDIR}/atalkd.conf\" \
+ ${CPPFLAGS} -c config.c
+
+install : all
+ ${INSTALL} -c atalkd ${SBINDIR}
+
+clean :
+ rm -f a.out core* *.o *.bak *[Ee]rrs tags
+ rm -f atalkd
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netatalk/at.h>
+#include <atalk/aep.h>
+#include <atalk/ddp.h>
+
+#include "atserv.h"
+
+aep_packet( ap, from, data, len )
+ struct atport *ap;
+ struct sockaddr_at *from;
+ char *data;
+ int len;
+{
+ char *end;
+
+ end = data + len;
+ if ( data + 2 > end || *data != DDPTYPE_AEP ||
+ *( data + 1 ) != AEPOP_REQUEST ) {
+ syslog( LOG_INFO, "aep_packet malformed packet" );
+ return 1;
+ }
+
+ *( data + 1 ) = AEPOP_REPLY;
+ if ( sendto( ap->ap_fd, data, len, 0, (struct sockaddr *)from,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "aep sendto: %m" );
+ return 1;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1992 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+struct atserv {
+ char *as_name;
+ u_char as_port; /* Used as a fall back */
+ int (*as_packet)();
+};
+
+struct atport {
+ int ap_fd;
+ struct atport *ap_next;
+ struct interface *ap_iface;
+ u_char ap_port;
+ int (*ap_packet)();
+};
+
+extern struct atserv atserv[];
+extern int atservNATSERV;
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <syslog.h>
+#include <sys/param.h>
+#include <net/if.h>
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <atalk/paths.h>
+#include <atalk/util.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef __svr4__
+#include <sys/sockio.h>
+#include <sys/stropts.h>
+#endif __svr4__
+
+#include "interface.h"
+#include "multicast.h"
+#include "rtmp.h"
+#include "zip.h"
+#include "list.h"
+
+#ifndef IFF_SLAVE /* a little backward compatibility */
+#define IFF_SLAVE 0
+#endif
+
+int router(), dontroute(), seed(), phase(), net(), addr(), zone();
+
+static struct param {
+ char *p_name;
+ int (*p_func)();
+} params[] = {
+ { "router", router },
+ { "dontroute", dontroute },
+ { "seed", seed },
+ { "phase", phase },
+ { "net", net },
+ { "addr", addr },
+ { "zone", zone },
+};
+
+static char **
+parseline( line )
+ char *line;
+{
+ char *p;
+ int argc = 0;
+ static char *argv[ 128 ];
+ static char buf[ 1024 ];
+
+ if ( *line == '#' ) {
+ return( NULL );
+ }
+ argc = 0;
+
+ memset(argv, 0, sizeof(argv));
+ strcpy( buf, line );
+ p = buf;
+
+ /*
+ * This parser should be made more powerful -- it should
+ * handle various escapes, e.g. \" and \031.
+ */
+ while ( *p != '\0' ) {
+ while ( *p != '\0' && *p != '"' && isspace( *p )) {
+ p++;
+ }
+ if ( *p == '\0' ) {
+ argv[ argc ] = 0;
+ break;
+ }
+ if ( *p == '"' ) {
+ argv[ argc ] = ++p;
+ while ( *p != '\0' && *p != '"' ) {
+ p++;
+ }
+ } else {
+ argv[ argc ] = p;
+ while ( *p != '\0' && !isspace( *p )) {
+ p++;
+ }
+ }
+ *p++ = '\0';
+ argc++;
+ }
+ if ( argv[ 0 ] == '\0' || *argv[ 0 ] == '#' ) {
+ return( NULL );
+ }
+ return( argv );
+}
+
+writeconf( cf )
+ char *cf;
+{
+ struct stat st;
+ char *path, *p, newpath[ MAXPATHLEN ], line[ 1024 ];
+ char **argv;
+ FILE *conf, *newconf;
+ struct interface *iface;
+ struct list *l;
+ int mode = 0644, fd;
+
+ if ( cf == NULL ) {
+ path = _PATH_ATALKDCONF;
+ } else {
+ path = cf;
+ }
+
+ /* check if old conf is writable */
+ if ( stat( path, &st ) == 0 ) {
+ if (( st.st_mode & S_IWUSR ) == 0 ) {
+ syslog( LOG_INFO, "%s not writable, won't rewrite", path );
+ return( -1 );
+ }
+ mode = st.st_mode;
+ }
+
+ if (( p = strrchr( path, '/' )) == NULL ) {
+ strcpy( newpath, _PATH_ATALKDTMP );
+ } else {
+ sprintf( newpath, "%.*s/%s", p - path, path, _PATH_ATALKDTMP );
+ }
+ if (( fd = open( newpath, O_WRONLY|O_CREAT|O_TRUNC, mode )) < 0 ) {
+ syslog( LOG_ERR, "%s: %m", newpath );
+ return( -1 );
+ }
+ if (( newconf = fdopen( fd, "w" )) == NULL ) {
+ syslog( LOG_ERR, "fdreopen %s: %m", newpath );
+ return( -1 );
+ }
+
+ if (( conf = fopen( path, "r" )) == NULL && cf ) {
+ syslog( LOG_ERR, "%s: %m", path );
+ return( -1 );
+ }
+
+ iface = interfaces->i_next;
+
+ while ( conf == NULL || fgets( line, sizeof( line ), conf ) != NULL ) {
+ if ( conf != NULL && ( argv = parseline( line )) == NULL ) {
+ if ( fputs( line, newconf ) == EOF ) {
+ syslog( LOG_ERR, "fputs: %m" );
+ return( -1 );
+ }
+ continue;
+ }
+
+ /* write real lines */
+ if ( iface ) {
+ fprintf( newconf, "%s", iface->i_name );
+ if ( iface->i_flags & IFACE_RSEED ) {
+ fprintf( newconf, " -router" );
+ } else if ( iface->i_flags & IFACE_SEED ) {
+ fprintf( newconf, " -seed" );
+ }
+ if ( iface->i_flags & IFACE_DONTROUTE) {
+ fprintf( newconf, " -dontroute");
+ }
+
+ fprintf( newconf, " -phase %d",
+ ( iface->i_flags & IFACE_PHASE1 ) ? 1 : 2 );
+ fprintf( newconf, " -net %d", ntohs( iface->i_rt->rt_firstnet ));
+ if ( iface->i_rt->rt_lastnet != iface->i_rt->rt_firstnet ) {
+ fprintf( newconf, "-%d", ntohs( iface->i_rt->rt_lastnet ));
+ }
+ fprintf( newconf, " -addr %u.%u",
+ ntohs( iface->i_addr.sat_addr.s_net ),
+ iface->i_addr.sat_addr.s_node );
+ for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
+ fprintf( newconf, " -zone \"%.*s\"",
+ ((struct ziptab *)l->l_data)->zt_len,
+ ((struct ziptab *)l->l_data)->zt_name );
+ }
+ fprintf( newconf, "\n" );
+
+ iface = iface->i_next;
+ if ( conf == NULL && iface == NULL ) {
+ break;
+ }
+ }
+ }
+ if ( conf != NULL ) {
+ fclose( conf );
+ }
+ fclose( newconf );
+
+ if ( rename( newpath, path ) < 0 ) {
+ syslog( LOG_ERR, "rename %s to %s: %m", newpath, path );
+ return( -1 );
+ }
+ return( 0 );
+}
+
+/*
+ * Read our config file. If it's not there, return -1. If it is there,
+ * but has syntax errors, exit. Format of the file is as follows:
+ *
+ * interface [ -seed ] [ -phase number ] [ -net net-range ]
+ * [ -addr net.node ] [ -zone zonename ]...
+ * e.g.
+ * le0 -phase 1 -net 7938 -zone Argus
+ * or
+ * le0 -phase 2 -net 8043-8044 -zone Argus -zone "Research Systems"
+ * le0 -phase 1 -net 7938 -zone Argus
+ *
+ * Pretty much everything is optional. Anything that is unspecified is
+ * searched for on the network. If -seed is not specified, the
+ * configuration is assumed to be soft, i.e. it can be overridden by
+ * another router. If -seed is specified, atalkd will exit if another
+ * router disagrees. If the phase is unspecified, it defaults to phase
+ * 2 (the default can be overridden on the command line). -addr can
+ * replace -net, if the network in question isn't a range. The default
+ * zone for an interface is the first zone encountered for that
+ * interface.
+ */
+readconf( cf )
+ char *cf;
+{
+ struct ifreq ifr;
+ struct interface *iface, *niface;
+ char line[ 1024 ], **argv, *p;
+ int i, j, s, cc;
+ FILE *conf;
+
+ if ( cf == NULL ) {
+ p = _PATH_ATALKDCONF;
+ } else {
+ p = cf;
+ }
+ if (( conf = fopen( p, "r" )) == NULL ) {
+ return( -1 );
+ }
+
+#ifndef __svr4__
+ if (( s = socket( AF_APPLETALK, SOCK_DGRAM, 0 )) < 0 ) {
+ perror( "socket" );
+ fclose(conf);
+ return -1;
+ }
+#endif __svr4__
+
+ while ( fgets( line, sizeof( line ), conf ) != NULL ) {
+ if (( argv = parseline( line )) == NULL ) {
+ continue;
+ }
+
+#ifndef __svr4__
+ /*
+ * Check that av[ 0 ] is a valid interface.
+ * Not possible under sysV.
+ */
+ strcpy( ifr.ifr_name, argv[ 0 ] );
+
+ /* for devices that don't support appletalk */
+ if ((ioctl(s, SIOCGIFADDR, &ifr) < 0) && (errno == ENODEV)) {
+ perror(argv[0]);
+ goto read_conf_err;
+ }
+
+ if ( ioctl( s, SIOCGIFFLAGS, &ifr ) < 0 ) {
+ perror( argv[ 0 ] );
+ goto read_conf_err;
+ }
+
+ if (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT |IFF_SLAVE)) {
+ fprintf( stderr, "%s: can't configure.\n", ifr.ifr_name );
+ goto read_conf_err;
+ }
+
+#ifdef IFF_MULTICAST
+ if ((ifr.ifr_flags & IFF_MULTICAST) == 0)
+ fprintf(stderr, "%s: multicast may not work properly.\n",
+ ifr.ifr_name);
+#endif
+
+ /* configure hw multicast for this interface. */
+ if (addmulti(ifr.ifr_name, NULL) < 0) {
+ perror(ifr.ifr_name);
+ fprintf(stderr, "Can't configure multicast.\n");
+ goto read_conf_err;
+ }
+#endif __svr4__
+
+ if (( niface = newiface( argv[ 0 ] )) == NULL ) {
+ perror( "newiface" );
+ goto read_conf_err;
+ }
+
+ for ( i = 1; argv[ i ]; i += cc ) {
+ if ( argv[ i ][ 0 ] == '-' ) {
+ argv[ i ]++;
+ }
+ for ( j = 0; j < sizeof( params ) / sizeof( params[ 0 ] ); j++ ) {
+ if ( strcmp( argv[ i ], params[ j ].p_name ) == 0 ) {
+ if ( params[ j ].p_func != NULL ) {
+ cc = (*params[ j ].p_func)( niface, &argv[ i + 1 ] );
+ if (cc < 0)
+ goto read_conf_err;
+ break;
+ }
+ }
+ }
+ if ( j >= sizeof( params ) / sizeof( params[ 0 ] )) {
+ fprintf( stderr, "%s: attribute not found.\n", argv[ i ] );
+ goto read_conf_err;
+ }
+ }
+
+ for ( iface = interfaces; iface; iface = iface->i_next ) {
+ if ( strcmp( niface->i_name, iface->i_name ) == 0 &&
+ ((( niface->i_flags & iface->i_flags &
+ ( IFACE_PHASE1|IFACE_PHASE2 )) != 0 ) ||
+ niface->i_flags == 0 || iface->i_flags == 0 )) {
+ break;
+ }
+ }
+ if ( iface ) { /* Already have this interface and phase */
+ fprintf( stderr, "%s already configured!\n", niface->i_name );
+ goto read_conf_err;
+ }
+
+ if ( interfaces == NULL ) {
+ interfaces = niface;
+ } else {
+ for ( iface = interfaces; iface->i_next; iface = iface->i_next )
+ ;
+ iface->i_next = niface;
+ }
+ niface->i_next = NULL;
+ }
+
+#ifndef __svr4__
+ close( s );
+#endif
+
+ fclose( conf );
+
+ /*
+ * Note: we've added lo0 to the interface list previously, so we must
+ * have configured more than one interface...
+ */
+ for ( iface = interfaces, cc = 0; iface; iface = iface->i_next, cc++ )
+ ;
+ if ( cc >= IFBASE ) {
+ return( 0 );
+ } else {
+ return( -1 );
+ }
+
+read_conf_err:
+#ifndef __svr4__
+ close(s);
+#endif
+ fclose(conf);
+ return -1;
+}
+
+/*ARGSUSED*/
+router( iface, av )
+ struct interface *iface;
+ char **av;
+{
+ /* make sure "-router" and "-dontroute" aren't both on the same line. */
+ if (iface->i_flags & IFACE_DONTROUTE) {
+ fprintf( stderr, "Can't specify both -router and -dontroute.\n");
+ return -1;
+ }
+
+ /*
+ * Check to be sure "-router" is before "-zone".
+ */
+ if ( iface->i_czt ) {
+ fprintf( stderr, "Must specify -router before -zone.\n");
+ return -1;
+ }
+
+ /* -router also implies -seed */
+ iface->i_flags |= IFACE_RSEED | IFACE_SEED | IFACE_ISROUTER;
+ return( 1 );
+}
+
+/*ARGSUSED*/
+dontroute( iface, av )
+ struct interface *iface;
+ char **av;
+{
+ /* make sure "-router" and "-dontroute" aren't both on the same line. */
+ if (iface->i_flags & IFACE_RSEED) {
+ fprintf( stderr, "Can't specify both -router and -dontroute.\n");
+ return -1;
+ }
+
+ iface->i_flags |= IFACE_DONTROUTE;
+ return( 1 );
+}
+
+/*ARGSUSED*/
+seed( iface, av )
+ struct interface *iface;
+ char **av;
+{
+ /*
+ * Check to be sure "-seed" is before "-zone". we keep the old
+ * semantics of just ignoring this in a routerless world.
+ */
+ if ( iface->i_czt ) {
+ fprintf( stderr, "Must specify -seed before -zone(%s).\n",
+ iface->i_czt->zt_name);
+ return -1;
+ }
+
+ iface->i_flags |= IFACE_SEED;
+ return( 1 );
+}
+
+phase( iface, av )
+ struct interface *iface;
+ char **av;
+{
+ int n;
+ char *pnum;
+
+ if (( pnum = av[ 0 ] ) == NULL ) {
+ fprintf( stderr, "No phase.\n" );
+ return -1;
+ }
+
+ switch ( n = atoi( pnum )) {
+ case 1 :
+ iface->i_flags |= IFACE_PHASE1;
+ break;
+
+ case 2 :
+ iface->i_flags |= IFACE_PHASE2;
+ break;
+
+ default :
+ fprintf( stderr, "No phase %d.\n", n );
+ return -1;
+ }
+ return( 2 );
+}
+
+net( iface, av )
+ struct interface *iface;
+ char **av;
+{
+ char *nrange;
+ char *stop;
+ int net;
+
+ if (( nrange = av[ 0 ] ) == NULL ) {
+ fprintf( stderr, "No network.\n" );
+ return -1;
+ }
+
+ if (( stop = strchr( nrange, '-' )) != 0 ) {
+ stop++;
+ }
+ net = atoi( nrange );
+ if ( net < 0 || net >= 0xffff ) {
+ fprintf( stderr, "Bad network: %d\n" );
+ return -1;
+ }
+
+ if ( iface->i_rt == NULL && ( iface->i_rt = newrt(iface)) == NULL ) {
+ perror( "newrt" );
+ return -1;
+ }
+
+ if ( iface->i_flags & IFACE_PHASE1 ) {
+ if ( stop != 0 ) {
+ fprintf( stderr, "Phase 1 doesn't use an address range.\n" );
+ return -1;
+ }
+ if ( iface->i_caddr.sat_addr.s_net != ATADDR_ANYNET &&
+ ntohs( iface->i_caddr.sat_addr.s_net ) != net ) {
+ fprintf( stderr, "Net-range (%u) doesn't match net %u.\n",
+ net, ntohs( iface->i_caddr.sat_addr.s_net ));
+ return -1;
+ }
+ iface->i_rt->rt_firstnet = iface->i_rt->rt_lastnet = htons( net );
+ } else if ( iface->i_flags & IFACE_PHASE2 ) {
+ iface->i_rt->rt_firstnet = htons( net );
+ if ( stop != 0 ) {
+ net = atoi( stop );
+ if ( net < 0 || net >= 0xffff ) {
+ fprintf( stderr, "Bad network: %d\n" );
+ return -1;
+ }
+ }
+ iface->i_rt->rt_lastnet = htons( net );
+ if ( iface->i_caddr.sat_addr.s_net != ATADDR_ANYNET &&
+ ( ntohs( iface->i_rt->rt_firstnet ) >
+ ntohs( iface->i_caddr.sat_addr.s_net ) ||
+ ntohs( iface->i_rt->rt_lastnet ) <
+ ntohs( iface->i_caddr.sat_addr.s_net ))) {
+ fprintf( stderr, "Net-range (%u-%u) doesn't contain net (%u).\n",
+ ntohs( iface->i_rt->rt_firstnet ),
+ ntohs( iface->i_rt->rt_lastnet ),
+ ntohs( iface->i_caddr.sat_addr.s_net ));
+ return -1;
+ }
+ if ( iface->i_rt->rt_firstnet != iface->i_rt->rt_lastnet ) {
+ iface->i_rt->rt_flags |= RTMPTAB_EXTENDED;
+ }
+ } else {
+ fprintf( stderr, "Must specify phase before networks.\n" );
+ return -1;
+ }
+ return( 2 );
+}
+
+addr( iface, av )
+ struct interface *iface;
+ char **av;
+{
+ if ( av[ 0 ] == NULL ) {
+ fprintf( stderr, "No address.\n" );
+ return -1;
+ }
+ if ( atalk_aton( av[ 0 ], &iface->i_caddr.sat_addr ) == 0 ) {
+ fprintf( stderr, "Bad address, %s\n", av[ 0 ] );
+ return -1;
+ }
+
+ if ( iface->i_rt ) {
+ if ( ntohs( iface->i_rt->rt_firstnet ) >
+ ntohs( iface->i_caddr.sat_addr.s_net ) ||
+ ntohs( iface->i_rt->rt_lastnet ) <
+ ntohs( iface->i_caddr.sat_addr.s_net )) {
+ fprintf( stderr, "Net (%u) not in net-range (%u-%u).\n",
+ ntohs( iface->i_caddr.sat_addr.s_net ),
+ ntohs( iface->i_rt->rt_firstnet ),
+ ntohs( iface->i_rt->rt_lastnet ));
+ return -1;
+ }
+ } else {
+ if (( iface->i_rt = newrt(iface)) == NULL ) {
+ perror( "newrt" );
+ return -1;
+ }
+ iface->i_rt->rt_firstnet = iface->i_rt->rt_lastnet =
+ iface->i_caddr.sat_addr.s_net;
+ }
+
+ return( 2 );
+}
+
+zone( iface, av )
+ struct interface *iface;
+ char **av;
+{
+ struct ziptab *zt;
+ char *zname;
+
+ if (( zname = av[ 0 ] ) == NULL ) {
+ fprintf( stderr, "No zone.\n" );
+ return -1;
+ }
+
+ /*
+ * Only process "-zone" if this interface has "-seed". We keep our
+ * list of configured zones in the interface structure. Then we can
+ * check that the network has given us good zones.
+ */
+ if ( iface->i_flags & IFACE_SEED ) {
+ if ( iface->i_rt == NULL ) {
+ fprintf( stderr, "Must specify net-range before zones.\n" );
+ return -1;
+ }
+ if (( zt = newzt( strlen( zname ), zname )) == NULL ) {
+ perror( "newzt" );
+ return -1;
+ }
+ if ( iface->i_czt == NULL ) {
+ iface->i_czt = zt;
+ } else {
+ zt->zt_next = iface->i_czt->zt_next;
+ iface->i_czt->zt_next = zt;
+ }
+ }
+ return( 2 );
+}
+
+/*
+ * Get the configuration from the kernel. Only called if there's no
+ * configuration.
+ */
+getifconf()
+{
+ struct interface *iface, *niface;
+ struct ifreq ifr;
+ char **start, **list;
+ int s;
+
+ if (( s = socket( AF_APPLETALK, SOCK_DGRAM, 0 )) < 0 ) {
+ perror( "socket" );
+ return -1;
+ }
+
+ start = list = getifacelist();
+ while (list && *list) {
+ strncpy(ifr.ifr_name, *list, sizeof(ifr.ifr_name));
+ list++;
+
+ if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
+ continue;
+
+ if (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_SLAVE))
+ continue;
+
+ if ((ifr.ifr_flags & IFF_UP) == 0)
+ continue;
+
+ /* for devices that don't support appletalk */
+ if (ioctl(s, SIOCGIFADDR, &ifr) < 0 && (errno == ENODEV))
+ continue;
+
+ for ( iface = interfaces; iface; iface = iface->i_next ) {
+ if ( strcmp( iface->i_name, ifr.ifr_name ) == 0 ) {
+ break;
+ }
+ }
+ if ( iface ) { /* Already have this interface name */
+ continue;
+ }
+
+
+#ifdef IFF_MULTICAST
+ if ((ifr.ifr_flags & IFF_MULTICAST) == 0)
+ fprintf(stderr, "%s: multicast may not work correctly.\n",
+ ifr.ifr_name);
+#endif
+
+ if (addmulti(ifr.ifr_name, NULL) < 0) {
+ fprintf(stderr, "%s: disabled.\n", ifr.ifr_name);
+ continue;
+ }
+
+ if (( niface = newiface( ifr.ifr_name )) == NULL ) {
+ perror( "newiface" );
+ close(s);
+ freeifacelist(start);
+ return -1;
+ }
+ /*
+ * Could try to get the address from the kernel...
+ */
+
+ if ( interfaces == NULL ) {
+ interfaces = niface;
+ } else {
+ for ( iface = interfaces; iface->i_next; iface = iface->i_next )
+ ;
+ iface->i_next = niface;
+ }
+ niface->i_next = NULL;
+ }
+ freeifacelist(start);
+ (void)close( s );
+ return( 0 );
+}
+
+/*
+ * Allocate a new interface structure. Centralized here so we can change
+ * the interface structure and have it updated nicely.
+ */
+ struct interface *
+newiface( name )
+ const char *name;
+{
+ struct interface *niface;
+
+ if (( niface = (struct interface *)calloc(1, sizeof( struct interface )))
+ == NULL ) {
+ return( NULL );
+ }
+ strncpy( niface->i_name, name, sizeof(niface->i_name));
+#ifdef BSD4_4
+ niface->i_addr.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ niface->i_addr.sat_family = AF_APPLETALK;
+#ifdef BSD4_4
+ niface->i_caddr.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ niface->i_caddr.sat_family = AF_APPLETALK;
+ return( niface );
+}
+
+#ifdef __svr4__
+ int
+plumb()
+{
+ struct interface *iface;
+ char device[ MAXPATHLEN + 1], *p;
+ int fd, ppa;
+
+ for ( iface = interfaces; iface != NULL; iface = iface->i_next ) {
+ if ( strcmp( iface->i_name, LOOPIFACE ) == 0 ) {
+ continue;
+ }
+
+ strcpy( device, "/dev/" );
+ strcat( device, iface->i_name );
+ if (( p = strpbrk( device, "0123456789" )) == NULL ) {
+ syslog( LOG_ERR, "plumb: invalid device: %s", device );
+ return -1;
+ }
+ ppa = atoi( p );
+ *p = '\0';
+
+ if (( fd = open( device, O_RDWR, 0 )) < 0 ) {
+ syslog( LOG_ERR, "%s: %m", device );
+ return -1;
+ }
+ if ( ioctl( fd, I_PUSH, "ddp" ) < 0 ) {
+ syslog( LOG_ERR, "I_PUSH: %m" );
+ close(fd);
+ return -1;
+ }
+ if ( ioctl( fd, IF_UNITSEL, ppa ) < 0 ) {
+ syslog( LOG_ERR, "IF_UNITSEL: %m" );
+ close(fd);
+ return -1;
+ }
+
+ /* configure multicast. */
+ if (addmulti(iface->i_name, NULL) < 0) {
+ perror(iface->i_name);
+ fprintf(stderr,"Can't configure multicast.\n");
+ close(fd);
+ return -1;
+ }
+
+ syslog( LOG_INFO, "plumbed %s%d", device, ppa );
+ }
+
+ return( 0 );
+}
+#endif __svr4__
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+struct gate {
+ struct gate *g_next,
+ *g_prev;
+ int g_state;
+ struct interface *g_iface;
+ struct rtmptab *g_rt;
+ struct sockaddr_at g_sat;
+};
--- /dev/null
+/*
+ * Copyright (c) 1990,1992 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef ATALKD_INTERFACE_H
+#define ATALKD_INTERFACE_H 1
+
+#include <sys/cdefs.h>
+
+struct interface {
+ struct interface *i_next;
+ char i_name[ IFNAMSIZ ];
+ int i_flags;
+ int i_time;
+ int i_group; /* for isolated appletalk domains */
+ struct sockaddr_at i_addr;
+ struct sockaddr_at i_caddr;
+ struct ziptab *i_czt;
+ struct rtmptab *i_rt;
+ struct gate *i_gate;
+ struct atport *i_ports;
+};
+
+#define IFACE_PHASE1 0x001
+#define IFACE_PHASE2 0x002
+#define IFACE_LOOPBACK 0x004 /* is the loopback interface */
+#define IFACE_SEED 0x008 /* act as seed */
+#define IFACE_ADDR 0x010 /* has an address set */
+#define IFACE_CONFIG 0x020 /* has been configured */
+#define IFACE_NOROUTER 0x040 /* no router on interface */
+#define IFACE_LOOP 0x080 /* has a loopback route */
+#define IFACE_RSEED 0x100 /* almost the same as seed. RSEED
+ says that we should try to
+ do routing. */
+#define IFACE_DONTROUTE 0x200 /* don't route this interface */
+#define IFACE_ISROUTER 0x400 /* act as a router. */
+
+#define UNSTABLE 2
+#define STABLE 0
+#define STABLEANYWAY -2
+
+#define IFBASE 2 /* base number of interfaces */
+
+#ifdef linux
+#define LOOPIFACE "lo"
+#else linux
+#define LOOPIFACE "lo0"
+#endif linux
+
+extern struct interface *interfaces;
+extern struct interface *ciface;
+struct interface *newiface __P((const char *));
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1992 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+struct list {
+ void *l_data;
+ struct list *l_next,
+ *l_prev;
+};
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#if defined( sun ) && defined( __svr4__ )
+#include </usr/ucbinclude/sys/file.h>
+#else sun __svr4__
+#include <sys/file.h>
+#endif sun __svr4__
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <net/if.h>
+#include <net/route.h>
+
+#include <signal.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/compat.h>
+#include <atalk/zip.h>
+#include <atalk/rtmp.h>
+#include <atalk/ddp.h>
+#include <atalk/atp.h>
+#include <atalk/paths.h>
+#include <atalk/util.h>
+
+#ifdef __svr4__
+#include <sys/sockio.h>
+#include <termios.h>
+#endif __svr4__
+
+#include "interface.h"
+#include "gate.h"
+#include "list.h"
+#include "rtmp.h"
+#include "zip.h"
+#include "atserv.h"
+
+/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature */
+#ifndef SOCKLEN_T
+#define SOCKLEN_T unsigned int
+#endif
+
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(x) ((x).w_retcode)
+#endif WEXITSTATUS
+
+/* linux has a special ioctl for appletalk device destruction. as of
+ * 2.1.57, SIOCDIFADDR works w/ linux. okay, we need to deal with the
+ * fact that SIOCDIFADDR may be defined on linux despite the fact that
+ * it doesn't work. */
+#if !defined(SIOCDIFADDR) && defined(SIOCATALKDIFADDR)
+#define SIOCDIFADDR SIOCATALKDIFADDR
+#endif
+
+#define elements(a) (sizeof(a)/sizeof((a)[0]))
+
+#define PKTSZ 1024
+
+extern int rtmp_packet();
+extern int nbp_packet();
+extern int aep_packet();
+extern int zip_packet();
+
+int rtfd;
+
+struct atserv atserv[] = {
+ { "rtmp", 1, rtmp_packet }, /* 0 */
+ { "nbp", 2, nbp_packet }, /* 1 */
+ { "echo", 4, aep_packet }, /* 2 */
+ { "zip", 6, zip_packet }, /* 3 */
+};
+int atservNATSERV = elements( atserv );
+
+struct interface *interfaces = NULL, *ciface = NULL;
+
+int debug = 0, quiet = 0, chatty = 0;
+char *configfile = NULL;
+int ziptimeout = 0, transition = 0;
+int stabletimer, stable = 0, newrtmpdata = 0, noparent = 0;
+static int ninterfaces;
+int defphase = IFACE_PHASE2;
+int nfds = 0;
+fd_set fds;
+char Packet[ PKTSZ ];
+char *version = VERSION;
+static char *pidfile = _PATH_ATALKDLOCK;
+
+
+/* this is the messiest of the bunch as atalkd can exit pretty much
+ * everywhere. we delete interfaces here instead of in as_down. */
+static void atalkd_exit(const int i)
+{
+#ifdef SIOCDIFADDR
+ struct interface *iface;
+
+ for (iface = interfaces; iface; iface = iface->i_next) {
+ if (ifconfig( iface->i_name, SIOCDIFADDR, &iface->i_addr)) {
+#ifdef SIOCATALKDIFADDR
+#if (SIOCDIFADDR != SIOCATALKDIFADDR)
+ if (!ifconfig(iface->i_name, SIOCATALKDIFADDR, &iface->i_addr))
+ continue;
+#endif
+#endif
+ syslog( LOG_ERR, "difaddr(%u.%u): %m",
+ ntohs(iface->i_addr.sat_addr.s_net),
+ iface->i_addr.sat_addr.s_node);
+ }
+ }
+#endif
+
+ server_unlock(pidfile);
+ exit(i);
+}
+
+
+#if !defined( ibm032 ) && !defined( _IBMR2 )
+ void
+#endif ibm032 _IBMR2
+as_timer()
+{
+ struct sockaddr_at sat;
+ struct ziphdr zh;
+ struct rtmp_head rh;
+ struct rtmp_tuple rt;
+ struct atport *ap, *zap, *rap;
+ struct interface *iface, *iface2;
+ struct gate *gate, *fgate = NULL;
+ struct rtmptab *rtmp, *frtmp;
+ struct ziptab *zt;
+ char *data, *end, packet[ ATP_BUFSIZ ];
+ int sentzipq = 0;
+ int n, cc;
+
+ memset(&sat, 0, sizeof( struct sockaddr_at ));
+ for ( iface = interfaces; iface; iface = iface->i_next ) {
+ if ( iface->i_flags & IFACE_LOOPBACK ) {
+ continue;
+ }
+ for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+ if ( ap->ap_packet == zip_packet ) {
+ zap = ap;
+ }
+ if ( ap->ap_packet == rtmp_packet ) {
+ rap = ap;
+ }
+ }
+
+ if (( iface->i_flags & ( IFACE_ADDR|IFACE_CONFIG|IFACE_NOROUTER )) ==
+ IFACE_ADDR ) {
+ if ( iface->i_time < 3 ) {
+ if ( iface->i_flags & IFACE_PHASE1 ) {
+ if (rtmp_request( iface ) < 0) {
+ syslog(LOG_ERR, "rtmp_request: %m");
+ atalkd_exit(1);
+ }
+ newrtmpdata = 1;
+ } else {
+ if (zip_getnetinfo( iface ) < 0) {
+ syslog(LOG_ERR, "zip_getnetinfo: %m");
+ atalkd_exit(1);
+ }
+ sentzipq = 1;
+ }
+ iface->i_time++;
+ } else {
+ iface->i_flags |= IFACE_NOROUTER;
+ if ((iface->i_flags & IFACE_ISROUTER)) {
+ if (( iface->i_flags & IFACE_SEED ) == 0 ) {
+ /*
+ * No seed info, and we've got multiple interfaces.
+ * Wait forever.
+ */
+ syslog( LOG_INFO,
+ "as_timer multiple interfaces, no seed" );
+ syslog( LOG_INFO, "as_timer can't configure %s",
+ iface->i_name );
+ syslog( LOG_INFO, "as_timer waiting for router" );
+ iface->i_time = 0;
+ continue;
+ } else {
+ /*
+ * Complete configuration for iface, and boot next
+ * interface.
+ */
+ iface->i_flags |= IFACE_CONFIG;
+ for ( zt = iface->i_czt; zt; zt = zt->zt_next ) {
+ if (addzone( iface->i_rt, zt->zt_len,
+ zt->zt_name) < 0) {
+ syslog(LOG_ERR, "addzone: %m");
+ atalkd_exit(1);
+ }
+ }
+ if ( iface->i_rt->rt_zt ) {
+ iface->i_rt->rt_flags &= ~RTMPTAB_ZIPQUERY;
+ iface->i_rt->rt_flags |= RTMPTAB_HASZONES;
+ }
+ if ( iface->i_flags & IFACE_PHASE1 ) {
+ syslog( LOG_INFO,
+ "as_timer configured %s phase 1 from seed",
+ iface->i_name );
+ setaddr( iface, IFACE_PHASE1,
+ iface->i_caddr.sat_addr.s_net,
+ iface->i_addr.sat_addr.s_node,
+ iface->i_caddr.sat_addr.s_net,
+ iface->i_caddr.sat_addr.s_net );
+ } else {
+ syslog( LOG_INFO,
+ "as_timer configured %s phase 2 from seed",
+ iface->i_name );
+ }
+
+ if ( looproute( iface, RTMP_ADD )) { /* -1 or 1 */
+ syslog( LOG_ERR,
+ "as_timer: can't route %u.%u to loop: %m",
+ ntohs( iface->i_addr.sat_addr.s_net ),
+ iface->i_addr.sat_addr.s_node );
+ atalkd_exit( 1 );
+ }
+ if ( iface == ciface ) {
+ ciface = ciface->i_next;
+ bootaddr( ciface );
+ }
+ }
+ } else {
+ /*
+ * Configure for no router operation. Wait for a route
+ * to become available in rtmp_packet().
+ */
+ syslog( LOG_INFO, "config for no router" );
+
+ if ( iface->i_flags & IFACE_PHASE2 ) {
+ iface->i_rt->rt_firstnet = 0;
+ iface->i_rt->rt_lastnet = htons( STARTUP_LASTNET );
+ setaddr( iface, IFACE_PHASE2,
+ iface->i_addr.sat_addr.s_net,
+ iface->i_addr.sat_addr.s_node,
+ 0, htons( STARTUP_LASTNET ));
+ }
+ if ( looproute( iface, RTMP_ADD ) ) { /* -1 or 1 */
+ syslog( LOG_ERR,
+ "as_timer: can't route %u.%u to loopback: %m",
+ ntohs( iface->i_addr.sat_addr.s_net ),
+ iface->i_addr.sat_addr.s_node );
+ atalkd_exit( 1 );
+ }
+
+ if ( iface == ciface ) {
+ ciface = ciface->i_next;
+ bootaddr( ciface );
+ }
+ }
+ }
+ }
+
+ for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
+ if ( fgate ) {
+ free( (caddr_t)fgate );
+ fgate = NULL;
+ }
+
+ n = 0;
+ data = packet + 1 + sizeof( struct ziphdr );
+ end = packet + sizeof( packet );
+
+ sat = gate->g_sat;
+ sat.sat_port = zap->ap_port;
+
+ /*
+ * Perform timeouts on routers. If we've only got one
+ * interface, we'll use these timeouts to decide that
+ * our zone has gone away.
+ */
+ if ( ++gate->g_state >= RTMPTAB_BAD ) {
+ syslog( LOG_INFO, "as_timer gateway %u.%u down",
+ ntohs( gate->g_sat.sat_addr.s_net ),
+ gate->g_sat.sat_addr.s_node );
+ rtmp = gate->g_rt;
+ while ( rtmp ) {
+ frtmp = rtmp->rt_next;
+ if ( rtmp->rt_hops == RTMPHOPS_POISON ||
+ rtmp->rt_iprev == 0 ) {
+ rtmp_free( rtmp );
+ } else {
+ rtmp->rt_hops = RTMPHOPS_POISON;
+ if ((cc = rtmp_replace( rtmp )) < 0) {
+ syslog(LOG_ERR, "rtmp_replace: %m");
+ atalkd_exit(1);
+ }
+ if (cc) {
+ gate->g_state = rtmp->rt_state = RTMPTAB_GOOD;
+ }
+ }
+ rtmp = frtmp;
+ }
+ if ( gate->g_rt == 0 ) {
+ if ( gate->g_prev == 0 ) {
+ gate->g_iface->i_gate = gate->g_next;
+ } else {
+ gate->g_prev->g_next = gate->g_next;
+ }
+ if ( gate->g_next != 0 ) {
+ gate->g_next->g_prev = gate->g_prev;
+ }
+ fgate = gate; /* can't free here, just mark it */
+ }
+ /*
+ * If this is the last router on the only interface,
+ * reconfigure our netrange. By marking the interface
+ * as having no router, we will notice when a router
+ * comes back up.
+ *
+ * XXX: actually, we always reconfigure an interface
+ * if we're not a seed router.
+ */
+
+ if ( gate->g_iface->i_gate == 0 &&
+ ((iface->i_flags & IFACE_SEED) == 0)) {
+ gate->g_iface->i_flags |= IFACE_NOROUTER;
+ gate->g_iface->i_flags &= ~IFACE_CONFIG;
+
+ /* get rid of any zones associated with this iface */
+ if (gate->g_iface->i_rt->rt_zt) {
+ rtmp_delzonemap(gate->g_iface->i_rt);
+ gate->g_iface->i_rt->rt_flags &= ~RTMPTAB_HASZONES;
+ }
+
+ syslog( LOG_INFO, "as_timer last gateway down" );
+
+ /* Set netrange to 0-fffe. */
+ if ( gate->g_iface->i_flags & IFACE_PHASE2 ) {
+ gate->g_iface->i_rt->rt_firstnet = 0;
+ gate->g_iface->i_rt->rt_lastnet =
+ htons( STARTUP_LASTNET );
+ setaddr( iface, IFACE_PHASE2,
+ iface->i_addr.sat_addr.s_net,
+ iface->i_addr.sat_addr.s_node,
+ 0, htons( STARTUP_LASTNET ));
+ }
+ }
+ continue;
+ }
+
+ /*
+ * If we don't have a zone for our interface yet, ask for
+ * it from any router (all routers) on the interface.
+ */
+ if (( iface->i_rt->rt_flags & RTMPTAB_HASZONES ) == 0 ) {
+ iface->i_rt->rt_flags |= RTMPTAB_ZIPQUERY;
+ memcpy( data, &iface->i_rt->rt_firstnet, sizeof( u_short ));
+ data += sizeof( u_short );
+ n++;
+ }
+
+ rtmp = gate->g_rt;
+ while ( rtmp ) {
+ /*
+ * Delete old routing tuples.
+ */
+ if ( rtmp->rt_state != RTMPTAB_PERM ) {
+ rtmp->rt_state++;
+ }
+
+ /*
+ * We've not been updated for this route in a while. If
+ * it's not in use, go ahead and remove it. If it is in
+ * use, mark the route as down (POISON), and look for a
+ * better route. If one is found, delete this route and use
+ * the new one. If it's not found, mark the route as GOOD
+ * (so we'll propogate our poison) and delete it the next
+ * time it becomes BAD.
+ */
+ if ( rtmp->rt_state >= RTMPTAB_BAD ) {
+ frtmp = rtmp->rt_next;
+ if ( rtmp->rt_iprev == 0 ) { /* not in use */
+ rtmp_free( rtmp );
+ } else { /* in use */
+ if ( rtmp->rt_hops == RTMPHOPS_POISON ) {
+ rtmp_free( rtmp );
+ } else {
+ rtmp->rt_hops = RTMPHOPS_POISON;
+ if ((cc = rtmp_replace( rtmp )) < 0) {
+ syslog(LOG_ERR, "rtmp_replace: %m");
+ atalkd_exit(1);
+ }
+ if (cc)
+ rtmp->rt_state = RTMPTAB_GOOD;
+ }
+ }
+ rtmp = frtmp;
+ continue;
+ }
+
+ /*
+ * Do ZIP lookups.
+ */
+ if ( rtmp->rt_iprev &&
+ ( rtmp->rt_flags & RTMPTAB_HASZONES ) == 0 ) {
+ if ( data + sizeof( u_short ) > end || n == 255 ) {
+ /* send what we've got */
+ zh.zh_op = ZIPOP_QUERY;
+ zh.zh_count = n;
+ cc = data - packet;
+ data = packet;
+ *data++ = DDPTYPE_ZIP;
+ memcpy( data, &zh, sizeof( struct ziphdr ));
+
+ if ( sendto( zap->ap_fd, packet, cc, 0,
+ (struct sockaddr *)&sat,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "as_timer sendto: %m" );
+ }
+ sentzipq = 1;
+
+ n = 0;
+ data = packet + 1 + sizeof( struct ziphdr );
+ end = packet + sizeof( packet );
+ }
+
+ /*
+ * rt_nzq is number of ZIP Queries we've issued for a
+ * given netrange. If we've got ziptimeout on, we
+ * will only ask 3 times for any given netrange.
+ * Interestingly enough, since rt_nzq is a u_char,
+ * it will overflow after a while. This means we will
+ * periodically ask for nets that we've decided not to
+ * ask about, and warn that we can't get it's zone.
+ */
+ if ( rtmp->rt_nzq++ == 3 ) {
+ syslog( LOG_INFO, "as_timer can't get zone for %u",
+ ntohs( rtmp->rt_firstnet ));
+ }
+ if ( rtmp->rt_nzq > 3 ) {
+ if ( ziptimeout ) {
+ rtmp = rtmp->rt_next;
+ continue;
+ }
+ } else {
+ sentzipq = 1;
+ }
+ rtmp->rt_flags |= RTMPTAB_ZIPQUERY;
+ memcpy( data, &rtmp->rt_firstnet, sizeof( u_short ));
+ data += sizeof( u_short );
+ n++;
+ }
+ rtmp = rtmp->rt_next;
+ }
+
+ /* send what we've got */
+ if ( n > 0 ) {
+ zh.zh_op = ZIPOP_QUERY;
+ zh.zh_count = n;
+ cc = data - packet;
+ data = packet;
+ *data++ = DDPTYPE_ZIP;
+ memcpy( data, &zh, sizeof( struct ziphdr ));
+
+ if ( sendto( zap->ap_fd, packet, cc, 0, (struct sockaddr *)&sat,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "as_timer sendto: %m" );
+ }
+ }
+ }
+ if ( fgate ) {
+ free( (caddr_t)fgate );
+ fgate = NULL;
+ }
+
+ /*
+ * Send RTMP broadcasts if we have multiple interfaces or our
+ * interface is configured as a router.
+ */
+ if ((iface->i_flags & IFACE_ISROUTER)) {
+#ifdef BSD4_4
+ sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ sat.sat_family = AF_APPLETALK;
+ sat.sat_addr.s_net = ATADDR_ANYNET;
+ sat.sat_addr.s_node = ATADDR_BCAST;
+ sat.sat_port = rap->ap_port;
+
+ data = packet;
+ end = data + sizeof( packet );
+ *data++ = DDPTYPE_RTMPRD;
+ rh.rh_net = iface->i_addr.sat_addr.s_net;
+ rh.rh_nodelen = 8;
+ rh.rh_node = iface->i_addr.sat_addr.s_node;
+ memcpy( data, &rh, sizeof( struct rtmp_head ));
+ data += sizeof( struct rtmp_head );
+ n = 0;
+
+
+ if ( iface->i_flags & IFACE_PHASE1 ) {
+ rt.rt_net = 0;
+ rt.rt_dist = 0x82;
+ memcpy( data, &rt, SZ_RTMPTUPLE );
+ data += SZ_RTMPTUPLE;
+ } else {
+ rt.rt_net = iface->i_rt->rt_firstnet;
+ rt.rt_dist = 0x80;
+ memcpy( data, &rt, SZ_RTMPTUPLE );
+ data += SZ_RTMPTUPLE;
+
+ rt.rt_net = iface->i_rt->rt_lastnet;
+ rt.rt_dist = 0x82;
+ memcpy( data, &rt, SZ_RTMPTUPLE );
+ data += SZ_RTMPTUPLE;
+ }
+
+ for ( iface2 = interfaces; iface2; iface2 = iface2->i_next ) {
+
+ /* XXX: there used to be a bit checking against iface ==
+ iface2. also, we don't want to send an rtmp broadcast
+ to an interface that doesn't want it. */
+ if ((( iface2->i_flags & IFACE_CONFIG ) == 0) ||
+ ((iface2->i_flags & IFACE_ISROUTER) == 0)) {
+ continue;
+ }
+ /*
+ * Fill in tuples. Always send the same thing, regardless
+ * of the phase of the destination. Routers who don't
+ * understand extended rtmp packets will toss extended
+ * tuples because their distance will have the high bit set.
+ */
+ for ( rtmp = iface2->i_rt; rtmp; rtmp = rtmp->rt_inext ) {
+ /* don't broadcast routes we have no zone for */
+ if ( rtmp->rt_zt == 0 ||
+ ( rtmp->rt_flags & RTMPTAB_ZIPQUERY ) ||
+ ( rtmp->rt_flags & RTMPTAB_HASZONES ) == 0 ) {
+ continue;
+ }
+
+ if ((( rtmp->rt_flags & RTMPTAB_EXTENDED ) &&
+ data + 2 * SZ_RTMPTUPLE > end ) ||
+ data + SZ_RTMPTUPLE > end ) {
+ if ( sendto( rap->ap_fd, packet, data - packet, 0,
+ (struct sockaddr *)&sat,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "as_timer sendto %u.%u (%u): %m",
+ ntohs( sat.sat_addr.s_net ),
+ sat.sat_addr.s_node,
+ ntohs( iface->i_rt->rt_firstnet ));
+ }
+
+ if ( iface->i_flags & IFACE_PHASE2 ) {
+ data = packet + 1 + sizeof( struct rtmp_head ) +
+ 2 * SZ_RTMPTUPLE;
+ } else {
+ data = packet + 1 + sizeof( struct rtmp_head ) +
+ SZ_RTMPTUPLE;
+ }
+ n = 0;
+ }
+
+ rt.rt_net = rtmp->rt_firstnet;
+ rt.rt_dist = rtmp->rt_hops;
+ if ( rtmp->rt_flags & RTMPTAB_EXTENDED ) {
+ rt.rt_dist |= 0x80;
+ }
+ memcpy( data, &rt, SZ_RTMPTUPLE );
+ data += SZ_RTMPTUPLE;
+
+ if ( rtmp->rt_flags & RTMPTAB_EXTENDED ) {
+ rt.rt_net = rtmp->rt_lastnet;
+ rt.rt_dist = 0x82;
+ memcpy( data, &rt, SZ_RTMPTUPLE );
+ data += SZ_RTMPTUPLE;
+ }
+ n++;
+ }
+ }
+
+ /* send rest */
+ if ( n ) {
+ if ( sendto( rap->ap_fd, packet, data - packet, 0,
+ (struct sockaddr *)&sat,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "as_timer sendto %u.%u (%u): %m",
+ ntohs( sat.sat_addr.s_net ),
+ sat.sat_addr.s_node,
+ ntohs( iface->i_rt->rt_firstnet ));
+ }
+ }
+ }
+ }
+
+ /*
+ * Check if we're stable. Each time we configure an interface, we
+ * sent stabletimer to UNSTABLE. If stabletimer ever gets to
+ * STABLEANYWAY, we give up and decide to "be" stable anyway.
+ * Normally, we wait for stabletimer get <= STABLE with no new rtmp
+ * data and all zip data complete.
+ */
+ if ( !stable ) {
+ if ( stabletimer <= STABLE && !newrtmpdata && !sentzipq ) {
+ /* write out config file */
+ stable = 1;
+ writeconf( configfile );
+ } else {
+ if ( stabletimer-- <= STABLEANYWAY ) {
+ stable = 1;
+ }
+ }
+ newrtmpdata = 0;
+
+ if ( stable && !noparent ) {
+ noparent = 1;
+ syslog( LOG_INFO, "ready %d/%d/%d", stabletimer, newrtmpdata,
+ sentzipq );
+ if ( !debug ) {
+ /*
+ * Seems like we could get here more than once...
+ */
+ if ( kill( getpid(), SIGSTOP ) < 0 ) {
+ syslog( LOG_ERR, "as_timer: kill-self failed!" );
+ atalkd_exit( 1 );
+ }
+ }
+ }
+ }
+
+#ifdef DEBUG
+ consistency();
+#endif DEBUG
+}
+
+#ifdef DEBUG
+/*
+* Consistency check...
+*/
+consistency()
+{
+ struct rtmptab *rtmp;
+ struct list *lr, *lz;
+ struct ziptab *zt;
+
+ for ( zt = ziptab; zt; zt = zt->zt_next ) {
+ for ( lr = zt->zt_rt; lr; lr = lr->l_next ) {
+ rtmp = (struct rtmptab *)lr->l_data;
+ if ( rtmp->rt_iprev == 0 && rtmp->rt_gate != 0 ) {
+ syslog( LOG_ERR, "%.*s has %u-%u (unused)\n",
+ zt->zt_len, zt->zt_name, ntohs( rtmp->rt_firstnet ),
+ ntohs( rtmp->rt_lastnet ));
+ atalkd_exit(1);
+ }
+ for ( lz = rtmp->rt_zt; lz; lz = lz->l_next ) {
+ if ( zt == (struct ziptab *)lz->l_data ) {
+ break;
+ }
+ }
+ if ( lz == 0 ) {
+ syslog( LOG_ERR, "no map from %u-%u to %.*s\n",
+ ntohs( rtmp->rt_firstnet ),
+ ntohs( rtmp->rt_lastnet ),
+ zt->zt_len, zt->zt_name );
+ atalkd_exit(1);
+ }
+ }
+ }
+}
+#endif DEBUG
+
+#if !defined( ibm032 ) && !defined( _IBMR2 )
+ void
+#endif ibm032 _IBMR2
+as_debug()
+{
+ struct interface *iface;
+ struct list *l;
+ struct ziptab *zt;
+ struct gate *gate;
+ struct rtmptab *rt;
+ FILE *rtmpdebug;
+
+ if (( rtmpdebug = fopen( _PATH_ATALKDEBUG, "w" )) == NULL ) {
+ syslog( LOG_ERR, "rtmp: %m" );
+ }
+
+ for ( iface = interfaces; iface; iface = iface->i_next ) {
+ fprintf( rtmpdebug, "interface %s %u.%u ", iface->i_name,
+ ntohs( iface->i_addr.sat_addr.s_net ),
+ iface->i_addr.sat_addr.s_node );
+ if ( iface->i_flags & IFACE_PHASE1 ) {
+ putc( '1', rtmpdebug );
+ }
+ if ( iface->i_flags & IFACE_PHASE2 ) {
+ putc( '2', rtmpdebug );
+ }
+ if ( iface->i_flags & IFACE_RSEED ) {
+ putc( 'R', rtmpdebug );
+ }
+ if ( iface->i_flags & IFACE_SEED ) {
+ putc( 'S', rtmpdebug );
+ }
+ if ( iface->i_flags & IFACE_DONTROUTE ) {
+ putc( 'D', rtmpdebug );
+ }
+ if ( iface->i_flags & IFACE_ADDR ) {
+ putc( 'A', rtmpdebug );
+ }
+ if ( iface->i_flags & IFACE_CONFIG ) {
+ putc( 'C', rtmpdebug );
+ }
+ if ( iface->i_flags & IFACE_NOROUTER ) {
+ putc( 'N', rtmpdebug );
+ }
+ if ( iface->i_flags & IFACE_LOOP ) {
+ putc( 'L', rtmpdebug );
+ }
+ putc( '\n', rtmpdebug );
+
+ if ( iface->i_rt ) {
+ fprintf( rtmpdebug, "\t%u-%u ",
+ ntohs( iface->i_rt->rt_firstnet ),
+ ntohs( iface->i_rt->rt_lastnet ));
+ if ( iface->i_rt->rt_flags & RTMPTAB_ZIPQUERY ) {
+ putc( 'q', rtmpdebug );
+ }
+ if ( iface->i_rt->rt_flags & RTMPTAB_HASZONES ) {
+ putc( 'z', rtmpdebug );
+ }
+ if ( iface->i_rt->rt_flags & RTMPTAB_EXTENDED ) {
+ putc( 'x', rtmpdebug );
+ }
+ putc( 'i', rtmpdebug );
+ for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
+ zt = (struct ziptab *)l->l_data;
+ fprintf( rtmpdebug, " '%.*s'", zt->zt_len, zt->zt_name );
+ }
+ fprintf( rtmpdebug, "\n" );
+ }
+
+ for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
+ fprintf( rtmpdebug, "gate %u.%u %X\n",
+ ntohs( gate->g_sat.sat_addr.s_net ),
+ gate->g_sat.sat_addr.s_node, gate->g_state );
+ for ( rt = gate->g_rt; rt; rt = rt->rt_next ) {
+ fprintf( rtmpdebug, "\t%u-%u ", ntohs( rt->rt_firstnet ),
+ ntohs( rt->rt_lastnet ));
+ if ( rt->rt_flags & RTMPTAB_ZIPQUERY ) {
+ putc( 'q', rtmpdebug );
+ }
+ if ( rt->rt_flags & RTMPTAB_HASZONES ) {
+ putc( 'z', rtmpdebug );
+ }
+ if ( rt->rt_flags & RTMPTAB_EXTENDED ) {
+ putc( 'x', rtmpdebug );
+ }
+ if ( rt->rt_iprev ) {
+ putc( 'i', rtmpdebug );
+ }
+ for ( l = rt->rt_zt; l; l = l->l_next ) {
+ zt = (struct ziptab *)l->l_data;
+ fprintf( rtmpdebug, " '%.*s'", zt->zt_len, zt->zt_name );
+ }
+ fprintf( rtmpdebug, "\n" );
+ }
+ }
+ }
+
+ fclose( rtmpdebug );
+}
+
+/*
+ * Called when SIGTERM is recieved. Remove all routes and then exit.
+ */
+#if !defined( ibm032 ) && !defined( _IBMR2 )
+ void
+#endif ibm032 _IBMR2
+as_down()
+{
+ struct interface *iface;
+ struct gate *gate;
+ struct rtmptab *rt;
+
+ for ( iface = interfaces; iface; iface = iface->i_next ) {
+ for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
+ for ( rt = gate->g_rt; rt; rt = rt->rt_next ) {
+ if ( rt->rt_iprev ) {
+ if ( gateroute( RTMP_DEL, rt ) < 0 ) {
+ syslog( LOG_ERR, "as_down remove %u-%u failed: %m",
+ ntohs( rt->rt_firstnet ),
+ ntohs( rt->rt_lastnet ));
+ }
+ }
+ }
+ }
+ if ( iface->i_flags & IFACE_LOOP ) {
+ if (looproute( iface, RTMP_DEL )) {
+ syslog( LOG_ERR, "as_down remove %s %u.%u failed: %m",
+ iface->i_name, ntohs( iface->i_addr.sat_addr.s_net ),
+ iface->i_addr.sat_addr.s_node );
+ }
+ }
+ }
+
+ syslog( LOG_INFO, "done" );
+ atalkd_exit( 0 );
+}
+
+main( ac, av )
+ int ac;
+ char **av;
+{
+ extern char *optarg;
+ extern int optind;
+
+ struct sockaddr_at sat;
+ struct sigaction sv;
+ struct itimerval it;
+ struct interface *iface;
+ int status;
+ struct atport *ap;
+ fd_set readfds;
+ int i, mask, c;
+ SOCKLEN_T fromlen;
+ char *prog;
+;
+
+ while (( c = getopt( ac, av, "12qsdtf:P:" )) != EOF ) {
+ switch ( c ) {
+ case '1' :
+ defphase = IFACE_PHASE1;
+ break;
+
+ case '2' :
+ defphase = IFACE_PHASE2;
+ break;
+
+ case 'd' :
+ debug++;
+ break;
+
+ case 'f' :
+ configfile = optarg;
+ break;
+
+ case 'q' : /* don't seed */
+ quiet++;
+ break;
+
+ case 's' : /* seed */
+ chatty++;
+ break;
+
+ case 't' : /* transition */
+ transition++;
+ break;
+
+ case 'P' : /* pid file */
+ pidfile = optarg;
+ break;
+
+ default :
+ fprintf( stderr, "Unknown option -- '%c'\n", c );
+ exit( 1 );
+ }
+ }
+ if ( optind != ac ) {
+ fprintf( stderr, "Too many arguments.\n" );
+ exit( 1 );
+ }
+
+ if (( prog = strrchr( av[ 0 ], '/' )) == NULL ) {
+ prog = av[ 0 ];
+ } else {
+ prog++;
+ }
+
+ /*
+ * Configure loop back address first, so appearances of "lo0" in
+ * the config file fail. Also insures that lo0 gets configured,
+ * even if there's some hangup during configuration of some
+ * other interface.
+ */
+ if (( interfaces = newiface( LOOPIFACE )) == NULL ) {
+ perror( "newiface" );
+ exit( 1 );
+ }
+ interfaces->i_flags |= IFACE_PHASE2 | IFACE_LOOPBACK;
+
+ /*
+ * Check our initial configuration before we fork. This way we can
+ * complain about syntax errors on stdout.
+ *
+ * Basically, if we're going to read our config file, we should read
+ * it and initialize our data structures. If we're not going to read
+ * our config file, use GIFCONF to initialize our data structures.
+ */
+ if ( readconf( configfile ) < 0 && getifconf() < 0 ) {
+ fprintf( stderr, "%s: can't get interfaces, exiting.\n", prog );
+ exit( 1 );
+ }
+
+ /* we need to count up our interfaces so that we can simplify things
+ * later. we also need to figure out if we have more than one interface
+ * that is routing. */
+ for (i = 0, ninterfaces = 0, iface = interfaces; iface;
+ iface=iface->i_next) {
+ if (iface->i_flags & IFACE_DONTROUTE)
+ i++;
+ ninterfaces++;
+ }
+ i = ninterfaces - i; /* number of routable interfaces */
+
+ /*
+ * At this point, we have (at least partially) initialized data
+ * structures. Fill in what we can and verify that nothing is obviously
+ * broken.
+ */
+ for (iface = interfaces; iface; iface = iface->i_next) {
+ /* Apply the default phase */
+ if (( iface->i_flags & IFACE_PHASE1 ) == 0 &&
+ ( iface->i_flags & IFACE_PHASE2 ) == 0 ) {
+ iface->i_flags |= defphase;
+ }
+
+ /* set up router flag information. if we have multiple interfaces
+ * and DONTROUTE isn't set, set up ROUTER. i is the number of
+ * interfaces that don't have the DONTROUTE flag set. */
+ if ((i > IFBASE) && ((iface->i_flags & IFACE_DONTROUTE) == 0)) {
+ iface->i_flags |= IFACE_ISROUTER;
+ }
+
+ /* Set default addresses */
+ if ( iface->i_rt == NULL ) {
+ if (( iface->i_rt = newrt(iface)) == NULL ) {
+ perror( "newrt" );
+ exit( 1 );
+ }
+
+ if ( iface->i_flags & IFACE_PHASE1 ) {
+ iface->i_rt->rt_firstnet = iface->i_rt->rt_lastnet =
+ iface->i_caddr.sat_addr.s_net;
+ } else {
+ if ( iface->i_caddr.sat_addr.s_net != ATADDR_ANYNET ||
+ ( iface->i_flags & IFACE_LOOPBACK )) {
+ iface->i_rt->rt_firstnet = iface->i_rt->rt_lastnet =
+ iface->i_caddr.sat_addr.s_net;
+ } else {
+ iface->i_rt->rt_firstnet = htons( STARTUP_FIRSTNET );
+ iface->i_rt->rt_lastnet = htons( STARTUP_LASTNET );
+ }
+ }
+ }
+
+ if (( iface->i_flags & IFACE_PHASE1 ) == 0 ) {
+ iface->i_rt->rt_flags |= RTMPTAB_EXTENDED;
+ }
+
+ if ( iface->i_caddr.sat_addr.s_net == ATADDR_ANYNET ) {
+ iface->i_caddr.sat_addr.s_net = iface->i_rt->rt_firstnet;
+ }
+
+ if ( debug ) {
+ dumpconfig( iface ); /* probably needs args */
+ }
+ }
+
+ /*
+ * A little consistency check...
+ */
+ if ( ninterfaces < IFBASE ) {
+ fprintf( stderr, "%s: zero interfaces, exiting.\n", prog );
+ exit( 1 );
+ }
+
+ /* do this here so that we can use ifconfig */
+#ifdef __svr4__
+ if ( plumb() < 0 ) {
+ fprintf(stderr, "can't establish STREAMS plumbing, exiting.\n" );
+ atalkd_exit( 1 );
+ }
+#endif __svr4__
+
+ /* delete pre-existing interface addresses. */
+#ifdef SIOCDIFADDR
+ for (iface = interfaces; iface; iface = iface->i_next) {
+ if (ifconfig(iface->i_name, SIOCDIFADDR, &iface->i_addr)) {
+#ifdef SIOCATALKDIFADDR
+#if (SIOCDIFADDR != SIOCATALKDIFADDR)
+ ifconfig(iface->i_name, SIOCATALKDIFADDR, &iface->i_addr);
+#endif
+#endif
+ }
+ }
+#endif
+
+ /*
+ * Disassociate. The child will send itself a signal when it is
+ * stable. This indicates that other processes may begin using
+ * AppleTalk.
+ */
+ switch (i = server_lock("atalkd", pidfile, debug)) {
+ case -1:
+ exit(1);
+ case 0: /* child */
+ break;
+ default: /* parent */
+ /*
+ * Wait for the child to send itself a SIGSTOP, after which
+ * we send it a SIGCONT and exit ourself.
+ */
+ if ( wait3( &status, WUNTRACED, (struct rusage *)0 ) != i) {
+ perror( "wait3" ); /* Child died? */
+ atalkd_exit( 1 );
+ }
+ if ( !WIFSTOPPED( status )) {
+ fprintf( stderr, "AppleTalk not up! Check your syslog for the reason." );
+ if ( WIFEXITED( status )) {
+ fprintf( stderr, " Child exited with %d.\n",
+ WEXITSTATUS( status ));
+ } else {
+ fprintf( stderr, " Child died.\n" );
+ }
+ atalkd_exit( 1 );
+ }
+ if ( kill(i, SIGCONT ) < 0 ) {
+ perror( "kill" );
+ atalkd_exit( 1 );
+ }
+ exit( 0 );
+ }
+
+#ifdef ultrix
+ openlog( prog, LOG_PID );
+#else ultrix
+ openlog( prog, LOG_PID, LOG_DAEMON );
+#endif ultrix
+
+ syslog( LOG_INFO, "restart (%s)", version );
+
+ /*
+ * Socket for use in routing ioctl()s. Can't add routes to our
+ * interfaces until we have our routing socket.
+ */
+#ifdef BSD4_4
+ if (( rtfd = socket( PF_ROUTE, SOCK_RAW, AF_APPLETALK )) < 0 ) {
+ syslog( LOG_ERR, "route socket: %m" );
+ atalkd_exit( 1 );
+ }
+ if ( shutdown( rtfd, 0 ) < 0 ) {
+ syslog( LOG_ERR, "route shutdown: %m" );
+ atalkd_exit( 1 );
+ }
+#else BSD4_4
+ if (( rtfd = socket( AF_APPLETALK, SOCK_DGRAM, 0 )) < 0 ) {
+ syslog( LOG_ERR, "route socket: %m" );
+ atalkd_exit( 1 );
+ }
+#endif BSD4_4
+
+ memset(&sv, 0, sizeof(sv));
+ sv.sa_handler = as_down;
+ sigemptyset( &sv.sa_mask );
+ sigaddset( &sv.sa_mask, SIGUSR1 );
+ sigaddset( &sv.sa_mask, SIGALRM );
+ sigaddset( &sv.sa_mask, SIGTERM );
+ sv.sa_flags = SA_RESTART;
+ if ( sigaction( SIGTERM, &sv, NULL) < 0 ) {
+ syslog( LOG_ERR, "sigterm: %m" );
+ atalkd_exit( 1 );
+ }
+
+ sv.sa_handler = as_debug;
+ sigemptyset( &sv.sa_mask );
+ sigaddset( &sv.sa_mask, SIGUSR1 );
+ sigaddset( &sv.sa_mask, SIGALRM );
+ sigaddset( &sv.sa_mask, SIGTERM );
+ sv.sa_flags = SA_RESTART;
+ if ( sigaction( SIGUSR1, &sv, NULL) < 0 ) {
+ syslog( LOG_ERR, "sigusr1: %m" );
+ atalkd_exit( 1 );
+ }
+
+ sv.sa_handler = as_timer;
+ sigemptyset( &sv.sa_mask );
+ sigaddset( &sv.sa_mask, SIGUSR1 );
+ sigaddset( &sv.sa_mask, SIGALRM );
+ sigaddset( &sv.sa_mask, SIGTERM );
+ sv.sa_flags = SA_RESTART;
+ if ( sigaction( SIGALRM, &sv, NULL) < 0 ) {
+ syslog( LOG_ERR, "sigalrm: %m" );
+ atalkd_exit( 1 );
+ }
+
+ it.it_interval.tv_sec = 10L;
+ it.it_interval.tv_usec = 0L;
+ it.it_value.tv_sec = 10L;
+ it.it_value.tv_usec = 0L;
+ if ( setitimer( ITIMER_REAL, &it, NULL) < 0 ) {
+ syslog( LOG_ERR, "setitimer: %m" );
+ atalkd_exit( 1 );
+ }
+
+ ciface = interfaces;
+ bootaddr( ciface );
+ for (;;) {
+ readfds = fds;
+ if ( select( nfds, &readfds, NULL, NULL, NULL) < 0 ) {
+ if ( errno == EINTR ) {
+ errno = 0;
+ continue;
+ } else {
+ syslog( LOG_ERR, "select: %m" );
+ atalkd_exit( 1 );
+ }
+ }
+
+ for ( iface = interfaces; iface; iface = iface->i_next ) {
+ for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+ if ( FD_ISSET( ap->ap_fd, &readfds )) {
+ if ( ap->ap_packet ) {
+ fromlen = sizeof( struct sockaddr_at );
+ if (( c = recvfrom( ap->ap_fd, Packet, sizeof( Packet ),
+ 0, (struct sockaddr *)&sat, &fromlen )) < 0 ) {
+ syslog( LOG_ERR, "recvfrom: %m" );
+ continue;
+ }
+#ifdef DEBUG
+ if ( debug ) {
+ printf( "packet from %u.%u on %s (%x) %d (%d)\n",
+ ntohs( sat.sat_addr.s_net ),
+ sat.sat_addr.s_node, iface->i_name,
+ iface->i_flags, ap->ap_port, ap->ap_fd );
+ bprint( Packet, c );
+ }
+#endif DEBUG
+#ifdef __svr4__
+ if ( sighold( SIGALRM ) || sighold( SIGUSR1 )) {
+ syslog( LOG_ERR, "sighold: %m" );
+ atalkd_exit( 1 );
+ }
+#else __svr4__
+ mask = sigsetmask( sigmask( SIGALRM ) |
+ sigmask( SIGUSR1 ));
+#endif __svr4__
+ if (( *ap->ap_packet )( ap, &sat, Packet, c ) < 0) {
+ syslog(LOG_ERR, "ap->ap_packet: %m");
+ atalkd_exit(1);
+ }
+
+#ifdef DEBUG
+ consistency();
+#endif DEBUG
+#ifdef __svr4__
+ if ( sigrelse( SIGUSR1 ) || sigrelse( SIGALRM )) {
+ syslog( LOG_ERR, "sigrelse: %m" );
+ atalkd_exit( 1 );
+ }
+#else __svr4__
+ sigsetmask( mask );
+#endif __svr4__
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+ * This code is called (from main(), as_timer(), zip_packet(),
+ * and rtmp_packet()) to set the initial "bootstrapping" address
+ * on an interface.
+ */
+bootaddr( iface )
+ struct interface *iface;
+{
+ if ( iface == 0 ) {
+ return;
+ }
+
+ /* consistency */
+ if ( iface->i_flags & IFACE_ADDR ) {
+ syslog( LOG_ERR, "bootaddr OOPS!" );
+ atalkd_exit(1);
+ }
+
+ if ( iface->i_flags & IFACE_PHASE1 ) {
+ setaddr( iface, IFACE_PHASE1, 0,
+ iface->i_caddr.sat_addr.s_node, 0, 0 );
+
+ if ( iface->i_flags & IFACE_LOOPBACK ) {
+ iface->i_flags |= IFACE_CONFIG | IFACE_ADDR;
+ if ( ciface == iface ) {
+ ciface = ciface->i_next;
+ bootaddr( ciface );
+ }
+
+ } else if (rtmp_request( iface ) < 0) {
+ syslog(LOG_ERR, "bootaddr (rtmp_request): %m");
+ atalkd_exit(1);
+ }
+
+ } else {
+ setaddr( iface, IFACE_PHASE2, iface->i_caddr.sat_addr.s_net,
+ iface->i_caddr.sat_addr.s_node,
+ iface->i_rt->rt_firstnet, iface->i_rt->rt_lastnet );
+
+ if ( iface->i_flags & IFACE_LOOPBACK ) {
+ iface->i_flags |= IFACE_CONFIG | IFACE_ADDR;
+ if ( ciface == iface ) {
+ ciface = ciface->i_next;
+ bootaddr( ciface );
+ }
+
+ } else if (zip_getnetinfo( iface ) < 0) {
+ syslog(LOG_ERR, "bootaddr (zip_getnetinfo): %m");
+ atalkd_exit(1);
+ }
+ }
+ ++iface->i_time;
+ iface->i_flags |= IFACE_ADDR;
+ stabletimer = UNSTABLE;
+}
+
+
+/*
+ * Change setaddr()
+ * to manage the i_ports field and the fds for select().
+ */
+setaddr( iface, phase, net, node, first, last )
+ struct interface *iface;
+ u_int8_t phase;
+ u_int16_t net;
+ u_int8_t node;
+ u_int16_t first, last;
+{
+ int i;
+ struct atserv *as;
+ struct atport *ap;
+ struct servent *se;
+ struct sockaddr_at sat;
+ struct netrange nr;
+
+ if ( iface->i_ports == NULL ) { /* allocate port structures */
+ for ( i = 0, as = atserv; i < atservNATSERV; i++, as++ ) {
+ if (( se = getservbyname( as->as_name, "ddp" )) == NULL ) {
+ syslog( LOG_INFO, "%s: service unknown", as->as_name );
+ } else {
+ as->as_port = ntohs( se->s_port );
+ }
+ if (( ap = (struct atport *)malloc( sizeof( struct atport ))) ==
+ NULL ) {
+ syslog( LOG_ERR, "malloc: %m" );
+ atalkd_exit( 1 );
+ }
+ ap->ap_fd = 0;
+ ap->ap_next = iface->i_ports;
+ ap->ap_iface = iface;
+ ap->ap_port = as->as_port;
+ ap->ap_packet = as->as_packet;
+
+ iface->i_ports = ap;
+ }
+ } else { /* close ports */
+ for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+ (void)close( ap->ap_fd );
+ }
+ }
+
+#ifdef BSD4_4
+ iface->i_addr.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ iface->i_addr.sat_family = AF_APPLETALK;
+ iface->i_addr.sat_addr.s_net = net;
+ iface->i_addr.sat_addr.s_node = node;
+
+ nr.nr_phase = phase;
+ nr.nr_firstnet = first;
+ nr.nr_lastnet = last;
+ memcpy( iface->i_addr.sat_zero, &nr, sizeof( struct netrange ));
+
+ if ( ifconfig( iface->i_name, SIOCSIFADDR, &iface->i_addr )) {
+ syslog( LOG_ERR, "setifaddr: %s (%u-%u): %m. try specifying a \
+smaller net range.", iface->i_name, ntohs(first), ntohs(last));
+ atalkd_exit( 1 );
+ }
+ if ( ifconfig( iface->i_name, SIOCGIFADDR, &iface->i_addr )) {
+ syslog( LOG_ERR, "getifaddr: %s: %m", iface->i_name );
+ atalkd_exit( 1 );
+ }
+
+ /* open ports */
+ i = 1; /* enable broadcasts */
+#if defined(__svr4__)
+ syslog(LOG_INFO, "setsockopt incompatible w/ Solaris STREAMS module.");
+#endif
+ for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+ if (( ap->ap_fd = socket( AF_APPLETALK, SOCK_DGRAM, 0 )) < 0 ) {
+ syslog( LOG_ERR, "socket: %m" );
+ atalkd_exit( 1 );
+ }
+#if !defined(__svr4__)
+ setsockopt(ap->ap_fd, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i));
+#endif
+
+ memset( &sat, 0, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+ sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ sat.sat_family = AF_APPLETALK;
+ sat.sat_addr.s_net = iface->i_addr.sat_addr.s_net;
+ sat.sat_addr.s_node = iface->i_addr.sat_addr.s_node;
+ sat.sat_port = ap->ap_port;
+
+ if ( bind( ap->ap_fd, (struct sockaddr *)&sat,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "bind %u.%u:%u: %m",
+ ntohs( sat.sat_addr.s_net ),
+ sat.sat_addr.s_node, sat.sat_port );
+#ifdef SIOCDIFADDR
+ /* remove all interfaces if we have a problem with bind */
+ for (iface = interfaces; iface; iface = iface->i_next) {
+ if (ifconfig( iface->i_name, SIOCDIFADDR, &iface->i_addr )) {
+#ifdef SIOCATALKDIFADDR
+#if (SIOCDIFADDR != SIOCATALKDIFADDR)
+ ifconfig( iface->i_name, SIOCATALKDIFADDR, &iface->i_addr );
+#endif
+#endif
+ }
+ }
+#endif
+ atalkd_exit( 1 );
+ }
+ }
+
+ /* recalculate nfds and fds */
+ FD_ZERO( &fds );
+ for ( nfds = 0, iface = interfaces; iface; iface = iface->i_next ) {
+ for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+ FD_SET( ap->ap_fd, &fds );
+ if ( ap->ap_fd > nfds ) {
+ nfds = ap->ap_fd;
+ }
+ }
+ }
+ nfds++;
+}
+
+ifconfig( iname, cmd, sa )
+ char *iname;
+ unsigned long cmd;
+ struct sockaddr_at *sa;
+{
+ struct ifreq ifr;
+ int s;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy( ifr.ifr_name, iname );
+ ifr.ifr_addr = *(struct sockaddr *)sa;
+
+ if (( s = socket( AF_APPLETALK, SOCK_DGRAM, 0 )) < 0 ) {
+ return( 1 );
+ }
+ if ( ioctl( s, cmd, &ifr ) < 0 ) {
+ close(s);
+ return( 1 );
+ }
+ close( s );
+ if ( cmd == SIOCGIFADDR ) {
+ *(struct sockaddr *)sa = ifr.ifr_addr;
+ }
+ return( 0 );
+}
+
+dumpconfig( iface )
+ struct interface *iface;
+{
+ struct list *l;
+
+ printf( "%s", iface->i_name );
+ if ( iface->i_flags & IFACE_RSEED ) {
+ printf( " -router" );
+ } else if ( iface->i_flags & IFACE_SEED ) {
+ printf( " -seed" );
+ }
+
+ if ( iface->i_flags & IFACE_DONTROUTE)
+ printf( " -dontroute");
+
+ printf( " -phase" );
+ if ( iface->i_flags & IFACE_PHASE1 ) {
+ printf( " 1" );
+ } else {
+ printf( " 2" );
+ }
+ printf( " -net %d", ntohs( iface->i_rt->rt_firstnet ));
+ if ( iface->i_rt->rt_lastnet != iface->i_rt->rt_firstnet ) {
+ printf( "-%d", ntohs( iface->i_rt->rt_lastnet ));
+ }
+ printf( " -addr %u.%u", ntohs( iface->i_addr.sat_addr.s_net ),
+ iface->i_addr.sat_addr.s_node );
+ printf( " -caddr %u.%u", ntohs( iface->i_caddr.sat_addr.s_net ),
+ iface->i_caddr.sat_addr.s_node );
+ for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
+ printf( " -zone %.*s", ((struct ziptab *)l->l_data)->zt_len,
+ ((struct ziptab *)l->l_data)->zt_name );
+ }
+ printf( "\n" );
+}
+
+#ifdef DEBUG
+dumproutes()
+{
+ struct interface *iface;
+ struct rtmptab *rtmp;
+ struct list *l;
+ struct ziptab *zt;
+
+ for ( iface = interfaces; iface; iface = iface->i_next ) {
+ for ( rtmp = iface->i_rt; rtmp; rtmp = rtmp->rt_inext ) {
+ if ( rtmp->rt_gate == 0 ) {
+ if ( rtmp->rt_flags & RTMPTAB_EXTENDED ) {
+ printf( "%u-%u", ntohs( rtmp->rt_firstnet ),
+ ntohs( rtmp->rt_lastnet ));
+ } else {
+ printf( "%u", ntohs( rtmp->rt_firstnet ));
+ }
+ } else {
+ if ( rtmp->rt_flags & RTMPTAB_EXTENDED ) {
+ printf( "%u.%u for %u-%u",
+ ntohs( rtmp->rt_gate->g_sat.sat_addr.s_net ),
+ rtmp->rt_gate->g_sat.sat_addr.s_node,
+ ntohs( rtmp->rt_firstnet ),
+ ntohs( rtmp->rt_lastnet ));
+ } else {
+ printf( "%u.%u for %u",
+ ntohs( rtmp->rt_gate->g_sat.sat_addr.s_net ),
+ rtmp->rt_gate->g_sat.sat_addr.s_node,
+ ntohs( rtmp->rt_firstnet ));
+ }
+ }
+
+ if ( rtmp->rt_iprev == 0 && rtmp != iface->i_rt ) {
+ printf( " *" );
+ }
+
+ for ( l = rtmp->rt_zt; l; l = l->l_next ) {
+ zt = (struct ziptab *)l->l_data;
+ printf( " %.*s", zt->zt_len, zt->zt_name );
+ }
+
+ printf( "\n" );
+ }
+ }
+
+ printf( "\n" );
+ fflush( stdout );
+}
+
+dumpzones()
+{
+ struct interface *iface;
+ struct rtmptab *rtmp;
+ struct list *l;
+ struct ziptab *zt;
+
+ for ( zt = ziptab; zt; zt = zt->zt_next ) {
+ printf( "%.*s", zt->zt_len, zt->zt_name );
+ for ( l = zt->zt_rt; l; l = l->l_next ) {
+ rtmp = (struct rtmptab *)l->l_data;
+ if ( rtmp->rt_flags & RTMPTAB_EXTENDED ) {
+ printf( " %u-%u", ntohs( rtmp->rt_firstnet ),
+ ntohs( rtmp->rt_lastnet ));
+ } else {
+ printf( " %u", ntohs( rtmp->rt_firstnet ));
+ }
+ if ( rtmp->rt_iprev == 0 && rtmp->rt_gate != 0 ) {
+ printf( "*" );
+ }
+ }
+ printf( "\n" );
+ }
+
+ printf( "\n" );
+ fflush( stdout );
+}
+#endif DEBUG
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <syslog.h>
+
+/* work around for FreeBSD */
+#if defined(__FreeBSD__) && (__FreeBSD__ >= 2)
+#include <osreldate.h>
+#if __FreeBSD_version >= 300000
+#include <net/if_dl.h>
+#define NO_DATA_LINK_PASSTHROUGH
+#endif
+#endif
+
+#ifdef __svr4__
+#include <sys/sockio.h>
+#endif __svr4__
+
+#include <atalk/util.h>
+#include <netatalk/endian.h>
+#include "zip.h"
+
+static const unsigned char ethermulti[ 6 ] = {
+ 0x09, 0x00, 0x07, 0xff, 0xff, 0xff,
+};
+
+static const unsigned char ethermultitab[ 253 ][ 6 ] = {
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x00, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x01, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x02, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x03, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x04, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x05, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x06, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x07, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x08, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x09, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x0a, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x0b, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x0c, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x0d, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x0e, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x0f, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x10, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x11, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x12, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x13, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x14, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x15, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x16, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x17, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x18, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x19, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x1a, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x1b, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x1c, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x1d, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x1e, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x1f, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x20, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x21, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x22, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x23, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x24, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x25, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x26, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x27, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x28, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x29, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x2a, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x2b, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x2c, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x2d, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x2e, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x2f, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x30, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x31, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x32, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x33, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x34, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x35, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x36, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x37, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x38, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x39, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x3a, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x3b, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x3c, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x3d, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x3e, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x3f, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x40, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x41, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x42, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x43, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x44, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x45, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x46, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x47, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x48, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x49, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x4a, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x4b, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x4c, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x4d, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x4e, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x4f, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x50, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x51, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x52, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x53, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x54, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x55, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x56, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x57, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x58, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x59, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x5a, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x5b, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x5c, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x5d, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x5e, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x5f, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x60, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x61, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x62, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x63, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x64, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x65, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x66, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x67, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x68, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x69, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x6a, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x6b, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x6c, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x6d, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x6e, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x6f, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x70, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x71, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x72, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x73, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x74, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x75, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x76, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x77, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x78, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x79, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x7a, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x7b, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x7c, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x7d, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x7e, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x7f, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x80, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x81, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x82, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x83, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x84, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x85, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x86, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x87, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x88, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x89, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x8a, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x8b, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x8c, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x8d, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x8e, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x8f, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x90, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x91, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x92, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x93, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x94, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x95, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x96, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x97, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x98, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x99, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x9a, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x9b, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x9c, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x9d, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x9e, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0x9f, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa0, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa1, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa2, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa3, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa4, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa5, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa6, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa7, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa8, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa9, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xaa, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xab, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xac, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xad, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xae, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xaf, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb0, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb1, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb2, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb3, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb4, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb5, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb6, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb7, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb8, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb9, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xba, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xbb, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xbc, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xbd, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xbe, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xbf, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc0, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc1, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc2, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc3, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc4, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc5, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc6, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc7, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc8, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc9, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xca, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xcb, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xcc, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xcd, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xce, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xcf, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd0, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd1, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd2, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd3, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd4, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd5, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd6, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd7, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd8, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd9, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xda, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xdb, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xdc, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xdd, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xde, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xdf, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe0, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe1, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe2, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe3, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe4, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe5, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe6, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe7, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe8, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe9, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xea, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xeb, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xec, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xed, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xee, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xef, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf0, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf1, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf2, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf3, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf4, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf5, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf6, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf7, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf8, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf9, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xfa, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xfb, },
+ { 0x09, 0x00, 0x07, 0x00, 0x00, 0xfc, },
+};
+
+static const unsigned char tokenmulti[ 6 ] = {
+ 0xc0, 0x00, 0x40, 0x00, 0x00, 0x00,
+};
+
+static const unsigned char tokenmultitab[ 19 ][ 6 ] = {
+ { 0xc0, 0x00, 0x00, 0x00, 0x08, 0x00, },
+ { 0xc0, 0x00, 0x00, 0x00, 0x10, 0x00, },
+ { 0xc0, 0x00, 0x00, 0x00, 0x20, 0x00, },
+ { 0xc0, 0x00, 0x00, 0x00, 0x40, 0x00, },
+ { 0xc0, 0x00, 0x00, 0x00, 0x80, 0x00, },
+ { 0xc0, 0x00, 0x00, 0x01, 0x00, 0x00, },
+ { 0xc0, 0x00, 0x00, 0x02, 0x00, 0x00, },
+ { 0xc0, 0x00, 0x00, 0x04, 0x00, 0x00, },
+ { 0xc0, 0x00, 0x00, 0x08, 0x00, 0x00, },
+ { 0xc0, 0x00, 0x00, 0x10, 0x00, 0x00, },
+ { 0xc0, 0x00, 0x00, 0x20, 0x00, 0x00, },
+ { 0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, },
+ { 0xc0, 0x00, 0x00, 0x80, 0x00, 0x00, },
+ { 0xc0, 0x00, 0x01, 0x00, 0x00, 0x00, },
+ { 0xc0, 0x00, 0x02, 0x00, 0x00, 0x00, },
+ { 0xc0, 0x00, 0x04, 0x00, 0x00, 0x00, },
+ { 0xc0, 0x00, 0x08, 0x00, 0x00, 0x00, },
+ { 0xc0, 0x00, 0x10, 0x00, 0x00, 0x00, },
+ { 0xc0, 0x00, 0x20, 0x00, 0x00, 0x00, },
+};
+
+
+
+/* configure multicast for a given named interface */
+int addmulti(const char *name, const unsigned char *data)
+{
+#ifdef NO_DATA_LINK_PASSTHROUGH
+ struct sockaddr_dl sa;
+#else
+ struct sockaddr sa;
+#endif
+
+ memset(&sa, 0, sizeof(sa));
+#ifdef NO_DATA_LINK_PASSTHROUGH
+ sa.sdl_family = AF_LINK;
+ memcpy(LLADDR(&sa), data ? data : ethermulti, sizeof(ethermulti));
+ sa.sdl_alen = sizeof(ethermulti);
+ sa.sdl_len = sizeof(sa);
+#else
+ memcpy(sa.sa_data, data ? data : ethermulti, sizeof( ethermulti ));
+#endif
+ if (ifconfig(name, SIOCADDMULTI, &sa))
+ return -1;
+
+ return 0;
+}
+
+static u_int16_t
+atalk_cksum( data, len )
+ u_char *data;
+ int len;
+{
+ u_char *end;
+ u_int32_t cksum = 0;
+
+ for ( end = data + len; data < end; data++ ) {
+ cksum = ( cksum + *data ) << 1;
+ if ( cksum & 0x00010000 ) {
+ cksum++;
+ }
+ cksum &= 0x0000ffff;
+ }
+
+ if ( cksum == 0 ) {
+ cksum = 0x0000ffff;
+ }
+
+ return( (u_int16_t) cksum );
+}
+
+/*
+ * Fill in multicast for zone. There is a general issue here: how can
+ * we tell the type of interface we're configuring for? E.g. Is it
+ * ethernet, tokenring, or FDDI? (Of course, FDDI and Ethernet look just
+ * alike.)
+ */
+int
+zone_bcast( zt )
+ struct ziptab *zt;
+{
+ u_char uname[ 32 ];
+ u_int16_t cksum;
+ int i;
+
+ if (!zt->zt_bcast &&
+ (zt->zt_bcast = (u_char *) malloc(sizeof( ethermulti ))) == NULL) {
+ syslog( LOG_ERR, "zone_bcast malloc: %m" );
+ return -1;
+ }
+
+ for ( i = 0; i < zt->zt_len; i++ ) {
+ uname[ i ] = diatoupper(zt->zt_name[ i ]);
+ }
+ cksum = atalk_cksum( uname, zt->zt_len );
+#define elements(a) (sizeof(a)/sizeof((a)[0]))
+ memcpy(zt->zt_bcast, ethermultitab[ cksum % elements( ethermultitab ) ],
+ sizeof( ethermulti ));
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1997 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef ATALKD_MULTICAST_H
+#define ATALKD_MULTICAST_H 1
+
+#include <sys/cdefs.h>
+#include "zip.h"
+
+extern unsigned char ethermultitab[ 253 ][ 6 ];
+extern unsigned char tokenmultitab[ 19 ][ 6 ];
+extern unsigned char ethermulti[ 6 ];
+
+int addmulti __P((const char *, const unsigned char *));
+int zone_bcast __P((struct ziptab *));
+
+#endif /* atalkd/multicast.h */
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netatalk/at.h>
+#include <atalk/ddp.h>
+#include <atalk/atp.h>
+#include <atalk/nbp.h>
+#include <atalk/util.h>
+
+#ifdef __svr4__
+#include <sys/sockio.h>
+#endif __svr4__
+
+#include "atserv.h"
+#include "interface.h"
+#include "list.h"
+#include "rtmp.h"
+#include "gate.h"
+#include "zip.h"
+#include "nbp.h"
+#include "multicast.h"
+
+extern int transition;
+
+struct nbptab *nbptab = NULL;
+
+static
+void nbp_ack( fd, nh_op, nh_id, to )
+ int fd;
+ int nh_op;
+ int nh_id;
+ struct sockaddr_at *to;
+{
+ struct nbphdr nh;
+ char *data, packet[ SZ_NBPHDR + 1 ];
+
+ nh.nh_op = nh_op;
+ nh.nh_cnt = 0;
+ nh.nh_id = nh_id;
+ data = packet;
+ *data++ = DDPTYPE_NBP;
+ bcopy( &nh, data, SZ_NBPHDR );
+ data += SZ_NBPHDR;
+ if ( sendto( fd, packet, data - packet, 0, (struct sockaddr *)to,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "sendto: %m" );
+ }
+}
+
+int nbp_packet( ap, from, data, len )
+ struct atport *ap;
+ struct sockaddr_at *from;
+ char *data;
+ int len;
+{
+ struct nbphdr nh;
+ struct nbptuple nt;
+ struct nbpnve nn;
+ struct sockaddr_at sat;
+ struct nbptab *ntab;
+ struct ziptab *zt;
+ struct interface *iface;
+ struct list *l;
+ struct rtmptab *rtmp;
+ char *end, *nbpop, *zonep, packet[ ATP_BUFSIZ ];
+ int n, i, cc, locallkup;
+
+ end = data + len;
+ if ( data >= end ) {
+ syslog( LOG_INFO, "nbp_packet malformed packet" );
+ return 1;
+ }
+ if ( *data++ != DDPTYPE_NBP ) {
+ syslog( LOG_INFO, "nbp_packet bad ddp type" );
+ return 1;
+ }
+
+ if ( data + SZ_NBPHDR + SZ_NBPTUPLE > end ) {
+ syslog( LOG_INFO, "nbp_packet: malformed packet" );
+ return 1;
+ }
+ memcpy( &nh, data, SZ_NBPHDR );
+ nbpop = data; /* remember for fwd and brrq */
+ data += SZ_NBPHDR;
+ if ( nh.nh_cnt != 1 ) {
+ syslog( LOG_INFO, "nbp_packet: bad tuple count (%d/%d)", nh.nh_cnt,
+ nh.nh_op );
+ return 1;
+ }
+
+ memcpy( &nt, data, SZ_NBPTUPLE );
+ data += SZ_NBPTUPLE;
+
+ memset( &nn.nn_sat, 0, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+ nn.nn_sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ nn.nn_sat.sat_family = AF_APPLETALK;
+ nn.nn_sat.sat_addr.s_net = nt.nt_net;
+ nn.nn_sat.sat_addr.s_node = nt.nt_node;
+ nn.nn_sat.sat_port = nt.nt_port;
+
+ /* object */
+ if ( data >= end || ( *data < 0 || *data > 32 ) || data + *data > end ) {
+ syslog( LOG_INFO, "nbp_packet: malformed packet" );
+ return 1;
+ }
+ nn.nn_objlen = *data++;
+ memcpy( nn.nn_obj, data, nn.nn_objlen );
+ data += nn.nn_objlen;
+
+ /* type */
+ if ( data >= end || ( *data < 0 || *data > 32 ) || data + *data > end ) {
+ syslog( LOG_INFO, "nbp_packet: malformed packet" );
+ return 1;
+ }
+ nn.nn_typelen = *data++;
+ memcpy( nn.nn_type, data, nn.nn_typelen );
+ data += nn.nn_typelen;
+
+ /* zone */
+ if ( data >= end || ( *data < 0 || *data > 32 ) || data + *data > end ) {
+ syslog( LOG_INFO, "nbp_packet: malformed packet" );
+ return 1;
+ }
+ zonep = data; /* remember for fwd */
+ nn.nn_zonelen = *data++;
+ memcpy( nn.nn_zone, data, nn.nn_zonelen );
+ data += nn.nn_zonelen;
+
+ if ( data != end ) {
+ syslog( LOG_INFO, "nbp_packet: malformed packet" );
+ return 1;
+ }
+
+ locallkup = 0;
+ switch ( nh.nh_op ) {
+
+ case NBPOP_RGSTR :
+ /*
+ * Find the ziptab entry for the zone we're trying to register in.
+ */
+ if ( nn.nn_zonelen == 0 ||
+ ( nn.nn_zonelen == 1 && *nn.nn_zone == '*' )) {
+ if ( interfaces->i_next->i_rt->rt_zt ) {
+ zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
+ } else {
+ zt = 0;
+ }
+ } else {
+ for ( zt = ziptab; zt; zt = zt->zt_next ) {
+ if ( zt->zt_len == nn.nn_zonelen && strndiacasecmp( zt->zt_name,
+ nn.nn_zone, zt->zt_len ) == 0 ) {
+ break;
+ }
+ }
+ if ( zt == 0 ) {
+ nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
+ return 0;
+ }
+ }
+
+ /*
+ * Observe that we don't have to do any local-zone verification
+ * if the zone aleady has a multicast address set.
+ */
+ if ( zt != 0 && zt->zt_bcast == 0 ) {
+ /*
+ * Check if zone is associated with any of our local interfaces.
+ */
+ for ( iface = interfaces; iface; iface = iface->i_next ) {
+ for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
+ if ( zt == (struct ziptab *)l->l_data ) {
+ break;
+ }
+ }
+ if ( l != 0 ) {
+ break;
+ }
+ }
+ if ( iface == 0 ) {
+ nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
+ return 0;
+ }
+
+ /* calculate and save multicast address */
+ if (zone_bcast(zt) < 0) {
+ syslog(LOG_ERR, "nbp_packet: zone_bcast");
+ return -1;
+ }
+
+ for ( iface = interfaces; iface; iface = iface->i_next ) {
+ if (( iface->i_flags & IFACE_PHASE2 ) == 0 ) {
+ continue;
+ }
+ for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
+ if ( zt == (struct ziptab *)l->l_data ) {
+ /* add multicast */
+ if (addmulti(iface->i_name, zt->zt_bcast) < 0) {
+ syslog( LOG_ERR, "nbp_packet: addmulti: %m" );
+ return -1;
+ }
+ }
+ }
+ }
+ }
+
+ if (( ntab = (struct nbptab *)malloc( sizeof( struct nbptab )))
+ == 0 ) {
+ syslog( LOG_ERR, "nbp_packet: malloc: %m" );
+ return -1;
+ }
+ memcpy( &ntab->nt_nve, &nn, sizeof( struct nbpnve ));
+ ntab->nt_iface = ap->ap_iface;
+ ntab->nt_next = nbptab;
+ ntab->nt_prev = 0;
+ if ( nbptab ) {
+ nbptab->nt_prev = ntab;
+ }
+ nbptab = ntab;
+
+ nbp_ack( ap->ap_fd, NBPOP_OK, (int)nh.nh_id, from );
+ break;
+
+ case NBPOP_UNRGSTR :
+ /* deal with local zone info */
+ if (( nn.nn_zonelen == 1 && *nn.nn_zone == '*' ) ||
+ ( nn.nn_zonelen == 0 )) {
+ locallkup = 1;
+ if ( interfaces->i_next->i_rt->rt_zt ) {
+ zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
+ } else {
+ zt = NULL;
+ }
+ }
+
+ /* remove from our data, perhaps removing a multicast address */
+ for ( ntab = nbptab; ntab; ntab = ntab->nt_next ) {
+ if ( ntab->nt_nve.nn_objlen != nn.nn_objlen ||
+ strndiacasecmp( ntab->nt_nve.nn_obj, nn.nn_obj,
+ nn.nn_objlen )) {
+ continue;
+ }
+ if ( ntab->nt_nve.nn_typelen != nn.nn_typelen ||
+ strndiacasecmp( ntab->nt_nve.nn_type, nn.nn_type,
+ nn.nn_typelen )) {
+ continue;
+ }
+ /*
+ * I *think* we really do check the zone, here.
+ *
+ * i changed it to better handle local zone cases as well.
+ * -- asun
+ */
+
+ /* match local zones */
+ if (locallkup) {
+ /* ntab is also local zone */
+ if (( ntab->nt_nve.nn_zonelen == 1 &&
+ *ntab->nt_nve.nn_zone == '*' ) ||
+ (ntab->nt_nve.nn_zonelen == 0))
+ break;
+
+ /* ntab is default zone */
+ if (zt && (zt->zt_len == ntab->nt_nve.nn_zonelen) &&
+ (strndiacasecmp(ntab->nt_nve.nn_zone, zt->zt_name,
+ zt->zt_len) == 0)) {
+ break;
+ }
+ }
+
+ /* match particular zone */
+ if ((ntab->nt_nve.nn_zonelen == nn.nn_zonelen) &&
+ (strndiacasecmp( ntab->nt_nve.nn_zone, nn.nn_zone,
+ nn.nn_zonelen ) == 0)) {
+ break;
+ }
+ }
+ if ( ntab == 0 ) {
+ nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
+ return 0;
+ }
+
+ if ( ntab->nt_next != 0 ) {
+ ntab->nt_next->nt_prev = ntab->nt_prev;
+ }
+ if ( ntab->nt_prev != 0 ) {
+ ntab->nt_prev->nt_next = ntab->nt_next;
+ }
+ if ( ntab == nbptab ) {
+ nbptab = ntab->nt_next;
+ }
+
+ /*
+ * Check for another nbptab entry with the same zone. If
+ * there isn't one, find the ziptab entry for the zone and
+ * remove the multicast address from the appropriate interfaces.
+ * XXX
+ */
+
+ nbp_ack( ap->ap_fd, NBPOP_OK, (int)nh.nh_id, from );
+ break;
+
+ case NBPOP_BRRQ :
+ /*
+ * Couple of things: 1. Unless we have the -t flag (which is sort
+ * of a misnomer, since you need it if you're doing any phase 1
+ * work), always send NBPOP_FWD. 2. If we get a zone of '*',
+ * and we know what the sender meant by '*', we copy the real
+ * zone into the packet.
+ */
+ if ( nn.nn_zonelen == 0 ||
+ ( nn.nn_zonelen == 1 && *nn.nn_zone == '*' )) {
+ iface = ap->ap_iface;
+ if ( iface && iface->i_rt->rt_zt ) {
+ zt = (struct ziptab *)iface->i_rt->rt_zt->l_data;
+ } else if ( interfaces->i_next->i_rt->rt_zt ) {
+ zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
+ } else {
+ zt = NULL;
+ }
+
+ /*
+ * Copy zone into packet. Note that we're changing len, data, and
+ * nbpop. Later, we'll use ( data - len ) to mean the beginning
+ * of this packet.
+ */
+ if ( zt ) {
+ memcpy( packet, data - len, len );
+ nbpop = packet + ( len - ( data - nbpop ));
+ data = packet + ( len - ( data - zonep ));
+ *data++ = zt->zt_len;
+ memcpy( data, zt->zt_name, zt->zt_len );
+ data += zt->zt_len;
+ len = data - packet;
+ }
+ } else {
+ for ( zt = ziptab; zt; zt = zt->zt_next ) {
+ if ( zt->zt_len == nn.nn_zonelen && strndiacasecmp( zt->zt_name,
+ nn.nn_zone, zt->zt_len ) == 0 ) {
+ break;
+ }
+ }
+ if ( zt == 0 ) {
+ nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
+ return 0;
+ }
+ }
+
+ /*
+ * If we've got no zones, send out LKUP on the local net.
+ * Otherwise, look through the zone table.
+ */
+ if ( zt == 0 ) {
+#ifdef BSD4_4
+ sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ sat.sat_family = AF_APPLETALK;
+ sat.sat_port = ap->ap_port;
+
+ nh.nh_op = NBPOP_LKUP;
+ memcpy( nbpop, &nh, SZ_NBPHDR );
+ sat.sat_addr.s_net = 0; /* XXX */
+ sat.sat_addr.s_node = ATADDR_BCAST;
+
+ /* Find the first non-loopback ap */
+ for ( iface = interfaces; iface; iface = iface->i_next ) {
+ if ((( iface->i_flags & IFACE_LOOPBACK ) == 0) &&
+ (iface == ap->ap_iface ||
+ (iface->i_flags & IFACE_ISROUTER))) {
+ break;
+ }
+ }
+ if ( iface == 0 ) {
+ return 0;
+ }
+ for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+ if ( ap->ap_packet == nbp_packet ) {
+ break;
+ }
+ }
+
+ if ( sendto( ap->ap_fd, data - len, len, 0, (struct sockaddr *)&sat,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "nbp brrq sendto: %m" );
+ }
+
+ locallkup = 1;
+ } else {
+#ifdef BSD4_4
+ sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ sat.sat_family = AF_APPLETALK;
+ sat.sat_port = ap->ap_port;
+ for ( l = zt->zt_rt; l; l = l->l_next ) {
+ rtmp = (struct rtmptab *)l->l_data;
+
+ if ( rtmp->rt_gate == 0 ) {
+ for ( iface = interfaces; iface;
+ iface = iface->i_next ) {
+ if ( iface->i_rt == rtmp ) {
+ break;
+ }
+ }
+ if ( !iface ) {
+ syslog( LOG_ERR, "nbp_packet: \
+Can't find route's interface!" );
+ return -1;
+ }
+ ap = iface->i_ports;
+ } else {
+ ap = rtmp->rt_gate->g_iface->i_ports;
+ }
+ for ( ; ap; ap = ap->ap_next ) {
+ if ( ap->ap_packet == nbp_packet ) {
+ break;
+ }
+ }
+ if ( !ap ) {
+ syslog( LOG_ERR, "nbp_packet: Can't find port!" );
+ return -1;
+ }
+
+ if ( transition &&
+ ( rtmp->rt_flags & RTMPTAB_EXTENDED ) == 0 ) {
+ if ( rtmp->rt_gate == 0 ) {
+ locallkup = 1;
+ }
+ nh.nh_op = NBPOP_LKUP;
+ bcopy( &nh, nbpop, SZ_NBPHDR );
+ sat.sat_addr.s_net = rtmp->rt_firstnet;
+ sat.sat_addr.s_node = ATADDR_BCAST;
+ } else {
+ if ( rtmp->rt_gate == 0 ) {
+ nh.nh_op = NBPOP_LKUP;
+ bcopy( &nh, nbpop, SZ_NBPHDR );
+ sat.sat_addr.s_net = 0;
+ sat.sat_addr.s_node = ATADDR_BCAST;
+ locallkup = 1;
+ } else {
+ nh.nh_op = NBPOP_FWD;
+ bcopy( &nh, nbpop, SZ_NBPHDR );
+ sat.sat_addr.s_net = rtmp->rt_firstnet;
+ sat.sat_addr.s_node = 0;
+ }
+ }
+
+ if ( sendto( ap->ap_fd, data - len, len, 0,
+ (struct sockaddr *)&sat,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "nbp brrq sendto %u.%u: %m",
+ ntohs( sat.sat_addr.s_net ), sat.sat_addr.s_node );
+ continue;
+ }
+ }
+ }
+
+ if ( !locallkup ) {
+ break;
+ }
+ /*FALL THROUGH*/
+
+ case NBPOP_FWD :
+ /* send lkup on net. we need to make sure we're a router. */
+ if ( !locallkup && (ap->ap_iface->i_flags & IFACE_ISROUTER)) {
+ nh.nh_op = NBPOP_LKUP;
+ bcopy( &nh, nbpop, SZ_NBPHDR );
+ from->sat_addr.s_net = 0;
+ from->sat_addr.s_node = ATADDR_BCAST;
+ if ( sendto( ap->ap_fd, data - len, len, 0, (struct sockaddr *)from,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "nbp fwd sendto %u.%u: %m",
+ ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+ return 0;
+ }
+ }
+ /*FALL THROUGH*/
+
+ case NBPOP_LKUP :
+ /* search our data */
+ n = i = 0;
+ data = packet + 1 + SZ_NBPHDR;
+ end = packet + sizeof( packet );
+
+ for ( ntab = nbptab; ntab; ntab = ntab->nt_next ) {
+ /* don't send out entries if we don't want to route. */
+ if ((ap->ap_iface != ntab->nt_iface) &&
+ (ntab->nt_iface->i_flags & IFACE_ISROUTER) == 0) {
+ continue;
+ }
+
+ if ( nn.nn_objlen != 1 || *nn.nn_obj != '=' ) {
+ if ( ntab->nt_nve.nn_objlen != nn.nn_objlen ||
+ strndiacasecmp( ntab->nt_nve.nn_obj, nn.nn_obj,
+ nn.nn_objlen )) {
+ continue;
+ }
+ }
+
+ if ( nn.nn_typelen != 1 || *nn.nn_type != '=' ) {
+ if ( ntab->nt_nve.nn_typelen != nn.nn_typelen ||
+ strndiacasecmp( ntab->nt_nve.nn_type, nn.nn_type,
+ nn.nn_typelen )) {
+ continue;
+ }
+ }
+
+ if ( nn.nn_zonelen != 0 &&
+ ( nn.nn_zonelen != 1 || *nn.nn_zone != '*' )) {
+ if ( ntab->nt_nve.nn_zonelen == 0 ||
+ ( ntab->nt_nve.nn_zonelen == 1 &&
+ *ntab->nt_nve.nn_zone == '*' )) {
+ if ( interfaces->i_next->i_rt->rt_zt ) {
+ zt = (struct ziptab *)interfaces->i_next->i_rt->
+ rt_zt->l_data;
+ if ( zt->zt_len != nn.nn_zonelen ||
+ strndiacasecmp( zt->zt_name, nn.nn_zone,
+ zt->zt_len )) {
+ continue;
+ }
+ }
+ } else {
+ if ( ntab->nt_nve.nn_zonelen != nn.nn_zonelen ||
+ strndiacasecmp( ntab->nt_nve.nn_zone, nn.nn_zone,
+ nn.nn_zonelen )) {
+ continue;
+ }
+ }
+ }
+
+ /*
+ * Another tuple won't fit. Send what we've already
+ * got, and start the next packet.
+ */
+ if ( data + SZ_NBPTUPLE + 3 + ntab->nt_nve.nn_objlen +
+ ntab->nt_nve.nn_typelen + ntab->nt_nve.nn_zonelen > end ) {
+ nh.nh_op = NBPOP_LKUPREPLY;
+ nh.nh_cnt = n;
+ cc = data - packet;
+ data = packet;
+ *data++ = DDPTYPE_NBP;
+ bcopy( &nh, data, SZ_NBPHDR );
+
+ if ( sendto( ap->ap_fd, packet, cc, 0,
+ (struct sockaddr *)&nn.nn_sat,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "nbp lkup sendto %u.%u: %m",
+ ntohs( nn.nn_sat.sat_addr.s_net ),
+ nn.nn_sat.sat_addr.s_node );
+ return 0;
+ }
+
+ n = 0;
+ data = packet + 1 + SZ_NBPHDR;
+ end = packet + sizeof( packet );
+ }
+
+ nt.nt_net = ntab->nt_nve.nn_sat.sat_addr.s_net;
+ nt.nt_node = ntab->nt_nve.nn_sat.sat_addr.s_node;
+ nt.nt_port = ntab->nt_nve.nn_sat.sat_port;
+ /*
+ * Right now, we'll just give each name a unique enum. In
+ * the future, we might need to actually assign and save
+ * an enum, based on the associated address. For the moment,
+ * the enums will be unique and constant, since the order
+ * is fixed.
+ */
+ nt.nt_enum = i++;
+
+ memcpy( data, &nt, SZ_NBPTUPLE );
+ data += SZ_NBPTUPLE;
+
+ *data++ = ntab->nt_nve.nn_objlen;
+ memcpy( data, ntab->nt_nve.nn_obj, ntab->nt_nve.nn_objlen );
+ data += ntab->nt_nve.nn_objlen;
+
+ *data++ = ntab->nt_nve.nn_typelen;
+ memcpy(data, ntab->nt_nve.nn_type, ntab->nt_nve.nn_typelen );
+ data += ntab->nt_nve.nn_typelen;
+
+ /*
+ * Macs won't see something with a zone of 0 length. We
+ * will always return '*' instead. Perhaps we should
+ * unconditionally return the real zone?
+ */
+ if ( ntab->nt_nve.nn_zonelen ) {
+ *data++ = ntab->nt_nve.nn_zonelen;
+ bcopy( ntab->nt_nve.nn_zone, data, ntab->nt_nve.nn_zonelen );
+ data += ntab->nt_nve.nn_zonelen;
+ } else {
+ *data++ = 1;
+ *data++ = '*';
+ }
+
+ n++;
+ }
+
+ if ( n != 0 ) {
+ nh.nh_op = NBPOP_LKUPREPLY;
+ nh.nh_cnt = n;
+ cc = data - packet;
+ data = packet;
+ *data++ = DDPTYPE_NBP;
+ bcopy( &nh, data, SZ_NBPHDR );
+
+ if ( sendto( ap->ap_fd, packet, cc, 0,
+ (struct sockaddr *)&nn.nn_sat,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "nbp lkup sendto %u.%u: %m",
+ ntohs( nn.nn_sat.sat_addr.s_net ),
+ nn.nn_sat.sat_addr.s_node );
+ return 0;
+ }
+ }
+ break;
+
+ default :
+ syslog( LOG_INFO, "nbp_packet: bad op (%d)", nh.nh_op );
+ return 1;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+struct nbptab {
+ struct nbptab *nt_prev, *nt_next;
+ struct nbpnve nt_nve;
+ struct interface *nt_iface;
+};
+
+extern struct nbptab *nbptab;
--- /dev/null
+/*
+ * Copyright (c) 1990,1996 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/route.h>
+
+#include <netatalk/at.h>
+
+#include "rtmp.h"
+
+#ifndef BSD4_4
+route( message, dst, gate, flags )
+ int message;
+ struct sockaddr *dst, *gate;
+ int flags;
+{
+ struct rtentry rtent;
+
+ bzero( &rtent, sizeof( struct rtentry ));
+ rtent.rt_dst = *dst;
+ rtent.rt_gateway = *gate;
+ rtent.rt_flags = flags;
+ return( ioctl( rtfd, message, &rtent ));
+}
+
+#else BSD4_4
+
+struct sockaddr_m {
+ u_char sam_len;
+ u_char sam_family;
+ u_short sam_pad;
+ u_short sam_mask;
+} mask = { sizeof( struct sockaddr_m ), 0, 0, 0xffff };
+
+struct rt_msg_at {
+ struct rt_msghdr rtma_rtm;
+ struct sockaddr_at rtma_dst;
+ struct sockaddr_at rtma_gate;
+ struct sockaddr_m rtma_mask;
+} rtma;
+
+route( message, dst, gate, flags )
+ int message;
+ struct sockaddr_at *dst, *gate;
+ int flags;
+{
+ int rc;
+
+ bzero( &rtma, sizeof( struct rt_msg_at ));
+ rtma.rtma_rtm.rtm_msglen = sizeof( struct rt_msg_at );
+ rtma.rtma_rtm.rtm_version = RTM_VERSION;
+ rtma.rtma_rtm.rtm_type = message;
+ rtma.rtma_rtm.rtm_pid = getpid();
+ rtma.rtma_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
+ if ( flags & RTF_HOST ) {
+ rtma.rtma_rtm.rtm_msglen = sizeof( struct rt_msg_at ) -
+ sizeof( struct sockaddr_m );
+ } else {
+ rtma.rtma_rtm.rtm_msglen = sizeof( struct rt_msg_at );
+ rtma.rtma_rtm.rtm_addrs |= RTA_NETMASK;
+ rtma.rtma_mask = mask;
+ }
+
+ rtma.rtma_rtm.rtm_flags = flags;
+ rtma.rtma_dst = *dst;
+ rtma.rtma_gate = *gate;
+ if (( rc = write( rtfd, &rtma, rtma.rtma_rtm.rtm_msglen )) !=
+ rtma.rtma_rtm.rtm_msglen ) {
+ return( -1 );
+ }
+ return( 0 );
+}
+#endif BSD4_4
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdlib.h>
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+
+#ifdef __svr4__
+#include <sys/sockio.h>
+#endif __svr4__
+
+#include <atalk/ddp.h>
+#include <atalk/atp.h>
+#include <atalk/rtmp.h>
+
+#include "interface.h"
+#include "gate.h"
+#include "rtmp.h"
+#include "zip.h"
+#include "list.h"
+#include "atserv.h"
+
+
+void rtmp_delzonemap(rtmp)
+ struct rtmptab *rtmp;
+{
+ struct list *lz, *flz, *lr, *flr;
+ struct ziptab *zt;
+
+ lz = rtmp->rt_zt;
+ while ( lz ) { /* for each zone */
+ zt = (struct ziptab *)lz->l_data;
+ lr = zt->zt_rt;
+ while ( lr ) { /* for each route */
+ if ( (struct rtmptab *)lr->l_data == rtmp ) {
+ if ( lr->l_prev == NULL ) { /* head */
+ if ( lr->l_next == NULL ) { /* last route in zone */
+ if ( zt->zt_prev == NULL ) {
+ ziptab = zt->zt_next;
+ } else {
+ zt->zt_prev->zt_next = zt->zt_next;
+ }
+ if ( zt->zt_next == NULL ) {
+ ziplast = zt->zt_prev;
+ } else {
+ zt->zt_next->zt_prev = zt->zt_prev;
+ }
+ free( zt->zt_bcast );
+ free( zt->zt_name );
+ free( zt );
+ } else {
+ zt->zt_rt = lr->l_next;
+ }
+ } else {
+ lr->l_prev->l_next = lr->l_next;
+ }
+ if ( lr->l_next != NULL ) {
+ lr->l_next->l_prev = lr->l_prev;
+ }
+ flr = lr;
+ lr = lr->l_next;
+ free( flr );
+ } else {
+ lr = lr->l_next;
+ }
+ }
+ flz = lz;
+ lz = lz->l_next;
+ free( flz );
+ }
+ rtmp->rt_zt = NULL;
+}
+
+
+/*
+ * Complete configuration for phase 1 interface using RTMP information.
+ */
+static int rtmp_config( rh, iface )
+ struct rtmp_head *rh;
+ struct interface *iface;
+{
+ extern int stabletimer;
+ int cc;
+
+ /*
+ * If we're configuring a phase 2 interface, don't complete
+ * configuration with RTMP.
+ */
+ if ( iface->i_flags & IFACE_PHASE2 ) {
+ syslog( LOG_INFO, "rtmp_config ignoring data" );
+ return 0;
+ }
+
+ /*
+ * Check our seed information, and reconfigure.
+ */
+ if ( rh->rh_net != iface->i_addr.sat_addr.s_net ) {
+ if (( iface->i_flags & IFACE_SEED ) &&
+ rh->rh_net != iface->i_caddr.sat_addr.s_net) {
+ syslog( LOG_ERR, "rtmp_config net mismatch %u != %u",
+ ntohs( rh->rh_net ),
+ ntohs( iface->i_addr.sat_addr.s_net ));
+ return 1;
+ }
+ iface->i_addr.sat_addr.s_net = rh->rh_net;
+
+ /*
+ * It is possible that we will corrupt our route database
+ * by just forcing this change. XXX
+ */
+ iface->i_rt->rt_firstnet = iface->i_rt->rt_lastnet = rh->rh_net;
+
+ setaddr( iface, IFACE_PHASE1, iface->i_addr.sat_addr.s_net,
+ iface->i_addr.sat_addr.s_node, rh->rh_net, rh->rh_net );
+ stabletimer = UNSTABLE;
+ }
+
+ /* add addr to loopback route */
+ if ((cc = looproute( iface, RTMP_ADD )) < 0 )
+ return -1;
+
+ if (cc) {
+ syslog( LOG_ERR, "rtmp_config: can't route %u.%u to loopback: %m",
+ ntohs( iface->i_addr.sat_addr.s_net ),
+ iface->i_addr.sat_addr.s_node );
+ }
+
+ syslog( LOG_INFO, "rtmp_config configured %s", iface->i_name );
+ iface->i_flags |= IFACE_CONFIG;
+ if ( iface == ciface ) {
+ ciface = ciface->i_next;
+ bootaddr( ciface );
+ }
+
+ return 0;
+}
+
+/*
+ * Delete rtmp from the per-interface in-use table, remove all
+ * zone references, and remove the route from the kernel.
+ */
+static void rtmp_delinuse( rtmp )
+ struct rtmptab *rtmp;
+{
+ struct rtmptab *irt;
+
+ irt = rtmp->rt_gate->g_iface->i_rt;
+ if ( irt->rt_inext == rtmp ) { /* first */
+ if ( rtmp->rt_iprev == rtmp ) { /* only */
+ irt->rt_inext = NULL;
+ } else {
+ irt->rt_inext = rtmp->rt_inext;
+ rtmp->rt_inext->rt_iprev = rtmp->rt_iprev;
+ }
+ } else {
+ if ( rtmp->rt_inext == NULL ) { /* last */
+ rtmp->rt_iprev->rt_inext = NULL;
+ irt->rt_inext->rt_iprev = rtmp->rt_iprev;
+ } else {
+ rtmp->rt_iprev->rt_inext = rtmp->rt_inext;
+ rtmp->rt_inext->rt_iprev = rtmp->rt_iprev;
+ }
+ }
+ rtmp->rt_iprev = NULL;
+ rtmp->rt_inext = NULL;
+
+ /* remove zone map */
+ rtmp_delzonemap(rtmp);
+
+ /* remove old route */
+ gateroute( RTMP_DEL, rtmp );
+}
+
+/*
+ * Add rtmp to the per-interface in-use table. No verification is done...
+ */
+static void rtmp_addinuse( rtmp )
+ struct rtmptab *rtmp;
+{
+ struct rtmptab *irt;
+
+ gateroute( RTMP_ADD, rtmp );
+
+ irt = rtmp->rt_gate->g_iface->i_rt;
+ if ( irt->rt_inext == NULL ) { /* empty list */
+ rtmp->rt_inext = NULL;
+ rtmp->rt_iprev = rtmp;
+ irt->rt_inext = rtmp;
+ } else {
+ rtmp->rt_inext = irt->rt_inext;
+ rtmp->rt_iprev = irt->rt_inext->rt_iprev;
+ irt->rt_inext->rt_iprev = rtmp;
+ irt->rt_inext = rtmp;
+ }
+}
+
+
+/*
+ * Change the zone mapping to replace "from" with "to". This code assumes
+ * the consistency of both the route -> zone map and the zone -> route map.
+ * This is probably a bad idea. How can we insure that the data is good
+ * at this point? What do we do if we get several copies of a route in
+ * an RTMP packet?
+ */
+static int rtmp_copyzones( to, from )
+ struct rtmptab *to, *from;
+{
+ struct list *lz, *lr;
+
+ to->rt_zt = from->rt_zt;
+ from->rt_zt = NULL;
+ if ( from->rt_flags & RTMPTAB_HASZONES ) {
+ to->rt_flags |= RTMPTAB_HASZONES;
+ }
+ for ( lz = to->rt_zt; lz; lz = lz->l_next ) {
+ for ( lr = ((struct ziptab *)lz->l_data)->zt_rt; lr; lr = lr->l_next ) {
+ if ( (struct rtmptab *)lr->l_data == from ) {
+ lr->l_data = (void *)to; /* cast BS */
+ break;
+ }
+ }
+ if ( lr == NULL ) {
+ syslog( LOG_ERR, "rtmp_copyzones z -> r without r -> z, abort" );
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Remove rtmp from the in-use table and the per-gate table.
+ * Free any associated space.
+ */
+void rtmp_free( rtmp )
+ struct rtmptab *rtmp;
+{
+ struct gate *gate;
+
+ syslog(LOG_INFO, "rtmp_free: %u-%u", ntohs(rtmp->rt_firstnet),
+ ntohs(rtmp->rt_lastnet));
+ if ( rtmp->rt_iprev ) {
+ rtmp_delinuse( rtmp );
+ }
+
+ /* remove from per-gate */
+ gate = rtmp->rt_gate;
+ if ( gate->g_rt == rtmp ) { /* first */
+ if ( rtmp->rt_prev == rtmp ) { /* only */
+ gate->g_rt = NULL;
+ } else {
+ gate->g_rt = rtmp->rt_next;
+ rtmp->rt_next->rt_prev = rtmp->rt_prev;
+ }
+ } else {
+ if ( rtmp->rt_next == NULL ) { /* last */
+ rtmp->rt_prev->rt_next = NULL;
+ gate->g_rt->rt_prev = rtmp->rt_prev;
+ } else {
+ rtmp->rt_prev->rt_next = rtmp->rt_next;
+ rtmp->rt_next->rt_prev = rtmp->rt_prev;
+ }
+ }
+
+ free( rtmp );
+}
+
+
+/*
+ * Find a replacement for "replace". If we can't find a replacement,
+ * return 1. If we do find a replacement, return 0. -1 on error.
+ */
+int rtmp_replace( replace )
+ struct rtmptab *replace;
+{
+ struct interface *iface;
+ struct gate *gate;
+ struct rtmptab *rtmp, *found = NULL;
+
+ syslog(LOG_INFO, "rtmp_replace %u-%u", ntohs(replace->rt_firstnet),
+ ntohs(replace->rt_lastnet));
+ for ( iface = interfaces; iface; iface = iface->i_next ) {
+ if ((replace->rt_iface != iface) &&
+ ((iface->i_flags & IFACE_ISROUTER) == 0))
+ continue;
+
+ for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
+ for ( rtmp = gate->g_rt; rtmp; rtmp = rtmp->rt_next ) {
+ if ( rtmp->rt_firstnet == replace->rt_firstnet &&
+ rtmp->rt_lastnet == replace->rt_lastnet ) {
+ if ( found == NULL || rtmp->rt_hops < found->rt_hops ) {
+ found = rtmp;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if ( found != replace ) {
+ if (rtmp_copyzones( found, replace ) < 0)
+ return -1;
+ rtmp_delinuse( replace );
+ rtmp_addinuse( found );
+ if ( replace->rt_state == RTMPTAB_BAD ) {
+ rtmp_free( replace );
+ }
+ return( 0 );
+ } else {
+ if ( replace->rt_hops == RTMPHOPS_POISON ) {
+ gateroute( RTMP_DEL, replace );
+ }
+ return( 1 );
+ }
+}
+
+
+static int rtmp_new( rtmp )
+ struct rtmptab *rtmp;
+{
+ struct interface *i;
+ struct rtmptab *r;
+ extern int newrtmpdata;
+
+ newrtmpdata = 1;
+
+ /*
+ * Do we already have a gateway for this route?
+ */
+ for ( i = interfaces; i; i = i->i_next ) {
+ if ((rtmp->rt_iface != i) &&
+ ((i->i_flags & IFACE_ISROUTER) == 0))
+ continue;
+
+ for ( r = i->i_rt; r; r = r->rt_inext ) {
+ /* Should check RTMPTAB_EXTENDED here. XXX */
+ if (( ntohs( r->rt_firstnet ) <= ntohs( rtmp->rt_firstnet ) &&
+ ntohs( r->rt_lastnet ) >= ntohs( rtmp->rt_firstnet )) ||
+ ( ntohs( r->rt_firstnet ) <= ntohs( rtmp->rt_lastnet ) &&
+ ntohs( r->rt_lastnet ) >= ntohs( rtmp->rt_lastnet ))) {
+ break;
+ }
+ }
+ if ( r ) {
+ break;
+ }
+ }
+
+ /*
+ * This part of this routine is almost never run.
+ */
+ if ( i ) { /* can we get here without r being set? */
+ if ( r->rt_firstnet != rtmp->rt_firstnet ||
+ r->rt_lastnet != rtmp->rt_lastnet ) {
+ syslog( LOG_INFO, "rtmp_new netrange mismatch %u-%u != %u-%u",
+ ntohs( r->rt_firstnet ), ntohs( r->rt_lastnet ),
+ ntohs( rtmp->rt_firstnet ), ntohs( rtmp->rt_lastnet ));
+ return 1;
+ }
+
+ /*
+ * Note that our whole methodology is wrong, if we want to do
+ * route "load balancing." This entails changing our route
+ * each time we receive a tuple of equal value. In fact, we can't
+ * do this, using our method, since we only check against in-use
+ * routes when a tuple is new from a router.
+ */
+ if ( r->rt_hops < rtmp->rt_hops ) {
+ return 1;
+ }
+
+ if (rtmp_copyzones( rtmp, r ) < 0)
+ return -1;
+ rtmp_delinuse( r );
+ }
+
+ rtmp_addinuse( rtmp );
+ return 0;
+}
+
+
+int rtmp_packet( ap, from, data, len )
+ struct atport *ap;
+ struct sockaddr_at *from;
+ char *data;
+ int len;
+{
+ struct rtmp_head rh;
+ struct rtmp_tuple rt, xrt;
+ struct gate *gate;
+ struct interface *iface;
+ struct rtmptab *rtmp;
+ char *end, packet[ ATP_BUFSIZ ];
+ int cc;
+
+ end = data + len;
+
+ if ( data >= end ) {
+ syslog( LOG_INFO, "rtmp_packet no data" );
+ return 1;
+ }
+
+ iface = ap->ap_iface;
+
+ /* ignore our own packets */
+ if ( from->sat_addr.s_net == iface->i_addr.sat_addr.s_net &&
+ from->sat_addr.s_node == iface->i_addr.sat_addr.s_node ) {
+ return 0;
+ }
+
+ switch( *data++ ) {
+ case DDPTYPE_RTMPRD :
+ /*
+ * Response and Data.
+ */
+ if ( data + sizeof( struct rtmprdhdr ) > end ) {
+ syslog( LOG_INFO, "rtmp_packet no data header" );
+ return 1;
+ }
+ bcopy( data, &rh, sizeof( struct rtmprdhdr ));
+ data += sizeof( struct rtmprdhdr );
+
+ /* check rh address against from address */
+ if ( rh.rh_nodelen != 8 ) {
+ syslog( LOG_INFO, "rtmp_packet bad node len (%d)", rh.rh_nodelen );
+ return 1;
+ }
+ if (( from->sat_addr.s_net != 0 &&
+ from->sat_addr.s_net != rh.rh_net ) ||
+ from->sat_addr.s_node != rh.rh_node ) {
+ syslog( LOG_INFO, "rtmp_packet address mismatch" );
+ return 1;
+ }
+
+ if (( iface->i_flags & ( IFACE_ADDR|IFACE_CONFIG )) == IFACE_ADDR ) {
+ if ( iface->i_flags & IFACE_NOROUTER ) {
+ /* remove addr to loopback route */
+ if ((cc = looproute( iface, RTMP_DEL )) < 0) {
+ syslog(LOG_ERR, "rtmp_packet: looproute");
+ return -1;
+ }
+
+ if (cc)
+ syslog( LOG_ERR, "rtmp_packet: can't remove loopback: %m" );
+
+ iface->i_flags &= ~IFACE_NOROUTER;
+ iface->i_time = 0;
+ syslog( LOG_INFO, "rtmp_packet router has become available" );
+ }
+ if ( iface->i_flags & IFACE_PHASE1 ) {
+ if (rtmp_config( &rh, iface ) < 0) {
+ syslog(LOG_ERR, "rtmp_packet: rtmp_config");
+ return -1;
+ }
+ } else if (zip_getnetinfo( iface ) < 0) {
+ syslog(LOG_ERR, "rtmp_packet: zip_getnetinfo");
+ return -1;
+ }
+ return 0;
+ }
+
+ if (( iface->i_flags & IFACE_CONFIG ) == 0 ) {
+ return 0;
+ }
+
+ /*
+ * Parse first tuple. For phase 2, verify that net is correct.
+ */
+ if ( data + SZ_RTMPTUPLE > end ) {
+ syslog( LOG_INFO, "rtmp_packet missing first tuple" );
+ return 1;
+ }
+ bcopy( data, &rt, SZ_RTMPTUPLE );
+ data += SZ_RTMPTUPLE;
+
+ if ( rt.rt_net == 0 ) {
+ if ( rt.rt_dist != 0x82 ) {
+ syslog( LOG_INFO, "rtmp_packet bad phase 1 version" );
+ return 1;
+ }
+
+ /*
+ * Grab the next tuple, since we don't want to pass the version
+ * number to the parsing code. We're assuming that there are
+ * no extended tuples in this packet.
+ */
+ if ( data + SZ_RTMPTUPLE > end ) {
+ syslog( LOG_INFO, "rtmp_packet missing second tuple" );
+ return 1;
+ }
+ bcopy( data, &rt, SZ_RTMPTUPLE );
+ data += SZ_RTMPTUPLE;
+ } else if ( rt.rt_dist & 0x80 ) {
+ if ( data + SZ_RTMPTUPLE > end ) {
+ syslog( LOG_INFO, "rtmp_packet missing first range-end" );
+ return 1;
+ }
+ bcopy( data, &xrt, SZ_RTMPTUPLE );
+ data += SZ_RTMPTUPLE;
+
+ if ( xrt.rt_dist != 0x82 ) {
+ syslog( LOG_INFO, "rtmp_packet bad phase 2 version" );
+ return 1;
+ }
+
+ /*
+ * Check for net range conflict.
+ */
+ if ( rt.rt_net != iface->i_rt->rt_firstnet ||
+ xrt.rt_net != iface->i_rt->rt_lastnet ) {
+ syslog( LOG_INFO, "rtmp_packet interface mismatch" );
+ return 1;
+ }
+ } else {
+#ifdef PHASE1NET
+ /*
+ * Gatorboxes put a net number in the first tuple, even on
+ * phase 1 nets. This is wrong, but since we've got it, we
+ * might just as well check it.
+ if ( rt.rt_net != iface->i_rt->rt_firstnet ||
+ rt.rt_net != iface->i_rt->rt_lastnet ) {
+ syslog( LOG_INFO, "rtmp_packet phase 1 interface mismatch" );
+ return 1;
+ }
+ */
+#else PHASE1NET
+ syslog( LOG_INFO, "rtmp_packet bad first tuple" );
+ return 1;
+#endif PHASE1NET
+ }
+
+ /*
+ * Find gateway.
+ */
+ for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
+ if ( gate->g_sat.sat_addr.s_net == from->sat_addr.s_net &&
+ gate->g_sat.sat_addr.s_node == from->sat_addr.s_node ) {
+ break;
+ }
+ }
+ if ( !gate ) { /* new gateway */
+ if (( gate = (struct gate *)malloc( sizeof( struct gate ))) == 0 ) {
+ syslog( LOG_ERR, "rtmp_packet: malloc: %m" );
+ return -1;
+ }
+ gate->g_next = iface->i_gate;
+ gate->g_prev = 0;
+ gate->g_rt = 0;
+ gate->g_iface = iface; /* need this? */
+ gate->g_sat = *from;
+ if ( iface->i_gate ) {
+ iface->i_gate->g_prev = gate;
+ }
+ iface->i_gate = gate;
+ syslog( LOG_INFO, "rtmp_packet gateway %u.%u up",
+ ntohs( gate->g_sat.sat_addr.s_net ),
+ gate->g_sat.sat_addr.s_node );
+ }
+
+ /*
+ * Reset the timeout on this gateway. We'll remove the gateway
+ * entry, if the timeout gets to RTMPTAB_BAD.
+ */
+ gate->g_state = RTMPTAB_GOOD;
+
+ /*
+ * Parse remaining tuples.
+ */
+ for (;;) {
+ /*
+ * Is route on this gateway?
+ */
+ for ( rtmp = gate->g_rt; rtmp; rtmp = rtmp->rt_next ) {
+ if ( ntohs( rtmp->rt_firstnet ) <= ntohs( rt.rt_net ) &&
+ ntohs( rtmp->rt_lastnet ) >= ntohs( rt.rt_net )) {
+ break;
+ }
+ if (( rt.rt_dist & 0x80 ) &&
+ ntohs( rtmp->rt_firstnet ) <= ntohs( xrt.rt_net ) &&
+ ntohs( rtmp->rt_lastnet ) >= ntohs( xrt.rt_net )) {
+ break;
+ }
+ }
+
+ if ( rtmp ) { /* found it */
+ /*
+ * Check for range conflicts. (This is getting a little
+ * ugly.)
+ */
+ if ( rtmp->rt_firstnet != rt.rt_net ) {
+ syslog( LOG_INFO, "rtmp_packet firstnet mismatch %u!=%u",
+ ntohs( rtmp->rt_firstnet ), ntohs( rt.rt_net ));
+ return 1;
+ }
+ if ( rt.rt_dist & 0x80 ) {
+ if (( rtmp->rt_flags & RTMPTAB_EXTENDED ) == 0 ) {
+ syslog( LOG_INFO, "rtmp_packet extended mismatch %u",
+ ntohs( rtmp->rt_firstnet ));
+ return 1;
+ }
+ if ( rtmp->rt_lastnet != xrt.rt_net ) {
+ syslog( LOG_INFO, "rtmp_packet lastnet mismatch %u!=%u",
+ ntohs( rtmp->rt_lastnet ), ntohs( xrt.rt_net ));
+ return 1;
+ }
+ } else {
+ if ( rtmp->rt_flags & RTMPTAB_EXTENDED ) {
+ syslog( LOG_INFO, "rtmp_packet !extended mismatch %u",
+ ntohs( rtmp->rt_firstnet ));
+ return 1;
+ }
+ if ( rtmp->rt_lastnet != rt.rt_net ) {
+ syslog( LOG_INFO, "rtmp_packet lastnet mismatch %u!=%u",
+ ntohs( rtmp->rt_lastnet ), ntohs( rt.rt_net ));
+ return 1;
+ }
+ }
+
+ rtmp->rt_state = RTMPTAB_GOOD;
+
+ /*
+ * Check hop count. If the count has changed, update
+ * the routing database.
+ */
+ if (( rtmp->rt_hops != ( rt.rt_dist & 0x7f ) + 1 ) &&
+ ( rtmp->rt_hops != RTMPHOPS_POISON ||
+ ( rt.rt_dist & 0x7f ) + 1 <= RTMPHOPS_MAX )) {
+ if ( rtmp->rt_iprev ) { /* route is in use */
+ if ( rtmp->rt_hops > ( rt.rt_dist & 0x7f ) + 1 ) {
+ /*
+ * If this was POISON, we've deleted it from
+ * the kernel. Add it back in.
+ */
+ if ( rtmp->rt_hops == RTMPHOPS_POISON ) {
+ gateroute( RTMP_ADD, rtmp );
+ }
+ rtmp->rt_hops = ( rt.rt_dist & 0x7f ) + 1;
+ } else {
+ /*
+ * Hop count has gone up for this route.
+ * Search for a new best route. If we can't
+ * find one, just keep this route. "poison"
+ * route are deleted in as_timer().
+ */
+ if (( rt.rt_dist & 0x7f ) + 1 > RTMPHOPS_MAX ) {
+ rtmp->rt_hops = RTMPHOPS_POISON;
+ } else {
+ rtmp->rt_hops = ( rt.rt_dist & 0x7f ) + 1;
+ }
+ if (rtmp_replace( rtmp ) < 0) {
+ syslog(LOG_ERR, "rtmp_packet: rtmp_replace");
+ return -1;
+ }
+ }
+ } else { /* route not in use */
+ rtmp->rt_hops = ( rt.rt_dist & 0x7f ) + 1;
+ if ( rtmp->rt_hops > ( rt.rt_dist & 0x7f ) + 1 ) {
+ if (rtmp_new( rtmp ) < 0) {
+ syslog(LOG_ERR, "rtmp_packet: rtmp_new");
+ return -1;
+ }
+ }
+ }
+ }
+
+ /*
+ * Make the *next* node the head, since
+ * we're not likely to be asked for the same tuple twice
+ * in a row.
+ */
+ if ( rtmp->rt_next != 0 ) {
+ gate->g_rt->rt_prev->rt_next = gate->g_rt;
+ gate->g_rt = rtmp->rt_next;
+ rtmp->rt_next = 0;
+ }
+ } else if (( rt.rt_dist & 0x7f ) + 1 > RTMPHOPS_MAX ) {
+ syslog( LOG_INFO, "rtmp_packet bad hop count from %u.%u for %u",
+ ntohs( from->sat_addr.s_net ), from->sat_addr.s_node,
+ ntohs( rt.rt_net ));
+ } else { /* new for router */
+ if (( rtmp = newrt(iface)) == NULL ) {
+ syslog( LOG_ERR, "rtmp_packet: newrt: %m" );
+ return -1;
+ }
+ rtmp->rt_firstnet = rt.rt_net;
+ if ( rt.rt_dist & 0x80 ) {
+ rtmp->rt_lastnet = xrt.rt_net;
+ rtmp->rt_flags = RTMPTAB_EXTENDED;
+ } else {
+ rtmp->rt_lastnet = rt.rt_net;
+ }
+ rtmp->rt_hops = ( rt.rt_dist & 0x7f ) + 1;
+ rtmp->rt_state = RTMPTAB_GOOD;
+ rtmp->rt_gate = gate;
+
+ /*
+ * Add rtmptab entry to end of list (leave head alone).
+ */
+ if ( gate->g_rt == 0 ) {
+ rtmp->rt_prev = rtmp;
+ gate->g_rt = rtmp;
+ } else {
+ rtmp->rt_prev = gate->g_rt->rt_prev;
+ gate->g_rt->rt_prev->rt_next = rtmp;
+ gate->g_rt->rt_prev = rtmp;
+ }
+
+ if (rtmp_new( rtmp ) < 0) {
+ syslog(LOG_ERR, "rtmp_packet: rtmp_new");
+ return -1;
+ }
+ }
+
+ if ( data + SZ_RTMPTUPLE > end ) {
+ break;
+ }
+ bcopy( data, &rt, SZ_RTMPTUPLE );
+ data += SZ_RTMPTUPLE;
+ if ( rt.rt_dist & 0x80 ) {
+ if ( data + SZ_RTMPTUPLE > end ) {
+ syslog( LOG_INFO, "rtmp_packet missing range-end" );
+ return 1;
+ }
+ bcopy( data, &xrt, SZ_RTMPTUPLE );
+ data += SZ_RTMPTUPLE;
+ }
+ }
+
+ /*
+ * Make sure we've processed the whole packet.
+ */
+ if ( data != end ) {
+ syslog( LOG_INFO, "rtmp_packet length and count mismatch" );
+ }
+ break;
+
+ case DDPTYPE_RTMPR :
+ /*
+ * Request and RDR.
+ */
+ if (((iface->i_flags & IFACE_ISROUTER) == 0) ||
+ iface->i_rt->rt_zt == 0 ||
+ ( iface->i_flags & IFACE_CONFIG ) == 0 ) {
+ return 0;
+ }
+ if ( *data == 1 ) {
+ data = packet;
+ *data++ = DDPTYPE_RTMPRD;
+ rh.rh_net = iface->i_addr.sat_addr.s_net;
+ rh.rh_nodelen = 8;
+ rh.rh_node = iface->i_addr.sat_addr.s_node;
+ bcopy( &rh, data, sizeof( struct rtmp_head ));
+ data += sizeof( struct rtmp_head );
+
+ if ( iface->i_flags & IFACE_PHASE2 ) {
+ rt.rt_net = iface->i_rt->rt_firstnet;
+ rt.rt_dist = 0x80;
+ bcopy( &rt, data, SZ_RTMPTUPLE );
+ data += SZ_RTMPTUPLE;
+
+ rt.rt_net = iface->i_rt->rt_lastnet;
+ rt.rt_dist = 0x82;
+ bcopy( &rt, data, SZ_RTMPTUPLE );
+ data += SZ_RTMPTUPLE;
+ }
+ if ( sendto( ap->ap_fd, packet, data - packet, 0,
+ (struct sockaddr *)from,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "as_timer sendto: %m" );
+ }
+ } else if ( *data == 2 || *data == 3 ) {
+#ifdef DEBUG
+ printf( "rtmp_packet rdr (%d) from %u.%u\n",
+ *data, ntohs( from->sat_addr.s_net ),
+ from->sat_addr.s_node );
+#endif DEBUG
+ } else {
+ syslog( LOG_INFO, "rtmp_packet unknown request from %u.%u\n",
+ ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+ }
+ break;
+
+ default :
+ syslog( LOG_INFO, "rtmp_packet bad ddp type from %u.%u",
+ ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+ return 0;
+ }
+
+ return 0;
+}
+
+int rtmp_request( iface )
+ struct interface *iface;
+{
+ struct sockaddr_at sat;
+ struct atport *ap;
+ char *data, packet[ 2 ];
+
+ syslog( LOG_INFO, "rtmp_request for %s", iface->i_name );
+
+ for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+ if ( ap->ap_packet == rtmp_packet ) {
+ break;
+ }
+ }
+ if ( ap == 0 ) {
+ syslog( LOG_ERR, "rtmp_request can't find rtmp socket!" );
+ return -1;
+ }
+
+ data = packet;
+ *data++ = DDPTYPE_RTMPR;
+ *data++ = RTMPROP_REQUEST;
+
+ /*
+ * There is a problem with the net zero "hint" hack.
+ */
+ bzero( &sat, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+ sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ sat.sat_family = AF_APPLETALK;
+ sat.sat_addr.s_net = iface->i_addr.sat_addr.s_net;
+ sat.sat_addr.s_node = ATADDR_BCAST;
+ sat.sat_port = ap->ap_port;
+ if ( sendto( ap->ap_fd, packet, data - packet, 0, (struct sockaddr *)&sat,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "rtmp_request sendto: %m" );
+ return -1;
+ }
+ return 0;
+}
+
+
+int looproute( iface, cmd )
+ struct interface *iface;
+ int cmd;
+{
+ struct sockaddr_at dst, loop;
+
+ if ( cmd == RTMP_DEL && ( iface->i_flags & IFACE_LOOP ) == 0 ) {
+ syslog( LOG_ERR, "looproute panic no route" );
+ return -1;
+ }
+
+ if ( cmd == RTMP_ADD && ( iface->i_flags & IFACE_LOOP )) {
+ syslog( LOG_ERR, "looproute panic two routes" );
+ return -1;
+ }
+
+ bzero( &dst, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+ dst.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ dst.sat_family = AF_APPLETALK;
+ dst.sat_addr.s_net = iface->i_addr.sat_addr.s_net;
+ dst.sat_addr.s_node = iface->i_addr.sat_addr.s_node;
+ bzero( &loop, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+ loop.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ loop.sat_family = AF_APPLETALK;
+ loop.sat_addr.s_net = htons( ATADDR_ANYNET );
+ loop.sat_addr.s_node = ATADDR_ANYNODE;
+
+ if ( route( cmd, &dst, &loop, RTF_UP | RTF_HOST )) {
+ return( 1 );
+ }
+ if ( cmd == RTMP_ADD ) {
+ iface->i_flags |= IFACE_LOOP;
+ }
+ if ( cmd == RTMP_DEL ) {
+ iface->i_flags &= ~IFACE_LOOP;
+ }
+ return( 0 );
+}
+
+int gateroute( command, rtmp )
+ int command;
+ struct rtmptab *rtmp;
+{
+ struct sockaddr_at dst, gate;
+ unsigned short net;
+
+ if ( command == RTMP_DEL && ( rtmp->rt_flags & RTMPTAB_ROUTE ) == 0 ) {
+ return( -1 );
+ }
+ if ( command == RTMP_ADD && ( rtmp->rt_flags & RTMPTAB_ROUTE )) {
+ return( -1 );
+ }
+
+ net = ntohs( rtmp->rt_firstnet );
+ /*
+ * Since we will accept routes from gateways who advertise their
+ * address as 0.YY, we must munge the gateway address we give to
+ * the kernel. Otherwise, we'll get a bunch of routes to the loop
+ * back interface, and who wants that?
+ */
+ bzero( &gate, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+ gate.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ gate.sat_family = AF_APPLETALK;
+ gate.sat_addr.s_net = rtmp->rt_gate->g_sat.sat_addr.s_net;
+ gate.sat_addr.s_node = rtmp->rt_gate->g_sat.sat_addr.s_node;
+ if ( gate.sat_addr.s_net == 0 ) {
+ gate.sat_addr.s_net = net;
+ }
+
+ bzero( &dst, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+ dst.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ dst.sat_family = AF_APPLETALK;
+ dst.sat_addr.s_node = ATADDR_ANYNODE;
+
+ do {
+ dst.sat_addr.s_net = htons( net );
+ if ( route( command, &dst, &gate, RTF_UP | RTF_GATEWAY )) {
+ syslog( LOG_ERR, "route: %u -> %u.%u: %m", net,
+ ntohs( gate.sat_addr.s_net ), gate.sat_addr.s_node );
+ continue;
+ }
+ } while ( net++ < ntohs( rtmp->rt_lastnet ));
+
+ if ( command == RTMP_ADD ) {
+ rtmp->rt_flags |= RTMPTAB_ROUTE;
+ }
+ if ( command == RTMP_DEL ) {
+ rtmp->rt_flags &= ~RTMPTAB_ROUTE;
+ }
+
+ return( 0 );
+}
+
+ struct rtmptab *
+newrt(const struct interface *iface)
+{
+ struct rtmptab *rtmp;
+
+ if (( rtmp = (struct rtmptab *)calloc(1, sizeof(struct rtmptab))) == 0 ) {
+ return( 0 );
+ }
+
+ rtmp->rt_iface = iface;
+ return( rtmp );
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+/*
+ * We have an rtmptab circular linked list for each gateway. Entries
+ * are inserted in the order we get them. The expectation is that
+ * we will get a complexity of N for the stable case. If we have N
+ * existing entries, and M new entries, we'll have on the order of
+ * N + ( M * N ) complexity (really it will be something more than
+ * that, maybe N + ( M * ( N + 1/2 M )). Note that having a list to
+ * search is superior to a hash table if you are expecting bad data:
+ * you have the opportunity to range-check the incoming data.
+ *
+ * We keep several ZIP related flags and counters here. For ZIP Extended
+ * Replies, we must keep a flag indicating that the zone is up or down.
+ * This flag is necessary for ZIP Extended Replies which cross packet
+ * boundaries: even tho the rtmptab entry has data, it is not yet
+ * complete. For ZIP in general, we keep a flag indicating that we've
+ * asked for a ZIP (E)Reply. If this flag is not set, we won't process
+ * ZIP Reply data for given rtmptab entries. Lastly, we keep a count of
+ * the number of times we've asked for ZIP Reply data. When this value
+ * reaches some value (3?), we can optionally stop asking.
+ */
+
+#ifndef ATALKD_RTMP_H
+#define ATALKD_RTMP_H 1
+
+#include <sys/cdefs.h>
+
+struct rtmptab {
+ struct rtmptab *rt_next,
+ *rt_prev;
+ struct rtmptab *rt_inext,
+ *rt_iprev;
+ u_short rt_firstnet, rt_lastnet;
+ u_char rt_hops;
+ u_char rt_state;
+ u_char rt_flags;
+ u_char rt_nzq; /* number of zip queries issued */
+ struct gate *rt_gate; /* gate is NULL for interfaces */
+ struct list *rt_zt;
+ const struct interface *rt_iface;
+};
+
+struct rtmp_head {
+ u_short rh_net;
+ u_char rh_nodelen;
+ u_char rh_node;
+};
+
+struct rtmp_tuple {
+ u_short rt_net;
+ u_char rt_dist;
+};
+#define SZ_RTMPTUPLE 3
+
+#define RTMPTAB_PERM 0
+#define RTMPTAB_GOOD 1
+#define RTMPTAB_SUSP1 2
+#define RTMPTAB_SUSP2 3
+#define RTMPTAB_BAD 4
+
+#define RTMPTAB_ZIPQUERY 0x01
+#define RTMPTAB_HASZONES 0x02
+#define RTMPTAB_EXTENDED 0x04
+#define RTMPTAB_ROUTE 0x08
+
+#ifndef BSD4_4
+#define RTMP_ADD SIOCADDRT
+#define RTMP_DEL SIOCDELRT
+#else BSD4_4
+#define RTMP_ADD RTM_ADD
+#define RTMP_DEL RTM_DELETE
+#endif BSD4_4
+
+#define STARTUP_FIRSTNET 0xff00
+#define STARTUP_LASTNET 0xfffe
+
+extern int rtfd;
+struct rtmptab *newrt __P((const struct interface *));
+void rtmp_delzonemap __P((struct rtmptab *));
+
+#endif /* atalkd/rtmp.h */
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/syslog.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+
+#ifdef __svr4__
+#include <sys/sockio.h>
+#endif __svr4__
+
+#include <atalk/ddp.h>
+#include <atalk/zip.h>
+#include <atalk/atp.h>
+#include <atalk/util.h>
+
+#include "atserv.h"
+#include "interface.h"
+#include "gate.h"
+#include "zip.h"
+#include "rtmp.h"
+#include "list.h"
+#include "multicast.h"
+
+struct ziptab *ziptab = NULL, *ziplast = NULL;
+
+static int zonecheck( rtmp, iface )
+ struct rtmptab *rtmp;
+ struct interface *iface;
+{
+ struct list *l;
+ struct ziptab *czt, *zt;
+ int cztcnt, ztcnt;
+
+ if (( iface->i_flags & IFACE_SEED ) == 0 ) {
+ return( 0 );
+ }
+
+ for ( cztcnt = 0, czt = iface->i_czt; czt; czt = czt->zt_next, cztcnt++ ) {
+ for ( l = rtmp->rt_zt; l; l = l->l_next ) {
+ zt = (struct ziptab *)l->l_data;
+ if ( czt->zt_len == zt->zt_len &&
+ !strndiacasecmp( czt->zt_name, zt->zt_name, czt->zt_len )) {
+ break;
+ }
+ }
+ if ( l == 0 ) {
+ syslog( LOG_ERR, "zonecheck: %.*s not in zone list", czt->zt_len,
+ czt->zt_name );
+ return( -1 ); /* configured zone not found in net zones */
+ }
+ }
+
+ for ( ztcnt = 0, l = rtmp->rt_zt; l; l = l->l_next, ztcnt++ )
+ ;
+
+ if ( cztcnt != ztcnt ) {
+ syslog( LOG_ERR, "zonecheck: %d configured zones, %d zones found",
+ cztcnt, ztcnt );
+ return( -1 ); /* more net zones than configured zones */
+ }
+
+ return( 0 );
+}
+
+
+int zip_packet( ap, from, data, len )
+ struct atport *ap;
+ struct sockaddr_at *from;
+ char *data;
+ int len;
+{
+ struct ziphdr zh;
+ struct atphdr ah;
+ struct interface *iface;
+ struct gate *gate;
+ struct rtmptab *rtmp;
+ struct list *l;
+ struct ziptab *zt;
+ u_short firstnet, lastnet, index, nz;
+ char *end, zname[ 32 ], packet[ ATP_BUFSIZ ], *nzones, *lastflag;
+ char *reply, *rend, *ziphdr;
+ int zlen, n, zipop, rcnt, qcnt, zcnt, zsz;
+ extern int debug, stabletimer;
+
+ end = data + len;
+
+ if ( data >= end ) {
+ syslog( LOG_INFO, "zip_packet malformed packet" );
+ return 1;
+ }
+
+ /* get interface */
+ iface = ap->ap_iface;
+
+ switch( *data++ ) {
+ case DDPTYPE_ZIP :
+ if ( data + sizeof( struct ziphdr ) > end ) {
+ syslog( LOG_INFO, "zip_packet malformed packet" );
+ return 1;
+ }
+ memcpy( &zh, data, sizeof( struct ziphdr ));
+ data += sizeof( struct ziphdr );
+
+ switch ( zh.zh_op ) {
+ case ZIPOP_QUERY :
+ /* set up reply */
+ reply = packet;
+ rend = packet + sizeof( packet );
+ *reply++ = DDPTYPE_ZIP;
+ ziphdr = reply;
+ reply += 2;
+ rcnt = 0;
+
+ qcnt = zh.zh_count;
+
+ while ( data + sizeof( u_short ) <= end && qcnt-- > 0 ) {
+ memcpy( &firstnet, data, sizeof( u_short ));
+ data += sizeof( u_short );
+
+ /*
+ * Look for the given network number (firstnet).
+ * Perhaps we could do better than brute force?
+ */
+ for ( iface = interfaces; iface; iface = iface->i_next ) {
+ for ( rtmp = iface->i_rt; rtmp; rtmp = rtmp->rt_inext ) {
+ if ( firstnet == rtmp->rt_firstnet ) {
+ break;
+ }
+ }
+ if ( rtmp ) {
+ break;
+ }
+ }
+ if ( rtmp == 0 ) {
+ continue;
+ }
+
+ /*
+ * Count the number of zones in this list, and the
+ * number of byte it will consume in a reply.
+ */
+ for ( zsz = 0, zcnt = 0, l = rtmp->rt_zt; l; l = l->l_next ) {
+ zcnt++;
+ zt = (struct ziptab *)l->l_data;
+ zsz += sizeof( u_short ) + 1 + zt->zt_len;
+ }
+
+ /*
+ * We might send this list in the current reply, as the
+ * first thing in the next reply, or as an extended packet.
+ */
+ if ( reply + zsz > rend ) {
+ if ( rcnt > 0 ) {
+ zh.zh_op = ZIPOP_REPLY;
+ zh.zh_cnt = rcnt;
+ memcpy( ziphdr, &zh, sizeof( struct ziphdr ));
+ if ( sendto( ap->ap_fd, packet, reply - packet, 0,
+ (struct sockaddr *)from,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "zip reply sendto: %m" );
+ }
+
+ reply = packet + 3;
+ rcnt = 0;
+ }
+
+ if ( reply + zsz > rend ) {
+ /* ereply */
+ for ( l = rtmp->rt_zt; l; l = l->l_next, rcnt++ ) {
+ zt = (struct ziptab *)l->l_data;
+ if ( reply + sizeof( u_short ) + 1 + zt->zt_len >
+ rend ) {
+ zh.zh_op = ZIPOP_EREPLY;
+ zh.zh_cnt = zcnt;
+ memcpy( ziphdr, &zh, sizeof( struct ziphdr ));
+ if ( sendto( ap->ap_fd, packet, reply - packet,
+ 0, (struct sockaddr *)from,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "zip reply sendto: %m" );
+ }
+
+ reply = packet + 3;
+ rcnt = 0;
+ }
+
+ memcpy( reply, &firstnet, sizeof( u_short ));
+ reply += sizeof( u_short );
+ *reply++ = zt->zt_len;
+ memcpy( reply, zt->zt_name, zt->zt_len );
+ reply += zt->zt_len;
+ }
+
+ if ( rcnt > 0 ) {
+ zh.zh_op = ZIPOP_EREPLY;
+ zh.zh_cnt = zcnt;
+ memcpy( ziphdr, &zh, sizeof( struct ziphdr ));
+ if ( sendto( ap->ap_fd, packet, reply - packet, 0,
+ (struct sockaddr *)from,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "zip reply sendto: %m" );
+ }
+
+ reply = packet + 3;
+ rcnt = 0;
+ }
+ continue;
+ }
+ }
+
+ for ( l = rtmp->rt_zt; l; l = l->l_next, rcnt++ ) {
+ zt = (struct ziptab *)l->l_data;
+ memcpy( reply, &firstnet, sizeof( u_short ));
+ reply += sizeof( u_short );
+ *reply++ = zt->zt_len;
+ memcpy( reply, zt->zt_name, zt->zt_len );
+ reply += zt->zt_len;
+ }
+ }
+
+ if ( rcnt > 0 ) {
+ zh.zh_op = ZIPOP_REPLY;
+ zh.zh_cnt = rcnt;
+ memcpy( ziphdr, &zh, sizeof( struct ziphdr ));
+ if ( sendto( ap->ap_fd, packet, reply - packet, 0,
+ (struct sockaddr *)from,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "zip reply sendto: %m" );
+ }
+ }
+ break;
+
+ case ZIPOP_REPLY :
+ for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
+ if (( from->sat_addr.s_net == 0 ||
+ gate->g_sat.sat_addr.s_net == from->sat_addr.s_net ) &&
+ gate->g_sat.sat_addr.s_node == from->sat_addr.s_node ) {
+ break;
+ }
+ }
+ if ( gate == NULL ) {
+ syslog( LOG_INFO, "zip reply from non-gateway %u.%u",
+ ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+ return 1;
+ }
+
+ rtmp = NULL;
+
+ do {
+ if ( data + sizeof( u_short ) + 1 > end ) { /* + strlen */
+ syslog( LOG_INFO, "zip reply short (%d)", len );
+ return 1;
+ }
+ memcpy( &firstnet, data, sizeof( u_short ));
+ data += sizeof( u_short );
+
+ if ( rtmp && rtmp->rt_firstnet != firstnet ) {
+ /* XXX */
+ if ( rtmp->rt_gate == NULL &&
+ zonecheck( rtmp, gate->g_iface ) != 0 ) {
+ syslog( LOG_ERR, "zip_packet seed zonelist mismatch" );
+ return -1;
+ }
+ rtmp->rt_flags &= ~RTMPTAB_ZIPQUERY;
+ }
+
+ /* Check if this is the interface's route. */
+ if ( firstnet == gate->g_iface->i_rt->rt_firstnet ) {
+ rtmp = gate->g_iface->i_rt;
+ } else {
+ for ( rtmp = gate->g_rt; rtmp; rtmp = rtmp->rt_next ) {
+ if ( rtmp->rt_firstnet == firstnet ) {
+ break;
+ }
+ }
+
+ /*
+ * Update head to this rtmp entry.
+ */
+ if ( rtmp != 0 && gate->g_rt != rtmp ) {
+ gate->g_rt->rt_prev->rt_next = gate->g_rt;
+ gate->g_rt = rtmp;
+ rtmp->rt_prev->rt_next = 0;
+ }
+ }
+
+ zlen = *data++;
+ if ( zlen > 32 || zlen <= 0 ) {
+ syslog( LOG_INFO, "zip reply bad packet" );
+ return 1;
+ }
+ if ( data + zlen > end ) {
+ syslog( LOG_INFO, "zip reply short (%d)", len );
+ return 1;
+ }
+ memcpy( zname, data, zlen );
+ data += zlen;
+
+ /*
+ * We won't find any rtmp entry if the gateway is no longer
+ * telling us about the entry.
+ */
+ if ( rtmp == 0 ) {
+ syslog( LOG_INFO, "zip skip reply %u from %u.%u (no rtmp)",
+ ntohs( firstnet ), ntohs( from->sat_addr.s_net ),
+ from->sat_addr.s_node );
+ /*
+ * Check if the route is still in use (the iprev check is
+ * no good if rtmp is the interface's route).
+ */
+ } else if ( rtmp->rt_iprev == NULL && rtmp->rt_prev != NULL ) {
+ syslog( LOG_INFO,
+ "zip skip reply %u-%u from %u.%u (rtmp not in use)",
+ ntohs( rtmp->rt_firstnet ),
+ ntohs( rtmp->rt_lastnet ),
+ ntohs( from->sat_addr.s_net ),
+ from->sat_addr.s_node );
+ /*
+ * Check if we've got an outstanding query for this route.
+ * We will often get this, since we ask every router on a
+ * net to verify our interface's zone(s).
+ */
+ } else if (( rtmp->rt_flags & RTMPTAB_ZIPQUERY ) == 0 ) {
+ syslog( LOG_INFO,
+ "zip skip reply %u-%u from %u.%u (no query)",
+ ntohs( rtmp->rt_firstnet ),
+ ntohs( rtmp->rt_lastnet ),
+ ntohs( from->sat_addr.s_net ),
+ from->sat_addr.s_node );
+ } else {
+ if (addzone( rtmp, zlen, zname ) < 0) {
+ syslog(LOG_ERR, "zip_packet: addzone");
+ return -1;
+ }
+ rtmp->rt_flags |= RTMPTAB_HASZONES;
+ }
+ } while ( data < end );
+
+ if ( rtmp && rtmp->rt_flags & RTMPTAB_HASZONES ) {
+ /* XXX */
+ if ( rtmp->rt_gate == 0 &&
+ zonecheck( rtmp, gate->g_iface ) != 0 ) {
+ syslog( LOG_ERR, "zip_packet seed zonelist mismatch" );
+ return -1;
+ }
+ rtmp->rt_flags &= ~RTMPTAB_ZIPQUERY;
+ }
+ break;
+
+ case ZIPOP_EREPLY :
+ for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
+ if (( from->sat_addr.s_net == 0 ||
+ gate->g_sat.sat_addr.s_net == from->sat_addr.s_net ) &&
+ gate->g_sat.sat_addr.s_node == from->sat_addr.s_node ) {
+ break;
+ }
+ }
+ if ( gate == NULL ) {
+ syslog( LOG_INFO, "zip ereply from non-gateway %u.%u",
+ ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+ return 1;
+ }
+
+ /*
+ * Note that we're not advancing "data" here. We do that
+ * at the top of the do-while loop, below.
+ */
+ if ( data + sizeof( u_short ) + 1 > end ) { /* + strlen */
+ syslog( LOG_INFO, "zip ereply short (%d)", len );
+ return 1;
+ }
+ memcpy( &firstnet, data, sizeof( u_short ));
+
+ /* Check if this is the interface's route. */
+ if ( firstnet == gate->g_iface->i_rt->rt_firstnet ) {
+ rtmp = gate->g_iface->i_rt;
+ } else {
+ for ( rtmp = gate->g_rt; rtmp; rtmp = rtmp->rt_next ) {
+ if ( rtmp->rt_firstnet == firstnet ) {
+ break;
+ }
+ }
+ if ( rtmp == NULL ) {
+ syslog( LOG_INFO, "zip ereply %u from %u.%u (no rtmp)",
+ ntohs( firstnet ), ntohs( from->sat_addr.s_net ),
+ from->sat_addr.s_node );
+ return 1;
+ }
+ if ( rtmp->rt_iprev == 0 ) {
+ syslog( LOG_INFO,
+ "zip ereply %u-%u from %u.%u (rtmp not in use)",
+ ntohs( rtmp->rt_firstnet ),
+ ntohs( rtmp->rt_lastnet ),
+ ntohs( from->sat_addr.s_net ),
+ from->sat_addr.s_node );
+ }
+
+ /* update head to *next* rtmp entry */
+ if ( rtmp->rt_next != 0 ) {
+ gate->g_rt->rt_prev->rt_next = gate->g_rt;
+ gate->g_rt = rtmp->rt_next;
+ rtmp->rt_next = 0;
+ }
+ }
+
+ if (( rtmp->rt_flags & RTMPTAB_ZIPQUERY ) == 0 ) {
+ syslog( LOG_INFO, "zip ereply %u-%u from %u.%u (no query)",
+ ntohs( rtmp->rt_firstnet ),
+ ntohs( rtmp->rt_lastnet ),
+ ntohs( from->sat_addr.s_net ),
+ from->sat_addr.s_node );
+ return 0;
+ }
+
+ do {
+ /*
+ * We copy out firstnet, twice (see above). Not
+ * a big deal, and it makes the end condition cleaner.
+ */
+ if ( data + sizeof( u_short ) + 1 > end ) { /* + strlen */
+ syslog( LOG_INFO, "zip ereply short (%d)", len );
+ return 1;
+ }
+ memcpy( &firstnet, data, sizeof( u_short ));
+ data += sizeof( u_short );
+
+ /* check route */
+ if ( firstnet != rtmp->rt_firstnet ) {
+ syslog( LOG_INFO, "zip ereply with multiple nets" );
+ return 1;
+ }
+
+ zlen = *data++;
+ if ( zlen > 32 || zlen <= 0 ) {
+ syslog( LOG_INFO, "zip ereply bad zone length (%d)", zlen );
+ return 1;
+ }
+ if ( data + zlen > end ) {
+ syslog( LOG_INFO, "zip ereply short (%d)", len );
+ return 1;
+ }
+ memcpy( zname, data, zlen );
+ data += zlen;
+ if (addzone( rtmp, zlen, zname ) < 0) {
+ syslog(LOG_ERR, "zip_packet: addzone");
+ return -1;
+ }
+ } while ( data < end );
+
+ if ( rtmp ) {
+ /*
+ * Count zones for rtmptab entry.
+ */
+ for ( n = 0, l = rtmp->rt_zt; l; l = l->l_next, n++ )
+ ;
+ if ( n == zh.zh_count ) {
+ rtmp->rt_flags |= RTMPTAB_HASZONES;
+ /* XXX */
+ if ( rtmp->rt_gate == 0 &&
+ zonecheck( rtmp, gate->g_iface ) != 0 ) {
+ syslog( LOG_ERR, "zip_packet seed zonelist mismatch" );
+ return -1;
+ }
+ rtmp->rt_flags &= ~RTMPTAB_ZIPQUERY;
+ }
+ }
+ break;
+
+ case ZIPOP_GNI :
+ /*
+ * Don't answer with bogus information.
+ */
+ if (((iface->i_flags & IFACE_ISROUTER) == 0) ||
+ iface->i_rt->rt_zt == 0 ||
+ ( iface->i_flags & IFACE_CONFIG ) == 0 ) {
+ return 0;
+ }
+
+ if ( zh.zh_zero != 0 || data + 2 * sizeof( u_short ) > end ) {
+ syslog( LOG_INFO, "zip_packet malformed packet" );
+ return 1;
+ }
+
+ memcpy( &firstnet, data, sizeof( u_short ));
+ data += sizeof( u_short );
+ memcpy( &lastnet, data, sizeof( u_short ));
+ data += sizeof( u_short );
+ if ( firstnet != 0 || lastnet != 0 || data >= end ) {
+ syslog( LOG_INFO, "zip_packet malformed packet" );
+ return 1;
+ }
+
+ zlen = *data++;
+ if ( zlen < 0 || zlen > 32 ) {
+ syslog( LOG_INFO, "zip_packet malformed packet" );
+ return 1;
+ }
+ memcpy( zname, data, zlen );
+
+ data = packet;
+ end = data + sizeof( packet );
+ zh.zh_op = ZIPOP_GNIREPLY;
+ zh.zh_flags = 0;
+
+ /*
+ * Skip to the nets. Fill in header when we're done.
+ */
+ data += 1 + sizeof( struct ziphdr );
+ memcpy( data, &iface->i_rt->rt_firstnet, sizeof( u_short ));
+ data += sizeof( u_short );
+ memcpy( data, &iface->i_rt->rt_lastnet, sizeof( u_short ));
+ data += sizeof( u_short );
+
+ *data++ = zlen;
+ memcpy( data, zname, zlen );
+ data += zlen;
+
+ /*
+ * Check if the given zone is valid. If it's valid, just fill in
+ * the multicast address. If it's not, fill the multicast address
+ * in with the default zone and return the default zone.
+ */
+ for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
+ zt = (struct ziptab *)l->l_data;
+ if ( zt->zt_len == zlen &&
+ strndiacasecmp( zname, zt->zt_name, zlen ) == 0 ) {
+ break;
+ }
+ }
+ if ( l == 0 ) {
+ zt = (struct ziptab *)iface->i_rt->rt_zt->l_data;
+ zh.zh_flags |= ZIPGNI_INVALID;
+ }
+
+ for ( n = 0, l = iface->i_rt->rt_zt; l; l = l->l_next, n++ )
+ ;
+ if ( n == 1 ) {
+ zh.zh_flags |= ZIPGNI_ONEZONE;
+ }
+
+ /* multicast */
+ *data++ = 6; /* sizeof ??? */
+ if (zone_bcast(zt) < 0) {
+ syslog(LOG_ERR, "zip_packet: zone_bcast");
+ return -1;
+ }
+ memcpy(data, zt->zt_bcast, 6);
+ data += 6;
+
+ /*
+ * Add default zone.
+ */
+ if ( zh.zh_flags & ZIPGNI_INVALID ) {
+ *data++ = zt->zt_len;
+ memcpy( data, zt->zt_name, zt->zt_len );
+ data += zt->zt_len;
+ }
+
+ /* fill in header */
+ *packet = DDPTYPE_ZIP;
+ memcpy( packet + 1, &zh, sizeof( struct ziphdr ));
+
+ /*
+ * If the address we received this request from isn't correct
+ * for the net we received it on, send a broadcast.
+ */
+ if ( ntohs( from->sat_addr.s_net ) <
+ ntohs( iface->i_rt->rt_firstnet ) ||
+ ntohs( from->sat_addr.s_net ) >
+ ntohs( iface->i_rt->rt_lastnet )) {
+ from->sat_addr.s_net = 0;
+ from->sat_addr.s_node = ATADDR_BCAST;
+ }
+
+ if ( sendto( ap->ap_fd, packet, data - packet, 0,
+ (struct sockaddr *)from,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "zip gni sendto %u.%u: %m",
+ ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+ return 1;
+ }
+ break;
+
+ case ZIPOP_GNIREPLY :
+ /*
+ * Ignore ZIP GNIReplys which are either late or unsolicited.
+ */
+ syslog( LOG_INFO, "zip gnireply from %u.%u (%s %x)",
+ ntohs( from->sat_addr.s_net ), from->sat_addr.s_node,
+ iface->i_name, iface->i_flags );
+
+ if (( iface->i_flags & ( IFACE_CONFIG|IFACE_PHASE1 )) ||
+ ( iface->i_flags & IFACE_ADDR ) == 0 ) {
+ syslog( LOG_INFO, "zip ignoring gnireply" );
+ return 1;
+ }
+
+ if ( data + 2 * sizeof( u_short ) > end ) {
+ syslog( LOG_INFO, "zip_packet malformed packet" );
+ return 1;
+ }
+ memcpy( &firstnet, data, sizeof( u_short ));
+ data += sizeof( u_short );
+ memcpy( &lastnet, data, sizeof( u_short ));
+ data += sizeof( u_short );
+
+ /*
+ * We never ask for a zone, so we can get back what the
+ * default zone is.
+ */
+ if ( data >= end || data + *data > end ) {
+ syslog( LOG_INFO, "zip_packet malformed packet" );
+ return 1;
+ }
+ if ( *data++ != 0 ) {
+ syslog( LOG_INFO, "zip_packet unsolicited zone" );
+ return 1;
+ }
+
+ /* skip multicast (should really check it) */
+ if ( data >= end || data + *data > end ) {
+ syslog( LOG_INFO, "zip_packet malformed packet" );
+ return 1;
+ }
+ data += *data + 1;
+
+ if ( data >= end || data + *data > end ) {
+ syslog( LOG_INFO, "zip_packet malformed packet" );
+ return 1;
+ }
+
+ /*
+ * First, if we're not seed, we always get our zone information
+ * from the net -- we don't even save what was in the file.
+ * Second, if we are seed, we keep our zone list in the
+ * interface structure, not in the zone table. This allows us
+ * to check that the net is giving us good zones.
+ */
+ if ( iface->i_flags & IFACE_SEED ) {
+ if ( iface->i_czt->zt_len != *data ||
+ strndiacasecmp( iface->i_czt->zt_name,
+ data + 1, *data ) != 0 ) {
+ syslog( LOG_ERR, "default zone mismatch on %s",
+ iface->i_name );
+ syslog( LOG_ERR, "%.*s != %.*s",
+ iface->i_czt->zt_len, iface->i_czt->zt_name,
+ *data, data + 1 );
+ syslog( LOG_ERR, "Seed error! Exiting!" );
+ return -1;
+ }
+ }
+
+ if (addzone( iface->i_rt, *data, data + 1 ) < 0) {
+ syslog(LOG_ERR, "zip_packet: addzone");
+ return -1;
+ }
+
+ /*
+ * The netrange we received from the router doesn't match the
+ * range we have locally. This is not a problem, unless we
+ * have seed information.
+ */
+ if ( firstnet != iface->i_rt->rt_firstnet ||
+ lastnet != iface->i_rt->rt_lastnet ) {
+ if ( iface->i_flags & IFACE_SEED ) {
+ syslog( LOG_ERR, "netrange mismatch on %s",
+ iface->i_name );
+ syslog( LOG_ERR, "%u-%u != %u-%u",
+ ntohs( firstnet ), ntohs( lastnet ),
+ ntohs( iface->i_rt->rt_firstnet ),
+ ntohs( iface->i_rt->rt_lastnet ));
+ syslog( LOG_ERR, "Seed error! Exiting!" );
+ return -1;
+ }
+
+
+ /*
+ * It is possible that we will corrupt our route database
+ * by just forcing this change. A better solution would
+ * be to search all of our current routes, looking for
+ * this new route, and delete any old versions. Also, we
+ * would call rtmp_delete() on the old net range, in case
+ * there is some other net which actually had that range. XXX
+ */
+ iface->i_rt->rt_firstnet = firstnet;
+ iface->i_rt->rt_lastnet = lastnet;
+
+ if ( ntohs( iface->i_addr.sat_addr.s_net ) <
+ ntohs( firstnet ) ||
+ ntohs( iface->i_addr.sat_addr.s_net ) >
+ ntohs( lastnet )) {
+ iface->i_addr.sat_addr.s_net = 0; /* ATADDR_ANYNET? */
+ }
+ setaddr( iface, IFACE_PHASE2, iface->i_addr.sat_addr.s_net,
+ iface->i_addr.sat_addr.s_node, firstnet, lastnet );
+ stabletimer = UNSTABLE;
+ }
+
+ /* add addr to loopback route */
+ if ( looproute( iface, RTMP_ADD )) { /* -1 or 1 */
+ syslog( LOG_ERR,
+ "zip_packet: can't route %u.%u to loopback: %m",
+ ntohs( iface->i_addr.sat_addr.s_net ),
+ iface->i_addr.sat_addr.s_node );
+ return -1;
+ }
+
+ syslog( LOG_INFO, "zip_packet configured %s from %u.%u",
+ iface->i_name, ntohs( from->sat_addr.s_net ),
+ from->sat_addr.s_node );
+ iface->i_flags |= IFACE_CONFIG;
+ if ( iface == ciface ) {
+ ciface = ciface->i_next;
+ bootaddr( ciface );
+ }
+ break;
+
+ case ZIPOP_NOTIFY :
+#ifdef DEBUG
+ printf( "zip notify from %u.%u\n", ntohs( from->sat_addr.s_net ),
+ from->sat_addr.s_node );
+#endif DEBUG
+ break;
+
+ default :
+ syslog( LOG_INFO, "zip_packet bad zip op from %u.%u\n",
+ ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+ }
+ break;
+
+ case DDPTYPE_ATP :
+ if ( data + sizeof( struct atphdr ) > end ) {
+ syslog( LOG_INFO, "zip atp malformed packet" );
+ return 1;
+ }
+ memcpy( &ah, data, sizeof( struct atphdr ));
+ data += sizeof( struct atphdr );
+ if ( ah.atphd_ctrlinfo != ATP_TREQ ) {
+ syslog( LOG_INFO, "zip atp bad control" );
+ return 1;
+ }
+ ah.atphd_ctrlinfo = ATP_TRESP | ATP_EOM;
+ if ( ah.atphd_bitmap != 1 ) {
+ syslog( LOG_ERR, "zip atp bad bitmap" );
+ return 1;
+ }
+ ah.atphd_bitmap = 0;
+
+ zipop = *data++;
+ data++;
+ memcpy( &index, data, sizeof( u_short ));
+ data += sizeof( u_short );
+ index = ntohs( index );
+ if ( data != end ) {
+ syslog( LOG_INFO, "zip atp malformed packet" );
+ return 1;
+ }
+
+ data = packet;
+ end = data + sizeof( packet );
+ *data++ = DDPTYPE_ATP;
+ memcpy( data, &ah, sizeof( struct atphdr ));
+ data += sizeof( struct atphdr );
+ lastflag = data++; /* mark and space for last flag */
+ *data++ = 0;
+ nzones = data; /* mark and space for zone count */
+ data += sizeof( u_short );
+
+ switch ( zipop ) {
+ case ZIPOP_GETMYZONE :
+ if ( index != 0 ) {
+ syslog( LOG_INFO, "zip atp gmz bad index" );
+ return 1;
+ }
+
+ if ( iface->i_flags & IFACE_LOOPBACK ) {
+ iface = interfaces->i_next; /* first interface */
+ } else if ( ntohs( iface->i_rt->rt_firstnet ) >
+ ntohs( from->sat_addr.s_net ) ||
+ ntohs( iface->i_rt->rt_lastnet ) <
+ ntohs( from->sat_addr.s_net )) {
+ return 0;
+ }
+
+ if ( iface->i_rt->rt_zt == 0 ) {
+ return 0;
+ }
+ zt = (struct ziptab *)iface->i_rt->rt_zt->l_data;
+ if ( data + 1 + zt->zt_len > end ) {
+ syslog( LOG_INFO, "zip atp gmz reply too long" );
+ return 1;
+ }
+ *data++ = zt->zt_len;
+ memcpy( data, zt->zt_name, zt->zt_len );
+ data += zt->zt_len;
+
+ *lastflag = 0;
+ nz = 1;
+ break;
+
+ case ZIPOP_GETZONELIST :
+ for ( zt = ziptab; zt && ( index > 1 ); zt = zt->zt_next, index-- )
+ ;
+ for ( nz = 0; zt; zt = zt->zt_next, nz++ ) {
+ if ( data + 1 + zt->zt_len > end ) {
+ break;
+ }
+ *data++ = zt->zt_len;
+ memcpy( data, zt->zt_name, zt->zt_len );
+ data += zt->zt_len;
+ }
+
+ *lastflag = ( zt == 0 ); /* Too clever? */
+ break;
+
+ case ZIPOP_GETLOCALZONES :
+ if ( iface->i_flags & IFACE_LOOPBACK ) {
+ iface = interfaces->i_next; /* first interface */
+ } else if ( ntohs( iface->i_rt->rt_firstnet ) >
+ ntohs( from->sat_addr.s_net ) ||
+ ntohs( iface->i_rt->rt_lastnet ) <
+ ntohs( from->sat_addr.s_net )) {
+ return 0;
+ }
+
+ for ( l = iface->i_rt->rt_zt; l && ( index > 1 );
+ l = l->l_next, index-- )
+ ;
+ for ( nz = 0; l; l = l->l_next, nz++ ) {
+ zt = (struct ziptab *)l->l_data;
+ if ( data + 1 + zt->zt_len > end ) {
+ break;
+ }
+ *data++ = zt->zt_len;
+ memcpy( data, zt->zt_name, zt->zt_len );
+ data += zt->zt_len;
+ }
+
+ *lastflag = ( l == 0 );
+ break;
+
+ default :
+ syslog( LOG_INFO, "zip atp bad option" );
+ return 1;
+ }
+
+ /* send reply */
+ if ( nz > 0 ) {
+ nz = htons( nz );
+ memcpy( nzones, &nz, sizeof( u_short ));
+ if ( sendto( ap->ap_fd, packet, data - packet, 0,
+ (struct sockaddr *)from,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "zip atp sendto %u.%u: %m",
+ ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+ return 1;
+ }
+ }
+ break;
+
+ default :
+ syslog( LOG_INFO, "zip_packet bad ddp type from %u.%u\n",
+ ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+ return 1;
+ }
+
+ return 0;
+}
+
+int zip_getnetinfo( iface )
+ struct interface *iface;
+{
+ struct atport *ap;
+ struct ziphdr zh;
+ struct sockaddr_at sat;
+ char *data, packet[ 40 ];
+ u_short net;
+
+ syslog( LOG_INFO, "zip_getnetinfo for %s", iface->i_name );
+
+ for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+ if ( ap->ap_packet == zip_packet ) {
+ break;
+ }
+ }
+ if ( ap == 0 ) {
+ syslog( LOG_ERR, "zip_getnetinfo can't find zip socket!" );
+ return -1;
+ }
+
+ data = packet;
+
+ *data++ = DDPTYPE_ZIP;
+
+ zh.zh_op = ZIPOP_GNI;
+ zh.zh_zero = 0;
+ memcpy( data, &zh, sizeof( struct ziphdr ));
+ data += sizeof( struct ziphdr );
+ net = 0;
+ memcpy( data, &net, sizeof( u_short ));
+ data += sizeof( u_short );
+ memcpy( data, &net, sizeof( u_short ));
+ data += sizeof( u_short );
+
+ /*
+ * Set our requesting zone to NULL, so the response will contain
+ * the default zone.
+ */
+ *data++ = 0;
+
+#ifdef BSD4_4
+ sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ sat.sat_family = AF_APPLETALK;
+ sat.sat_addr.s_net = 0;
+ sat.sat_addr.s_node = ATADDR_BCAST;
+ sat.sat_port = ap->ap_port;
+
+ if ( sendto( ap->ap_fd, packet, data - packet, 0, (struct sockaddr *)&sat,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ syslog( LOG_ERR, "zip_getnetinfo sendto: %m" );
+ return -1;
+ }
+ return 0;
+}
+
+ struct ziptab *
+newzt( len, name )
+ const int len;
+ const char *name;
+{
+ struct ziptab *zt;
+
+ if (( zt = (struct ziptab *)calloc(1, sizeof( struct ziptab ))) == NULL ) {
+ return( NULL );
+ }
+
+ zt->zt_len = len;
+ if (( zt->zt_name = (char *)malloc( len )) == NULL ) {
+ free(zt);
+ return( NULL );
+ }
+
+ memcpy( zt->zt_name, name, len );
+ return( zt );
+}
+
+
+/*
+ * Insert at the end. Return 1 if a mapping already exists, 0 otherwise.
+ * -1 on error.
+ */
+static int add_list( head, data )
+ struct list **head;
+ void *data;
+{
+ struct list *l, *l2;
+
+ for ( l = *head; l; l = l->l_next ) {
+ if ( l->l_data == data ) {
+ return( 1 );
+ }
+ }
+ if (( l = (struct list *)malloc( sizeof( struct list ))) == NULL ) {
+ syslog( LOG_ERR, "add_list malloc: %m" );
+ return -1;
+ }
+
+ l->l_data = data;
+ l->l_next = NULL;
+ if ( *head == NULL ) {
+ l->l_prev = NULL;
+ *head = l;
+ } else {
+ /* find end of list */
+ for ( l2 = *head; l2->l_next; l2 = l2->l_next )
+ ;
+ l->l_prev = l2;
+ l2->l_next = l;
+ }
+ return( 0 );
+}
+
+int addzone( rt, len, zone )
+ struct rtmptab *rt;
+ int len;
+ char *zone;
+{
+ struct ziptab *zt;
+ int cc, exists = 0;
+
+ for ( zt = ziptab; zt; zt = zt->zt_next ) {
+ if ( zt->zt_len == len &&
+ strndiacasecmp( zt->zt_name, zone, len ) == 0 ) {
+ break;
+ }
+ }
+ if ( zt == NULL ) {
+ if (( zt = newzt( len, zone )) == NULL ) {
+ syslog( LOG_ERR, "addzone newzt: %m" );
+ return -1;
+ }
+ if ( ziptab == NULL ) {
+ zt->zt_prev = NULL;
+ ziptab = zt;
+ } else {
+ zt->zt_prev = ziplast;
+ ziplast->zt_next = zt;
+ }
+ ziplast = zt;
+ }
+
+ if ((cc = add_list( &zt->zt_rt, rt )) < 0)
+ return -1;
+
+ if (cc)
+ exists++;
+
+ if ((cc = add_list( &rt->rt_zt, zt )) < 0 )
+ return -1;
+
+ if (cc) {
+ if ( !exists ) {
+ syslog( LOG_ERR, "addzone corrupted route/zone mapping" );
+ return -1;
+ }
+ /*
+ * We get the repeat for local nets which have zone information
+ * already: we ask anyway, just to make sure.
+ */
+
+ return 0;
+ }
+ if ( exists ) {
+ syslog( LOG_ERR, "addzone corrupted zone/route mapping" );
+ return -1;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef ATALKD_ZIP_H
+#define ATALKD_ZIP_H 1
+
+#include <sys/cdefs.h>
+
+struct ziptab {
+ struct ziptab *zt_next,
+ *zt_prev;
+ u_char zt_len;
+ char *zt_name;
+ u_char *zt_bcast;
+ struct list *zt_rt;
+};
+
+extern struct ziptab *ziptab, *ziplast;
+struct ziptab *newzt __P((const int, const char *));
+
+#endif /* atalkd/zip.h */
--- /dev/null
+SRC= main.c printcap.c session.c file.c comment.c lp.c ppd.c \
+ magics.c headers.c queries.c
+OBJ= main.o printcap.o session.o file.o comment.o lp.o ppd.o \
+ magics.o headers.o queries.o
+
+INCPATH = -I../../include ${KRBINCPATH} ${ABSINCPATH}
+CFLAGS= ${DEFS} ${KRBDEFS} ${ABSDEFS} ${OPTOPTS} ${INCPATH}
+TAGSFILE= tags
+LIBDIRS= -L../../libatalk ${KRBLIBDIRS} ${ABSLIBDIRS}
+LIBS= -latalk ${ABSLIBS} ${KRBLIBS} ${ADDLIBS}
+CC= cc
+INSTALL= install
+
+all :
+ if [ x"${KRBDIR}" != x ]; then \
+ KRBLIBS="-lkrb -ldes"; \
+ KRBLIBDIRS="-L${KRBDIR}/lib"; \
+ KRBINCPATH="-I${KRBDIR}/include"; \
+ KRBDEFS="-DKRB"; \
+ fi; \
+ ${MAKE} ${MFLAGS} CC="${CC}" ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" \
+ OPTOPTS="${OPTOPTS}" DESTDIR="${DESTDIR}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ KRBLIBS="$${KRBLIBS}" KRBLIBDIRS="$${KRBLIBDIRS}" \
+ KRBINCPATH="$${KRBINCPATH}" KRBDEFS="$${KRBDEFS}" papd
+
+# UMICH stuff. Don't ask, I'm not telling.
+CRAP=/afs/umich.edu/group/itd/software/packages
+ABSPATH=${CRAP}/a/abs1.5/sun4m_412
+AFSPATH=${CRAP}/a/afs-3.3a/sun4m_412
+papd.abs: FRC
+ ${MAKE} ${MFLAGS} \
+ KRBLIBS="-lkrb -ldes" \
+ KRBLIBDIRS="-L${KRBDIR}/lib" \
+ KRBINCPATH="-I${KRBDIR}/include" \
+ KRBDEFS="-DKRB" \
+ ABSLIBS="-labs -lsys -lrx -llwp -lrxkad -lrx \
+ -lubik -lkauth -lcom_err -lauth -lrxkad \
+ ${AFSPATH}/lib/afs/util.a" \
+ ABSLIBDIRS="-L${ABSPATH}/lib -L${AFSPATH}/lib \
+ -L${AFSPATH}/lib/afs" \
+ ABSINCPATH="-I${ABSPATH}/include -I${AFSPATH}/include" \
+ ABSDEFS="-DABS_PRINT" \
+ CC="gcc" SRC="${SRC} abs.c" OBJ="${OBJ} abs.o" \
+ papd
+
+FRC:
+
+papd : ${OBJ} ../../libatalk/libatalk.a
+ ${CC} ${CFLAGS} ${LDFLAGS} -o papd ${OBJ} ${LIBDIRS} ${LIBS}
+
+main.o : main.c
+ ${CC} ${CFLAGS} -D_PATH_PAPDCONF=\"${ETCDIR}/papd.conf\" \
+ -DVERSION=\"`cat ../../VERSION`\" \
+ ${CPPFLAGS} -c main.c
+
+install : all
+ ${INSTALL} -c papd ${SBINDIR}
+
+clean :
+ rm -f a.out core* *.o *.bak *[Ee]rrs tags
+ rm -f papd
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/syslog.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "comment.h"
+
+struct comstate *comstate;
+
+char *comcont = "%%+";
+
+compop()
+{
+ struct comstate *cs;
+
+ cs = comstate;
+ comstate = cs->cs_prev;
+ free( cs );
+}
+
+compush( comment )
+ struct comment *comment;
+{
+ struct comstate *cs;
+
+ if (( cs = (struct comstate *)malloc( sizeof( struct comstate ))) ==
+ NULL ) {
+ syslog( LOG_ERR, "malloc: %m" );
+ exit( 1 );
+ }
+
+ cs->cs_comment = comment;
+ cs->cs_prev = comstate;
+ cs->cs_flags = 0;
+ comstate = cs;
+}
+
+comswitch( comments, handler )
+ struct comment *comments;
+ int (*handler)();
+{
+ struct comment *c, *comment = NULL;
+
+ for ( c = comments; c->c_begin; c++ ) {
+ if ( c->c_handler == handler ) {
+ comment = c;
+ }
+ }
+ if ( comment == NULL || comment->c_handler != handler ) {
+ syslog( LOG_ERR, "comswitch: can't find handler!" );
+ return( -1 );
+ }
+ compop();
+ compush( comment );
+ return( 0 );
+}
+
+comcmp( start, stop, str, how )
+ char *start, *stop, *str;
+ int how;
+{
+ int cc, len;
+
+ len = stop - start;
+ cc = strlen( str );
+ if ( how & C_FULL ) {
+ if ( cc == len & strncmp( str, start, cc ) == 0 ) {
+ return( 0 );
+ }
+ } else {
+ if ( cc <= len && strncmp( str, start, cc ) == 0 ) {
+ return( 0 );
+ }
+ }
+
+ return( 1 );
+}
+
+ struct comment *
+commatch( start, stop, comments )
+ char *start, *stop;
+ struct comment comments[];
+{
+ struct comment *comment;
+
+ for ( comment = comments; comment->c_begin; comment++ ) {
+ if ( comcmp( start, stop, comment->c_begin, comment->c_flags ) == 0 ) {
+ break;
+ }
+ }
+ if ( comment->c_begin ) {
+ return( comment );
+ } else {
+ return( NULL );
+ }
+}
+
+ char *
+comtoken( start, stop, pos, delim )
+ char *start, *stop, *pos, *delim;
+{
+ if ( pos < start || pos > stop ) {
+ abort();
+ }
+
+ for ( ; pos < stop; pos++ ) {
+ if ( index( delim, *pos )) {
+ break;
+ }
+ }
+ if ( ++pos < stop ) {
+ return( pos );
+ } else {
+ return( NULL );
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+struct comment {
+ char *c_begin;
+ char *c_end;
+ int (*c_handler)();
+ int c_flags;
+};
+
+#define CH_DONE 0
+#define CH_MORE 1
+#define CH_ERROR -1
+
+struct comstate {
+ struct comment *cs_comment;
+ struct comstate *cs_prev;
+ int cs_flags;
+};
+
+extern struct comment *commatch();
+extern struct comstate *comstate;
+extern struct comment magics[];
+extern struct comment queries[];
+extern struct comment headers[];
+extern char *comcont;
+
+#define compeek() (comstate==NULL?NULL:(comstate->cs_comment))
+#define comgetflags() (comstate->cs_flags)
+#define comsetflags(f) (comstate->cs_flags=(f))
+
+/*
+ * Comment flags. 0-15 reserved for "global" flags, 16-31 for specific
+ * subtypes.
+ */
+#define C_FULL (1<<0) /* or prefix */
+#define C_CONTINUE (1<<1)
+
+/*
+ * Query subtypes.
+ */
+
+/*
+ * Magic "number" subtypes.
+ */
+#define CM_NOPRINT (1<<16) /* or print */
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/syslog.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "file.h"
+
+markline( start, stop, pf )
+ char **start, **stop;
+ struct papfile *pf;
+{
+ char *p;
+
+ if ( PF_BUFSIZ( pf ) == 0 && ( pf->pf_state & PF_EOF )) {
+ return( 0 );
+ }
+
+ /* get a line */
+ for ( p = pf->pf_cur; p < pf->pf_end; p++ ) {
+ if ( *p == '\n' || *p == '\r' ) {
+ break;
+ }
+ }
+ if ( p >= pf->pf_end ) {
+ if ( pf->pf_state & PF_EOF ) {
+ APPEND( pf, "\n", 1 );
+ } else {
+ return( -1 );
+ }
+ }
+
+ *start = pf->pf_cur;
+ *stop = p;
+ if ( *stop == *start ) {
+ return( 1 ); /* don't return len 0 lines */
+ } else {
+ return( *stop - *start );
+ }
+}
+
+consumetomark( start, stop, pf )
+ char *start, *stop;
+ struct papfile *pf;
+{
+ if ( start != pf->pf_cur || pf->pf_cur > stop || stop > pf->pf_end ) {
+ abort();
+ }
+
+ pf->pf_cur = stop + 1; /* past the stop char */
+ if ( pf->pf_cur > pf->pf_end ) {
+ abort();
+ }
+ if ( pf->pf_cur == pf->pf_end ) {
+ pf->pf_cur = pf->pf_end = pf->pf_buf;
+ }
+
+ return;
+}
+
+morespace( pf, data, len )
+ struct papfile *pf;
+ char *data;
+ int len;
+{
+ char *nbuf;
+ int nsize;
+
+ if ( pf->pf_cur != pf->pf_buf ) { /* pull up */
+ bcopy( pf->pf_cur, pf->pf_buf, PF_BUFSIZ( pf ));
+ pf->pf_end = pf->pf_buf + PF_BUFSIZ( pf );
+ pf->pf_cur = pf->pf_buf;
+ }
+
+ if ( pf->pf_end + len > pf->pf_buf + pf->pf_len ) { /* make more space */
+ nsize = (( pf->pf_len + len ) / PF_MORESPACE +
+ (( pf->pf_len + len ) % PF_MORESPACE != 0 )) * PF_MORESPACE;
+ if ( pf->pf_buf ) {
+ if (( nbuf = (char *)realloc( pf->pf_buf, nsize )) == 0 ) {
+ exit( 1 );
+ }
+ } else {
+ if (( nbuf = (char *)malloc( nsize )) == 0 ) {
+ exit( 1 );
+ }
+ }
+ pf->pf_len = nsize;
+ pf->pf_end = nbuf + ( pf->pf_end - pf->pf_buf );
+ pf->pf_cur = nbuf + ( pf->pf_cur - pf->pf_buf );
+ pf->pf_buf = nbuf;
+ }
+
+ bcopy( data, pf->pf_end, len );
+ pf->pf_end += len;
+}
+
+spoolerror( out, str )
+ struct papfile *out;
+ char *str;
+{
+ char *pserr1 = "%%[ Error: ";
+ char *pserr2 = " ]%%\n";
+
+ if ( str == NULL ) {
+ str = "Spooler error.";
+ }
+
+ APPEND( out, pserr1, strlen( pserr1 ));
+ APPEND( out, str, strlen( str ));
+ APPEND( out, pserr2, strlen( pserr2 ));
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+struct papfile {
+ int pf_state;
+ struct state *pf_xstate;
+ int pf_len;
+ char *pf_buf;
+ char *pf_cur;
+ char *pf_end;
+};
+
+#define PF_BOT (1<<0)
+#define PF_EOF (1<<1)
+#define PF_QUERY (1<<2)
+
+#define APPEND( pf, data, len ) \
+ if ( (pf)->pf_end + (len) > (pf)->pf_buf + (pf)->pf_len ) { \
+ morespace( (pf), (data), (len)); \
+ } else { \
+ bcopy( (data), (pf)->pf_end, (len)); \
+ (pf)->pf_end += (len); \
+ }
+#define PF_BUFSIZ( pf ) ((pf)->pf_end - (pf)->pf_cur)
+#define CONSUME( pf, len ) (((pf)->pf_cur += (len)), \
+ (((pf)->pf_cur >= (pf)->pf_end) && \
+ ((pf)->pf_cur = (pf)->pf_end = (pf)->pf_buf)))
+
+#define PF_MORESPACE 1024
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/syslog.h>
+#include <sys/param.h>
+#include <stdio.h>
+
+#include "file.h"
+#include "comment.h"
+
+ch_title( in, out )
+ struct papfile *in, *out;
+{
+ char *start, *stop, *p, *q, c;
+ struct comment *comment = compeek();
+
+ switch ( markline( &start, &stop, in )) {
+ case 0 :
+ return( 0 );
+
+ case -1 :
+ return( CH_MORE );
+ }
+
+ for ( p = start; p < stop; p++ ) {
+ if ( *p == ':' ) {
+ break;
+ }
+ }
+
+ for ( ; p < stop; p++ ) {
+ if ( *p == '(' ) {
+ break;
+ }
+ }
+
+ for ( q = p; q < stop; q++ ) {
+ if ( *q == ')' ) {
+ break;
+ }
+ }
+
+ if ( q < stop && p < stop ) {
+ p++;
+ c = *q;
+ *q = '\0';
+ lp_job( p );
+ *q = c;
+ }
+
+ *stop = '\n';
+ lp_write( start, stop - start + 1 );
+ compop();
+ consumetomark( start, stop, in );
+ return( CH_DONE );
+}
+
+/*
+ * "Header" comments.
+ */
+struct comment headers[] = {
+ { "%%Title:", 0, ch_title, 0 },
+ { 0 },
+};
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * Portions:
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Interface to lpr system.
+ */
+
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#if defined( sun ) && defined( __svr4__ )
+#include </usr/ucbinclude/sys/file.h>
+#else sun __svr4__
+#include <sys/file.h>
+#endif sun __svr4__
+#include <sys/un.h>
+#include <netinet/in.h>
+#undef s_net
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/paths.h>
+
+#ifdef ABS_PRINT
+#include <math.h>
+#endif ABS_PRINT
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <fcntl.h>
+
+#include "printer.h"
+#include "file.h"
+
+char hostname[ MAXHOSTNAMELEN ];
+
+/* initialize printing interface */
+int lp_init();
+/* cancel current job */
+int lp_cancel();
+/* print current job */
+int lp_print();
+
+/* open a file for spooling */
+int lp_open();
+/* open a buffer to the current open file */
+int lp_write();
+/* close current spooling file */
+int lp_close();
+
+struct lp {
+ int lp_flags;
+ FILE *lp_stream;
+ int lp_seq;
+ char lp_letter;
+ char *lp_person;
+ char *lp_host;
+ char *lp_job;
+} lp;
+#define LP_INIT (1<<0)
+#define LP_OPEN (1<<1)
+#define LP_PIPE (1<<2)
+#define LP_CONNECT (1<<3)
+#define LP_QUEUE (1<<4)
+
+lp_person( person )
+ char *person;
+{
+ if ( lp.lp_person != NULL ) {
+ free( lp.lp_person );
+ }
+ if (( lp.lp_person = (char *)malloc( strlen( person ) + 1 )) == NULL ) {
+ syslog( LOG_ERR, "malloc: %m" );
+ exit( 1 );
+ }
+ strcpy( lp.lp_person, person );
+}
+
+#ifdef ABS_PRINT
+lp_pagecost()
+{
+ char cost[ 22 ];
+ char balance[ 22 ];
+ int err;
+
+ if ( lp.lp_person == NULL ) {
+ return( -1 );
+ }
+ err = ABS_canprint( lp.lp_person, printer->p_role, printer->p_srvid,
+ cost, balance );
+ printer->p_pagecost = floor( atof( cost ) * 10000.0 );
+ printer->p_balance = atof( balance ) + atof( cost );
+ return( err < 0 ? -1 : 0 );
+}
+#endif ABS_PRINT
+
+lp_host( host )
+ char *host;
+{
+ if ( lp.lp_host != NULL ) {
+ free( lp.lp_host );
+ }
+ if (( lp.lp_host = (char *)malloc( strlen( host ) + 1 )) == NULL ) {
+ syslog( LOG_ERR, "malloc: %m" );
+ exit( 1 );
+ }
+ strcpy( lp.lp_host, host );
+}
+
+lp_job( job )
+ char *job;
+{
+ char *p, *q;
+
+ if ( lp.lp_job != NULL ) {
+ free( lp.lp_job );
+ }
+ if (( lp.lp_job = (char *)malloc( strlen( job ) + 1 )) == NULL ) {
+ syslog( LOG_ERR, "malloc: %m" );
+ exit( 1 );
+ }
+ for ( p = job, q = lp.lp_job; *p != '\0'; p++, q++ ) {
+ if ( !isascii( *p ) || !isprint( *p ) || *p == '\\' ) {
+ *q = '.';
+ } else {
+ *q = *p;
+ }
+ }
+ *q = '\0';
+}
+
+lp_init( out )
+ struct papfile *out;
+{
+ int fd, n, len;
+ char *cp, buf[ BUFSIZ ];
+ struct stat st;
+#ifdef ABS_PRINT
+ char cost[ 22 ];
+ char balance[ 22 ];
+#endif ABS_PRINT
+
+ if ( printer->p_flags & P_AUTH ) {
+ if ( lp.lp_person == NULL ) {
+ syslog( LOG_ERR, "lp_init: must authenticate" );
+ spoolerror( out, "Authentication required." );
+ return( -1 );
+ }
+#ifdef ABS_PRINT
+ if (( printer->p_flags & P_ACCOUNT ) && printer->p_pagecost > 0 &&
+ ! ABS_canprint( lp.lp_person, printer->p_role,
+ printer->p_srvid, cost, balance )) {
+ syslog( LOG_ERR, "lp_init: no ABS funds" );
+ spoolerror( out, "No ABS funds available." );
+ return( -1 );
+ }
+#endif ABS_PRINT
+ }
+
+ if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
+ syslog( LOG_ERR, "gethostname: %m" );
+ exit( 1 );
+ }
+
+ if ( lp.lp_flags & LP_INIT ) {
+ syslog( LOG_ERR, "lp_init: already inited, die!" );
+ abort();
+ }
+
+ lp.lp_flags = 0;
+ lp.lp_stream = NULL;
+ lp.lp_letter = 'A';
+
+ if ( printer->p_flags & P_SPOOLED ) {
+ /* check if queuing is enabled: mode & 010 on lock file */
+ if ( stat( printer->p_lock, &st ) < 0 ) {
+ syslog( LOG_ERR, "lp_init: %s: %m", printer->p_lock );
+ spoolerror( out, NULL );
+ return( -1 );
+ }
+ if ( st.st_mode & 010 ) {
+ syslog( LOG_INFO, "lp_init: queuing is disabled" );
+ spoolerror( out, "Queuing is disabled." );
+ return( -1 );
+ }
+
+ if (( fd = open( ".seq", O_RDWR|O_CREAT, 0661 )) < 0 ) {
+ syslog( LOG_ERR, "lp_init: can't create .seq" );
+ spoolerror( out, NULL );
+ return( -1 );
+ }
+
+ if ( flock( fd, LOCK_EX ) < 0 ) {
+ syslog( LOG_ERR, "lp_init: can't lock .seq" );
+ spoolerror( out, NULL );
+ return( -1 );
+ }
+
+ n = 0;
+ if (( len = read( fd, buf, sizeof( buf ))) < 0 ) {
+ syslog( LOG_ERR, "lp_init read: %m" );
+ spoolerror( out, NULL );
+ return( -1 );
+ }
+ if ( len > 0 ) {
+ for ( cp = buf; len; len--, cp++ ) {
+ if ( *cp < '0' || *cp > '9' ) {
+ break;
+ }
+ n = n * 10 + ( *cp - '0' );
+ }
+ }
+ lp.lp_seq = n;
+
+ n = ( n + 1 ) % 1000;
+ sprintf( buf, "%03d\n", n );
+ lseek( fd, 0L, 0 );
+ write( fd, buf, strlen( buf ));
+ close( fd );
+ } else {
+ lp.lp_flags |= LP_PIPE;
+ lp.lp_seq = getpid();
+ }
+
+ lp.lp_flags |= LP_INIT;
+ return( 0 );
+}
+
+lp_open( out )
+ struct papfile *out;
+{
+ char name[ MAXPATHLEN ];
+ int fd;
+
+ if (( lp.lp_flags & LP_INIT ) == 0 && lp_init( out ) != 0 ) {
+ return( -1 );
+ }
+ if ( lp.lp_flags & LP_OPEN ) {
+ syslog( LOG_ERR, "lp_open already open" );
+ abort();
+ }
+
+ if ( lp.lp_flags & LP_PIPE ) {
+ /* go right to program */
+ if (( lp.lp_stream = popen( printer->p_printer, "w" )) == NULL ) {
+ syslog( LOG_ERR, "lp_open popen %s: %m", printer->p_printer );
+ spoolerror( out, NULL );
+ return( -1 );
+ }
+ } else {
+ sprintf( name, "df%c%03d%s", lp.lp_letter++, lp.lp_seq, hostname );
+
+ if (( fd = open( name, O_WRONLY|O_CREAT|O_EXCL, 0660 )) < 0 ) {
+ syslog( LOG_ERR, "lp_open %s: %m", name );
+ spoolerror( out, NULL );
+ return( -1 );
+ }
+ if (( lp.lp_stream = fdopen( fd, "w" )) == NULL ) {
+ syslog( LOG_ERR, "lp_open fdopen: %m" );
+ spoolerror( out, NULL );
+ return( -1 );
+ }
+ }
+ lp.lp_flags |= LP_OPEN;
+
+ return( 0 );
+}
+
+lp_close()
+{
+ if (( lp.lp_flags & LP_INIT ) == 0 || ( lp.lp_flags & LP_OPEN ) == 0 ) {
+ return;
+ }
+ fclose( lp.lp_stream );
+ lp.lp_stream = NULL;
+ lp.lp_flags &= ~LP_OPEN;
+ return;
+}
+
+lp_write( buf, len )
+ char *buf;
+ int len;
+{
+ if (( lp.lp_flags & LP_OPEN ) == 0 ) {
+ return( -1 );
+ }
+
+ if ( fwrite( buf, 1, len, lp.lp_stream ) != len ) {
+ syslog( LOG_ERR, "lp_write: %m" );
+ abort();
+ }
+ return( 0 );
+}
+
+lp_cancel()
+{
+ char name[ MAXPATHLEN ];
+ char letter;
+
+ if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
+ return;
+ }
+
+ if ( lp.lp_flags & LP_OPEN ) {
+ lp_close();
+ }
+
+ for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
+ sprintf( name, "df%c%03d%s", letter, lp.lp_seq, hostname );
+ if ( unlink( name ) < 0 ) {
+ syslog( LOG_ERR, "lp_cancel unlink %s: %m", name );
+ }
+ }
+
+ return;
+}
+
+/*
+ * Create printcap control file, signal printer. Errors here should
+ * remove queue files.
+ *
+ * XXX piped?
+ */
+lp_print()
+{
+ char buf[ MAXPATHLEN ];
+ char tfname[ MAXPATHLEN ];
+ char cfname[ MAXPATHLEN ];
+ char letter;
+ int fd, n, s;
+ FILE *cfile;
+
+ if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
+ return;
+ }
+ lp_close();
+
+ if ( printer->p_flags & P_SPOOLED ) {
+ sprintf( tfname, "tfA%03d%s", lp.lp_seq, hostname );
+ if (( fd = open( tfname, O_WRONLY|O_EXCL|O_CREAT, 0660 )) < 0 ) {
+ syslog( LOG_ERR, "lp_print %s: %m", tfname );
+ return;
+ }
+ if (( cfile = fdopen( fd, "w" )) == NULL ) {
+ syslog( LOG_ERR, "lp_print %s: %m", tfname );
+ return;
+ }
+ fprintf( cfile, "H%s\n", hostname ); /* XXX lp_host? */
+
+ if ( lp.lp_person ) {
+ fprintf( cfile, "P%s\n", lp.lp_person );
+ } else {
+ fprintf( cfile, "P%s\n", printer->p_operator );
+ }
+
+ if ( lp.lp_job && *lp.lp_job ) {
+ fprintf( cfile, "J%s\n", lp.lp_job );
+ fprintf( cfile, "T%s\n", lp.lp_job );
+ } else {
+ fprintf( cfile, "JMac Job\n" );
+ fprintf( cfile, "TMac Job\n" );
+ }
+
+ fprintf( cfile, "C%s\n", hostname ); /* XXX lp_host? */
+
+ if ( lp.lp_person ) {
+ fprintf( cfile, "L%s\n", lp.lp_person );
+ } else {
+ fprintf( cfile, "L%s\n", printer->p_operator );
+ }
+
+ for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
+ fprintf( cfile, "fdf%c%03d%s\n", letter, lp.lp_seq, hostname );
+ fprintf( cfile, "Udf%c%03d%s\n", letter, lp.lp_seq, hostname );
+ }
+
+ if ( lp.lp_job && *lp.lp_job ) {
+ fprintf( cfile, "N%s\n", lp.lp_job );
+ } else {
+ fprintf( cfile, "NMac Job\n" );
+ }
+ fclose( cfile );
+
+ sprintf( cfname, "cfA%03d%s", lp.lp_seq, hostname );
+ if ( link( tfname, cfname ) < 0 ) {
+ syslog( LOG_ERR, "lp_print can't link %s to %s: %m", cfname,
+ tfname );
+ return;
+ }
+ unlink( tfname );
+
+ if (( s = lp_conn_unix()) < 0 ) {
+ syslog( LOG_ERR, "lp_print: lp_conn_unix: %m" );
+ return;
+ }
+
+ sprintf( buf, "\1%s\n", printer->p_printer );
+ n = strlen( buf );
+ if ( write( s, buf, n ) != n ) {
+ syslog( LOG_ERR, "lp_print write: %m" );
+ return;
+ }
+ if ( read( s, buf, 1 ) != 1 ) {
+ syslog( LOG_ERR, "lp_print read: %m" );
+ return;
+ }
+
+ lp_disconn_unix( s );
+
+ if ( buf[ 0 ] != '\0' ) {
+ syslog( LOG_ERR, "lp_print lpd said %c: %m", buf[ 0 ] );
+ return;
+ }
+ }
+ syslog( LOG_INFO, "lp_print queued" );
+ return;
+}
+
+lp_disconn_unix( fd )
+{
+ return( close( fd ));
+}
+
+lp_conn_unix()
+{
+ int s;
+ struct sockaddr_un saun;
+
+ if (( s = socket( AF_UNIX, SOCK_STREAM, 0 )) < 0 ) {
+ syslog( LOG_ERR, "lp_conn_unix socket: %m" );
+ return( -1 );
+ }
+ bzero( &saun, sizeof( struct sockaddr_un ));
+ saun.sun_family = AF_UNIX;
+ strcpy( saun.sun_path, _PATH_DEVPRINTER );
+ if ( connect( s, (struct sockaddr *)&saun,
+ strlen( saun.sun_path ) + 2 ) < 0 ) {
+ syslog( LOG_ERR, "lp_conn_unix connect %s: %m", saun.sun_path );
+ close( s );
+ return( -1 );
+ }
+
+ return( s );
+}
+
+lp_disconn_inet( fd )
+{
+ return( close( fd ));
+}
+
+lp_conn_inet()
+{
+ int privfd, port = IPPORT_RESERVED - 1;
+ struct sockaddr_in sin;
+ struct servent *sp;
+ struct hostent *hp;
+
+ if (( sp = getservbyname( "printer", "tcp" )) == NULL ) {
+ syslog( LOG_ERR, "printer/tcp: unknown service\n" );
+ return( -1 );
+ }
+
+ if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
+ syslog( LOG_ERR, "gethostname: %m" );
+ exit( 1 );
+ }
+
+ if (( hp = gethostbyname( hostname )) == NULL ) {
+ syslog( LOG_ERR, "%s: unknown host\n", hostname );
+ return( -1 );
+ }
+
+ if (( privfd = rresvport( &port )) < 0 ) {
+ syslog( LOG_ERR, "lp_connect: socket: %m" );
+ close( privfd );
+ return( -1 );
+ }
+
+ bzero( &sin, sizeof( struct sockaddr_in ));
+ sin.sin_family = AF_INET;
+/* sin.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); */
+ bcopy( hp->h_addr, &sin.sin_addr, hp->h_length );
+ sin.sin_port = sp->s_port;
+
+ if ( connect( privfd, (struct sockaddr *)&sin,
+ sizeof( struct sockaddr_in )) < 0 ) {
+ syslog( LOG_ERR, "lp_connect: %m" );
+ close( privfd );
+ return( -1 );
+ }
+
+ return( privfd );
+}
+
+lp_rmjob( job )
+ int job;
+{
+ char buf[ 1024 ];
+ int n, s;
+
+ if (( s = lp_conn_inet()) < 0 ) {
+ syslog( LOG_ERR, "lp_rmjob: %m" );
+ return( -1 );
+ }
+
+ if ( lp.lp_person == NULL ) {
+ return( -1 );
+ }
+
+ sprintf( buf, "\5%s %s %d\n", printer->p_printer, lp.lp_person, job );
+ n = strlen( buf );
+ if ( write( s, buf, n ) != n ) {
+ syslog( LOG_ERR, "lp_rmjob write: %m" );
+ lp_disconn_inet( s );
+ return( -1 );
+ }
+ while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
+ syslog( LOG_DEBUG, "read %.*s", n, buf );
+ }
+
+ lp_disconn_inet( s );
+ return( 0 );
+}
+
+char *kw_rank = "Rank";
+char *kw_active = "active";
+
+char *tag_rank = "rank: ";
+char *tag_owner = "owner: ";
+char *tag_job = "job: ";
+char *tag_files = "files: ";
+char *tag_size = "size: ";
+char *tag_status = "status: ";
+
+lp_queue( out )
+ struct papfile *out;
+{
+ char buf[ 1024 ], *start, *stop, *p, *q;
+ static struct papfile pf;
+ int n, len, s;
+
+ if (( s = lp_conn_unix()) < 0 ) {
+ syslog( LOG_ERR, "lp_queue: %m" );
+ return( -1 );
+ }
+
+ sprintf( buf, "\3%s\n", printer->p_printer );
+ n = strlen( buf );
+ if ( write( s, buf, n ) != n ) {
+ syslog( LOG_ERR, "lp_queue write: %m" );
+ lp_disconn_unix( s );
+ return( -1 );
+ }
+ pf.pf_state = PF_BOT;
+
+ while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
+ APPEND( &pf, buf, n );
+ }
+
+ for (;;) {
+ if ( markline( &start, &stop, &pf ) > 0 ) {
+ /* parse */
+ for ( p = start; p < stop; p++ ) {
+ if ( *p == ' ' || *p == '\t' ) {
+ break;
+ }
+ }
+ if ( p >= stop ) {
+ consumetomark( start, stop, &pf );
+ continue;
+ }
+
+ /*
+ * Keys: "Rank", a number, "active"
+ * Anything else is status.
+ */
+ len = p - start;
+ if ( len == strlen( kw_rank ) &&
+ strncmp( kw_rank, start, len ) == 0 ) {
+ consumetomark( start, stop, &pf );
+ continue;
+ }
+ if (( len == strlen( kw_active ) &&
+ strncmp( kw_active, start, len ) == 0 ) ||
+ isdigit( *start )) { /* a job line */
+ APPEND( out, tag_rank, strlen( tag_rank ));
+ APPEND( out, start, p - start );
+ APPEND( out, "\n", 1 );
+
+ for ( ; p < stop; p++ ) {
+ if ( *p != ' ' && *p != '\t' ) {
+ break;
+ }
+ }
+ for ( q = p; p < stop; p++ ) {
+ if ( *p == ' ' || *p == '\t' ) {
+ break;
+ }
+ }
+ if ( p >= stop ) {
+ APPEND( out, ".\n", 2 );
+ consumetomark( start, stop, &pf );
+ continue;
+ }
+ APPEND( out, tag_owner, strlen( tag_owner ));
+ APPEND( out, q, p - q );
+ APPEND( out, "\n", 1 );
+
+ for ( ; p < stop; p++ ) {
+ if ( *p != ' ' && *p != '\t' ) {
+ break;
+ }
+ }
+ for ( q = p; p < stop; p++ ) {
+ if ( *p == ' ' || *p == '\t' ) {
+ break;
+ }
+ }
+ if ( p >= stop ) {
+ APPEND( out, ".\n", 2 );
+ consumetomark( start, stop, &pf );
+ continue;
+ }
+ APPEND( out, tag_job, strlen( tag_job ));
+ APPEND( out, q, p - q );
+ APPEND( out, "\n", 1 );
+
+ for ( ; p < stop; p++ ) {
+ if ( *p != ' ' && *p != '\t' ) {
+ break;
+ }
+ }
+ for ( q = p, p = stop; p > q; p-- ) {
+ if ( *p == ' ' || *p == '\t' ) {
+ break;
+ }
+ }
+ for ( ; p > q; p-- ) {
+ if ( *p != ' ' && *p != '\t' ) {
+ break;
+ }
+ }
+ for ( ; p > q; p-- ) {
+ if ( *p == ' ' || *p == '\t' ) {
+ break;
+ }
+ }
+ if ( p <= q ) {
+ APPEND( out, ".\n", 2 );
+ consumetomark( start, stop, &pf );
+ continue;
+ }
+ APPEND( out, tag_files, strlen( tag_files ));
+ APPEND( out, q, p - q );
+ APPEND( out, "\n", 1 );
+
+ for ( ; p < stop; p++ ) {
+ if ( *p != ' ' && *p != '\t' ) {
+ break;
+ }
+ }
+ APPEND( out, tag_size, strlen( tag_size ));
+ APPEND( out, p, stop - p );
+ APPEND( out, "\n.\n", 3 );
+
+ consumetomark( start, stop, &pf );
+ continue;
+ }
+
+ /* status */
+ APPEND( out, tag_status, strlen( tag_status ));
+ APPEND( out, start, stop - start );
+ APPEND( out, "\n.\n", 3 );
+
+ consumetomark( start, stop, &pf );
+ } else {
+ APPEND( out, "*\n", 2 );
+ lp_disconn_unix( s );
+ return( 0 );
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/syslog.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "file.h"
+#include "comment.h"
+
+ps( infile, outfile )
+ struct papfile *infile, *outfile;
+{
+ char *start, *stop;
+ struct comment *comment;
+
+ for (;;) {
+ if ( comment = compeek()) {
+ switch( (*comment->c_handler)( infile, outfile )) {
+ case CH_DONE :
+ continue;
+
+ case CH_MORE :
+ return( CH_MORE );
+
+ default :
+ return( CH_ERROR );
+ }
+
+ } else {
+ switch ( markline( &start, &stop, infile )) {
+ case 0 :
+ /* eof on infile */
+ outfile->pf_state |= PF_EOF;
+ lp_close();
+ return( 0 );
+
+ case -1 :
+ return( 0 );
+ }
+
+ if ( infile->pf_state & PF_BOT ) {
+ if (( comment = commatch( start, stop, magics )) != NULL ) {
+ compush( comment );
+ continue; /* top of for (;;) */
+ }
+ infile->pf_state &= ~PF_BOT;
+
+ /* set up spool file */
+ if ( lp_open( outfile ) < 0 ) {
+ syslog( LOG_ERR, "lp_open failed" );
+ spoolerror( outfile, "Ignoring job." );
+ }
+ }
+
+ /* write to file */
+ *stop = '\n';
+ lp_write( start, stop - start + 1 );
+ consumetomark( start, stop, infile );
+ }
+ }
+}
+
+cm_psquery( in, out )
+ struct papfile *in, *out;
+{
+ struct comment *comment;
+ char *start, *stop;
+
+ for (;;) {
+ switch ( markline( &start, &stop, in )) {
+ case 0 :
+ /* eof on infile */
+ out->pf_state |= PF_EOF;
+ compop();
+ return( CH_DONE );
+
+ case -1 :
+ return( CH_MORE );
+ }
+
+ if ( in->pf_state & PF_BOT ) {
+ in->pf_state &= ~PF_BOT;
+ } else {
+ if (( comment = commatch( start, stop, queries )) != NULL ) {
+ compush( comment );
+ return( CH_DONE );
+ }
+ }
+
+ consumetomark( start, stop, in );
+ }
+}
+
+cm_psadobe( in, out )
+ struct papfile *in, *out;
+{
+ char *start, *stop;
+ struct comment *comment = compeek();
+
+ for (;;) {
+ switch ( markline( &start, &stop, in )) {
+ case 0 :
+ /* eof on infile */
+ out->pf_state |= PF_EOF;
+ compop();
+ return( CH_DONE );
+
+ case -1 :
+ return( CH_MORE );
+ }
+
+ if ( in->pf_state & PF_BOT ) {
+ in->pf_state &= ~PF_BOT;
+ if ( lp_open( out ) < 0 ) {
+ syslog( LOG_ERR, "lp_open failed" );
+ spoolerror( out, "Ignoring job." );
+ }
+ } else {
+ if (( comment = commatch( start, stop, headers )) != NULL ) {
+ compush( comment );
+ return( CH_DONE );
+ }
+ }
+
+ *stop = '\n';
+ lp_write( start, stop - start + 1 );
+ consumetomark( start, stop, in );
+ }
+}
+
+char *Query = "Query";
+
+cm_psswitch( in, out )
+ struct papfile *in, *out;
+{
+ char *start, *stop, *p;
+ struct comment *comment = compeek();
+
+ switch ( markline( &start, &stop, in )) {
+ case 0 :
+ /* eof on infile */
+ out->pf_state |= PF_EOF;
+ compop();
+ return( 0 );
+
+ case -1 :
+ return( CH_MORE );
+ }
+
+ for ( p = start; p < stop; p++ ) {
+ if ( *p == ' ' || *p == '\t' ) {
+ break;
+ }
+ }
+ for ( ; p < stop; p++ ) {
+ if ( *p != ' ' && *p != '\t' ) {
+ break;
+ }
+ }
+
+ if ( stop - p >= strlen( Query ) &&
+ strncmp( p, Query, strlen( Query )) == 0 ) {
+ if ( comswitch( magics, cm_psquery ) < 0 ) {
+ syslog( LOG_ERR, "cm_psswitch: can't find psquery!" );
+ exit( 1 );
+ }
+ } else {
+ if ( comswitch( magics, cm_psadobe ) < 0 ) {
+ syslog( LOG_ERR, "cm_psswitch: can't find psadobe!" );
+ exit( 1 );
+ }
+ }
+ return( CH_DONE );
+}
+
+struct comment magics[] = {
+ { "%!PS-Adobe-3.0 Query", 0, cm_psquery, C_FULL },
+ { "%!PS-Adobe-3.0", 0, cm_psadobe, C_FULL },
+ { "%!PS-Adobe-", 0, cm_psswitch, 0 },
+ { 0 },
+};
--- /dev/null
+/*
+ * Copyright (c) 1990,1995 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#if defined( sun ) && defined( __svr4__ )
+#include </usr/ucbinclude/sys/file.h>
+#else sun __svr4__
+#include <sys/file.h>
+#endif sun __svr4__
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/compat.h>
+#include <atalk/atp.h>
+#include <atalk/pap.h>
+#include <atalk/paths.h>
+#include <atalk/util.h>
+
+#include "printer.h"
+
+#define _PATH_PAPDPPDFILE ".ppd"
+
+#define PIPED_STATUS "status: print spooler processing job"
+
+struct printer defprinter;
+struct printer *printers = NULL;
+
+int debug = 0;
+char *conffile = _PATH_PAPDCONF;
+char *printcap = _PATH_PAPDPRINTCAP;
+unsigned char connid, quantum, sock, oquantum = PAP_MAXQUANTUM;
+char *cannedstatus = PIPED_STATUS;
+struct printer *printer = NULL;
+char *version = VERSION;
+static char *pidfile = _PATH_PAPDLOCK;
+
+/* this only needs to be used by the server process */
+static void papd_exit(const int i)
+{
+ server_unlock(pidfile);
+ exit(i);
+}
+
+#if !defined( ibm032 ) && !defined( _IBMR2 )
+ void
+#endif ibm032 _IBMR2
+die( n )
+ int n;
+{
+ struct printer *pr;
+
+ for ( pr = printers; pr; pr = pr->p_next ) {
+ if ( pr->p_flags & P_REGISTERED ) {
+ if ( nbp_unrgstr( pr->p_name, pr->p_type, pr->p_zone ) < 0 ) {
+ syslog( LOG_ERR, "can't unregister %s:%s@%s\n", pr->p_name,
+ pr->p_type, pr->p_zone );
+ papd_exit( n + 1 );
+ }
+ syslog( LOG_ERR, "unregister %s:%s@%s\n", pr->p_name, pr->p_type,
+ pr->p_zone );
+ }
+ }
+ papd_exit( n );
+}
+
+#if !defined( ibm032 ) && !defined( _IBMR2 )
+ void
+#endif ibm032 _IBMR2
+reap()
+{
+ int status;
+ int pid;
+
+ while (( pid = wait3( &status, WNOHANG, 0 )) > 0 ) {
+ if ( WIFEXITED( status )) {
+ if ( WEXITSTATUS( status )) {
+ syslog( LOG_ERR, "child %d exited with %d", pid,
+ WEXITSTATUS( status ));
+ } else {
+ syslog( LOG_INFO, "child %d done", pid );
+ }
+ } else {
+ if ( WIFSIGNALED( status )) {
+ syslog( LOG_ERR, "child %d killed with %d", pid,
+ WTERMSIG( status ));
+ } else {
+ syslog( LOG_ERR, "child %d died", pid );
+ }
+ }
+ }
+ return;
+}
+
+char rbuf[ 255 + 1 + 8 ];
+
+main( ac, av )
+ int ac;
+ char **av;
+{
+ extern char *optarg;
+ extern int optind;
+
+ ATP atp;
+ struct atp_block atpb;
+ struct sockaddr_at sat;
+ struct sigaction sv;
+ struct iovec iov;
+ fd_set fdset;
+ struct printer *pr;
+ char *p, hostname[ MAXHOSTNAMELEN ];
+ char cbuf[ 8 ];
+ int c;
+
+ if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
+ perror( "gethostname" );
+ exit( 1 );
+ }
+ if (( p = strchr( hostname, '.' )) != 0 ) {
+ *p = '\0';
+ }
+ if (( defprinter.p_name = (char *)malloc( strlen( hostname ) + 1 ))
+ == NULL ) {
+ perror( "malloc" );
+ exit( 1 );
+ }
+ strcpy( defprinter.p_name, hostname );
+ defprinter.p_type = "LaserWriter";
+ defprinter.p_zone = "*";
+ memset(&defprinter.p_addr, 0, sizeof(defprinter.p_addr));
+ defprinter.p_ppdfile = _PATH_PAPDPPDFILE;
+#ifdef __svr4__
+ defprinter.p_flags = P_PIPED;
+ defprinter.p_printer = "/usr/bin/lp -T PS";
+#else
+ defprinter.p_flags = P_SPOOLED;
+ defprinter.p_printer = "lp";
+#endif
+ defprinter.p_operator = "operator";
+ defprinter.p_spool = _PATH_PAPDSPOOLDIR;
+#ifdef ABS_PRINT
+ defprinter.p_role = NULL;
+ defprinter.p_srvid = 0;
+#endif ABS_PRINT
+ defprinter.p_pagecost = 200; /* default cost */
+ defprinter.p_pagecost_msg = NULL;
+ defprinter.p_lock = "lock";
+
+ while (( c = getopt( ac, av, "adf:p:P:" )) != EOF ) {
+ switch ( c ) {
+ case 'a' : /* for compatibility with old papd */
+ break;
+
+ case 'd' : /* debug */
+ debug++;
+ break;
+
+ case 'f' : /* conffile */
+ conffile = optarg;
+ break;
+
+ case 'p' : /* printcap */
+ printcap = optarg;
+ break;
+
+ case 'P' :
+ pidfile = optarg;
+ break;
+
+ default :
+ fprintf( stderr,
+ "Usage:\t%s [ -d ] [ -f conffile ] [ -p printcap ]\n",
+ *av );
+ exit( 1 );
+ }
+ }
+
+ getprinters( conffile );
+
+ switch (server_lock("papd", pidfile, debug)) {
+ case 0: /* open a couple things again in the child */
+ if ((c = open("/", O_RDONLY)) >= 0) {
+ dup2(c, 1);
+ dup2(c, 2);
+ }
+ break;
+ case -1:
+ exit(1);
+ default:
+ exit(0);
+ }
+
+ /*
+ * Start logging.
+ */
+ if (( p = strrchr( av[ 0 ], '/' )) == NULL ) {
+ p = av[ 0 ];
+ } else {
+ p++;
+ }
+#ifdef ultrix
+ openlog( p, LOG_PID );
+#else ultrix
+ openlog( p, LOG_NDELAY|LOG_PID, LOG_LPR );
+#endif ultrix
+
+ syslog( LOG_INFO, "restart (%s)", version );
+
+ for ( pr = printers; pr; pr = pr->p_next ) {
+ if (( pr->p_flags & P_SPOOLED ) && rprintcap( pr ) < 0 ) {
+ syslog( LOG_ERR, "printcap problem: %s", pr->p_printer );
+ }
+ if (( pr->p_atp = atp_open( ATADDR_ANYPORT, &pr->p_addr )) == NULL ) {
+ syslog( LOG_ERR, "atp_open: %m" );
+ papd_exit( 1 );
+ }
+ if ( nbp_rgstr( atp_sockaddr( pr->p_atp ), pr->p_name, pr->p_type,
+ pr->p_zone ) < 0 ) {
+ syslog( LOG_ERR, "can't register %s:%s@%s", pr->p_name, pr->p_type,
+ pr->p_zone );
+ die( 1 );
+ }
+ syslog( LOG_INFO, "register %s:%s@%s", pr->p_name, pr->p_type,
+ pr->p_zone );
+ pr->p_flags |= P_REGISTERED;
+ }
+
+ memset(&sv, 0, sizeof(sv));
+ sv.sa_handler = die;
+ sigemptyset( &sv.sa_mask );
+ sv.sa_flags = SA_RESTART;
+ if ( sigaction( SIGTERM, &sv, 0 ) < 0 ) {
+ syslog( LOG_ERR, "sigaction: %m" );
+ papd_exit( 1 );
+ }
+
+ sv.sa_handler = reap;
+ sigemptyset( &sv.sa_mask );
+ sv.sa_flags = SA_RESTART;
+ if ( sigaction( SIGCHLD, &sv, 0 ) < 0 ) {
+ syslog( LOG_ERR, "sigaction: %m" );
+ papd_exit( 1 );
+ }
+
+ /*
+ * Begin accepting connections.
+ */
+ FD_ZERO( &fdset );
+ for (;;) {
+ for ( pr = printers; pr; pr = pr->p_next ) {
+ FD_SET( atp_fileno( pr->p_atp ), &fdset );
+ }
+ if (( c = select( FD_SETSIZE, &fdset, 0, 0, 0 )) < 0 ) {
+ if ( errno == EINTR ) {
+ continue;
+ }
+ syslog( LOG_ERR, "select: %m" );
+ papd_exit( 1 );
+ }
+
+ for ( pr = printers; pr; pr = pr->p_next ) {
+ if ( FD_ISSET( atp_fileno( pr->p_atp ), &fdset )) {
+ int err = 0;
+
+ bzero( &sat, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+ sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ sat.sat_family = AF_APPLETALK;
+ sat.sat_addr.s_net = ATADDR_ANYNET;
+ sat.sat_addr.s_node = ATADDR_ANYNODE;
+ sat.sat_port = ATADDR_ANYPORT;
+ /* do an atp_rsel(), to prevent hangs */
+ if (( c = atp_rsel( pr->p_atp, &sat, ATP_TREQ )) != ATP_TREQ ) {
+ continue;
+ }
+ atpb.atp_saddr = &sat;
+ atpb.atp_rreqdata = cbuf;
+ atpb.atp_rreqdlen = sizeof( cbuf );
+ if ( atp_rreq( pr->p_atp, &atpb ) < 0 ) {
+ syslog( LOG_ERR, "atp_rreq: %m" );
+ continue;
+ }
+
+ /* should check length of req buf */
+
+ switch( cbuf[ 1 ] ) {
+ case PAP_OPEN :
+ connid = (unsigned char)cbuf[ 0 ];
+ sock = (unsigned char)cbuf[ 4 ];
+ quantum = (unsigned char)cbuf[ 5 ];
+ rbuf[ 0 ] = cbuf[ 0 ];
+ rbuf[ 1 ] = PAP_OPENREPLY;
+ rbuf[ 2 ] = rbuf[ 3 ] = 0;
+
+ if (( pr->p_flags & P_SPOOLED ) && rprintcap( pr ) != 0 ) {
+ syslog( LOG_ERR, "printcap problem: %s",
+ pr->p_printer );
+ rbuf[ 2 ] = rbuf[ 3 ] = 0xff;
+ err = 1;
+ }
+
+ /*
+ * If this fails, we've run out of sockets. Rather than
+ * just die(), let's try to continue. Maybe some sockets
+ * will close, and we can continue;
+ */
+ if (( atp = atp_open( ATADDR_ANYPORT,
+ &pr->p_addr)) == NULL ) {
+ syslog( LOG_ERR, "atp_open: %m" );
+ rbuf[ 2 ] = rbuf[ 3 ] = 0xff;
+ err = 1;
+ }
+ rbuf[ 4 ] = atp_sockaddr( atp )->sat_port;
+ rbuf[ 5 ] = oquantum;
+ rbuf[ 6 ] = rbuf[ 7 ] = 0;
+
+ iov.iov_base = rbuf;
+ iov.iov_len = 8 + getstatus( pr, &rbuf[ 8 ] );
+ atpb.atp_sresiov = &iov;
+ atpb.atp_sresiovcnt = 1;
+ /*
+ * This may error out if we lose a route, so we won't die().
+ */
+ if ( atp_sresp( pr->p_atp, &atpb ) < 0 ) {
+ syslog( LOG_ERR, "atp_sresp: %m" );
+ continue;
+ }
+
+ if ( err ) {
+ continue;
+ }
+
+ switch ( c = fork()) {
+ case -1 :
+ syslog( LOG_ERR, "fork: %m" );
+ continue;
+
+ case 0 : /* child */
+ printer = pr;
+
+ if (( printer->p_flags & P_SPOOLED ) &&
+ chdir( printer->p_spool ) < 0 ) {
+ syslog( LOG_ERR, "chdir %s: %m", printer->p_spool );
+ exit( 1 );
+ }
+
+ sv.sa_handler = SIG_DFL;
+ sigemptyset( &sv.sa_mask );
+ sv.sa_flags = SA_RESTART;
+ if ( sigaction( SIGTERM, &sv, 0 ) < 0 ) {
+ syslog( LOG_ERR, "sigaction: %m" );
+ exit( 1 );
+ }
+
+ for ( pr = printers; pr; pr = pr->p_next ) {
+ atp_close( pr->p_atp );
+ }
+ sat.sat_port = sock;
+ if ( session( atp, &sat ) < 0 ) {
+ syslog( LOG_ERR, "bad session" );
+ exit( 1 );
+ }
+ exit( 0 );
+ break;
+
+ default : /* parent */
+ syslog( LOG_INFO, "child %d for \"%s\" from %u.%u",
+ c, pr->p_name, ntohs( sat.sat_addr.s_net ),
+ sat.sat_addr.s_node);
+ atp_close( atp );
+ }
+ break;
+
+ case PAP_SENDSTATUS :
+ rbuf[ 0 ] = 0;
+ rbuf[ 1 ] = PAP_STATUS;
+ rbuf[ 2 ] = rbuf[ 3 ] = 0;
+ rbuf[ 4 ] = rbuf[ 5 ] = 0;
+ rbuf[ 6 ] = rbuf[ 7 ] = 0;
+
+ iov.iov_base = rbuf;
+ iov.iov_len = 8 + getstatus( pr, &rbuf[ 8 ] );
+ atpb.atp_sresiov = &iov;
+ atpb.atp_sresiovcnt = 1;
+ /*
+ * This may error out if we lose a route, so we won't die().
+ */
+ if ( atp_sresp( pr->p_atp, &atpb ) < 0 ) {
+ syslog( LOG_ERR, "atp_sresp: %m" );
+ }
+ break;
+
+ default :
+ syslog( LOG_ERR, "Bad request from %u.%u!",
+ ntohs( sat.sat_addr.s_net ), sat.sat_addr.s_node );
+ continue;
+ break;
+ }
+
+#ifdef notdef
+ /*
+ * Sometimes the child process will send its first READ
+ * before the parent has sent the OPEN REPLY. Moving this
+ * code into the OPEN/STATUS switch fixes this problem.
+ */
+ iov.iov_base = rbuf;
+ iov.iov_len = 8 + getstatus( pr, &rbuf[ 8 ] );
+ atpb.atp_sresiov = &iov;
+ atpb.atp_sresiovcnt = 1;
+ /*
+ * This may error out if we lose a route, so we won't die().
+ */
+ if ( atp_sresp( pr->p_atp, &atpb ) < 0 ) {
+ syslog( LOG_ERR, "atp_sresp: %m" );
+ }
+#endif notdef
+ }
+ }
+ }
+}
+
+/*
+ * We assume buf is big enough for 255 bytes of data and a length byte.
+ */
+ int
+getstatus( pr, buf )
+ struct printer *pr;
+ char *buf;
+{
+ char path[ MAXPATHLEN ];
+ int fd = -1, rc;
+
+ if ( pr->p_flags & P_SPOOLED && ( pr->p_spool != NULL )) {
+ strcpy( path, pr->p_spool );
+ strcat( path, "/status" );
+ fd = open( path, O_RDONLY);
+ }
+
+ if (( pr->p_flags & P_PIPED ) || ( fd < 0 )) {
+ *buf = strlen( cannedstatus );
+ strncpy( &buf[ 1 ], cannedstatus, *buf );
+ return( *buf + 1 );
+ } else {
+ if (( rc = read( fd, &buf[ 1 ], 255 )) < 0 ) {
+ rc = 0;
+ }
+ close( fd );
+ if ( rc && buf[ rc ] == '\n' ) { /* remove trailing newline */
+ rc--;
+ }
+ *buf = rc;
+ return( rc + 1 );
+ }
+}
+
+char *pgetstr();
+char *getpname();
+
+getprinters( cf )
+ char *cf;
+{
+ char buf[ 1024 ], area[ 1024 ], *a, *p, *name, *type, *zone;
+ struct printer *pr;
+ int c;
+
+ while (( c = getprent( cf, buf )) > 0 ) {
+ a = area;
+ /*
+ * Get the printer's nbp name.
+ */
+ if (( p = getpname( &a )) == NULL ) {
+ fprintf( stderr, "No printer name\n" );
+ exit( 1 );
+ }
+
+ if (( pr = (struct printer *)malloc( sizeof( struct printer )))
+ == NULL ) {
+ perror( "malloc" );
+ exit( 1 );
+ }
+ bzero( pr, sizeof( struct printer ));
+
+ name = defprinter.p_name;
+ type = defprinter.p_type;
+ zone = defprinter.p_zone;
+ if ( nbp_name( p, &name, &type, &zone )) {
+ fprintf( stderr, "Can't parse \"%s\"\n", name );
+ exit( 1 );
+ }
+ if ( name != defprinter.p_name ) {
+ if (( pr->p_name = (char *)malloc( strlen( name ) + 1 )) == NULL ) {
+ perror( "malloc" );
+ exit( 1 );
+ }
+ strcpy( pr->p_name, name );
+ } else {
+ pr->p_name = name;
+ }
+ if ( type != defprinter.p_type ) {
+ if (( pr->p_type = (char *)malloc( strlen( type ) + 1 )) == NULL ) {
+ perror( "malloc" );
+ exit( 1 );
+ }
+ strcpy( pr->p_type, type );
+ } else {
+ pr->p_type = type;
+ }
+ if ( zone != defprinter.p_zone ) {
+ if (( pr->p_zone = (char *)malloc( strlen( zone ) + 1 )) == NULL ) {
+ perror( "malloc" );
+ exit( 1 );
+ }
+ strcpy( pr->p_zone, zone );
+ } else {
+ pr->p_zone = zone;
+ }
+
+ if ( pnchktc( cf ) != 1 ) {
+ fprintf( stderr, "Bad papcap entry\n" );
+ exit( 1 );
+ }
+
+ /*
+ * Get PPD file.
+ */
+ if (( p = pgetstr( "pd", &a )) == NULL ) {
+ pr->p_ppdfile = defprinter.p_ppdfile;
+ } else {
+ if (( pr->p_ppdfile = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
+ perror( "malloc" );
+ exit( 1 );
+ }
+ strcpy( pr->p_ppdfile, p );
+ }
+
+ /*
+ * Get lpd printer name.
+ */
+ if (( p = pgetstr( "pr", &a )) == NULL ) {
+ pr->p_printer = defprinter.p_printer;
+ pr->p_flags = defprinter.p_flags;
+ } else {
+ if ( *p == '|' ) {
+ p++;
+ pr->p_flags = P_PIPED;
+ } else {
+ pr->p_flags = P_SPOOLED;
+ }
+ if (( pr->p_printer = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
+ perror( "malloc" );
+ exit( 1 );
+ }
+ strcpy( pr->p_printer, p );
+ }
+
+ if ( pr->p_flags & P_SPOOLED ) {
+ /*
+ * Get operator name.
+ */
+ if (( p = pgetstr( "op", &a )) == NULL ) {
+ pr->p_operator = defprinter.p_operator;
+ } else {
+ if (( pr->p_operator = (char *)malloc( strlen( p ) + 1 ))
+ == NULL ) {
+ perror( "malloc" );
+ exit( 1 );
+ }
+ strcpy( pr->p_operator, p );
+ }
+ }
+
+ /* get printer's appletalk address. */
+ if (( p = pgetstr( "pa", &a )) == NULL )
+ memcpy(&pr->p_addr, &defprinter.p_addr, sizeof(pr->p_addr));
+ else
+ atalk_aton(p, &pr->p_addr);
+
+ pr->p_next = printers;
+ printers = pr;
+ }
+ if ( c == 0 ) {
+ endprent();
+ } else { /* No capability file, do default */
+ printers = &defprinter;
+ }
+}
+
+rprintcap( pr )
+ struct printer *pr;
+{
+ char buf[ 1024 ], area[ 1024 ], *a, *p;
+ int c;
+
+ /*
+ * Spool directory from printcap file.
+ */
+ if ( pr->p_flags & P_SPOOLED ) {
+ if ( pgetent( printcap, buf, pr->p_printer ) != 1 ) {
+ syslog( LOG_ERR, "No such printer: %s", pr->p_printer );
+ return( -1 );
+ }
+
+ /*
+ * Spool directory.
+ */
+ if ( pr->p_spool != NULL && pr->p_spool != defprinter.p_spool ) {
+ free( pr->p_spool );
+ }
+ a = area;
+ if (( p = pgetstr( "sd", &a )) == NULL ) {
+ pr->p_spool = defprinter.p_spool;
+ } else {
+ if (( pr->p_spool = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
+ syslog( LOG_ERR, "malloc: %m" );
+ exit( 1 );
+ }
+ strcpy( pr->p_spool, p );
+ }
+
+ /*
+ * Is accounting on?
+ */
+ a = area;
+ if ( pgetstr( "af", &a ) == NULL ) {
+ pr->p_flags &= ~P_ACCOUNT;
+ } else {
+ pr->p_flags |= P_ACCOUNT;
+#ifdef ABS_PRINT
+ if ( pr->p_role != NULL && pr->p_role != defprinter.p_role ) {
+ free( pr->p_role );
+ }
+ a = area;
+ if (( p = pgetstr( "ro", &a )) == NULL ) {
+ pr->p_role = defprinter.p_role;
+ } else {
+ if (( pr->p_role =
+ (char *)malloc( strlen( p ) + 1 )) == NULL ) {
+ syslog( LOG_ERR, "malloc: %m" );
+ exit( 1 );
+ }
+ strcpy( pr->p_role, p );
+ }
+
+ if (( c = pgetnum( "si" )) < 0 ) {
+ pr->p_srvid = defprinter.p_srvid;
+ } else {
+ pr->p_srvid = c;
+ }
+#endif ABS_PRINT
+ }
+
+
+ /*
+ * Cost of printer.
+ */
+ if ( pr->p_pagecost_msg != NULL &&
+ pr->p_pagecost_msg != defprinter.p_pagecost_msg ) {
+ free( pr->p_pagecost_msg );
+ }
+ a = area;
+ if (( p = pgetstr( "pc", &a )) != NULL ) {
+ if (( pr->p_pagecost_msg =
+ (char *)malloc( strlen( p ) + 1 )) == NULL ) {
+ syslog( LOG_ERR, "malloc: %m" );
+ exit( 1 );
+ }
+ strcpy( pr->p_pagecost_msg, p );
+ pr->p_pagecost = 0;
+ } else if ( pr->p_flags & P_ACCOUNT ) {
+ if (( c = pgetnum( "pc" )) < 0 ) {
+ pr->p_pagecost = defprinter.p_pagecost;
+ } else {
+ pr->p_pagecost = c;
+ }
+ pr->p_pagecost_msg = NULL;
+ }
+
+ /*
+ * Get lpd lock file.
+ */
+ if ( pr->p_lock != NULL && pr->p_lock != defprinter.p_lock ) {
+ free( pr->p_lock );
+ }
+ a = area;
+ if (( p = pgetstr( "lo", &a )) == NULL ) {
+ pr->p_lock = defprinter.p_lock;
+ } else {
+ if (( pr->p_lock = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
+ syslog( LOG_ERR, "malloc: %m" );
+ exit( 1 );
+ }
+ strcpy( pr->p_lock, p );
+ }
+
+#ifdef KRB
+ /*
+ * Must Kerberos authenticate?
+ */
+ if ( pgetflag( "ka" ) == 1 ) {
+ pr->p_flags |= P_AUTH;
+ } else {
+ pr->p_flags &= ~P_AUTH;
+ }
+#endif
+
+ endprent();
+ }
+
+ return( 0 );
+}
--- /dev/null
+/*
+ * Copyright (c) 1995 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+
+#include "printer.h"
+#include "ppd.h"
+
+struct ppd_font *ppd_fonts = NULL;
+
+struct ppd_feature ppd_features[] = {
+ { "*LanguageLevel", 0 },
+ { "*PSVersion", 0 },
+ { "*FreeVM", 0 },
+ { "*Product", 0 },
+ { "*PCFileName", 0 },
+ { "*ModelName", 0 },
+ { "*NickName", 0 },
+ { "*ColorDevice", 0 },
+ { "*FaxSupport", 0 },
+ { "*TTRasterizer", 0 },
+ { 0, 0 },
+};
+
+struct ppdent {
+ char *pe_main;
+ char *pe_option;
+ char *pe_translation;
+ char *pe_value;
+};
+
+#ifdef notdef
+main( ac, av )
+ int ac;
+ char **av;
+{
+ struct ppd_feature *pfe;
+ struct ppd_font *pfo;
+
+ if ( ac != 2 ) {
+ fprintf( stderr, "Usage:\t%s ppdfile\n", av[ 0 ] );
+ exit( 1 );
+ }
+
+ read_ppd( av[ 1 ], 0 );
+ for ( pfo = ppd_fonts; pfo; pfo = pfo->pd_next ) {
+ printf( "Font: %s\n", pfo->pd_font );
+ }
+ for ( pfe = ppd_features; pfe->pd_name; pfe++ ) {
+ printf( "Feature: %s %s\n", pfe->pd_name, pfe->pd_value );
+ }
+
+ exit( 0 );
+}
+#endif notdef
+
+int ppd_inited = 0;
+
+ppd_init()
+{
+ if ( ppd_inited ) {
+ return( -1 );
+ }
+ ppd_inited++;
+
+ read_ppd( printer->p_ppdfile, 0 );
+}
+
+ struct ppdent *
+getppdent( stream )
+ FILE *stream;
+{
+ static char buf[ 1024 ];
+ static struct ppdent ppdent;
+ char *p, *q;
+
+ ppdent.pe_main = ppdent.pe_option = ppdent.pe_translation =
+ ppdent.pe_value = NULL;
+
+ while (( p = fgets( buf, sizeof( buf ), stream )) != NULL ) {
+ if ( *p != '*' ) { /* main key word */
+ continue;
+ }
+ if ( p[ strlen( p ) - 1 ] != '\n' ) {
+ syslog( LOG_ERR, "getppdent: line too long" );
+ continue;
+ }
+
+ q = p;
+ while ( *p != ' ' && *p != '\t' && *p != ':' && *p != '\n' ) {
+ p++;
+ }
+ if ( *( q + 1 ) == '%' || *( q + 1 ) == '?' ) { /* comments & queries */
+ continue;
+ }
+ ppdent.pe_main = q;
+ if ( *p == '\n' ) {
+ *p = '\0';
+ ppdent.pe_option = ppdent.pe_translation = ppdent.pe_value = NULL;
+ return( &ppdent );
+ }
+
+ if ( *p != ':' ) { /* option key word */
+ *p++ = '\0';
+
+ while ( *p == ' ' || *p == '\t' ) {
+ p++;
+ }
+
+ q = p;
+ while ( *p != ':' && *p != '/' && *p != '\n' ) {
+ p++;
+ }
+
+ if ( *p == '\n' ) {
+ continue;
+ }
+
+ ppdent.pe_option = q;
+ if ( *p == '/' ) { /* translation string */
+ *p++ = '\0';
+ q = p;
+ while ( *p != ':' && *p != '\n' ) {
+ p++;
+ }
+ if ( *p != ':' ) {
+ continue;
+ }
+
+ ppdent.pe_translation = q;
+ } else {
+ ppdent.pe_translation = NULL;
+ }
+ }
+ *p++ = '\0';
+
+ while ( *p == ' ' || *p == '\t' ) {
+ p++;
+ }
+
+ /* value */
+ if ( *p == '"' ) {
+ p++;
+ q = p;
+
+ while ( *p != '"' && *p != '\n' ) {
+ p++;
+ }
+
+ if ( *p == '\n' ) {
+ continue;
+ }
+ *p = '\0';
+ ppdent.pe_value = q;
+ } else {
+ q = p;
+ while ( *p != '\n' ) {
+ p++;
+ }
+ *p = '\0';
+ ppdent.pe_value = q;
+ }
+ return( &ppdent );
+ }
+
+ return( NULL );
+}
+
+read_ppd( file, fcnt )
+ char *file;
+ int fcnt;
+{
+ FILE *ppdfile;
+ struct ppdent *pe;
+ struct ppd_feature *pfe;
+ struct ppd_font *pfo;
+
+ if ( fcnt > 20 ) {
+ syslog( LOG_ERR, "read_ppd: %s: Too many files!", file );
+ return( -1 );
+ }
+
+ if (( ppdfile = fopen( file, "r" )) == NULL ) {
+ syslog( LOG_ERR, "read_ppd %s: %m", file );
+ return( -1 );
+ }
+
+ while (( pe = getppdent( ppdfile )) != NULL ) {
+ /* *Include files */
+ if ( strcmp( pe->pe_main, "*Include" ) == 0 ) {
+ read_ppd( pe->pe_value, fcnt + 1 );
+ continue;
+ }
+
+ /* *Font */
+ if ( strcmp( pe->pe_main, "*Font" ) == 0 ) {
+ for ( pfo = ppd_fonts; pfo; pfo = pfo->pd_next ) {
+ if ( strcmp( pfo->pd_font, pe->pe_option ) == 0 ) {
+ break;
+ }
+ }
+ if ( pfo ) {
+ continue;
+ }
+
+ if (( pfo = (struct ppd_font *)malloc( sizeof( struct ppd_font )))
+ == NULL ) {
+ syslog( LOG_ERR, "malloc: %m" );
+ exit( 1 );
+ }
+ if (( pfo->pd_font =
+ (char *)malloc( strlen( pe->pe_option ) + 1 )) == NULL ) {
+ syslog( LOG_ERR, "malloc: %m" );
+ exit( 1 );
+ }
+ strcpy( pfo->pd_font, pe->pe_option );
+ pfo->pd_next = ppd_fonts;
+ ppd_fonts = pfo;
+ continue;
+ }
+
+
+ /* Features */
+ for ( pfe = ppd_features; pfe->pd_name; pfe++ ) {
+ if ( strcmp( pe->pe_main, pfe->pd_name ) == 0 ) {
+ break;
+ }
+ }
+ if ( pfe->pd_name && pfe->pd_value == NULL ) {
+ if (( pfe->pd_value =
+ (char *)malloc( strlen( pe->pe_value ) + 1 )) == NULL ) {
+ syslog( LOG_ERR, "malloc: %m" );
+ exit( 1 );
+ }
+
+ strcpy( pfe->pd_value, pe->pe_value );
+ continue;
+ }
+ }
+
+ fclose( ppdfile );
+ return( 0 );
+}
+
+ struct ppd_font *
+ppd_font( font )
+ char *font;
+{
+ struct ppd_font *pfo;
+
+ if ( ! ppd_inited ) {
+ ppd_init();
+ }
+
+ for ( pfo = ppd_fonts; pfo; pfo = pfo->pd_next ) {
+ if ( strcmp( pfo->pd_font, font ) == 0 ) {
+ return( pfo );
+ }
+ }
+ return( NULL );
+}
+
+ struct ppd_feature *
+ppd_feature( feature, len )
+ char *feature;
+ int len;
+{
+ struct ppd_feature *pfe;
+ char main[ 256 ];
+ char *end, *p, *q;
+
+ if ( ! ppd_inited ) {
+ ppd_init();
+ }
+
+ for ( end = feature + len, p = feature, q = main;
+ p <= end && *p != '\n' && *p != '\r'; p++, q++ ) {
+ *q = *p;
+ }
+ if ( p > end ) {
+ return( NULL );
+ }
+ *q = '\0';
+
+ for ( pfe = ppd_features; pfe->pd_name; pfe++ ) {
+ if ( strcmp( pfe->pd_name, main ) == 0 && pfe->pd_value ) {
+ return( pfe );
+ }
+ }
+
+ return( NULL );
+}
--- /dev/null
+/*
+ * Copyright (c) 1995 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+struct ppd_font {
+ char *pd_font;
+ struct ppd_font *pd_next;
+};
+
+struct ppd_feature {
+ char *pd_name;
+ char *pd_value;
+};
+
+struct ppd_feature *ppd_feature();
+struct ppd_font *ppd_font();
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)printcap.c 5.7 (Berkeley) 3/4/91";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <atalk/paths.h>
+
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+#define MAXHOP 32 /* max number of tc= indirections */
+
+/*
+ * termcap - routines for dealing with the terminal capability data base
+ *
+ * BUG: Should use a "last" pointer in tbuf, so that searching
+ * for capabilities alphabetically would not be a n**2/2
+ * process when large numbers of capabilities are given.
+ * Note: If we add a last pointer now we will screw up the
+ * tc capability. We really should compile termcap.
+ *
+ * Essentially all the work here is scanning and decoding escapes
+ * in string capabilities. We don't use stdio because the editor
+ * doesn't, and because living w/o it is not hard.
+ */
+
+#define PRINTCAP
+
+#ifdef PRINTCAP
+#define tgetent pgetent
+#define tskip pskip
+#define tgetstr pgetstr
+#define tdecode pdecode
+#define tgetnum pgetnum
+#define tgetflag pgetflag
+#define tdecode pdecode
+#define tnchktc pnchktc
+#define tnamatch pnamatch
+#define V6
+#endif
+
+static FILE *pfp = NULL; /* printcap data base file pointer */
+static char *tbuf;
+static int hopcount; /* detect infinite loops in termcap, init 0 */
+static char *tskip();
+char *tgetstr();
+static char *tdecode();
+char *getenv();
+
+/*
+ * Similar to tgetent except it returns the next entry instead of
+ * doing a lookup.
+ *
+ * Added a "cap" parameter, so we can use these calls for printcap
+ * and papd.conf.
+ */
+getprent( cap, bp)
+ register char *cap;
+ register char *bp;
+{
+ register int c, skip = 0;
+
+ if (pfp == NULL && (pfp = fopen( cap, "r")) == NULL)
+ return(-1);
+ tbuf = bp;
+ for (;;) {
+ switch (c = getc(pfp)) {
+ case EOF:
+ fclose(pfp);
+ pfp = NULL;
+ return(0);
+ case '\n':
+ if (bp == tbuf) {
+ skip = 0;
+ continue;
+ }
+ if (bp[-1] == '\\') {
+ bp--;
+ continue;
+ }
+ *bp = '\0';
+ return(1);
+ case '#':
+ if (bp == tbuf)
+ skip++;
+ default:
+ if (skip)
+ continue;
+ if (bp >= tbuf+BUFSIZ) {
+ write(2, "Termcap entry too long\n", 23);
+ *bp = '\0';
+ return(1);
+ }
+ *bp++ = c;
+ }
+ }
+}
+
+endprent()
+{
+ if (pfp != NULL)
+ fclose(pfp);
+}
+
+/*
+ * Get an entry for terminal name in buffer bp,
+ * from the termcap file. Parse is very rudimentary;
+ * we just notice escaped newlines.
+ *
+ * Added a "cap" parameter, so we can use these calls for printcap
+ * and papd.conf.
+ */
+tgetent( cap, bp, name)
+ char *cap, *bp, *name;
+{
+ register char *cp;
+ register int c;
+ register int i = 0, cnt = 0;
+ char ibuf[BUFSIZ];
+ int tf;
+
+ hopcount = 0;
+ tbuf = bp;
+ tf = 0;
+#ifndef V6
+ cp = getenv("TERMCAP");
+ /*
+ * TERMCAP can have one of two things in it. It can be the
+ * name of a file to use instead of /etc/termcap. In this
+ * case it better start with a "/". Or it can be an entry to
+ * use so we don't have to read the file. In this case it
+ * has to already have the newlines crunched out.
+ */
+ if (cp && *cp) {
+ if (*cp!='/') {
+ cp2 = getenv("TERM");
+ if (cp2==(char *) 0 || strcmp(name,cp2)==0) {
+ strcpy(bp,cp);
+ return(tnchktc());
+ } else {
+ tf = open(cap, 0);
+ }
+ } else
+ tf = open(cp, 0);
+ }
+ if (tf==0)
+ tf = open(cap, 0);
+#else
+ tf = open(cap, 0);
+#endif
+ if (tf < 0)
+ return (-1);
+ for (;;) {
+ cp = bp;
+ for (;;) {
+ if (i == cnt) {
+ cnt = read(tf, ibuf, BUFSIZ);
+ if (cnt <= 0) {
+ close(tf);
+ return (0);
+ }
+ i = 0;
+ }
+ c = ibuf[i++];
+ if (c == '\n') {
+ if (cp > bp && cp[-1] == '\\'){
+ cp--;
+ continue;
+ }
+ break;
+ }
+ if (cp >= bp+BUFSIZ) {
+ write(2,"Termcap entry too long\n", 23);
+ break;
+ } else
+ *cp++ = c;
+ }
+ *cp = 0;
+
+ /*
+ * The real work for the match.
+ */
+ if (tnamatch(name)) {
+ close(tf);
+ return(tnchktc());
+ }
+ }
+}
+
+/*
+ * tnchktc: check the last entry, see if it's tc=xxx. If so,
+ * recursively find xxx and append that entry (minus the names)
+ * to take the place of the tc=xxx entry. This allows termcap
+ * entries to say "like an HP2621 but doesn't turn on the labels".
+ * Note that this works because of the left to right scan.
+ *
+ * Added a "cap" parameter, so we can use these calls for printcap
+ * and papd.conf.
+ */
+tnchktc( cap )
+ char *cap;
+{
+ register char *p, *q;
+ char tcname[16]; /* name of similar terminal */
+ char tcbuf[BUFSIZ];
+ char *holdtbuf = tbuf;
+ int l;
+
+ p = tbuf + strlen(tbuf) - 2; /* before the last colon */
+ while (*--p != ':')
+ if (p<tbuf) {
+ write(2, "Bad termcap entry\n", 18);
+ return (0);
+ }
+ p++;
+ /* p now points to beginning of last field */
+ if (p[0] != 't' || p[1] != 'c')
+ return(1);
+ strcpy(tcname,p+3);
+ q = tcname;
+ while (q && *q != ':')
+ q++;
+ *q = 0;
+ if (++hopcount > MAXHOP) {
+ write(2, "Infinite tc= loop\n", 18);
+ return (0);
+ }
+ if (tgetent( cap, tcbuf, tcname) != 1)
+ return(0);
+ for (q=tcbuf; *q != ':'; q++)
+ ;
+ l = p - holdtbuf + strlen(q);
+ if (l > BUFSIZ) {
+ write(2, "Termcap entry too long\n", 23);
+ q[BUFSIZ - (p-tbuf)] = 0;
+ }
+ strcpy(p, q+1);
+ tbuf = holdtbuf;
+ return(1);
+}
+
+/*
+ * Tnamatch deals with name matching. The first field of the termcap
+ * entry is a sequence of names separated by |'s, so we compare
+ * against each such name. The normal : terminator after the last
+ * name (before the first field) stops us.
+ */
+tnamatch(np)
+ char *np;
+{
+ register char *Np, *Bp;
+
+ Bp = tbuf;
+ if (*Bp == '#')
+ return(0);
+ for (;;) {
+ for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
+ continue;
+ if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
+ return (1);
+ while (*Bp && *Bp != ':' && *Bp != '|')
+ Bp++;
+ if (*Bp == 0 || *Bp == ':')
+ return (0);
+ Bp++;
+ }
+}
+
+/*
+ * Skip to the next field. Notice that this is very dumb, not
+ * knowing about \: escapes or any such. If necessary, :'s can be put
+ * into the termcap file in octal.
+ */
+static char *
+tskip(bp)
+ register char *bp;
+{
+
+ while (*bp && *bp != ':')
+ bp++;
+ if (*bp == ':')
+ bp++;
+ return (bp);
+}
+
+/*
+ * Return the (numeric) option id.
+ * Numeric options look like
+ * li#80
+ * i.e. the option string is separated from the numeric value by
+ * a # character. If the option is not found we return -1.
+ * Note that we handle octal numbers beginning with 0.
+ */
+tgetnum(id)
+ char *id;
+{
+ register int i, base;
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (*bp == 0)
+ return (-1);
+ if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
+ continue;
+ if (*bp == '@')
+ return(-1);
+ if (*bp != '#')
+ continue;
+ bp++;
+ base = 10;
+ if (*bp == '0')
+ base = 8;
+ i = 0;
+ while (isdigit(*bp))
+ i *= base, i += *bp++ - '0';
+ return (i);
+ }
+}
+
+/*
+ * Handle a flag option.
+ * Flag options are given "naked", i.e. followed by a : or the end
+ * of the buffer. Return 1 if we find the option, or 0 if it is
+ * not given.
+ */
+tgetflag(id)
+ char *id;
+{
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (!*bp)
+ return (0);
+ if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
+ if (!*bp || *bp == ':')
+ return (1);
+ else if (*bp == '@')
+ return(0);
+ }
+ }
+}
+
+/*
+ * Get a string valued option.
+ * These are given as
+ * cl=^Z
+ * Much decoding is done on the strings, and the strings are
+ * placed in area, which is a ref parameter which is updated.
+ * No checking on area overflow.
+ */
+char *
+tgetstr(id, area)
+ char *id, **area;
+{
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (!*bp)
+ return (0);
+ if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
+ continue;
+ if (*bp == '@')
+ return(0);
+ if (*bp != '=')
+ continue;
+ bp++;
+ return (tdecode(bp, area));
+ }
+}
+
+/*
+ * Tdecode does the grung work to decode the
+ * string capability escapes.
+ */
+static char *
+tdecode(str, area)
+ register char *str;
+ char **area;
+{
+ register char *cp;
+ register int c;
+ register char *dp;
+ int i;
+
+ cp = *area;
+ while ((c = *str++) && c != ':') {
+ switch (c) {
+
+ case '^':
+ c = *str++ & 037;
+ break;
+
+ case '\\':
+ dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
+ c = *str++;
+nextc:
+ if (*dp++ == c) {
+ c = *dp++;
+ break;
+ }
+ dp++;
+ if (*dp)
+ goto nextc;
+ if (isdigit(c)) {
+ c -= '0', i = 2;
+ do
+ c <<= 3, c |= *str++ - '0';
+ while (--i && isdigit(*str));
+ }
+ break;
+ }
+ *cp++ = c;
+ }
+ *cp++ = 0;
+ str = *area;
+ *area = cp;
+ return (str);
+}
+
+static char *
+decodename(str, area)
+ register char *str;
+ char **area;
+{
+ register char *cp;
+ register int c;
+ register char *dp;
+ int i;
+
+ cp = *area;
+ while ((c = *str++) && c != ':' && c != '|' ) {
+ switch (c) {
+
+ case '^':
+ c = *str++ & 037;
+ break;
+
+ case '\\':
+ dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
+ c = *str++;
+nextc:
+ if (*dp++ == c) {
+ c = *dp++;
+ break;
+ }
+ dp++;
+ if (*dp)
+ goto nextc;
+ if (isdigit(c)) {
+ c -= '0', i = 2;
+ do
+ c <<= 3, c |= *str++ - '0';
+ while (--i && isdigit(*str));
+ }
+ break;
+ }
+ *cp++ = c;
+ }
+ *cp++ = 0;
+ str = *area;
+ *area = cp;
+ return (str);
+}
+
+char *
+getpname( area )
+ char **area;
+{
+ return( decodename( tbuf, area ));
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1995 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+struct printer {
+ char *p_name;
+ char *p_type;
+ char *p_zone;
+#ifdef notdef
+ char *p_fonts;
+ char *p_psetdir;
+#endif notdef
+ char *p_ppdfile;
+ int p_flags;
+ struct at_addr p_addr;
+ union {
+ struct {
+ char *pr_printer;
+ char *pr_operator;
+ char *pr_spool;
+#ifdef ABS_PRINT
+ char *pr_role;
+ double pr_balance;
+ int pr_srvid;
+#endif ABS_PRINT
+ int pr_pagecost;
+ char *pr_pagecost_msg;
+ char *pr_lock;
+ } pu_pr;
+ char *pu_cmd;
+ } p_un;
+ ATP p_atp;
+ struct printer *p_next;
+};
+#define p_cmd p_un.pu_cmd
+#define p_printer p_un.pu_pr.pr_printer
+#define p_operator p_un.pu_pr.pr_operator
+#define p_spool p_un.pu_pr.pr_spool
+#ifdef ABS_PRINT
+#define p_role p_un.pu_pr.pr_role
+#define p_balance p_un.pu_pr.pr_balance
+#define p_srvid p_un.pu_pr.pr_srvid
+#endif ABS_PRINT
+#define p_pagecost p_un.pu_pr.pr_pagecost
+#define p_pagecost_msg p_un.pu_pr.pr_pagecost_msg
+#define p_lock p_un.pu_pr.pr_lock
+
+#define P_PIPED (1<<0)
+#define P_SPOOLED (1<<1)
+#define P_REGISTERED (1<<2)
+#define P_ACCOUNT (1<<3)
+#define P_AUTH (1<<4)
+
+extern struct printer *printer;
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <sys/syslog.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+
+#ifdef KRB
+#ifdef SOLARIS
+#include <kerberos/krb.h>
+#else
+#include <krb.h>
+#endif
+#endif KRB
+
+#include "file.h"
+#include "comment.h"
+#include "printer.h"
+#include "ppd.h"
+
+cq_default( in, out )
+ struct papfile *in, *out;
+{
+ char *start, *stop, *p;
+ struct comment *comment = compeek();
+
+ for (;;) {
+ switch ( markline( &start, &stop, in )) {
+ case 0 :
+ return( 0 );
+
+ case -1 :
+ return( CH_MORE );
+ }
+
+ if ( comgetflags() == 0 ) { /* started */
+ if ( comment->c_end ) {
+ comsetflags( 1 );
+ } else {
+ compop();
+ consumetomark( start, stop, in );
+ return( CH_DONE );
+ }
+ } else {
+ /* return default */
+ if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) {
+ for ( p = start; p < stop; p++ ) {
+ if ( *p == ':' ) {
+ break;
+ }
+ }
+ p++;
+ while ( *p == ' ' ) {
+ p++;
+ }
+
+ *stop = '\n';
+ APPEND( out, p, stop - p + 1 );
+ compop();
+ consumetomark( start, stop, in );
+ return( CH_DONE );
+ }
+ }
+
+ consumetomark( start, stop, in );
+ }
+}
+
+#ifdef KRB
+char *LoginOK = "LoginOK\n";
+char *LoginFailed = "LoginFailed\n";
+
+#define h2b(x) (isdigit((x))?(x)-'0':(isupper((x))?(x)-'A':(x)-'a')+10)
+
+cq_k4login( in, out )
+ struct papfile *in, *out;
+{
+ char *start, *stop, *p;
+ unsigned char *t;
+ struct comment *comment = compeek();
+ KTEXT_ST tkt;
+ AUTH_DAT ad;
+ int rc, i;
+
+ switch ( markline( &start, &stop, in )) {
+ case 0 :
+ return( 0 );
+
+ case -1 :
+ return( CH_MORE );
+ }
+
+ p = start + strlen( comment->c_begin );
+ while ( *p == ' ' ) {
+ p++;
+ }
+
+ bzero( &tkt, sizeof( tkt ));
+ for ( i = 0, t = tkt.dat; p < stop; p += 2, t++, i++ ) {
+ *t = ( h2b( (unsigned char)*p ) << 4 ) +
+ h2b( (unsigned char)*( p + 1 ));
+ }
+ tkt.length = i;
+
+ if (( rc = krb_rd_req( &tkt, "LaserWriter", printer->p_name,
+ 0, &ad, "" )) != RD_AP_OK ) {
+ syslog( LOG_ERR, "cq_k4login: %s", krb_err_txt[ rc ] );
+ APPEND( out, LoginFailed, strlen( LoginFailed ));
+ compop();
+ consumetomark( start, stop, in );
+ return( CH_DONE );
+ }
+ syslog( LOG_INFO, "cq_k4login: %s.%s@%s", ad.pname, ad.pinst,
+ ad.prealm );
+ lp_person( ad.pname );
+ lp_host( ad.prealm );
+
+ APPEND( out, LoginOK, strlen( LoginOK ));
+ compop();
+ consumetomark( start, stop, in );
+ return( CH_DONE );
+}
+
+char *uameth = "UMICHKerberosIV\n*\n";
+
+cq_uameth( in, out )
+ struct papfile *in, *out;
+{
+ char *start, *stop;
+ struct comment *c, *comment = compeek();
+
+ for (;;) {
+ switch ( markline( &start, &stop, in )) {
+ case 0 :
+ return( 0 );
+
+ case -1 :
+ return( CH_MORE );
+ }
+
+ if ( comgetflags() == 0 ) { /* start */
+ if (( printer->p_flags & P_AUTH ) == 0 ) { /* no kerberos */
+ if ( comswitch( queries, cq_default ) < 0 ) {
+ syslog( LOG_ERR, "cq_uameth: can't find default!" );
+ exit( 1 );
+ }
+ return( CH_DONE );
+ }
+ comsetflags( 1 );
+ } else {
+ if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) { /* end */
+ APPEND( out, uameth, strlen( uameth ));
+ compop();
+ return( CH_DONE );
+ }
+ }
+
+ consumetomark( start, stop, in );
+ }
+}
+#endif KRB
+
+gq_true( out )
+ struct papfile *out;
+{
+ if ( printer->p_flags & P_SPOOLED ) {
+ APPEND( out, "true\n", 5 );
+ return( 0 );
+ } else {
+ return( -1 );
+ }
+}
+
+gq_pagecost( out )
+ struct papfile *out;
+{
+ char cost[ 60 ];
+
+ /* check for spooler? XXX */
+ if ( printer->p_pagecost_msg != NULL ) {
+ APPEND( out, printer->p_pagecost_msg,
+ strlen( printer->p_pagecost_msg ));
+ } else if ( printer->p_flags & P_ACCOUNT ) {
+#ifdef ABS_PRINT
+ lp_pagecost();
+#endif ABS_PRINT
+ sprintf( cost, "%d", printer->p_pagecost );
+ APPEND( out, cost, strlen( cost ));
+ } else {
+ return( -1 );
+ }
+ APPEND( out, "\n", 1 );
+ return( 0 );
+}
+
+#ifdef ABS_PRINT
+gq_balance( out )
+ struct papfile *out;
+{
+ char balance[ 60 ];
+
+ if ( lp_pagecost() != 0 ) {
+ return( -1 );
+ }
+ sprintf( balance, "$%1.2f\n", printer->p_balance );
+ APPEND( out, balance, strlen( balance ));
+ return( 0 );
+}
+#endif ABS_PRINT
+
+struct genquery {
+ char *gq_name;
+ int (*gq_handler)();
+} genqueries[] = {
+ { "UMICHCostPerPage", gq_pagecost },
+#ifdef notdef
+ { "UMICHUserBalance", gq_balance },
+#endif
+ { "UMICHListQueue", gq_true },
+ { "UMICHDeleteJob", gq_true },
+ { NULL },
+};
+
+cq_query( in, out )
+ struct papfile *in, *out;
+{
+ char *start, *stop, *p, *q;
+ struct comment *comment = compeek();
+ struct genquery *gq;
+
+
+ for (;;) {
+ switch ( markline( &start, &stop, in )) {
+ case 0 :
+ return( 0 );
+
+ case -1 :
+ return( CH_MORE );
+ }
+
+ if ( comgetflags() == 0 ) { /* started */
+ comsetflags( 1 );
+
+ for ( p = start; p < stop; p++ ) {
+ if ( *p == ':' ) {
+ break;
+ }
+ }
+
+ for ( p++; p < stop; p++ ) {
+ if ( *p != ' ' && *p != '\t' ) {
+ break;
+ }
+ }
+
+ for ( q = p; q < stop; q++ ) {
+ if ( *q == ' ' || *q == '\t' || *q == '\r' || *q == '\n' ) {
+ break;
+ }
+ }
+
+ for ( gq = genqueries; gq->gq_name; gq++ ) {
+ if (( strlen( gq->gq_name ) == q - p ) &&
+ ( strncmp( gq->gq_name, p, q - p ) == 0 )) {
+ break;
+ }
+ }
+ if ( gq->gq_name == NULL || gq->gq_handler == NULL ||
+ (gq->gq_handler)( out ) < 0 ) {
+ if ( comswitch( queries, cq_default ) < 0 ) {
+ syslog( LOG_ERR, "cq_feature: can't find default!" );
+ exit( 1 );
+ }
+ return( CH_DONE );
+ }
+ } else {
+ if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) {
+ compop();
+ consumetomark( start, stop, in );
+ return( CH_DONE );
+ }
+ }
+
+ consumetomark( start, stop, in );
+ }
+}
+
+cq_font_answer( start, stop, out )
+ char *start, *stop;
+ struct papfile *out;
+{
+ char *p, *q, buf[ 256 ];
+ struct ppd_font *pfo;
+
+ p = start;
+ while ( p < stop ) {
+ while (( *p == ' ' || *p == '\t' ) && p < stop ) {
+ p++;
+ }
+
+ q = buf;
+ while ( *p != ' ' && *p != '\t' &&
+ *p != '\n' && *p != '\r' && p < stop ) {
+ *q++ = *p++;
+ }
+
+ if ( q != buf ) {
+ *q = '\0';
+
+ APPEND( out, "/", 1 );
+ APPEND( out, buf, strlen( buf ));
+ APPEND( out, ":", 1 );
+
+ if (( pfo = ppd_font( buf )) == NULL ) {
+ APPEND( out, "No\n", 3 );
+ } else {
+ APPEND( out, "Yes\n", 4 );
+ }
+ }
+ }
+
+ return;
+}
+
+cq_font( in, out )
+ struct papfile *in, *out;
+{
+ char *start, *stop, *p;
+ struct comment *comment = compeek();
+
+ for (;;) {
+ switch ( markline( &start, &stop, in )) {
+ case 0 :
+ return( 0 );
+
+ case -1 :
+ return( CH_MORE );
+ }
+
+ if ( comgetflags() == 0 ) {
+ comsetflags( 1 );
+
+ for ( p = start; p < stop; p++ ) {
+ if ( *p == ':' ) {
+ break;
+ }
+ }
+ p++;
+
+ cq_font_answer( p, stop, out );
+ } else {
+ if ( comgetflags() == 1 &&
+ comcmp( start, stop, comcont, 0 ) == 0 ) {
+ /* continuation */
+
+ for ( p = start; p < stop; p++ ) {
+ if ( *p == ' ' ) {
+ break;
+ }
+ }
+ p++;
+
+ cq_font_answer( p, stop, out );
+ } else {
+ comsetflags( 2 );
+ if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) {
+ APPEND( out, "*\n", 2 );
+ compop();
+ consumetomark( start, stop, in );
+ return( CH_DONE );
+ }
+ }
+ }
+
+ consumetomark( start, stop, in );
+ }
+}
+
+cq_feature( in, out )
+ struct papfile *in, *out;
+{
+ char *start, *stop, *p;
+ struct comment *comment = compeek();
+ struct ppd_feature *pfe;
+
+ for (;;) {
+ switch ( markline( &start, &stop, in )) {
+ case 0 :
+ return( 0 );
+
+ case -1 :
+ return( CH_MORE );
+ }
+
+ if ( comgetflags() == 0 ) {
+ comsetflags( 1 );
+
+ /* parse for feature */
+ for ( p = start; p < stop; p++ ) {
+ if ( *p == ':' ) {
+ break;
+ }
+ }
+ p++;
+ while ( *p == ' ' ) {
+ p++;
+ }
+
+ if (( pfe = ppd_feature( p, stop - p )) == NULL ) {
+ if ( comswitch( queries, cq_default ) < 0 ) {
+ syslog( LOG_ERR, "cq_feature: can't find default!" );
+ exit( 1 );
+ }
+ return( CH_DONE );
+ }
+
+ APPEND( out, pfe->pd_value, strlen( pfe->pd_value ));
+ APPEND( out, "\r", 1 );
+ } else {
+ if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) {
+ compop();
+ consumetomark( start, stop, in );
+ return( CH_DONE );
+ }
+ }
+
+ consumetomark( start, stop, in );
+ }
+}
+
+static const char *psver = "*PSVersion\n";
+static const char *prod = "*Product\n";
+
+cq_printer( in, out )
+ struct papfile *in, *out;
+{
+ char *start, *stop, *p;
+ struct comment *c, *comment = compeek();
+ struct ppd_feature *pdpsver, *pdprod;
+
+ for (;;) {
+ switch ( markline( &start, &stop, in )) {
+ case 0 :
+ return( 0 );
+
+ case -1 :
+ return( CH_MORE );
+ }
+
+ if ( comgetflags() == 0 ) {
+ comsetflags( 1 );
+
+ if (( pdpsver = ppd_feature( psver, strlen( psver ))) == NULL ) {
+ if ( comswitch( queries, cq_default ) < 0 ) {
+ syslog( LOG_ERR, "cq_printer: can't find default!" );
+ exit( 1 );
+ }
+ return( CH_DONE );
+ }
+
+ for ( p = pdpsver->pd_value; *p != '\0'; p++ ) {
+ if ( *p == ' ' ) {
+ break;
+ }
+ }
+ if ( *p == '\0' ) {
+ syslog( LOG_ERR, "cq_printer: can't parse PSVersion!" );
+ if ( comswitch( queries, cq_default ) < 0 ) {
+ syslog( LOG_ERR, "cq_printer: can't find default!" );
+ exit( 1 );
+ }
+ return( CH_DONE );
+ }
+
+ if (( pdprod = ppd_feature( prod, strlen( prod ))) == NULL ) {
+ if ( comswitch( queries, cq_default ) < 0 ) {
+ syslog( LOG_ERR, "cq_printer: can't find default!" );
+ exit( 1 );
+ }
+ return( CH_DONE );
+ }
+
+ /* revision */
+ APPEND( out, p + 1, strlen( p + 1 ));
+ APPEND( out, "\r", 1 );
+
+ /* version */
+ APPEND( out, pdpsver->pd_value, p - pdpsver->pd_value );
+ APPEND( out, "\r", 1 );
+
+ /* product */
+ APPEND( out, pdprod->pd_value, strlen( pdprod->pd_value ));
+ APPEND( out, "\r", 1 );
+ } else {
+ if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) {
+ compop();
+ consumetomark( start, stop, in );
+ return( CH_DONE );
+ }
+ }
+
+ consumetomark( start, stop, in );
+ }
+}
+
+static const char *rmjobfailed = "Failed\n";
+static const char *rmjobok = "Ok\n";
+
+cq_rmjob( in, out )
+ struct papfile *in, *out;
+{
+ char *start, *stop, *p;
+ int job;
+
+ switch ( markline( &start, &stop, in )) {
+ case 0 :
+ return( 0 );
+
+ case -1 :
+ return( CH_MORE );
+ }
+
+ for ( p = start; p < stop; p++ ) {
+ if ( *p == ' ' || *p == '\t' ) {
+ break;
+ }
+ }
+ for ( ; p < stop; p++ ) {
+ if ( *p != ' ' && *p != '\t' ) {
+ break;
+ }
+ }
+
+ *stop = '\0';
+ if ( p < stop && ( job = atoi( p )) > 0 ) {
+ lp_rmjob( job );
+ APPEND( out, rmjobok, strlen( rmjobok ));
+ } else {
+ APPEND( out, rmjobfailed, strlen( rmjobfailed ));
+ }
+
+ compop();
+ consumetomark( start, stop, in );
+ return( CH_DONE );
+}
+
+cq_listq( in, out )
+ struct papfile *in, *out;
+{
+ char *start, *stop;
+
+ switch ( markline( &start, &stop, in )) {
+ case 0 :
+ return( 0 );
+
+ case -1 :
+ return( CH_MORE );
+ }
+
+ if ( lp_queue( out )) {
+ syslog( LOG_ERR, "cq_listq: lp_queue failed" );
+ }
+
+ compop();
+ consumetomark( start, stop, in );
+ return( CH_DONE );
+}
+
+/*
+ * All queries start with %%?Begin and end with %%?End. Note that the
+ * "Begin"/"End" general queries have to be last.
+ */
+struct comment queries[] = {
+#ifdef KRB
+ { "%%Login: UMICHKerberosIV", 0, cq_k4login, 0 },
+ { "%%?BeginUAMethodsQuery", "%%?EndUAMethodsQuery:", cq_uameth, C_FULL },
+#endif KRB
+ { "%UMICHListQueue", 0, cq_listq, C_FULL },
+ { "%UMICHDeleteJob", 0, cq_rmjob, 0 },
+ { "%%?BeginQuery", "%%?EndQuery", cq_query, 0 },
+ { "%%?BeginFeatureQuery", "%%?EndFeatureQuery", cq_feature, 0 },
+ { "%%?BeginFontQuery", "%%?EndFontQuery", cq_font, 0 },
+ { "%%?BeginPrinterQuery", "%%?EndPrinterQuery", cq_printer, C_FULL },
+ { "%%?Begin", "%%?End", cq_default, 0 },
+ { 0 },
+};
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/syslog.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/pap.h>
+
+#include "file.h"
+
+extern unsigned char connid, quantum, oquantum;
+
+char buf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
+struct iovec niov[ PAP_MAXQUANTUM ] = {
+ { buf[ 0 ], 0 },
+ { buf[ 1 ], 0 },
+ { buf[ 2 ], 0 },
+ { buf[ 3 ], 0 },
+ { buf[ 4 ], 0 },
+ { buf[ 5 ], 0 },
+ { buf[ 6 ], 0 },
+ { buf[ 7 ], 0 },
+};
+struct iovec iov[ PAP_MAXQUANTUM ] = {
+ { buf[ 0 ] + 4, 0 },
+ { buf[ 1 ] + 4, 0 },
+ { buf[ 2 ] + 4, 0 },
+ { buf[ 3 ] + 4, 0 },
+ { buf[ 4 ] + 4, 0 },
+ { buf[ 5 ] + 4, 0 },
+ { buf[ 6 ] + 4, 0 },
+ { buf[ 7 ] + 4, 0 },
+};
+
+/*
+ * Accept files until the client closes the connection.
+ * Read lines of a file, until the client sends eof, after
+ * which we'll send eof also.
+ */
+session( atp, sat )
+ ATP atp;
+ struct sockaddr_at *sat;
+{
+ struct timeval tv;
+ struct atp_block atpb;
+ struct sockaddr_at ssat;
+ struct papfile infile, outfile;
+ fd_set fds;
+ char cbuf[ 578 ];
+ int i, cc, timeout = 0, readpending = 0;
+ u_int16_t seq = 0, rseq = 1, netseq;
+ u_char readport;
+
+ infile.pf_state = PF_BOT;
+ infile.pf_len = 0;
+ infile.pf_buf = 0;
+ infile.pf_cur = 0;
+ infile.pf_end = 0;
+
+ outfile.pf_state = PF_BOT;
+ outfile.pf_len = 0;
+ outfile.pf_buf = 0;
+ outfile.pf_cur = 0;
+ outfile.pf_end = 0;
+
+ /*
+ * Ask for data.
+ */
+ cbuf[ 0 ] = connid;
+ cbuf[ 1 ] = PAP_READ;
+ if (++seq == 0) seq = 1;
+ netseq = htons( seq );
+ bcopy( &netseq, &cbuf[ 2 ], sizeof( netseq ));
+ atpb.atp_saddr = sat;
+ atpb.atp_sreqdata = cbuf;
+ atpb.atp_sreqdlen = 4; /* bytes in SendData request */
+ atpb.atp_sreqto = 5; /* retry timer */
+ atpb.atp_sreqtries = -1; /* infinite retries */
+ if ( atp_sreq( atp, &atpb, oquantum, ATP_XO )) {
+ syslog( LOG_ERR, "atp_sreq: %m" );
+ exit( 1 );
+ }
+
+ for (;;) {
+ /*
+ * Time between tickles.
+ */
+ tv.tv_sec = 60;
+ tv.tv_usec = 0;
+
+ /*
+ * If we don't get anything for a while, time out.
+ */
+ FD_ZERO( &fds );
+ FD_SET( atp_fileno( atp ), &fds );
+
+ if (( cc = select( FD_SETSIZE, &fds, 0, 0, &tv )) < 0 ) {
+ syslog( LOG_ERR, "select: %m" );
+ exit( 1 );
+ }
+ if ( cc == 0 ) {
+ if ( timeout++ > 2 ) {
+ syslog( LOG_ERR, "connection timed out" );
+ lp_cancel();
+ exit( 1 );
+ }
+
+ /*
+ * Send a tickle.
+ */
+ cbuf[ 0 ] = connid;
+ cbuf[ 1 ] = PAP_TICKLE;
+ cbuf[ 2 ] = cbuf[ 3 ] = 0;
+ atpb.atp_saddr = sat;
+ atpb.atp_sreqdata = cbuf;
+ atpb.atp_sreqdlen = 4; /* bytes in Tickle request */
+ atpb.atp_sreqto = 0; /* best effort */
+ atpb.atp_sreqtries = 1; /* try once */
+ if ( atp_sreq( atp, &atpb, 0, 0 )) {
+ syslog( LOG_ERR, "atp_sreq: %m" );
+ exit( 1 );
+ }
+ continue;
+ } else {
+ timeout = 0;
+ }
+
+ bzero( &ssat, sizeof( struct sockaddr_at ));
+ switch( atp_rsel( atp, &ssat, ATP_TRESP | ATP_TREQ )) {
+ case ATP_TREQ :
+ atpb.atp_saddr = &ssat;
+ atpb.atp_rreqdata = cbuf;
+ atpb.atp_rreqdlen = sizeof( cbuf );
+ if ( atp_rreq( atp, &atpb ) < 0 ) {
+ syslog( LOG_ERR, "atp_rreq: %m" );
+ exit( 1 );
+ }
+ /* sanity */
+ if ( (unsigned char)cbuf[ 0 ] != connid ) {
+ syslog( LOG_ERR, "Bad ATP request!" );
+ continue;
+ }
+
+ switch( cbuf[ 1 ] ) {
+ case PAP_READ :
+ /*
+ * Other side is ready for some data.
+ */
+ bcopy( &cbuf[ 2 ], &netseq, sizeof( netseq ));
+ if ( netseq != 0 ) {
+ if ( rseq != ntohs( netseq )) {
+ break;
+ }
+ if ( rseq++ == 0xffff ) rseq = 1;
+ }
+ readpending = 1;
+ readport = ssat.sat_port;
+ break;
+
+ case PAP_CLOSE :
+ /*
+ * Respond to the close request.
+ * If we're in the middle of a file, clean up.
+ */
+ if (( infile.pf_state & PF_BOT ) ||
+ ( PF_BUFSIZ( &infile ) == 0 &&
+ ( infile.pf_state & PF_EOF ))) {
+ lp_print();
+ } else {
+ lp_cancel();
+ }
+
+ niov[ 0 ].iov_len = 4;
+ ((char *)niov[ 0 ].iov_base)[ 0 ] = connid;
+ ((char *)niov[ 0 ].iov_base)[ 1 ] = PAP_CLOSEREPLY;
+ ((char *)niov[ 0 ].iov_base)[ 2 ] =
+ ((char *)niov[ 0 ].iov_base)[ 3 ] = 0;
+ atpb.atp_sresiov = niov;
+ atpb.atp_sresiovcnt = 1;
+ if ( atp_sresp( atp, &atpb ) < 0 ) {
+ syslog( LOG_ERR, "atp_sresp: %m" );
+ exit( 1 );
+ }
+ exit( 0 );
+ break;
+
+ case PAP_TICKLE :
+ break;
+ default :
+ syslog( LOG_ERR, "Bad PAP request!" );
+ }
+
+ break;
+
+ case ATP_TRESP :
+ atpb.atp_saddr = &ssat;
+ for ( i = 0; i < oquantum; i++ ) {
+ niov[ i ].iov_len = PAP_MAXDATA + 4;
+ }
+ atpb.atp_rresiov = niov;
+ atpb.atp_rresiovcnt = oquantum;
+ if ( atp_rresp( atp, &atpb ) < 0 ) {
+ syslog( LOG_ERR, "atp_rresp: %m" );
+ exit( 1 );
+ }
+
+ /* sanity */
+ if ( ((unsigned char *)niov[ 0 ].iov_base)[ 0 ] != connid ||
+ ((char *)niov[ 0 ].iov_base)[ 1 ] != PAP_DATA ) {
+ syslog( LOG_ERR, "Bad data response!" );
+ continue;
+ }
+
+ for ( i = 0; i < atpb.atp_rresiovcnt; i++ ) {
+ APPEND( &infile,
+ niov[ i ].iov_base + 4, niov[ i ].iov_len - 4 );
+ if (( infile.pf_state & PF_EOF ) == 0 &&
+ ((char *)niov[ 0 ].iov_base)[ 2 ] ) {
+ infile.pf_state |= PF_EOF;
+ }
+ }
+
+ /* move data */
+ if ( ps( &infile, &outfile ) < 0 ) {
+ syslog( LOG_ERR, "parse: bad return" );
+ exit( 1 ); /* really? close? */
+ }
+
+ /*
+ * Ask for more data.
+ */
+ cbuf[ 0 ] = connid;
+ cbuf[ 1 ] = PAP_READ;
+ if ( ++seq == 0 ) seq = 1;
+ netseq = htons( seq );
+ bcopy( &netseq, &cbuf[ 2 ], sizeof( netseq ));
+ atpb.atp_saddr = sat;
+ atpb.atp_sreqdata = cbuf;
+ atpb.atp_sreqdlen = 4; /* bytes in SendData request */
+ atpb.atp_sreqto = 5; /* retry timer */
+ atpb.atp_sreqtries = -1; /* infinite retries */
+ if ( atp_sreq( atp, &atpb, oquantum, ATP_XO )) {
+ syslog( LOG_ERR, "atp_sreq: %m" );
+ exit( 1 );
+ }
+ break;
+
+ case 0:
+ break;
+
+ default :
+ syslog( LOG_ERR, "atp_rsel: %m" );
+ exit( 1 );
+ }
+
+ /* send any data that we have */
+ if ( readpending &&
+ ( PF_BUFSIZ( &outfile ) || ( outfile.pf_state & PF_EOF ))) {
+ for ( i = 0; i < quantum; i++ ) {
+ ((char *)niov[ i ].iov_base)[ 0 ] = connid;
+ ((char *)niov[ i ].iov_base)[ 1 ] = PAP_DATA;
+ ((char *)niov[ i ].iov_base)[ 2 ] =
+ ((char *)niov[ i ].iov_base)[ 3 ] = 0;
+
+ if ( PF_BUFSIZ( &outfile ) > PAP_MAXDATA ) {
+ cc = PAP_MAXDATA;
+ } else {
+ cc = PF_BUFSIZ( &outfile );
+ if ( outfile.pf_state & PF_EOF ) {
+ ((char *)niov[ 0 ].iov_base)[ 2 ] = 1; /* eof */
+ outfile.pf_state = PF_BOT;
+ infile.pf_state = PF_BOT;
+ }
+ }
+
+ niov[ i ].iov_len = 4 + cc;
+ bcopy( outfile.pf_cur, niov[ i ].iov_base + 4, cc );
+ CONSUME( &outfile, cc );
+ if ( PF_BUFSIZ( &outfile ) == 0 ) {
+ i++;
+ break;
+ }
+ }
+ ssat.sat_port = readport;
+ atpb.atp_saddr = &ssat;
+ atpb.atp_sresiov = niov;
+ atpb.atp_sresiovcnt = i; /* reported by stevebn@pc1.eos.co.uk */
+ if ( atp_sresp( atp, &atpb ) < 0 ) {
+ syslog( LOG_ERR, "atp_sresp: %m" );
+ exit( 1 );
+ }
+ readpending = 0;
+ }
+ }
+}
--- /dev/null
+TARGETS= psf psa
+SRC= psf.c psa.c
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH} -DZEROWIDTH
+LIBS= ${ADDLIBS}
+TAGSFILE= tags
+CC= cc
+INSTALL= install
+LIBDIRS=
+
+LINKS= ofpap ifpap tfpap ifpaprev tfpaprev \
+ ofwpap ifwpap tfwpap ifwpaprev tfwpaprev \
+ ofmpap ifmpap tfmpap ifmpaprev tfmpaprev \
+ ofwmpap ifwmpap tfwmpap ifwmpaprev tfwmpaprev
+
+all : ${TARGETS} ${LINKS}
+
+${LINKS} :
+ -ln -sf ${SBINDIR}/psf $@
+
+psf : psf.o
+ ${CC} ${CFLAGS} -o psf psf.o ${LIBDIRS} ${LIBS}
+
+psa : psa.o
+ ${CC} ${CFLAGS} -o psa psa.o ${LIBDIRS} ${LIBS}
+
+psf.o : psf.c
+ ${CC} ${CFLAGS} -D_PATH_PAP=\"${BINDIR}/pap\" \
+ -D_PATH_PSORDER=\"${BINDIR}/psorder\" \
+ -D_PATH_PSA=\"${SBINDIR}/psa\" \
+ -D_PATH_PSFILTER=\"${SBINDIR}/etc2ps\" \
+ -D_PATH_PAGECOUNT=\"${RESDIR}/pagecount.ps\" \
+ ${CPPFLAGS} -c psf.c
+
+install : all
+ -mkdir -p ${RESDIR}/filters
+ ${INSTALL} -c psa ${SBINDIR}
+ ${INSTALL} -c etc2ps.sh ${SBINDIR}/etc2ps
+ ${INSTALL} -c pagecount.ps ${RESDIR}
+ ${INSTALL} -c psf ${SBINDIR}
+ tar cBf - ${LINKS} | (cd ${RESDIR}/filters; tar xBf -)
+
+clean :
+ rm -f a.out core* *.o *.bak *[Ee]rrs tags
+ rm -f ${TARGETS} ${LINKS}
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+#!/bin/sh
+#
+# This filter is called by psf to convert "other" formats to PostScript.
+# psf handles text and PostScript native. "Other" formats, e.g. DVI, C/A/T,
+# need to be converted before the page reverser and the printer can use
+# them.
+#
+# $0 begins with the filter name, e.g. df, tf. Each format is a separate
+# tag in the case.
+#
+
+DVIPSPATH=/usr/local/tex/bin
+DVIPS=/usr/local/tex/bin/dvips
+DVIPSARGS="-f -q"
+
+TROFF2PS=/usr/local/psroff/troff2/troff2ps
+TROFF2PSARGS="-Z -O-.10"
+
+PATH=/usr/bin:$DVIPSPATH; export PATH
+
+case $1 in
+
+#
+# Use "dvips" by Radical Eye Software to convert TeX DVI files to PostScript.
+# Note that you *must* have METAFONT, etc, in your path.
+#
+df*)
+ if [ -x "$DVIPS" ]; then
+ cat > /tmp/psfilter.$$
+ $DVIPS $DVIPSARGS < /tmp/psfilter.$$
+ rm -f /tmp/psfilter.$$
+ else
+ echo "$0: filter dvips uninstalled" 1>&2
+ exit 2
+ fi
+ ;;
+
+#
+# troff2ps is from psroff by Chris Lewis.
+#
+tf*)
+ if [ -x "$TROFF2PS" ]; then
+ exec $TROFF2PS $TROFF2PSARGS
+ else
+ echo "$0: filter troff2ps uninstalled" 1>&2
+ exit 2
+ fi
+ ;;
+
+*)
+ echo "$0: filter $1 unavailable" 1>&2
+ exit 2
+ ;;
+esac
+
+exit 0
--- /dev/null
+%!PS-Adobe-3.0 Query
+%%?BeginQuery: PageCount
+statusdict begin pagecount (*) print = flush end
+%%?EndQuery: Unknown
+%%EOF
--- /dev/null
+/*
+ * Copyright (c) 1990,1995 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+/*
+ * PostScript Accounting, psa.
+ *
+ * psa is invoked by psf, as output for a communication program. The
+ * communication program is expected to send a small program before and
+ * after each job, which causes the page count to be emitted in a well
+ * known format. psa reads its input, looking for page counts and other
+ * interesting data. Any data that it doesn't understand, it emits to
+ * stderr, the lpd log file. Data that it does understand may be written
+ * to a status file or logged. Once all input has been received, psa
+ * subtracts the beginning and end page counts, and log an accounting
+ * record in the accounting file.
+ */
+
+#include <stdio.h>
+
+main( ac, av )
+ int ac;
+ char **av;
+{
+ FILE *af;
+ char *acc, *user, *host;
+ char buf[ 1024 ], *p, *end;
+ int cc, n, ipc = -1, fpc = -1;
+
+ if ( ac != 4 ) {
+ fprintf( stderr, "Usage:\t%s accounting-file user host\n", av[ 0 ] );
+ exit( 2 );
+ }
+
+ acc = av[ 1 ];
+ user = av[ 2 ];
+ host = av[ 3 ];
+
+ /*
+ * Explain n = !n ... Is there no beauty in truth?
+ */
+ while (( cc = read( 0, buf, sizeof( buf ))) > 0 ) {
+ if ( ipc < 0 && *buf == '*' ) {
+ /* find initial pagecount */
+ for ( p = buf, end = buf + cc; p < end; p++ ) {
+ if ( *p == '\n' || *p == '\r' ) {
+ break;
+ }
+ }
+ if ( p == end ) {
+ fprintf( stderr, "Can't find initial page count!\n" );
+ exit( 2 );
+ }
+
+ p++;
+ ipc = atoi( buf + 1 );
+ cc -= ( p - buf );
+ if ( cc != 0 ) {
+ bcopy( p, buf, cc );
+ }
+ } else {
+ /* find final pagecount */
+ for ( p = buf + cc - 1; p >= buf; p-- ) {
+ if ( *p != '\n' && *p != '\r' ) {
+ break;
+ }
+ }
+ if ( p < buf ) {
+ fprintf( stderr, "Can't find final page count!\n" );
+ exit( 2 );
+ }
+
+ for ( ; p >= buf; p-- ) {
+ if ( *p == '\n' || *p == '\r' ) {
+ break;
+ }
+ }
+
+ if ( p < buf ) {
+ p = buf;
+ } else {
+ cc -= p - buf;
+ p++;
+ }
+
+ if ( *p == '*' ) {
+ n = atoi( p + 1 );
+#define max(x,y) ((x)>(y)?(x):(y))
+ fpc = max( n, fpc );
+ }
+ }
+ if ( cc != 0 && write( 2, buf, cc ) != cc ) {
+ fprintf( stderr, "write 1: 2 %X %d\n", buf, cc );
+ perror( "write" );
+ exit( 2 );
+ }
+ }
+ if ( cc < 0 ) {
+ perror( "read" );
+ exit( 2 );
+ }
+
+ if ( ipc < 0 ) {
+ fprintf( stderr, "Didn't find initial page count!\n" );
+ exit( 2 );
+ }
+
+ if ( fpc < 0 ) {
+ fprintf( stderr, "Didn't find final page count!\n" );
+ exit( 2 );
+ }
+
+ /*
+ * Write accounting record.
+ */
+ if (( af = fopen( acc, "a" )) != NULL ) {
+ fprintf( af, "%7.2f\t%s:%s\n", (float)( fpc - ipc ), host, user );
+ } else {
+ perror( acc );
+ exit( 2 );
+ }
+
+ exit( 0 );
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1995 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+/*
+ * PostScript Filter, psf.
+ *
+ * Handles both PostScript files and text files. Files with the
+ * '%!' PostScript header are sent directly to the printer,
+ * unmodified. Text files are first converted to PostScript,
+ * then sent. Printers may be directly attached or on an AppleTalk
+ * network. Other media are possible. Currently, psf invokes
+ * pap to send files to AppleTalk-ed printers. Replace the pap*
+ * variables to use another program for communication. psf only
+ * converts plain-text. If called as "tf" or "df", psf will invoke
+ * a troff or dvi to PostScript converter.
+ */
+
+#define FUCKED
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+#include <sys/syslog.h>
+#include <atalk/paths.h>
+#include <stdio.h>
+#include <strings.h>
+#include <ctype.h>
+#include <signal.h>
+
+char psapath[] = _PATH_PSA;
+char *psaargv[] = { "psa", 0, 0, 0, 0 };
+
+/*
+ * If we're not doing accounting, we just call pap as below.
+ * If we are doing accounting, we call pap twice. The first time,
+ * we call it with "-E" in arg 2, pagecount.ps in arg 3, and "-" in
+ * arg 4. The second time, we call it with "-c" in arg 2, pagecount.ps
+ * in arg 3, and 0 in arg 4.
+ */
+char pappath[] = _PATH_PAP;
+char *papargv[] = { "pap", "-sstatus", 0, 0, 0, 0, 0, 0 };
+
+char revpath[] = _PATH_PSORDER;
+char *revargv[] = { "psorder", "-d", 0 };
+
+char *filtargv[] = { 0, 0, 0 };
+
+char inbuf[ 1024 * 8 ];
+int inlen;
+
+FILE *acctfile = NULL;
+int literal;
+int width = 80, length = 66, indent = 0;
+char *prog, *name, *host;
+
+struct papersize {
+ int width;
+ int length;
+ float win;
+ float lin;
+} papersizes[] = {
+ { 80, 66, 8.5, 11.0 }, /* US Letter */
+ { 80, 70, 8.27, 11.69 }, /* A4 */
+};
+
+main( ac, av )
+ int ac;
+ char **av;
+{
+ int c, rc, children = 0;
+#ifdef FUCKED
+ int psafileno, multiconn = 0, waitidle = 0, waitidle2 = 0;
+#endif FUCKED
+ int status;
+ extern char *optarg;
+ extern int optind, opterr;
+
+ opterr = 0;
+ if (( prog = rindex( av[ 0 ], '/' )) == NULL ) {
+ prog = av[ 0 ];
+ } else {
+ prog++;
+ }
+#ifdef ultrix
+ openlog( prog, LOG_PID );
+#else ultrix
+ openlog( prog, LOG_PID, LOG_LPR );
+#endif ultrix
+
+ while (( c = getopt( ac, av, "P:C:D:F:L:J:x:y:n:h:w:l:i:c" )) != EOF ) {
+ switch ( c ) {
+ case 'n' :
+ name = optarg;
+ break;
+
+ case 'h' :
+ host = optarg;
+ break;
+
+ case 'w' :
+ width = atoi( optarg );
+#ifdef ZEROWIDTH
+ /*
+ * Some version of lpd pass 0 for the page width.
+ */
+ if ( width == 0 ) {
+ width = 80;
+ }
+#endif ZEROWIDTH
+ break;
+
+ case 'l' :
+ length = atoi( optarg );
+ break;
+
+ case 'i' :
+ indent = atoi( optarg );
+ break;
+
+ case 'c' : /* Print control chars */
+ literal++;
+ break;
+
+ case 'F' :
+ case 'L' :
+ case 'J' :
+ case 'P' :
+ case 'x' :
+ case 'y' :
+ break;
+
+#ifdef notdef
+ default :
+ syslog( LOG_ERR, "bad option: %c", c );
+ exit( 2 );
+#endif notdef
+ }
+ }
+ if ( ac - optind > 1 ) {
+ syslog( LOG_ERR, "Too many arguments" );
+ exit( 2 );
+ }
+#ifdef FUCKED
+ if ( index( prog, 'w' )) {
+ waitidle++;
+ }
+ if ( index( prog, 'W' )) {
+ waitidle2++;
+ }
+ if ( index( prog, 'm' )) {
+ multiconn++;
+ }
+#endif FUCKED
+
+ syslog( LOG_INFO, "starting for %s", name ? name : "?" );
+
+restart:
+ if (( inlen = read( 0, inbuf, sizeof( inbuf ))) < 0 ) {
+ syslog( LOG_ERR, "read: %m" );
+ exit( 1 );
+ }
+ if ( inlen == 0 ) { /* nothing to be done */
+ syslog( LOG_INFO, "done" );
+ exit( 0 );
+ }
+
+ /*
+ * If we've been given an accounting file, start the accounting
+ * process.
+ */
+ if ( optind < ac ) {
+ /* build arguments */
+ psaargv[ 1 ] = av[ optind ];
+ psaargv[ 2 ] = name;
+ psaargv[ 3 ] = host;
+ if (( c = pexecv( psapath, psaargv )) < 0 ) {
+ syslog( LOG_ERR, "%s: %m", psapath );
+ exit( 2 );
+ }
+ children++;
+ syslog( LOG_INFO, "accounting with psa[%d]", c );
+ }
+
+ /*
+ * Check prog's name to decide what programs to execute.
+ */
+ if ( strstr( prog, "pap" ) != NULL ) {
+ if ( optind < ac ) { /* accounting */
+#ifdef FUCKED
+ if ( multiconn ) {
+ psafileno = getdtablesize();
+ psafileno--;
+ dup2( 1, psafileno );
+
+ if ( waitidle2 ) {
+ papargv[ 2 ] = "-W";
+ papargv[ 3 ] = "-c";
+ papargv[ 4 ] = "-E";
+ papargv[ 5 ] = _PATH_PAGECOUNT;
+ papargv[ 6 ] = "-";
+ papargv[ 7 ] = 0;
+ } else if ( waitidle ) {
+ papargv[ 2 ] = "-w";
+ papargv[ 3 ] = "-c";
+ papargv[ 4 ] = "-E";
+ papargv[ 5 ] = _PATH_PAGECOUNT;
+ papargv[ 6 ] = "-";
+ papargv[ 7 ] = 0;
+ } else {
+ papargv[ 2 ] = "-c";
+ papargv[ 3 ] = "-E";
+ papargv[ 4 ] = _PATH_PAGECOUNT;
+ papargv[ 5 ] = "-";
+ papargv[ 6 ] = 0;
+ }
+ } else {
+ /*
+ * This is how it should be done.
+ */
+ papargv[ 2 ] = "-c";
+ papargv[ 3 ] = _PATH_PAGECOUNT;
+ papargv[ 4 ] = "-";
+ papargv[ 5 ] = _PATH_PAGECOUNT;
+ papargv[ 6 ] = 0;
+ }
+#endif FUCKED
+ } else {
+ papargv[ 2 ] = "-c";
+ papargv[ 3 ] = "-E";
+ papargv[ 4 ] = 0;
+ }
+
+ if (( c = pexecv( pappath, papargv )) < 0 ) {
+ syslog( LOG_ERR, "%s: %m", pappath );
+ exit( 2 );
+ }
+ children++;
+ syslog( LOG_INFO, "sending to pap[%d]", c );
+ }
+
+ /*
+ * Might be a good idea to have both a "forw" and a "rev", so that
+ * reversed documents can be reordered for the printing device.
+ */
+ if ( strstr( prog, "rev" ) != NULL ) {
+ if (( c = pexecv( revpath, revargv )) < 0 ) {
+ syslog( LOG_ERR, "%s: %m", revpath );
+ exit( 2 );
+ }
+ syslog( LOG_INFO, "sending to rev[%d]", c );
+ children++;
+ }
+
+ /*
+ * Invoke an external (script) filter to produce PostScript from
+ * non-text input.
+ */
+ if ( *prog != 'i' && *prog != 'o' && *( prog + 1 ) == 'f' ) {
+ filtargv[ 0 ] = filtargv[ 1 ] = prog;
+ if (( c = pexecv( _PATH_PSFILTER, filtargv )) < 0 ) {
+ syslog( LOG_ERR, "%s: %m", _PATH_PSFILTER );
+ exit( 2 );
+ }
+ syslog( LOG_INFO, "external filter[%d]", c );
+ children++;
+ rc = copyio(); /* external filter */
+ } else {
+ if ( inlen >= 2 && inbuf[ 0 ] == '%' && inbuf[ 1 ] == '!' ) {
+ syslog( LOG_INFO, "PostScript" );
+ rc = copyio(); /* PostScript */
+ } else if ( inlen >= 2 && inbuf[ 0 ] == '\033' && inbuf[ 1 ] == '%' ) {
+ syslog( LOG_INFO, "PostScript w/PJL" );
+ rc = copyio(); /* PostScript */
+ } else {
+ syslog( LOG_INFO, "straight text" );
+ rc = textps(); /* straight text */
+ }
+ }
+
+#ifdef FUCKED
+ if ( strstr( prog, "pap" ) != NULL && optind < ac && multiconn ) {
+ dup2( psafileno, 1 );
+ close( psafileno );
+ papargv[ 2 ] = "-c";
+ if ( waitidle2 ) {
+ papargv[ 3 ] = "-W";
+ papargv[ 4 ] = _PATH_PAGECOUNT;
+ papargv[ 5 ] = 0;
+ } else if ( waitidle ) {
+ papargv[ 3 ] = "-w";
+ papargv[ 4 ] = _PATH_PAGECOUNT;
+ papargv[ 5 ] = 0;
+ } else {
+ papargv[ 3 ] = _PATH_PAGECOUNT;
+ papargv[ 4 ] = 0;
+ }
+
+ if (( c = pexecv( pappath, papargv )) < 0 ) {
+ syslog( LOG_ERR, "%s: %m", pappath );
+ exit( 2 );
+ }
+ children++;
+ syslog( LOG_INFO, "pagecount with pap[%d]", c );
+ }
+#endif FUCKED
+
+ if ( children ) {
+ close( 1 );
+ }
+ while ( children ) {
+ if (( c = wait3( &status, 0, 0 )) < 0 ) {
+ syslog( LOG_ERR, "wait3: %m" );
+ exit( 1 );
+ }
+ if ( WIFEXITED( status )) {
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(x) ((x).w_status)
+#endif WEXITSTATUS
+ if ( WEXITSTATUS( status ) != 0 ) {
+ syslog( LOG_ERR, "%d died with %d", c, WEXITSTATUS( status ));
+ exit( WEXITSTATUS( status ));
+ } else {
+ syslog( LOG_INFO, "%d done", c );
+ children--;
+ }
+ } else {
+ syslog( LOG_ERR, "%d died badly", c );
+ exit( 1 );
+ }
+ }
+
+ if ( rc == 3 ) {
+ syslog( LOG_INFO, "pausing" );
+ kill( getpid(), SIGSTOP );
+ syslog( LOG_INFO, "restarting" );
+ goto restart;
+ }
+
+ syslog( LOG_INFO, "done" );
+ exit( rc );
+}
+
+copyio()
+{
+ /* implement the FSM needed to do the suspend. Note that
+ * the last characters will be \031\001 so don't worry
+ * Fun things: 1. \031\001 should not be written to output device
+ * 2. The \031 can be last char of one read, \001 first of next
+ * - we need to write \031 if not followed by \001
+ */
+ struct timeval tv;
+ fd_set fdset;
+ int ctl = 0, i;
+
+notdone:
+ do {
+ /*
+ * First, \031 and \001 *must* be the last things in the buffer
+ * (\001 can be the first thing in the next buffer). There's no
+ * need to scan any of the intervening bytes. Second, if there's
+ * more input, the escape sequence was bogus, and we should keep
+ * reading.
+ */
+ if ( inlen == 1 ) {
+ if ( ctl == 1 ) {
+ if ( inbuf[ 0 ] == '\001' ) {
+ ctl = 2;
+ break;
+ }
+ if ( write( 1, "\031", 1 ) != 1 ) {
+ syslog( LOG_ERR, "write: %m" );
+ return( 1 );
+ }
+ ctl = 0;
+ }
+
+ if ( inbuf[ 0 ] == '\031' ) {
+ ctl = 1;
+ }
+
+ } else {
+ if ( ctl == 1 ) {
+ if ( write( 1, "\031", 1 ) != 1 ) {
+ syslog( LOG_ERR, "write: %m" );
+ return( 1 );
+ }
+ }
+ ctl = 0;
+ if ( inbuf[ inlen - 2 ] == '\031' &&
+ inbuf[ inlen - 1 ] == '\001' ) {
+ ctl = 2;
+ } else if ( inbuf[ inlen - 1 ] == '\031' ) {
+ ctl = 1;
+ }
+ }
+
+ inlen -= ctl;
+ if (( inlen > 0 ) && ( write( 1, inbuf, inlen ) != inlen )) {
+ syslog( LOG_ERR, "write: %m" );
+ return( 1 );
+ }
+ if ( ctl == 2 ) {
+ break;
+ }
+ } while (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 );
+
+ if ( ctl == 2 ) {
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ FD_ZERO( &fdset );
+ FD_SET( 0, &fdset );
+ if ( select( 1, &fdset, NULL, NULL, &tv ) != 0 ) {
+ if (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 ) {
+ goto notdone;
+ }
+ }
+ }
+
+ if ( inlen < 0 ) {
+ syslog( LOG_ERR, "read: %m" );
+ return( 1 );
+ }
+
+ if ( ctl == 1 ) {
+ if ( write( 1, "\031", 1 ) != 1 ) {
+ syslog( LOG_ERR, "write: %m" );
+ return( 1 );
+ }
+ } else if ( ctl == 2 ) {
+ return( 3 );
+ }
+ return( 0 );
+}
+
+char *font = "Courier";
+int point = 11;
+
+char pspro[] = "\
+/GSV save def % global VM\n\
+/SP {\n\
+ /SV save def % save vmstate\n\
+ dup /H exch def % save font height\n\
+ exch findfont exch scalefont setfont % select font\n\
+ ( ) stringwidth pop /W exch def % save font width\n\
+ 0.5 sub 72 mul /CY exch def % save start Y\n\
+ pop 0.5 add 72 mul /CX exch def % save start X\n\
+ CX CY moveto % make current point\n\
+} bind def\n\
+/S /show load def\n\
+/NL { CX CY H sub dup /CY exch def moveto } bind def\n\
+/CR { CX CY moveto } bind def\n\
+/B { W neg 0 rmoveto}bind def\n\
+/T { W mul 0 rmoveto}bind def\n\
+/EP { SV restore showpage } bind def\n\
+%%EndProlog\n";
+
+textps()
+{
+ struct papersize papersize;
+ int state = 0, line = 0, col = 0, npages = 0, rc, i;
+ char *p, *end;
+
+#define elements(x) (sizeof(x)/sizeof((x)[0]))
+ for ( i = 0; i < elements( papersizes ); i++ ) {
+ if ( width == papersizes[ 0 ].width &&
+ length == papersizes[ 0 ].length ) {
+ papersize = papersizes[ i ];
+ break;
+ }
+ }
+ if ( i >= elements( papersizes )) {
+ papersize = papersizes[ 0 ]; /* default */
+ }
+
+#define ST_AVAIL (1<<0)
+#define ST_CONTROL (1<<1)
+#define ST_PAGE (1<<2)
+ /*
+ * convert text lines to postscript.
+ * A grungy little state machine. If I was more creative, I could
+ * probably think of a better way of doing this...
+ */
+ do {
+ p = inbuf;
+ end = inbuf + inlen;
+ while ( p < end ) {
+ if (( state & ST_PAGE ) == 0 && *p != '\031' && *p != '\001' ) {
+ if ( npages == 0 ) {
+ printf( "%%!PS-Adobe-2.0\n%%%%Pages: (atend)\n" );
+ printf( "%%%%DocumentFonts: %s\n", font );
+ fflush( stdout );
+
+ /* output postscript prologue: */
+ if ( write( 1, pspro, sizeof( pspro ) - 1 ) !=
+ sizeof( pspro ) - 1 ) {
+ syslog( LOG_ERR, "write prologue: %m" );
+ return( 1 );
+ }
+ if ( name && host ) {
+ printf( "statusdict /jobname (%s@%s) put\n", name,
+ host );
+ }
+ }
+
+ printf( "%%%%Page: ? %d\n", ++npages );
+ printf( "%d %f %f /%s %d SP\n", indent,
+ papersize.win, papersize.lin, font, point );
+ state |= ST_PAGE;
+ }
+ if ( state & ST_CONTROL && *p != '\001' ) {
+ if ( !literal ) {
+ fprintf( stderr, "unprintable character (0x%x)!\n",
+ (unsigned char)031 );
+ return( 2 ); /* Toss job */
+ }
+ printf( "\\%o", (unsigned char)031 );
+ state &= ~ST_CONTROL;
+ col++;
+ }
+
+ switch ( *p ) {
+ case '\n' : /* end of line */
+ if ( state & ST_AVAIL ) {
+ printf( ")S\n" );
+ state &= ~ST_AVAIL;
+ }
+ printf( "NL\n" );
+ line++;
+ col = 0;
+ if ( line >= length ) {
+ printf( "EP\n" );
+ state &= ~ST_PAGE;
+ line = 0;
+ }
+ break;
+
+ case '\r' : /* carriage return (for overtyping) */
+ if ( state & ST_AVAIL ) {
+ printf( ")S CR\n" );
+ state &= ~ST_AVAIL;
+ }
+ col = 0;
+ break;
+
+ case '\f' : /* form feed */
+ if ( state & ST_AVAIL ) {
+ printf( ")S\n" );
+ state &= ~ST_AVAIL;
+ }
+ printf( "EP\n" );
+ state &= ~ST_PAGE;
+ line = 0;
+ col = 0;
+ break;
+
+ case '\b' : /* backspace */
+ /* show line, back up one character */
+ if ( state & ST_AVAIL ) {
+ printf( ")S\n" );
+ state &= ~ST_AVAIL;
+ }
+ printf( "B\n" );
+ col--;
+ break;
+
+ case '\t' : /* tab */
+ if ( state & ST_AVAIL ) {
+ printf( ")S\n" );
+ state &= ~ST_AVAIL;
+ }
+ printf( "%d T\n", 8 - ( col % 8 ));
+ col += 8 - ( col % 8 );
+ break;
+
+ /*
+ * beginning of lpr control sequence
+ */
+ case '\031' :
+ state |= ST_CONTROL;
+ break;
+
+ case '\001' : /* lpr control sequence */
+ if ( state & ST_CONTROL ) {
+ rc = 3;
+ goto out;
+ }
+ /* FALLTHROUGH */
+
+ case '\\' :
+ case ')' :
+ case '(' :
+ if (( state & ST_AVAIL ) == 0 ) {
+ printf( "(" );
+ state |= ST_AVAIL;
+ }
+ putchar( '\\' );
+ /* FALLTHROUGH */
+
+ default :
+ if (( state & ST_AVAIL ) == 0 ) {
+ printf( "(" );
+ state |= ST_AVAIL;
+ }
+ if ( !isascii( *p ) || !isprint( *p )) {
+ if ( !literal ) {
+ fprintf( stderr, "unprintable character (0x%x)!\n",
+ (unsigned char)*p );
+ return( 2 ); /* Toss job */
+ }
+ printf( "\\%o", (unsigned char)*p );
+ } else {
+ putchar( *p );
+ }
+ col++;
+ break;
+ }
+ p++;
+ }
+ } while (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 );
+ if ( inlen < 0 ) {
+ syslog( LOG_ERR, "read: %m" );
+ return( 1 );
+ }
+ rc = 0;
+
+out:
+ if ( state & ST_AVAIL ) {
+ printf( ")S\n" );
+ state &= ~ST_AVAIL;
+ }
+
+ if ( state & ST_PAGE ) {
+ printf( "EP\n" );
+ state &= ~ST_PAGE;
+ }
+
+ if ( npages > 0 ) {
+ printf( "%%%%Trailer\nGSV restore\n%%%%Pages: %d\n%%%%EOF\n", npages );
+ fflush( stdout );
+ }
+
+ return( rc );
+}
+
+/*
+ * Interface to pipe and exec, for starting children in pipelines.
+ *
+ * Manipulates file descriptors 0, 1, and 2, such that the new child
+ * is reading from the parent's output.
+ */
+pexecv( path, argv )
+ char *path, *argv[];
+{
+ int fd[ 2 ], c;
+
+ if ( pipe( fd ) < 0 ) {
+ return( -1 );
+ }
+
+ switch ( c = fork()) {
+ case -1 :
+ return( -1 );
+ /* NOTREACHED */
+
+ case 0 :
+ if ( close( fd[ 1 ] ) < 0 ) {
+ return( -1 );
+ }
+ if ( dup2( fd[ 0 ], 0 ) < 0 ) {
+ return( -1 );
+ }
+ if ( close( fd[ 0 ] ) < 0 ) {
+ return( -1 );
+ }
+ execv( path, argv );
+ return( -1 );
+ /* NOTREACHED */
+
+ default :
+ if ( close( fd[ 0 ] ) < 0 ) {
+ return( -1 );
+ }
+ if ( dup2( fd[ 1 ], 1 ) < 0 ) {
+ return( -1 );
+ }
+ if ( close( fd[ 1 ] ) < 0 ) {
+ return( -1 );
+ }
+ return( c );
+ }
+}
--- /dev/null
+
+##########################################################################
+
+SRC = uams_pam.c uams_passwd.c uams_randnum.c uams_guest.c uams_dhx_pam.c \
+ uams_dhx_passwd.c
+OBJ = uams_pam.o uams_passwd.o uams_randnum.o uams_guest.o uams_dhx_pam.o \
+ uams_dhx_passwd.o
+SHAREDOBJS = uams_pam.so uams_passwd.so uams_randnum.so uams_guest.so \
+ uams_dhx_pam.so uams_dhx_passwd.so
+
+CFLAGS= -I../../include ${CSHAREDFLAGS} ${DEFS} ${AFSDEFS} ${OPTOPTS}
+TAGSFILE= tags
+CC= cc
+INSTALL= install
+
+SUBDIRS=
+
+all::
+ if [ x"${KRBDIR}" != x ]; then \
+ KRBLIBS="-lkrb -ldes"; \
+ KRBLIBDIRS="-L${KRBDIR}/lib"; \
+ KRBINCPATH="-I${KRBDIR}/include"; \
+ KRBDEFS="-DKRB"; \
+ fi; \
+ if [ x"${AFSDIR}" != x ]; then \
+ AFSLIBS="-lkauth -lprot -lubik -lauth -lsys -lrxkad -lrx -laudit \
+ -llwp -lcmd -lcom_err ${AFSDIR}/lib/afs/util.a -ldes"; \
+ AFSLIBDIRS="-L${AFSDIR}/lib -L${AFSDIR}/lib/afs"; \
+ AFSINCPATH="-I${AFSDIR}/include"; \
+ AFSDEFS="-DAFS"; \
+ fi; \
+ if [ x"${DESDIR}" != x ]; then \
+ CRYPTOLIBS="-ldes"; \
+ if [ "${DESDIR}" != "/usr" ]; then \
+ CRYPTOLIBDIRS="-L${DESDIR}/lib"; \
+ CRYPTOINCPATH="-I${DESDIR}/include"; \
+ fi; \
+ CRYPTODEFS="-DUAM_RNDNUM"; \
+ fi; \
+ if [ x"${CRYPTODIR}" != x ]; then \
+ CRYPTOLIBS="-lcrypto"; \
+ if [ "${CRYPTODIR}" != "/usr" ]; then \
+ CRYPTOLIBDIRS="-L${CRYPTODIR}/lib"; \
+ CRYPTOINCPATH="-I${CRYPTODIR}/include -I${CRYPTODIR}/include/openssl"; \
+ fi; \
+ CRYPTODEFS="-DUAM_RNDNUM -DUAM_DHX"; \
+ fi; \
+ if [ x"${PAMDIR}" != x ]; then \
+ PAMLIBS="-lpam"; \
+ if [ "${PAMDIR}" != "/usr" ]; then \
+ PAMLIBDIRS="-L${PAMDIR}/lib"; \
+ PAMINCPATH="-I${PAMDIR}/include"; \
+ fi; \
+ PAMDEFS="-DUSE_PAM"; \
+ fi; \
+ if [ x"${CRACKDIR}" != x ]; then \
+ CRACKLIBS="-lcrack"; \
+ if [ "${CRACKDIR}" != "/usr" ]; then \
+ CRACKLIBDIRS="-L${CRACKDIR}/lib"; \
+ CRACKINCPATH="-I${CRACKDIR}/include"; \
+ fi; \
+ CRACKDEFS="-DUSE_CRACKLIB -D_PATH_CRACKLIB=\\\"/usr/lib/cracklib_dict\\\""; \
+ fi; \
+ if [ -f /lib/libcrypt.a -o -f /usr/lib/libcrypt.a ] ; then \
+ if [ x"${USE_CRYPTLIB}" != x"no" ]; then \
+ CRYPTLIB="-lcrypt"; \
+ fi; \
+ fi; \
+ if [ ! -f /usr/include/crypt.h -a ! -f /usr/local/include/crypt.h ]; then \
+ CRYPTDEFS="-DNO_CRYPT_H"; \
+ fi; \
+ ${MAKE} ${MFLAGS} CC="${CC}" ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" \
+ OPTOPTS="${OPTOPTS}" DESDIR="${DESDIR}" \
+ PAMDIR="${PAMDIR}" \
+ KRBLIBS="$${KRBLIBS}" KRBLIBDIRS="$${KRBLIBDIRS}" \
+ KRBINCPATH="$${KRBINCPATH}" KRBDEFS="$${KRBDEFS}" \
+ AFSLIBS="$${AFSLIBS}" AFSLIBDIRS="$${AFSLIBDIRS}" \
+ CRYPTOLIBS="$${CRYPTOLIBS}" CRYPTOLIBDIRS="$${CRYPTOLIBDIRS}" \
+ PAMLIBS="$${PAMLIBS}" PAMLIBDIRS="$${PAMLIBDIRS}" \
+ CRYPTLIB="$${CRYPTLIB}" CRYPTDEFS="$${CRYPTDEFS}" \
+ CRYPTOINCPATH="$${CRYPTOINCPATH}" CRYPTODEFS="$${CRYPTODEFS}" \
+ PAMINCPATH="$${PAMINCPATH}" PAMDEFS="$${PAMDEFS}" \
+ AFSINCPATH="$${AFSINCPATH}" AFSDEFS="$${AFSDEFS}" \
+ CRACKLIBS="$${CRACKLIBS}" \
+ CRACKINCPATH="$${CRACKINCPATH}" CRACKDEFS="$${CRACKDEFS}" \
+ ${SHAREDOBJS} ${SUBDIRS}
+
+.SUFFIXES: .o .so
+
+.c.so:
+ ${CC} ${CFLAGS} -c $<
+ ${LDSHARED} ${LDSHAREDFLAGS} -o $@ $*.o
+
+uams_passwd.so: uams_passwd.c
+ ${CC} ${CFLAGS} ${CRYPTDEFS} -c $<
+ ${LDSHARED} ${LDSHAREDFLAGS} -o $@ $*.o ${CRYPTLIB}
+
+uams_pam.so: uams_pam.c
+ ${CC} ${CFLAGS} ${PAMINCPATH} ${PAMDEFS} -c $<
+ ${LDSHARED} ${LDSHAREDFLAGS} -o $@ $*.o ${PAMLIBDIRS} ${PAMLIBS}
+
+uams_randnum.so: uams_randnum.c
+ ${CC} ${CFLAGS} ${CRYPTOINCPATH} ${CRYPTODEFS} ${CRACKINCPATH} \
+ ${CRACKDEFS} -c $<
+ ${LDSHARED} ${LDSHAREDFLAGS} -o $@ $*.o ${CRYPTOLIBDIRS} \
+ ${CRYPTOLIBS} ${CRACKLIBS}
+
+uams_dhx_passwd.so: uams_dhx_passwd.c
+ ${CC} ${CFLAGS} ${CRYPTDEFS} ${CRYPTOINCPATH} ${CRYPTODEFS} -c $<
+ ${LDSHARED} ${LDSHAREDFLAGS} -o $@ $*.o ${CRYPTOLIBDIRS} ${CRYPTOLIBS}
+
+uams_dhx_pam.so: uams_dhx_pam.c
+ ${CC} ${CFLAGS} ${PAMINCPATH} ${CRYPTOINCPATH} ${CRYPTODEFS} \
+ ${PAMDEFS} -c $<
+ ${LDSHARED} ${LDSHAREDFLAGS} -o $@ $*.o ${CRYPTOLIBDIRS} \
+ ${CRYPTOLIBS} ${PAMLIBDIRS} ${PAMLIBS}
+
+# this is getting pushed into a subdirectory
+uams_kerberos.so: uams_kerberos.c
+ ${CC} ${CFLAGS} ${KRBINCPATH} ${KRBDEFS} -c $<
+ ${LDSHARED} ${LDSHAREDFLAGS} -o $@ $< ${KRBLIBDIRS} ${KRBLIBS}
+
+install : all
+ -for i in ${SUBDIRS}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" INSTALL="${INSTALL}" \
+ install); \
+ done
+ -mkdir ${RESDIR}/uams
+ ${INSTALL} -c *.so ${RESDIR}/uams
+ -rm ${RESDIR}/uams/uams_clrtxt.so ${RESDIR}/uams/uams_dhx.so
+ if [ x"${PAMDIR}" != x ]; then \
+ (cd ${RESDIR}/uams; ln -s uams_pam.so uams_clrtxt.so; \
+ ln -s uams_dhx_pam.so uams_dhx.so); \
+ else \
+ (cd ${RESDIR}/uams; ln -s uams_passwd.so uams_clrtxt.so; \
+ ln -s uams_dhx_passwd.so uams_dhx.so); \
+ fi;
+
+clean :
+ rm -f a.out core* *.o *.bak *[Ee]rrs tags
+ rm -f *.so
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ */
+#if defined(USE_PAM) && defined(UAM_DHX)
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <security/pam_appl.h>
+
+#include <bn.h>
+#include <dh.h>
+#include <cast.h>
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+#define KEYSIZE 16
+#define PASSWDLEN 64
+#define CRYPTBUFLEN (KEYSIZE*2)
+#define CRYPT2BUFLEN (KEYSIZE + PASSWDLEN)
+#define CHANGEPWBUFLEN (KEYSIZE + 2*PASSWDLEN)
+
+/* hash a number to a 16-bit quantity */
+#define dhxhash(a) ((((unsigned long) (a) >> 8) ^ \
+ (unsigned long) (a)) & 0xffff)
+
+/* the secret key */
+static CAST_KEY castkey;
+static struct passwd *dhxpwd;
+static u_int8_t randbuf[KEYSIZE];
+
+/* diffie-hellman bits */
+static unsigned char msg2_iv[] = "CJalbert";
+static unsigned char msg3_iv[] = "LWallace";
+static const u_int8_t p[] = {0xBA, 0x28, 0x73, 0xDF, 0xB0, 0x60, 0x57, 0xD4,
+ 0x3F, 0x20, 0x24, 0x74, 0x4C, 0xEE, 0xE7, 0x5B};
+static const u_int8_t g = 0x07;
+
+
+/* Static variables used to communicate between the conversation function
+ * and the server_login function
+ */
+static pam_handle_t *pamh = NULL;
+static char *PAM_username;
+static char *PAM_password;
+
+/* PAM conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+static int PAM_conv (int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr) {
+ int count = 0;
+ struct pam_response *reply;
+
+#define COPY_STRING(s) (s) ? strdup(s) : NULL
+
+ if (num_msg < 1)
+ return PAM_CONV_ERR;
+
+ reply = (struct pam_response *)
+ calloc(num_msg, sizeof(struct pam_response));
+
+ if (!reply)
+ return PAM_CONV_ERR;
+
+ for (count = 0; count < num_msg; count++) {
+ char *string = NULL;
+
+ switch (msg[count]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ if (!(string = COPY_STRING(PAM_username)))
+ goto pam_fail_conv;
+ break;
+ case PAM_PROMPT_ECHO_OFF:
+ if (!(string = COPY_STRING(PAM_password)))
+ goto pam_fail_conv;
+ break;
+ case PAM_TEXT_INFO:
+#ifdef PAM_BINARY_PROMPT
+ case PAM_BINARY_PROMPT:
+#endif
+ /* ignore it... */
+ break;
+ case PAM_ERROR_MSG:
+ default:
+ goto pam_fail_conv;
+ }
+
+ if (string) {
+ reply[count].resp_retcode = 0;
+ reply[count].resp = string;
+ string = NULL;
+ }
+ }
+
+ *resp = reply;
+ return PAM_SUCCESS;
+
+pam_fail_conv:
+ for (count = 0; count < num_msg; count++) {
+ if (!reply[count].resp)
+ continue;
+ switch (msg[count]->msg_style) {
+ case PAM_PROMPT_ECHO_OFF:
+ case PAM_PROMPT_ECHO_ON:
+ free(reply[count].resp);
+ break;
+ }
+ }
+ free(reply);
+ return PAM_CONV_ERR;
+}
+
+static struct pam_conv PAM_conversation = {
+ &PAM_conv,
+ NULL
+};
+
+
+static int dhx_setup(void *obj, char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ u_int16_t sessid;
+ int i;
+ BIGNUM *bn, *gbn, *pbn;
+ DH *dh;
+
+ /* get the client's public key */
+ if (!(bn = BN_bin2bn(ibuf, KEYSIZE, NULL)))
+ return AFPERR_PARAM;
+
+ /* get our primes */
+ if (!(gbn = BN_bin2bn(&g, sizeof(g), NULL))) {
+ BN_clear_free(bn);
+ return AFPERR_PARAM;
+ }
+
+ if (!(pbn = BN_bin2bn(p, sizeof(p), NULL))) {
+ BN_free(gbn);
+ BN_clear_free(bn);
+ return AFPERR_PARAM;
+ }
+
+ /* okay, we're ready */
+ if (!(dh = DH_new())) {
+ BN_free(pbn);
+ BN_free(gbn);
+ BN_clear_free(bn);
+ return AFPERR_PARAM;
+ }
+
+ /* generate key and make sure that we have enough space */
+ dh->p = pbn;
+ dh->g = gbn;
+ if (!DH_generate_key(dh) || (BN_num_bytes(dh->pub_key) > KEYSIZE)) {
+ goto pam_fail;
+ }
+
+ /* figure out the key. store the key in rbuf for now. */
+ i = DH_compute_key(rbuf, bn, dh);
+
+ /* set the key */
+ CAST_set_key(&castkey, i, rbuf);
+
+ /* session id. it's just a hashed version of the object pointer. */
+ sessid = dhxhash(obj);
+ memcpy(rbuf, &sessid, sizeof(sessid));
+ rbuf += sizeof(sessid);
+ *rbuflen += sizeof(sessid);
+
+ /* public key */
+ BN_bn2bin(dh->pub_key, rbuf);
+ rbuf += KEYSIZE;
+ *rbuflen += KEYSIZE;
+
+ /* buffer to be encrypted */
+ i = sizeof(randbuf);
+ if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM, (void *) randbuf,
+ &i) < 0) {
+ *rbuflen = 0;
+ goto pam_fail;
+ }
+ memcpy(rbuf, &randbuf, sizeof(randbuf));
+
+ /* get the signature. it's always 16 bytes. */
+#if 0
+ if (uam_afpserver_option(obj, UAM_OPTION_SIGNATURE,
+ (void *) &buf, NULL) < 0) {
+ *rbuflen = 0;
+ goto pam_fail;
+ }
+ memcpy(rbuf + KEYSIZE, buf, KEYSIZE);
+#else
+ memset(rbuf + KEYSIZE, 0, KEYSIZE);
+#endif
+
+ /* encrypt using cast */
+ CAST_cbc_encrypt(rbuf, rbuf, CRYPTBUFLEN, &castkey, msg2_iv,
+ CAST_ENCRYPT);
+ *rbuflen += CRYPTBUFLEN;
+ BN_free(bn);
+ DH_free(dh);
+ return AFPERR_AUTHCONT;
+
+pam_fail:
+ BN_free(bn);
+ DH_free(dh);
+ return AFPERR_PARAM;
+}
+
+
+/* dhx login: things are done in a slightly bizarre order to avoid
+ * having to clean things up if there's an error. */
+static int pam_login(void *obj, struct passwd **uam_pwd,
+ char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ char *buf;
+ int len, i;
+
+ *rbuflen = 0;
+
+ /* grab some of the options */
+ if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &buf,
+ &i) < 0)
+ return AFPERR_PARAM;
+
+ len = (unsigned char) *ibuf++;
+ if ( len > i ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy(buf, ibuf, len );
+ ibuf += len;
+ buf[ len ] = '\0';
+ if ((unsigned long) ibuf & 1) /* pad to even boundary */
+ ++ibuf;
+
+ if (( dhxpwd = uam_getname(buf, i)) == NULL ) {
+ return AFPERR_PARAM;
+ }
+
+ PAM_username = buf;
+ syslog( LOG_INFO, "dhx login: %s", buf);
+ return dhx_setup(obj, ibuf, ibuflen, rbuf, rbuflen);
+}
+
+
+static int pam_logincont(void *obj, struct passwd **uam_pwd,
+ char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ char *hostname;
+ BIGNUM *bn1, *bn2, *bn3;
+ u_int16_t sessid;
+ int err, PAM_error;
+
+ *rbuflen = 0;
+
+ /* check for session id */
+ memcpy(&sessid, ibuf, sizeof(sessid));
+ if (sessid != dhxhash(obj))
+ return AFPERR_PARAM;
+ ibuf += sizeof(sessid);
+
+ if (uam_afpserver_option(obj, UAM_OPTION_HOSTNAME,
+ (void *) &hostname, NULL) < 0)
+ return AFPERR_MISC;
+
+ CAST_cbc_encrypt(ibuf, rbuf, CRYPT2BUFLEN, &castkey,
+ msg3_iv, CAST_DECRYPT);
+ memset(&castkey, 0, sizeof(castkey));
+
+ /* check to make sure that the random number is the same. we
+ * get sent back an incremented random number. */
+ if (!(bn1 = BN_bin2bn(rbuf, KEYSIZE, NULL)))
+ return AFPERR_PARAM;
+
+ if (!(bn2 = BN_bin2bn(randbuf, sizeof(randbuf), NULL))) {
+ BN_free(bn1);
+ return AFPERR_PARAM;
+ }
+
+ /* zero out the random number */
+ memset(rbuf, 0, sizeof(randbuf));
+ memset(randbuf, 0, sizeof(randbuf));
+ rbuf += KEYSIZE;
+
+ if (!(bn3 = BN_new())) {
+ BN_free(bn2);
+ BN_free(bn1);
+ return AFPERR_PARAM;
+ }
+
+ BN_sub(bn3, bn1, bn2);
+ BN_free(bn2);
+ BN_free(bn1);
+
+ /* okay. is it one more? */
+ if (!BN_is_one(bn3)) {
+ BN_free(bn3);
+ return AFPERR_PARAM;
+ }
+ BN_free(bn3);
+
+ /* Set these things up for the conv function */
+ rbuf[PASSWDLEN] = '\0';
+ PAM_password = rbuf;
+
+ err = AFPERR_NOTAUTH;
+ PAM_error = pam_start("netatalk", PAM_username, &PAM_conversation,
+ &pamh);
+ if (PAM_error != PAM_SUCCESS)
+ goto logincont_err;
+
+ /* solaris craps out if PAM_TTY and PAM_RHOST aren't set. */
+ pam_set_item(pamh, PAM_TTY, "afpd");
+ pam_set_item(pamh, PAM_RHOST, hostname);
+ PAM_error = pam_authenticate(pamh,0);
+ if (PAM_error != PAM_SUCCESS) {
+ if (PAM_error == PAM_MAXTRIES)
+ err = AFPERR_PWDEXPR;
+ goto logincont_err;
+ }
+
+ PAM_error = pam_acct_mgmt(pamh, 0);
+ if (PAM_error != PAM_SUCCESS) {
+ if (PAM_error == PAM_ACCT_EXPIRED)
+ err = AFPERR_PWDEXPR;
+#ifdef PAM_AUTHTOKEN_REQD
+ else if (PAM_error == PAM_AUTHTOKEN_REQD)
+ err = AFPERR_PWDCHNG;
+#endif
+ goto logincont_err;
+ }
+
+#ifndef PAM_CRED_ESTABLISH
+#define PAM_CRED_ESTABLISH PAM_ESTABLISH_CRED
+#endif
+ PAM_error = pam_setcred(pamh, PAM_CRED_ESTABLISH);
+ if (PAM_error != PAM_SUCCESS)
+ goto logincont_err;
+
+ PAM_error = pam_open_session(pamh, 0);
+ if (PAM_error != PAM_SUCCESS)
+ goto logincont_err;
+
+ memset(rbuf, 0, PASSWDLEN); /* zero out the password */
+ *uam_pwd = dhxpwd;
+ return AFP_OK;
+
+logincont_err:
+ pam_end(pamh, PAM_error);
+ pamh = NULL;
+ memset(rbuf, 0, CRYPT2BUFLEN);
+ return err;
+}
+
+/* logout */
+static void pam_logout() {
+ pam_close_session(pamh, 0);
+ pam_end(pamh, 0);
+ pamh = NULL;
+}
+
+
+/* change pw for dhx needs a couple passes to get everything all
+ * right. basically, it's like the login/logincont sequence */
+static int pam_changepw(void *obj, char *username,
+ struct passwd *pwd, char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ BIGNUM *bn1, *bn2, *bn3;
+
+ char *hostname;
+ pam_handle_t *lpamh;
+ uid_t uid;
+ u_int16_t sessid;
+ int PAM_error;
+
+ /* grab the id */
+ memcpy(&sessid, ibuf, sizeof(sessid));
+ ibuf += sizeof(sessid);
+
+ if (!sessid) { /* no sessid -> initialization phase */
+ PAM_username = username;
+ ibuflen -= sizeof(sessid);
+ return dhx_setup(obj, ibuf, ibuflen, rbuf, rbuflen);
+ }
+
+
+ /* otherwise, it's like logincont but different. */
+
+ /* check out the session id */
+ if (sessid != dhxhash(obj))
+ return AFPERR_PARAM;
+
+ /* we need this for pam */
+ if (uam_afpserver_option(obj, UAM_OPTION_HOSTNAME,
+ (void *) &hostname, NULL) < 0)
+ return AFPERR_MISC;
+
+ /* grab the client's nonce, old password, and new password. */
+ CAST_cbc_encrypt(ibuf, ibuf, CHANGEPWBUFLEN, &castkey,
+ msg3_iv, CAST_DECRYPT);
+ memset(&castkey, 0, sizeof(castkey));
+
+ /* check to make sure that the random number is the same. we
+ * get sent back an incremented random number. */
+ if (!(bn1 = BN_bin2bn(ibuf, KEYSIZE, NULL)))
+ return AFPERR_PARAM;
+
+ if (!(bn2 = BN_bin2bn(randbuf, sizeof(randbuf), NULL))) {
+ BN_free(bn1);
+ return AFPERR_PARAM;
+ }
+
+ /* zero out the random number */
+ memset(rbuf, 0, sizeof(randbuf));
+ memset(randbuf, 0, sizeof(randbuf));
+
+ if (!(bn3 = BN_new())) {
+ BN_free(bn2);
+ BN_free(bn1);
+ return AFPERR_PARAM;
+ }
+
+ BN_sub(bn3, bn1, bn2);
+ BN_free(bn2);
+ BN_free(bn1);
+
+ /* okay. is it one more? */
+#if 0
+ if (!BN_is_one(bn3)) {
+ BN_free(bn3);
+ return AFPERR_PARAM;
+ }
+#endif
+ BN_free(bn3);
+
+ /* Set these things up for the conv function. the old password
+ * is at the end. */
+ ibuf += KEYSIZE;
+ ibuf[PASSWDLEN + PASSWDLEN] = '\0';
+ PAM_password = ibuf + PASSWDLEN;
+
+ PAM_error = pam_start("netatalk", username, &PAM_conversation,
+ &lpamh);
+ if (PAM_error != PAM_SUCCESS)
+ return AFPERR_PARAM;
+ pam_set_item(lpamh, PAM_TTY, "afpd");
+ pam_set_item(lpamh, PAM_RHOST, hostname);
+
+ /* we might need to do this as root */
+ uid = geteuid();
+ seteuid(0);
+ PAM_error = pam_authenticate(lpamh, 0);
+ if (PAM_error != PAM_SUCCESS) {
+ seteuid(uid);
+ pam_end(lpamh, PAM_error);
+ return AFPERR_NOTAUTH;
+ }
+
+ /* clear out old passwd */
+ memset(ibuf + PASSWDLEN, 0, PASSWDLEN);
+
+ /* new password */
+ PAM_password = ibuf;
+ ibuf[PASSWDLEN] = '\0';
+
+ /* this really does need to be done as root */
+ PAM_error = pam_chauthtok(lpamh, 0);
+ seteuid(uid); /* un-root ourselves. */
+ memset(ibuf, 0, PASSWDLEN);
+ if (PAM_error != PAM_SUCCESS) {
+ pam_end(lpamh, PAM_error);
+ return AFPERR_ACCESS;
+ }
+
+ pam_end(lpamh, 0);
+ return AFP_OK;
+}
+
+
+static int uam_setup(const char *path)
+{
+ if (uam_register(UAM_SERVER_LOGIN, path, "DHCAST128", pam_login,
+ pam_logincont, pam_logout) < 0)
+ return -1;
+
+ if (uam_register(UAM_SERVER_CHANGEPW, path, "DHCAST128",
+ pam_changepw) < 0) {
+ uam_unregister(UAM_SERVER_LOGIN, "DHCAST128");
+ return -1;
+ }
+
+ /*uam_register(UAM_SERVER_PRINTAUTH, path, "DHCAST128",
+ pam_printer);*/
+
+ return 0;
+}
+
+static void uam_cleanup(void)
+{
+ uam_unregister(UAM_SERVER_LOGIN, "DHCAST128");
+ uam_unregister(UAM_SERVER_CHANGEPW, "DHCAST128");
+ /*uam_unregister(UAM_SERVER_PRINTAUTH, "DHCAST128"); */
+}
+
+UAM_MODULE_EXPORT struct uam_export uams_dhx = {
+ UAM_MODULE_SERVER,
+ UAM_MODULE_VERSION,
+ uam_setup, uam_cleanup
+};
+
+#endif /* USE_PAM && UAM_DHX */
--- /dev/null
+/* Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifdef UAM_DHX
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifndef NO_CRYPT_H
+#include <crypt.h>
+#endif
+#include <pwd.h>
+#include <syslog.h>
+
+#ifdef SOLARIS
+#define SHADOWPW
+#endif SOLARIS
+
+#ifdef SHADOWPW
+#include <shadow.h>
+#endif SHADOWPW
+
+#include <bn.h>
+#include <dh.h>
+#include <cast.h>
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+#define KEYSIZE 16
+#define PASSWDLEN 64
+#define CRYPTBUFLEN (KEYSIZE*2)
+#define CRYPT2BUFLEN (KEYSIZE + PASSWDLEN)
+
+/* hash a number to a 16-bit quantity */
+#define dhxhash(a) ((((unsigned long) (a) >> 8) ^ \
+ (unsigned long) (a)) & 0xffff)
+
+/* the secret key */
+static CAST_KEY castkey;
+static struct passwd *dhxpwd;
+static u_int8_t randbuf[16];
+
+/* dhx passwd */
+static int passwd_login(void *obj, struct passwd **uam_pwd,
+ char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ unsigned char iv[] = "CJalbert";
+ u_int8_t p[] = {0xBA, 0x28, 0x73, 0xDF, 0xB0, 0x60, 0x57, 0xD4,
+ 0x3F, 0x20, 0x24, 0x74, 0x4C, 0xEE, 0xE7, 0x5B };
+ u_int8_t g = 0x07;
+#ifdef SHADOWPW
+ struct spwd *sp;
+#endif
+ BIGNUM *bn, *gbn, *pbn;
+ u_int16_t sessid;
+ int len, i;
+ char *name;
+ DH *dh;
+
+ *rbuflen = 0;
+
+ if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &name, &i) < 0)
+ return AFPERR_PARAM;
+
+ len = (unsigned char) *ibuf++;
+ if ( len > i ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy(name, ibuf, len );
+ ibuf += len;
+ name[ len ] = '\0';
+ if ((unsigned long) ibuf & 1) /* padding */
+ ++ibuf;
+
+ if (( dhxpwd = uam_getname(name, i)) == NULL ) {
+ return AFPERR_PARAM;
+ }
+
+ syslog( LOG_INFO, "dhx login: %s", name);
+ if (uam_checkuser(dhxpwd) < 0)
+ return AFPERR_NOTAUTH;
+
+#ifdef SHADOWPW
+ if (( sp = getspnam( dhxpwd->pw_name )) == NULL ) {
+ syslog( LOG_INFO, "no shadow passwd entry for %s", name);
+ return AFPERR_NOTAUTH;
+ }
+ dhxpwd->pw_passwd = sp->sp_pwdp;
+#endif SHADOWPW
+
+ if (!dhxpwd->pw_passwd)
+ return AFPERR_NOTAUTH;
+
+ /* get the client's public key */
+ if (!(bn = BN_bin2bn(ibuf, KEYSIZE, NULL))) {
+ return AFPERR_PARAM;
+ }
+
+ /* get our primes */
+ if (!(gbn = BN_bin2bn(&g, sizeof(g), NULL))) {
+ BN_free(bn);
+ return AFPERR_PARAM;
+ }
+
+ if (!(pbn = BN_bin2bn(p, sizeof(p), NULL))) {
+ BN_free(gbn);
+ BN_free(bn);
+ return AFPERR_PARAM;
+ }
+
+ /* okay, we're ready */
+ if (!(dh = DH_new())) {
+ BN_free(pbn);
+ BN_free(gbn);
+ BN_free(bn);
+ return AFPERR_PARAM;
+ }
+
+ /* generate key and make sure we have enough space */
+ dh->p = pbn;
+ dh->g = gbn;
+ if (!DH_generate_key(dh) || (BN_num_bytes(dh->pub_key) > KEYSIZE)) {
+ goto passwd_fail;
+ }
+
+ /* figure out the key. use rbuf as a temporary buffer. */
+ i = DH_compute_key(rbuf, bn, dh);
+
+ /* set the key */
+ CAST_set_key(&castkey, i, rbuf);
+
+ /* session id. it's just a hashed version of the object pointer. */
+ sessid = dhxhash(obj);
+ memcpy(rbuf, &sessid, sizeof(sessid));
+ rbuf += sizeof(sessid);
+ *rbuflen += sizeof(sessid);
+
+ /* send our public key */
+ BN_bn2bin(dh->pub_key, rbuf);
+ rbuf += KEYSIZE;
+ *rbuflen += KEYSIZE;
+
+ /* buffer to be encrypted */
+ i = sizeof(randbuf);
+ if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM, (void *) randbuf,
+ &i) < 0) {
+ *rbuflen = 0;
+ goto passwd_fail;
+ }
+ memcpy(rbuf, &randbuf, sizeof(randbuf));
+
+#if 0
+ /* get the signature. it's always 16 bytes. */
+ if (uam_afpserver_option(obj, UAM_OPTION_SIGNATURE,
+ (void *) &name, NULL) < 0) {
+ *rbuflen = 0;
+ goto passwd_fail;
+ }
+ memcpy(rbuf + KEYSIZE, name, KEYSIZE);
+#else
+ memset(rbuf + KEYSIZE, 0, KEYSIZE);
+#endif
+
+ /* encrypt using cast */
+ CAST_cbc_encrypt(rbuf, rbuf, CRYPTBUFLEN, &castkey, iv, CAST_ENCRYPT);
+ *rbuflen += CRYPTBUFLEN;
+ BN_free(bn);
+ DH_free(dh);
+ return AFPERR_AUTHCONT;
+
+passwd_fail:
+ BN_free(bn);
+ DH_free(dh);
+ return AFPERR_PARAM;
+}
+
+static int passwd_logincont(void *obj, struct passwd **uam_pwd,
+ char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ unsigned char iv[] = "LWallace";
+ BIGNUM *bn1, *bn2, *bn3;
+ u_int16_t sessid;
+ char *p;
+
+ *rbuflen = 0;
+
+ /* check for session id */
+ memcpy(&sessid, ibuf, sizeof(sessid));
+ if (sessid != dhxhash(obj))
+ return AFPERR_PARAM;
+ ibuf += sizeof(sessid);
+
+ /* use rbuf as scratch space */
+ CAST_cbc_encrypt(ibuf, rbuf, CRYPT2BUFLEN, &castkey,
+ iv, CAST_DECRYPT);
+
+ /* check to make sure that the random number is the same. we
+ * get sent back an incremented random number. */
+ if (!(bn1 = BN_bin2bn(rbuf, KEYSIZE, NULL)))
+ return AFPERR_PARAM;
+
+ if (!(bn2 = BN_bin2bn(randbuf, sizeof(randbuf), NULL))) {
+ BN_free(bn1);
+ return AFPERR_PARAM;
+ }
+
+ /* zero out the random number */
+ memset(rbuf, 0, sizeof(randbuf));
+ memset(randbuf, 0, sizeof(randbuf));
+ rbuf += KEYSIZE;
+
+ if (!(bn3 = BN_new())) {
+ BN_free(bn2);
+ BN_free(bn1);
+ return AFPERR_PARAM;
+ }
+
+ BN_sub(bn3, bn1, bn2);
+ BN_free(bn2);
+ BN_free(bn1);
+
+ /* okay. is it one more? */
+ if (!BN_is_one(bn3)) {
+ BN_free(bn3);
+ return AFPERR_PARAM;
+ }
+ BN_free(bn3);
+
+ rbuf[PASSWDLEN] = '\0';
+ p = crypt( rbuf, dhxpwd->pw_passwd );
+ memset(rbuf, 0, PASSWDLEN);
+ if ( strcmp( p, dhxpwd->pw_passwd ) == 0 ) {
+ *uam_pwd = dhxpwd;
+ return AFP_OK;
+ }
+
+ return AFPERR_NOTAUTH;
+}
+
+
+static int uam_setup(const char *path)
+{
+ if (uam_register(UAM_SERVER_LOGIN, path, "DHCAST128",
+ passwd_login, passwd_logincont, NULL) < 0)
+ return -1;
+ /*uam_register(UAM_SERVER_PRINTAUTH, path, "DHCAST128",
+ passwd_printer);*/
+
+ return 0;
+}
+
+static void uam_cleanup(void)
+{
+ uam_unregister(UAM_SERVER_LOGIN, "DHCAST128");
+ /*uam_unregister(UAM_SERVER_PRINTAUTH, "DHCAST128"); */
+}
+
+UAM_MODULE_EXPORT struct uam_export uams_dhx = {
+ UAM_MODULE_SERVER,
+ UAM_MODULE_VERSION,
+ uam_setup, uam_cleanup
+};
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <syslog.h>
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+static int noauth_login(void *obj, struct passwd **uam_pwd,
+ char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ struct passwd *pwent;
+ char *guest, *username;
+
+ *rbuflen = 0;
+ syslog( LOG_INFO, "login noauth" );
+
+ if (uam_afpserver_option(obj, UAM_OPTION_GUEST, (void *) &guest,
+ NULL) < 0)
+ return AFPERR_MISC;
+
+ if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
+ (void *) &username, NULL) < 0)
+ return AFPERR_MISC;
+
+ strcpy(username, guest);
+ if ((pwent = getpwnam(guest)) == NULL) {
+ syslog( LOG_ERR, "noauth_login: getpwnam( %s ): %m", guest);
+ return( AFPERR_BADUAM );
+ }
+
+#ifdef AFS
+ if ( setpag() < 0 ) {
+ syslog( LOG_ERR, "noauth_login: setpag: %m" );
+ return( AFPERR_BADUAM );
+ }
+#endif /* AFS */
+
+ *uam_pwd = pwent;
+ return( AFP_OK );
+}
+
+static int uam_setup(const char *path)
+{
+ if (uam_register(UAM_SERVER_LOGIN, path, "No User Authent",
+ noauth_login, NULL, NULL) < 0)
+ return -1;
+ /* uam_register(UAM_SERVER_PRINTAUTH, path,
+ "No User Authent", noauth_printer); */
+
+ return 0;
+}
+
+static void uam_cleanup()
+{
+ uam_unregister(UAM_SERVER_LOGIN, "No User Authent");
+ /* uam_unregister(UAM_SERVER_PRINTAUTH, "No User Authent"); */
+}
+
+UAM_MODULE_EXPORT struct uam_export uams_guest = {
+ UAM_MODULE_SERVER,
+ UAM_MODULE_VERSION,
+ uam_setup, uam_cleanup
+};
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+# ifdef UAM_AFSKRB
+
+#include <mit-copyright.h>
+#include <krb.h>
+#include <des.h>
+#include <prot.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+/* use the bsd time.h struct defs for PC too! */
+#include <sys/time.h>
+#include <sys/types.h>
+
+int swap_bytes;
+
+/*
+ * krb_get_in_tkt() gets a ticket for a given principal to use a given
+ * service and stores the returned ticket and session key for future
+ * use.
+ *
+ * The "user", "instance", and "realm" arguments give the identity of
+ * the client who will use the ticket. The "service" and "sinstance"
+ * arguments give the identity of the server that the client wishes
+ * to use. (The realm of the server is the same as the Kerberos server
+ * to whom the request is sent.) The "life" argument indicates the
+ * desired lifetime of the ticket; the "key_proc" argument is a pointer
+ * to the routine used for getting the client's private key to decrypt
+ * the reply from Kerberos. The "decrypt_proc" argument is a pointer
+ * to the routine used to decrypt the reply from Kerberos; and "arg"
+ * is an argument to be passed on to the "key_proc" routine.
+ *
+ * If all goes well, krb_get_in_tkt() returns INTK_OK, otherwise it
+ * returns an error code: If an AUTH_MSG_ERR_REPLY packet is returned
+ * by Kerberos, then the error code it contains is returned. Other
+ * error codes returned by this routine include INTK_PROT to indicate
+ * wrong protocol version, INTK_BADPW to indicate bad password (if
+ * decrypted ticket didn't make sense), INTK_ERR if the ticket was for
+ * the wrong server or the ticket store couldn't be initialized.
+ *
+ * The format of the message sent to Kerberos is as follows:
+ *
+ * Size Variable Field
+ * ---- -------- -----
+ *
+ * 1 byte KRB_PROT_VERSION protocol version number
+ * 1 byte AUTH_MSG_KDC_REQUEST | message type
+ * HOST_BYTE_ORDER local byte order in lsb
+ * string user client's name
+ * string instance client's instance
+ * string realm client's realm
+ * 4 bytes tlocal.tv_sec timestamp in seconds
+ * 1 byte life desired lifetime
+ * string service service's name
+ * string sinstance service's instance
+ */
+
+kuam_get_in_tkt(user, instance, realm, service, sinstance, life, rpkt )
+ char *user;
+ char *instance;
+ char *realm;
+ char *service;
+ char *sinstance;
+ int life;
+ KTEXT rpkt;
+{
+ KTEXT_ST pkt_st;
+ KTEXT pkt = &pkt_st; /* Packet to KDC */
+ KTEXT_ST cip_st;
+ KTEXT cip = &cip_st; /* Returned Ciphertext */
+ KTEXT_ST tkt_st;
+ KTEXT tkt = &tkt_st; /* Current ticket */
+ unsigned char *v = pkt->dat; /* Prot vers no */
+ unsigned char *t = (pkt->dat+1); /* Prot msg type */
+ int msg_byte_order;
+ int kerror;
+ struct timeval t_local;
+ u_int32_t rep_err_code;
+
+
+ /* BUILD REQUEST PACKET */
+
+ /* Set up the fixed part of the packet */
+ *v = (unsigned char) KRB_PROT_VERSION;
+ *t = (unsigned char) AUTH_MSG_KDC_REQUEST;
+ *t |= HOST_BYTE_ORDER;
+
+ /* Now for the variable info */
+ (void) strcpy((char *)(pkt->dat+2),user); /* aname */
+ pkt->length = 3 + strlen(user);
+ (void) strcpy((char *)(pkt->dat+pkt->length),
+ instance); /* instance */
+ pkt->length += 1 + strlen(instance);
+ (void) strcpy((char *)(pkt->dat+pkt->length),realm); /* realm */
+ pkt->length += 1 + strlen(realm);
+
+ (void) gettimeofday(&t_local,(struct timezone *) 0);
+ /* timestamp */
+ memcpy((pkt->dat+pkt->length), &(t_local.tv_sec), 4);
+ pkt->length += 4;
+
+ *(pkt->dat+(pkt->length)++) = (char) life;
+ (void) strcpy((char *)(pkt->dat+pkt->length),service);
+ pkt->length += 1 + strlen(service);
+ (void) strcpy((char *)(pkt->dat+pkt->length),sinstance);
+ pkt->length += 1 + strlen(sinstance);
+
+ rpkt->length = 0;
+
+ /* SEND THE REQUEST AND RECEIVE THE RETURN PACKET */
+
+ if (kerror = send_to_kdc(pkt, rpkt, realm)) return(kerror);
+
+ /* check packet version of the returned packet */
+ if (pkt_version(rpkt) != KRB_PROT_VERSION)
+ return(INTK_PROT);
+
+ /* Check byte order */
+ msg_byte_order = pkt_msg_type(rpkt) & 1;
+ swap_bytes = 0;
+ if (msg_byte_order != HOST_BYTE_ORDER) {
+ swap_bytes++;
+ }
+
+ switch (pkt_msg_type(rpkt) & ~1) {
+ case AUTH_MSG_KDC_REPLY:
+ break;
+ case AUTH_MSG_ERR_REPLY:
+ memcpy(&rep_err_code,pkt_err_code(rpkt),4);
+ if (swap_bytes) swap_u_long(rep_err_code);
+ return((int)rep_err_code);
+ default:
+ return(INTK_PROT);
+ }
+
+ return( INTK_OK );
+}
+
+kuam_set_in_tkt( user, instance, realm, service, sinstance, ptr)
+ char *user, *instance, *realm, *service, *sinstance, *ptr;
+{
+ KTEXT_ST tkt_st;
+ KTEXT tkt = &tkt_st;
+ struct timeval t_local;
+ int lifetime, kvno, kerror;
+ int32_t kdc_time;
+ C_Block ses;
+ char s_name[ SNAME_SZ ], s_instance[ INST_SZ ];
+ char rlm[ REALM_SZ ];
+
+ /* extract session key */
+ memcpy(ses, ptr, 8);
+ ptr += 8;
+
+ /* extract server's name */
+ (void) strcpy(s_name,ptr);
+ ptr += strlen(s_name) + 1;
+
+ /* extract server's instance */
+ (void) strcpy(s_instance,ptr);
+ ptr += strlen(s_instance) + 1;
+
+ /* extract server's realm */
+ (void) strcpy(rlm,ptr);
+ ptr += strlen(rlm) + 1;
+
+ /* extract ticket lifetime, server key version, ticket length */
+ /* be sure to avoid sign extension on lifetime! */
+ lifetime = (unsigned char) ptr[0];
+ kvno = (unsigned char) ptr[1];
+ tkt->length = (unsigned char) ptr[2];
+ ptr += 3;
+
+ /* extract ticket itself */
+ memcpy( tkt->dat, ptr, tkt->length);
+ ptr += tkt->length;
+
+ if (strcmp(s_name, service) || strcmp(s_instance, sinstance) ||
+ strcmp(rlm, realm)) /* not what we asked for */
+ return(INTK_ERR); /* we need a better code here XXX */
+
+ /* check KDC time stamp */
+ memcpy(&kdc_time, ptr, 4); /* Time (coarse) */
+ if (swap_bytes) swap_u_long(kdc_time);
+
+ ptr += 4;
+
+ (void) gettimeofday(&t_local,(struct timezone *) 0);
+ if (abs((int)(t_local.tv_sec - kdc_time)) > CLOCK_SKEW) {
+ return(RD_AP_TIME); /* XXX should probably be better
+ code */
+ }
+
+ /* initialize ticket cache */
+ if (in_tkt(user,instance) != KSUCCESS)
+ return(INTK_ERR);
+
+ /* stash ticket, session key, etc. for future use */
+ if (kerror = save_credentials(s_name, s_instance, rlm, ses,
+ lifetime, kvno, tkt, t_local.tv_sec))
+ return(kerror);
+
+ return(INTK_OK);
+}
+# endif UAM_AFSKRB
--- /dev/null
+/*
+ * Ticket lifetime. This defines the table used to lookup lifetime
+ * for the fixed part of rande of the one byte lifetime field. Values
+ * less than 0x80 are intrpreted as the number of 5 minute intervals.
+ * Values from 0x80 to 0xBF should be looked up in this table. The
+ * value of 0x80 is the same using both methods: 10 and two-thirds
+ * hours . The lifetime of 0xBF is 30 days. The intervening values
+ * of have a fixed ratio of roughly 1.06914. The value 0xFF is
+ * defined to mean a ticket has no expiration time. This should be
+ * used advisedly since individual servers may impose defacto
+ * upperbounds on ticket lifetimes.
+ */
+
+#include <sys/types.h>
+#include <netatalk/endian.h>
+
+#define TKTLIFENUMFIXED 64
+#define TKTLIFEMINFIXED 0x80
+#define TKTLIFEMAXFIXED 0xBF
+#define TKTLIFENOEXPIRE 0xFF
+#define MAXTKTLIFETIME (30*24*3600) /* 30 days */
+#ifndef NEVERDATE
+#define NEVERDATE ((u_int32_t)-1L)
+#endif
+
+static int tkt_lifetimes[TKTLIFENUMFIXED] = {
+ 38400, /* 10.67 hours, 0.44 days */
+ 41055, /* 11.40 hours, 0.48 days */
+ 43894, /* 12.19 hours, 0.51 days */
+ 46929, /* 13.04 hours, 0.54 days */
+ 50174, /* 13.94 hours, 0.58 days */
+ 53643, /* 14.90 hours, 0.62 days */
+ 57352, /* 15.93 hours, 0.66 days */
+ 61318, /* 17.03 hours, 0.71 days */
+ 65558, /* 18.21 hours, 0.76 days */
+ 70091, /* 19.47 hours, 0.81 days */
+ 74937, /* 20.82 hours, 0.87 days */
+ 80119, /* 22.26 hours, 0.93 days */
+ 85658, /* 23.79 hours, 0.99 days */
+ 91581, /* 25.44 hours, 1.06 days */
+ 97914, /* 27.20 hours, 1.13 days */
+ 104684, /* 29.08 hours, 1.21 days */
+ 111922, /* 31.09 hours, 1.30 days */
+ 119661, /* 33.24 hours, 1.38 days */
+ 127935, /* 35.54 hours, 1.48 days */
+ 136781, /* 37.99 hours, 1.58 days */
+ 146239, /* 40.62 hours, 1.69 days */
+ 156350, /* 43.43 hours, 1.81 days */
+ 167161, /* 46.43 hours, 1.93 days */
+ 178720, /* 49.64 hours, 2.07 days */
+ 191077, /* 53.08 hours, 2.21 days */
+ 204289, /* 56.75 hours, 2.36 days */
+ 218415, /* 60.67 hours, 2.53 days */
+ 233517, /* 64.87 hours, 2.70 days */
+ 249664, /* 69.35 hours, 2.89 days */
+ 266926, /* 74.15 hours, 3.09 days */
+ 285383, /* 79.27 hours, 3.30 days */
+ 305116, /* 84.75 hours, 3.53 days */
+ 326213, /* 90.61 hours, 3.78 days */
+ 348769, /* 96.88 hours, 4.04 days */
+ 372885, /* 103.58 hours, 4.32 days */
+ 398668, /* 110.74 hours, 4.61 days */
+ 426234, /* 118.40 hours, 4.93 days */
+ 455705, /* 126.58 hours, 5.27 days */
+ 487215, /* 135.34 hours, 5.64 days */
+ 520904, /* 144.70 hours, 6.03 days */
+ 556921, /* 154.70 hours, 6.45 days */
+ 595430, /* 165.40 hours, 6.89 days */
+ 636601, /* 176.83 hours, 7.37 days */
+ 680618, /* 189.06 hours, 7.88 days */
+ 727680, /* 202.13 hours, 8.42 days */
+ 777995, /* 216.11 hours, 9.00 days */
+ 831789, /* 231.05 hours, 9.63 days */
+ 889303, /* 247.03 hours, 10.29 days */
+ 950794, /* 264.11 hours, 11.00 days */
+ 1016537, /* 282.37 hours, 11.77 days */
+ 1086825, /* 301.90 hours, 12.58 days */
+ 1161973, /* 322.77 hours, 13.45 days */
+ 1242318, /* 345.09 hours, 14.38 days */
+ 1328218, /* 368.95 hours, 15.37 days */
+ 1420057, /* 394.46 hours, 16.44 days */
+ 1518247, /* 421.74 hours, 17.57 days */
+ 1623226, /* 450.90 hours, 18.79 days */
+ 1735464, /* 482.07 hours, 20.09 days */
+ 1855462, /* 515.41 hours, 21.48 days */
+ 1983758, /* 551.04 hours, 22.96 days */
+ 2120925, /* 589.15 hours, 24.55 days */
+ 2267576, /* 629.88 hours, 26.25 days */
+ 2424367, /* 673.44 hours, 28.06 days */
+ 2592000}; /* 720.00 hours, 30.00 days */
+
+/*
+ * krb_life_to_time - takes a start time and a Kerberos standard
+ * lifetime char and returns the corresponding end time. There are
+ * four simple cases to be handled. The first is a life of 0xff,
+ * meaning no expiration, and results in an end time of 0xffffffff.
+ * The second is when life is less than the values covered by the
+ * table. In this case, the end time is the start time plus the
+ * number of 5 minute intervals specified by life. The third case
+ * returns start plus the MAXTKTLIFETIME if life is greater than
+ * TKTLIFEMAXFIXED. The last case, uses the life value (minus
+ * TKTLIFEMINFIXED) as an index into the table to extract the lifetime
+ * in seconds, which is added to start to produce the end time.
+ */
+u_int32_t krb_life_to_time(start, life)
+u_int32_t start;
+int life;
+{
+ life = (unsigned char) life;
+ if (life == TKTLIFENOEXPIRE) return NEVERDATE;
+ if (life < TKTLIFEMINFIXED) return start + life*5*60;
+ if (life > TKTLIFEMAXFIXED) return start + MAXTKTLIFETIME;
+ return start + tkt_lifetimes[life - TKTLIFEMINFIXED];
+}
+
+/*
+ * krb_time_to_life - takes start and end times for the ticket and
+ * returns a Kerberos standard lifetime char, possibily using the
+ * tkt_lifetimes table for lifetimes above 127*5 minutes. First, the
+ * special case of (end == NEVERDATE) is handled to mean no
+ * expiration. Then negative lifetimes and those greater than the
+ * maximum ticket lifetime are rejected. Then lifetimes less than the
+ * first table entry are handled by rounding the requested lifetime
+ * *up* to the next 5 minute interval. The final step is to search
+ * the table for the smallest entry *greater than or equal* to the
+ * requested entry.
+ */
+int krb_time_to_life(start, end)
+u_int32_t start;
+u_int32_t end;
+{
+ int32_t lifetime;
+ int i;
+
+ if (end == NEVERDATE) return TKTLIFENOEXPIRE;
+ lifetime = end - start;
+ if (lifetime > MAXTKTLIFETIME || lifetime <= 0) return 0;
+ if (lifetime < tkt_lifetimes[0]) return (lifetime + 5*60 - 1)/(5*60);
+ for (i=0; i<TKTLIFENUMFIXED; i++) {
+ if (lifetime <= tkt_lifetimes[i]) {
+ return i+TKTLIFEMINFIXED;
+ }
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+/*
+ * $Source: /home/ralph/netatalk/rsync/netatalk/etc/uams/uams_krb4/send_to_kdc.c,v $
+ * $Author: rufustfirefly $
+ *
+ * Copyright 1987, 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ */
+
+#ifndef lint
+static char rcsid_send_to_kdc_c[] =
+"$Id: send_to_kdc.c,v 1.1 2000-07-25 21:09:02 rufustfirefly Exp $";
+#endif /* lint */
+
+# ifdef UAM_AFSKRB
+
+#include <mit-copyright.h>
+
+#include <krb.h>
+#include <prot.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#ifdef lint
+#include <sys/uio.h> /* struct iovec to make lint happy */
+#endif /* lint */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+
+#define S_AD_SZ sizeof(struct sockaddr_in)
+
+extern int krb_debug;
+
+extern char *malloc(), *calloc(), *realloc();
+
+int krb_udp_port = 0;
+
+/* CLIENT_KRB_TIMEOUT indicates the time to wait before
+ * retrying a server. It's defined in "krb.h".
+ */
+static struct timeval timeout = { CLIENT_KRB_TIMEOUT, 0};
+static char *prog = "send_to_kdc";
+static send_recv();
+
+/*
+ * This file contains two routines, send_to_kdc() and send_recv().
+ * send_recv() is a static routine used by send_to_kdc().
+ */
+
+/*
+ * send_to_kdc() sends a message to the Kerberos authentication
+ * server(s) in the given realm and returns the reply message.
+ * The "pkt" argument points to the message to be sent to Kerberos;
+ * the "rpkt" argument will be filled in with Kerberos' reply.
+ * The "realm" argument indicates the realm of the Kerberos server(s)
+ * to transact with. If the realm is null, the local realm is used.
+ *
+ * If more than one Kerberos server is known for a given realm,
+ * different servers will be queried until one of them replies.
+ * Several attempts (retries) are made for each server before
+ * giving up entirely.
+ *
+ * If an answer was received from a Kerberos host, KSUCCESS is
+ * returned. The following errors can be returned:
+ *
+ * SKDC_CANT - can't get local realm
+ * - can't find "kerberos" in /etc/services database
+ * - can't open socket
+ * - can't bind socket
+ * - all ports in use
+ * - couldn't find any Kerberos host
+ *
+ * SKDC_RETRY - couldn't get an answer from any Kerberos server,
+ * after several retries
+ */
+
+send_to_kdc(pkt,rpkt,realm)
+ KTEXT pkt;
+ KTEXT rpkt;
+ char *realm;
+{
+ int i, f;
+ int no_host; /* was a kerberos host found? */
+ int retry;
+ int n_hosts;
+ int retval;
+ struct sockaddr_in to;
+ struct hostent *host, *hostlist;
+ char *cp;
+ char krbhst[MAX_HSTNM];
+ char lrealm[REALM_SZ];
+
+ /*
+ * If "realm" is non-null, use that, otherwise get the
+ * local realm.
+ */
+ if (realm)
+ (void) strcpy(lrealm, realm);
+ else
+ if (krb_get_lrealm(lrealm,1)) {
+ if (krb_debug)
+ fprintf(stderr, "%s: can't get local realm\n", prog);
+ return(SKDC_CANT);
+ }
+ if (krb_debug)
+ printf("lrealm is %s\n", lrealm);
+ if (krb_udp_port == 0) {
+ register struct servent *sp;
+ if ((sp = getservbyname("kerberos","udp")) == 0) {
+ if (krb_debug)
+ fprintf(stderr, "%s: Can't get kerberos/udp service\n",
+ prog);
+ return(SKDC_CANT);
+ }
+ krb_udp_port = sp->s_port;
+ if (krb_debug)
+ printf("krb_udp_port is %d\n", krb_udp_port);
+ }
+ memset(&to, 0, S_AD_SZ);
+ hostlist = (struct hostent *) malloc(sizeof(struct hostent));
+ if (!hostlist)
+ return (/*errno */SKDC_CANT);
+ if ((f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ if (krb_debug)
+ fprintf(stderr,"%s: Can't open socket\n", prog);
+ return(SKDC_CANT);
+ }
+ /* from now on, exit through rtn label for cleanup */
+
+ no_host = 1;
+ /* get an initial allocation */
+ n_hosts = 0;
+ for (i = 1; krb_get_krbhst(krbhst, lrealm, i) == KSUCCESS; ++i) {
+ if (krb_debug) {
+ printf("Getting host entry for %s...",krbhst);
+ (void) fflush(stdout);
+ }
+ host = gethostbyname(krbhst);
+ if (krb_debug) {
+ printf("%s.\n",
+ host ? "Got it" : "Didn't get it");
+ (void) fflush(stdout);
+ }
+ if (!host)
+ continue;
+ no_host = 0; /* found at least one */
+ n_hosts++;
+ /* preserve host network address to check later
+ * (would be better to preserve *all* addresses,
+ * take care of that later)
+ */
+ hostlist = (struct hostent *)
+ realloc((char *)hostlist,
+ (unsigned)
+ sizeof(struct hostent)*(n_hosts+1));
+ if (!hostlist)
+ return /*errno */SKDC_CANT;
+ memcpy(&hostlist[n_hosts-1], host, sizeof(struct hostent));
+ host = &hostlist[n_hosts-1];
+ cp = malloc((unsigned)host->h_length);
+ if (!cp) {
+ retval = /*errno */SKDC_CANT;
+ goto rtn;
+ }
+ memcpy(cp, host->h_addr, host->h_length);
+/* At least Sun OS version 3.2 (or worse) and Ultrix version 2.2
+ (or worse) only return one name ... */
+#if !(defined(ULTRIX022) || (defined(SunOS) && SunOS < 40))
+ host->h_addr_list = (char **)malloc(sizeof(char *));
+ if (!host->h_addr_list) {
+ retval = /*errno */SKDC_CANT;
+ goto rtn;
+ }
+#endif /* ULTRIX022 || SunOS */
+ host->h_addr = cp;
+ memset(&hostlist[n_hosts], 0, sizeof(struct hostent));
+ to.sin_family = host->h_addrtype;
+ memcpy(&to.sin_addr, host->h_addr, host->h_length);
+ to.sin_port = krb_udp_port;
+ if (send_recv(pkt, rpkt, f, &to, hostlist)) {
+ retval = KSUCCESS;
+ goto rtn;
+ }
+ if (krb_debug) {
+ printf("Timeout, error, or wrong descriptor\n");
+ (void) fflush(stdout);
+ }
+ }
+ if (no_host) {
+ if (krb_debug)
+ fprintf(stderr, "%s: can't find any Kerberos host.\n",
+ prog);
+ retval = SKDC_CANT;
+ goto rtn;
+ }
+ /* retry each host in sequence */
+ for (retry = 0; retry < CLIENT_KRB_RETRY; ++retry) {
+ for (host = hostlist; host->h_name != (char *)NULL; host++) {
+ to.sin_family = host->h_addrtype;
+ memcpy(&to.sin_addr, host->h_addr, host->h_length);
+ if (send_recv(pkt, rpkt, f, &to, hostlist)) {
+ retval = KSUCCESS;
+ goto rtn;
+ }
+ }
+ }
+ retval = SKDC_RETRY;
+rtn:
+ (void) close(f);
+ if (hostlist) {
+ register struct hostent *hp;
+ for (hp = hostlist; hp->h_name; hp++)
+#if !(defined(ULTRIX022) || (defined(SunOS) && SunOS < 40))
+ if (hp->h_addr_list) {
+#endif /* ULTRIX022 || SunOS */
+ if (hp->h_addr)
+ free(hp->h_addr);
+#if !(defined(ULTRIX022) || (defined(SunOS) && SunOS < 40))
+ free((char *)hp->h_addr_list);
+ }
+#endif /* ULTRIX022 || SunOS */
+ free((char *)hostlist);
+ }
+ return(retval);
+}
+
+/*
+ * try to send out and receive message.
+ * return 1 on success, 0 on failure
+ */
+
+static send_recv(pkt,rpkt,f,_to,addrs)
+ KTEXT pkt;
+ KTEXT rpkt;
+ int f;
+ struct sockaddr_in *_to;
+ struct hostent *addrs;
+{
+ fd_set readfds;
+ register struct hostent *hp;
+ struct sockaddr_in from;
+ int sin_size, rc;
+ int numsent;
+
+ if (krb_debug) {
+ if (_to->sin_family == AF_INET)
+ printf("Sending message to %s...",
+ inet_ntoa(_to->sin_addr));
+ else
+ printf("Sending message...");
+ (void) fflush(stdout);
+ }
+ if ((numsent = sendto(f,(char *)(pkt->dat), pkt->length, 0,
+ (struct sockaddr *)_to,
+ S_AD_SZ)) != pkt->length) {
+ if (krb_debug)
+ printf("sent only %d/%d\n",numsent, pkt->length);
+ return 0;
+ }
+ if (krb_debug) {
+ printf("Sent\nWaiting for reply...");
+ (void) fflush(stdout);
+ }
+ FD_ZERO(&readfds);
+ FD_SET(f, &readfds);
+ errno = 0;
+ /* select - either recv is ready, or timeout */
+ /* see if timeout or error or wrong descriptor */
+ if (select(f + 1, &readfds, (fd_set *)0, (fd_set *)0, &timeout) < 1
+ || !FD_ISSET(f, &readfds)) {
+ if (krb_debug) {
+ fprintf(stderr, "select failed: readfds=%x",
+ readfds);
+ perror("");
+ }
+ return 0;
+ }
+ sin_size = sizeof(from);
+ if (( rc = recvfrom(f, (char *)(rpkt->dat), sizeof(rpkt->dat), 0,
+ (struct sockaddr *)&from, &sin_size)) < 0) {
+ if (krb_debug)
+ perror("recvfrom");
+ return 0;
+ }
+ rpkt->length = rc;
+ if (krb_debug) {
+ printf("received packet from %s\n", inet_ntoa(from.sin_addr));
+ fflush(stdout);
+ }
+ for (hp = addrs; hp->h_name != (char *)NULL; hp++) {
+ if (!memcmp(hp->h_addr, (char *)&from.sin_addr.s_addr,
+ hp->h_length)) {
+ if (krb_debug) {
+ printf("Received it\n");
+ (void) fflush(stdout);
+ }
+ return 1;
+ }
+ if (krb_debug)
+ fprintf(stderr,
+ "packet not from %x\n",
+ hp->h_addr);
+ }
+ if (krb_debug)
+ fprintf(stderr, "%s: received packet from wrong host! (%x)\n",
+ "send_to_kdc(send_rcv)", from.sin_addr.s_addr);
+ return 0;
+}
+# endif UAM_AFSKRB
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+
+#if defined( KRB ) || defined( UAM_AFSKRB )
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <netinet/in.h>
+#include <des.h>
+#include <krb.h>
+#include <prot.h>
+
+#include <netatalk/endian.h>
+#include <atalk/afp.h>
+#include <atalk/compat.h>
+#include <atalk/util.h>
+
+static C_Block seskey;
+static Key_schedule seskeysched;
+
+static char realm[ REALM_SZ ];
+
+#ifdef UAM_AFSKRB
+static int validseskey = 0;
+static int logged = 0;
+static char *tktfile;
+static char instance[ INST_SZ ], name[ ANAME_SZ ];
+#endif /*UAM_AFSKRB*/
+
+#ifdef AFS
+#include <afs/stds.h>
+#include <rx/rxkad.h>
+#include <afs/afs.h>
+#include <afs/venus.h>
+#include <afs/afsint.h>
+
+char *ka_LocalCell();
+
+struct ClearToken {
+ int32_t AuthHandle;
+ char HandShakeKey[8];
+ int32_t ViceId;
+ int32_t BeginTimestamp;
+ int32_t EndTimestamp;
+};
+#endif /*AFS*/
+
+
+#ifdef KRB
+
+static __inline__ void lcase( p )
+ char *p;
+{
+ for (; *p; p++ ) {
+ if ( isupper( *p )) {
+ *p = tolower( *p );
+ }
+ }
+ return;
+}
+
+static __inline__ void ucase( p )
+ char *p;
+{
+ for (; *p; p++ ) {
+ if ( islower( *p )) {
+ *p = toupper( *p );
+ }
+ }
+ return;
+}
+
+#define KRB4CMD_HELO 1
+#define KRB4RPL_REALM 2
+#define KRB4WRT_SESS 3
+#define KRB4RPL_DONE 4
+#define KRB4RPL_PRINC 5
+#define KRB4WRT_TOKEN 6
+#define KRB4WRT_SKIP 7
+#define KRB4RPL_DONEMUT 8
+
+
+static int krb4_login(void *obj, struct passwd **uam_pwd,
+ char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen )
+{
+ char *p;
+ int len;
+
+ *rbuflen = 0;
+ if ( *ibuf != KRB4CMD_HELO ) {
+ syslog( LOG_INFO, "krb4_login: bad command %d", *ibuf );
+ return( AFPERR_NOTAUTH );
+ }
+
+ p = rbuf;
+ if ( krb_get_lrealm( realm, 1 ) != KSUCCESS ) {
+ syslog( LOG_ERR, "krb4_login: can't get local realm!" );
+ return( AFPERR_NOTAUTH );
+ }
+
+ *p++ = KRB4RPL_REALM;
+ *p++ = 1;
+ len = strlen( realm );
+ *p++ = len;
+ strcpy( p, realm );
+ p += len + 1;
+
+#ifdef AFS
+ if ( setpag() < 0 ) {
+ syslog( LOG_ERR, "krb_login: setpag: %m" );
+ return( AFPERR_BADUAM );
+ }
+#endif /*AFS*/
+
+ *rbuflen = p - rbuf;
+ return( AFPERR_AUTHCONT );
+}
+
+static int krb4_logincont(void *obj, struct passwd **uam_pwd,
+ char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ static struct passwd *pwd;
+ KTEXT_ST tkt;
+ static AUTH_DAT ad;
+ int rc;
+ u_int16_t len;
+ char *p, *username;
+ CREDENTIALS cr;
+#ifdef AFS
+ struct ViceIoctl vi;
+ struct ClearToken ct;
+#endif /*AFS*/
+ char buf[ 1024 ];
+ int aint, ulen;
+
+ if (uam_afp_read(obj, rbuf, rbuflen) < 0) /* read in the rest. */
+ return AFPERR_PARAM;
+
+ *rbuflen = 0;
+ if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, &username) < 0)
+ return AFPERR_MISC;
+
+ if (uam_afpserver_option(obj, UAM_OPTION_USERNAMELEN, &ulen) < 0)
+ return AFPERR_MISC;
+
+ p = rbuf;
+
+ switch ( rc = *p++ ) {
+ case KRB4WRT_SESS :
+ memcpy( &len, p, sizeof( len ));
+ tkt.length = ntohs( len );
+ p += sizeof( short );
+
+ if ( tkt.length <= 0 || tkt.length > MAX_KTXT_LEN ) {
+ return( AFPERR_BADUAM );
+ }
+ memcpy( tkt.dat, p, tkt.length );
+ p += tkt.length;
+
+ if (( rc = krb_rd_req( &tkt, "afpserver", obj->Obj, 0, &ad, "" ))
+ != RD_AP_OK ) {
+ syslog( LOG_ERR, "krb4_logincont: krb_rd_req: %s",
+ krb_err_txt[ rc ] );
+ return( AFPERR_BADUAM );
+ }
+
+ syslog( LOG_INFO, "krb4_login: %s.%s@%s", ad.pname, ad.pinst,
+ ad.prealm );
+ memcpy(realm, ad.prealm, sizeof(realm));
+ memcpy(seskey, ad.session, sizeof( C_Block ));
+ key_sched((C_Block *) seskey, seskeysched );
+
+ strncpy(username, ad.pname, ulen);
+
+ p = rbuf;
+#ifndef AFS
+ *p = KRB4RPL_DONE; /* XXX */
+ *rbuflen = 1;
+
+ if (( pwd = uam_getname( ad.pname, strlen(ad.pname) )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+ *uam_pwd = pwd;
+ return AFP_OK;
+#else AFS
+ /* get principals */
+ *p++ = KRB4RPL_PRINC;
+ len = strlen( realm );
+ *p++ = len + 1;
+ *p++ = '@';
+ strcpy( p, realm );
+ p += len + 1;
+ *rbuflen = p - rbuf;
+ return( AFPERR_AUTHCONT );
+
+ case KRB4WRT_TOKEN :
+ memcpy( &len, p, sizeof( len ));
+ len = ntohs( len );
+ p += sizeof( len );
+ memcpy( &cr, p, len );
+
+ pcbc_encrypt((C_Block *)&cr, (C_Block *)&cr, len, seskeysched,
+ seskey, DES_DECRYPT );
+
+ p = buf;
+ cr.ticket_st.length = ntohl( cr.ticket_st.length );
+ memcpy( p, &cr.ticket_st.length, sizeof( int ));
+ p += sizeof( int );
+ memcpy( p, cr.ticket_st.dat, cr.ticket_st.length );
+ p += cr.ticket_st.length;
+
+ ct.AuthHandle = ntohl( cr.kvno );
+ memcpy( ct.HandShakeKey, cr.session, sizeof( cr.session ));
+ ct.ViceId = 0;
+ ct.BeginTimestamp = ntohl( cr.issue_date );
+ ct.EndTimestamp = krb_life_to_time( ntohl( cr.issue_date ),
+ ntohl( cr.lifetime ));
+
+ aint = sizeof( struct ClearToken );
+ memcpy( p, &aint, sizeof( int ));
+ p += sizeof( int );
+ memcpy( p, &ct, sizeof( struct ClearToken ));
+ p += sizeof( struct ClearToken );
+
+ aint = 0;
+ memcpy( p, &aint, sizeof( int ));
+ p += sizeof( int );
+
+ lcase( realm );
+ strcpy( p, realm );
+ p += strlen( realm ) + 1;
+
+ vi.in = buf;
+ vi.in_size = p - buf;
+ vi.out = buf;
+ vi.out_size = sizeof( buf );
+ if ( pioctl( 0, VIOCSETTOK, &vi, 0 ) < 0 ) {
+ syslog( LOG_ERR, "krb4_logincont: pioctl: %m" );
+ return( AFPERR_BADUAM );
+ }
+ /* FALL THROUGH */
+
+ case KRB4WRT_SKIP :
+ p = rbuf;
+ *p = KRB4RPL_DONE; /* XXX */
+ *rbuflen = 1;
+
+ if (( pwd = uam_getname( ad.pname, strlen(ad.pname) )) == NULL ) {
+ return( AFPERR_PARAM );
+ }
+ *uam_pwd = pwd;
+ return AFP_OK;
+#endif /*AFS*/
+
+ default :
+ syslog( LOG_INFO, "krb4_logincont: bad command %d", rc );
+ return( AFPERR_NOTAUTH );
+ break;
+ }
+}
+
+#endif /*KRB*/
+
+
+#ifdef AFS
+#include <rx/rxkad.h>
+#include <afs/afsint.h>
+
+char *ka_LocalCell();
+
+static void
+addrealm(realm,cells)
+ char *realm;
+ char ***cells;
+{
+ char **ptr;
+ int temp;
+
+ ptr= *cells;
+
+ for(;*ptr != 0 ;ptr++)
+ if(!strcmp(realm,*ptr))
+ return;
+
+ temp=ptr- *cells;
+ *cells=(char**)realloc(*cells,((2+temp)*sizeof(char*)));
+ ptr= *cells+temp;
+
+ *ptr=(char*)malloc(strlen(realm)+1);
+ strcpy(*ptr++,realm);
+ *ptr=0;
+ return;
+}
+
+static int kcheckuser(pwd,passwd)
+ struct passwd *pwd;
+ char *passwd;
+{
+ int32_t code;
+ char *instance="";
+ char realm[MAXKTCREALMLEN];
+ char lorealm[MAXKTCREALMLEN];
+ char *cell;
+ Date lifetime=MAXKTCTICKETLIFETIME;
+ int rval;
+ char **cells=(char **)malloc(sizeof(char*));
+ char *temp;
+ int rc,cellNum;
+ struct ktc_principal serviceName;
+
+ *cells=0;
+
+ code = ka_Init(0);
+
+ {
+ char *temp,*temp1;
+ temp=(char*)malloc(strlen(pwd->pw_dir)+1);
+ strcpy(temp,pwd->pw_dir);
+ temp1=temp;
+ temp=strtok(temp,"/");
+ temp=strtok('\0',"/");
+ ka_CellToRealm(temp,realm,0);
+ addrealm(realm,&cells);
+ free(temp1);
+ }
+
+ setpag();
+ authenticate(cells,pwd->pw_name,passwd);
+ cellNum=0;
+ rc=ktc_ListTokens(cellNum,&cellNum,&serviceName);
+ if(rc)
+ rval=1;
+ else{
+ rval=0;
+ }
+
+ return(rval);
+}
+
+static void authenticate(cells,name,passwd)
+ char **cells;
+ char *name;
+ char *passwd;
+{
+ char **ptr=cells;
+ char *errorstring;
+
+ while(*ptr){
+ ka_UserAuthenticate(name,/*instance*/"",/*cell*/*ptr++,
+ passwd,/*setpag*/0,&errorstring);
+ }
+}
+#endif /*AFS*/
+
+#if defined( UAM_AFSKRB ) && defined( AFS )
+static int afskrb_login(void *obj, struct passwd *uam_pwd,
+ char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen )
+{
+ KTEXT_ST authent, rpkt;
+ CREDENTIALS cr;
+ char *p, *q, *username;
+ int len, rc, whoserealm, ulen;
+ short slen;
+
+ *rbuflen = 0;
+ if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, &username) < 0)
+ return AFPERR_MISC;
+
+ if (uam_afpserver_option(obj, UAM_OPTION_USERNAMELEN, &ulen) < 0)
+ return AFPERR_MISC;
+
+ len = (unsigned char) *ibuf++;
+ ibuf[ len ] = '\0';
+ if (( p = strchr( ibuf, '@' )) != NULL ) {
+ *p++ = '\0';
+ strcpy( realm, p );
+ ucase( realm );
+ whoserealm = 0;
+ } else {
+ if ( krb_get_lrealm( realm, 1 ) != KSUCCESS ) {
+ return AFPERR_BADUAM;
+ }
+ whoserealm = 1;
+ }
+ if (( p = strchr( ibuf, '.' )) != NULL ) {
+ *p++ = '\0';
+ strcpy( instance, p );
+ } else {
+ *instance = '\0';
+ }
+ strcpy( name, ibuf );
+ /*
+ * We don't have the session key, yet. Get one.
+ */
+ p = rbuf;
+ if ( validseskey == 0 ) {
+ if ( setpag() < 0 ) {
+ syslog( LOG_ERR, "krb_login: setpag: %m" );
+ return AFPERR_BADUAM;
+ }
+ krb_set_tkt_string(( tktfile = mktemp( _PATH_AFPTKT )));
+ if (( rc = krb_get_svc_in_tkt( "afpserver", Obj, realm,
+ TICKET_GRANTING_TICKET, realm, 255, KEYFILE )) != INTK_OK ) {
+ syslog( LOG_ERR, "krb_login: can't get ticket-granting-ticket" );
+ return (( whoserealm ) ? AFPERR_BADUAM : AFPERR_PARAM );
+ }
+ if ( krb_mk_req( &authent, name, instance, realm, 0 ) != KSUCCESS ) {
+ return ( AFPERR_PARAM );
+ }
+ if ( krb_get_cred( name, instance, realm, &cr ) != KSUCCESS ) {
+ return ( AFPERR_BADUAM );
+ }
+
+ if ( unlink( tktfile ) < 0 ) {
+ syslog( LOG_ERR, "krb_login: unlink %s: %m", tktfile );
+ return ( AFPERR_BADUAM );
+ }
+
+ memcpy( seskey, cr.session, sizeof( C_Block ));
+ key_sched((C_Block *) seskey, seskeysched );
+ validseskey = 1;
+ strncpy(username, name, ulen);
+
+ memcpy( p, authent.dat, authent.length );
+ p += authent.length;
+ }
+
+ if ( kuam_get_in_tkt( name, instance, realm, TICKET_GRANTING_TICKET,
+ realm, 255, &rpkt ) != INTK_OK ) {
+ return ( AFPERR_PARAM );
+ }
+
+
+ q = (char *)rpkt.dat;
+ *p++ = *q++;
+ *p++ = *q++;
+ while ( *q++ )
+ ;
+ while ( *q++ )
+ ;
+ while ( *q++ )
+ ;
+ q += 10;
+
+ len = strlen( realm );
+ strcpy( p, realm );
+ p += len + 1;
+ memcpy( &slen, q, sizeof( short ));
+ memcpy( p, &slen, sizeof( short ));
+ p += sizeof( short );
+ q += sizeof( short );
+ memcpy( p, q, slen );
+ p += slen;
+
+ *rbuflen = p - rbuf;
+ return( AFPERR_AUTHCONT );
+}
+
+static int afskrb_logincont(void *obj, struct passwd *uam_pwd,
+ char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen )
+{
+ CREDENTIALS cr;
+ struct ViceIoctl vi;
+ struct ClearToken ct;
+ struct passwd *pwd;
+ char buf[ 1024 ], *p;
+ int aint;
+ short clen;
+
+ *rbuflen = 0;
+ if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, &username) < 0)
+ return AFPERR_MISC;
+
+ ibuf += 2;
+ memcpy( &clen, ibuf, sizeof( short ));
+ clen = ntohs( clen );
+ ibuf += sizeof( short );
+
+ pcbc_encrypt((C_Block *)ibuf, (C_Block *)ibuf,
+ clen, seskeysched, seskey, DES_DECRYPT );
+ if ( kuam_set_in_tkt( name, instance, realm, TICKET_GRANTING_TICKET,
+ realm, ibuf ) != INTK_OK ) {
+ return ( AFPERR_PARAM );
+ }
+
+ if ( get_ad_tkt( "afs", "", realm, 255 ) != KSUCCESS ) {
+ return ( AFPERR_PARAM );
+ }
+ if ( krb_get_cred( "afs", "", realm, &cr ) != KSUCCESS ) {
+ return ( AFPERR_PARAM );
+ }
+
+ p = buf;
+ memcpy( p, &cr.ticket_st.length, sizeof( int ));
+ p += sizeof( int );
+ memcpy( p, cr.ticket_st.dat, cr.ticket_st.length );
+ p += cr.ticket_st.length;
+
+ ct.AuthHandle = cr.kvno;
+ memcpy( ct.HandShakeKey, cr.session, sizeof( cr.session ));
+ ct.ViceId = 0;
+ ct.BeginTimestamp = cr.issue_date;
+ /* ct.EndTimestamp = cr.issue_date + ( cr.lifetime * 5 * 60 ); */
+ ct.EndTimestamp = krb_life_to_time( cr.issue_date, cr.lifetime );
+
+ aint = sizeof( struct ClearToken );
+ memcpy( p, &aint, sizeof( int ));
+ p += sizeof( int );
+ memcpy( p, &ct, sizeof( struct ClearToken ));
+ p += sizeof( struct ClearToken );
+
+ aint = 0;
+ memcpy( p, &aint, sizeof( int ));
+ p += sizeof( int );
+
+ lcase( realm );
+ strcpy( p, realm );
+ p += strlen( realm ) + 1;
+
+ vi.in = buf;
+ vi.in_size = p - buf;
+ vi.out = buf;
+ vi.out_size = sizeof( buf );
+ if ( pioctl( 0, VIOCSETTOK, &vi, 0 ) < 0 ) {
+ syslog( LOG_ERR, "krb_logincont: pioctl: %m" );
+ return ( AFPERR_BADUAM );
+ }
+
+ if ( unlink( tktfile ) < 0 ) {
+ syslog( LOG_ERR, "krb_logincont: %s: %m", tktfile );
+ return ( AFPERR_BADUAM );
+ }
+
+ if (( pwd = uam_getname( username )) == NULL ) {
+ return ( AFPERR_PARAM );
+ }
+
+ if ( logged == 0 ) {
+ logged = 1;
+ syslog( LOG_INFO, "authenticated %s.%s@%s", name, instance, realm );
+ *uam_pwd = pwd;
+ return AFP_OK;
+ }
+ syslog( LOG_INFO, "re-authenticated %s.%s@%s", name, instance, realm );
+ return( AFP_OK );
+}
+#endif /* UAM_AFSKRB AFS */
+
+int uam_setup()
+{
+#ifdef KRB
+ uam_register(UAM_SERVER_LOGIN, "Kerberos IV", krb4_login,
+ krb4_logincont, NULL);
+ /* uam_afpserver_action(); */
+#endif
+#ifdef UAM_AFSKRB
+ uam_register(UAM_SERVER_LOGIN, "AFS Kerberos", afskrb_login,
+ afskrb_logincont, NULL);
+ /* uam_afpserver_action(); */
+#endif
+}
+
+int uam_cleanup()
+{
+#ifdef KRB
+ /* uam_afpserver_action(); */
+ uam_unregister(UAM_SERVER_LOGIN, "Kerberos IV");
+#endif
+#ifdef UAM_AFSKRB
+ /* uam_afpserver_action(); */
+ uam_unregister(UAM_SERVER_LOGIN, "AFS Kerberos");
+#endif
+}
+
+#endif /* KRB or UAM_AFSKRB */
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ */
+#ifdef USE_PAM
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <security/pam_appl.h>
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+#define PASSWDLEN 8
+
+/* Static variables used to communicate between the conversation function
+ * and the server_login function
+ */
+static pam_handle_t *pamh = NULL;
+static char *hostname;
+static char *PAM_username;
+static char *PAM_password;
+
+/* PAM conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+static int PAM_conv (int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr)
+{
+ struct pam_response *reply;
+ int count;
+
+#define COPY_STRING(s) (s) ? strdup(s) : NULL
+
+ if (num_msg < 1)
+ return PAM_CONV_ERR;
+
+ reply = (struct pam_response *)
+ calloc(num_msg, sizeof(struct pam_response));
+
+ if (!reply)
+ return PAM_CONV_ERR;
+
+ for (count = 0; count < num_msg; count++) {
+ char *string = NULL;
+
+ switch (msg[count]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ if (!(string = COPY_STRING(PAM_username)))
+ goto pam_fail_conv;
+ break;
+ case PAM_PROMPT_ECHO_OFF:
+ if (!(string = COPY_STRING(PAM_password)))
+ goto pam_fail_conv;
+ break;
+ case PAM_TEXT_INFO:
+#ifdef PAM_BINARY_PROMPT
+ case PAM_BINARY_PROMPT:
+#endif
+ /* ignore it... */
+ break;
+ case PAM_ERROR_MSG:
+ default:
+ goto pam_fail_conv;
+ }
+
+ if (string) {
+ reply[count].resp_retcode = 0;
+ reply[count].resp = string;
+ string = NULL;
+ }
+ }
+
+ *resp = reply;
+ return PAM_SUCCESS;
+
+pam_fail_conv:
+ for (count = 0; count < num_msg; count++) {
+ if (!reply[count].resp)
+ continue;
+ switch (msg[count]->msg_style) {
+ case PAM_PROMPT_ECHO_OFF:
+ case PAM_PROMPT_ECHO_ON:
+ free(reply[count].resp);
+ break;
+ }
+ }
+ free(reply);
+ return PAM_CONV_ERR;
+}
+
+static struct pam_conv PAM_conversation = {
+ &PAM_conv,
+ NULL
+};
+
+
+/* cleartxt login */
+static int pam_login(void *obj, struct passwd **uam_pwd,
+ char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ struct passwd *pwd;
+ char *username;
+ int err, len, ulen, PAM_error;
+
+ *rbuflen = 0;
+
+ if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
+ (void *) &username, &ulen) < 0)
+ return AFPERR_MISC;
+
+ if (uam_afpserver_option(obj, UAM_OPTION_HOSTNAME,
+ (void *) &hostname, NULL) < 0)
+ return AFPERR_MISC;
+
+ len = (unsigned char) *ibuf++;
+ if ( len > ulen ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy(username, ibuf, len );
+ ibuf += len;
+ username[ len ] = '\0';
+ if ((unsigned long) ibuf & 1) /* pad character */
+ ++ibuf;
+ ibuf[ PASSWDLEN ] = '\0';
+
+ if (( pwd = uam_getname(username, ulen)) == NULL ) {
+ return AFPERR_PARAM;
+ }
+
+ syslog(LOG_INFO, "cleartext login: %s", username);
+ PAM_username = username;
+ PAM_password = ibuf; /* Set these things up for the conv function */
+
+ err = AFPERR_NOTAUTH;
+ PAM_error = pam_start("netatalk", username, &PAM_conversation,
+ &pamh);
+ if (PAM_error != PAM_SUCCESS)
+ goto login_err;
+
+ pam_set_item(pamh, PAM_TTY, "afpd");
+ pam_set_item(pamh, PAM_RHOST, hostname);
+ /* use PAM_DISALLOW_NULL_AUTHTOK if passwdminlen > 0 */
+ PAM_error = pam_authenticate(pamh,0);
+ if (PAM_error != PAM_SUCCESS) {
+ if (PAM_error == PAM_MAXTRIES)
+ err = AFPERR_PWDEXPR;
+ goto login_err;
+ }
+
+ PAM_error = pam_acct_mgmt(pamh, 0);
+ if (PAM_error != PAM_SUCCESS) {
+ if (PAM_error == PAM_ACCT_EXPIRED)
+ err = AFPERR_PWDEXPR;
+#ifdef PAM_AUTHTOKEN_REQD
+ else if (PAM_error == PAM_AUTHTOKEN_REQD)
+ err = AFPERR_PWDCHNG;
+#endif
+ goto login_err;
+ }
+
+#ifndef PAM_CRED_ESTABLISH
+#define PAM_CRED_ESTABLISH PAM_ESTABLISH_CRED
+#endif
+ PAM_error = pam_setcred(pamh, PAM_CRED_ESTABLISH);
+ if (PAM_error != PAM_SUCCESS)
+ goto login_err;
+
+ PAM_error = pam_open_session(pamh, 0);
+ if (PAM_error != PAM_SUCCESS)
+ goto login_err;
+
+ *uam_pwd = pwd;
+ return AFP_OK;
+
+login_err:
+ pam_end(pamh, PAM_error);
+ pamh = NULL;
+ return err;
+}
+
+/* logout */
+static void pam_logout() {
+ pam_close_session(pamh, 0);
+ pam_end(pamh, 0);
+ pamh = NULL;
+}
+
+/* change passwd */
+static int pam_changepw(void *obj, char *username,
+ struct passwd *pwd, char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ char pw[PASSWDLEN + 1];
+ pam_handle_t *lpamh;
+ uid_t uid;
+ int PAM_error;
+
+ /* old password */
+ memcpy(pw, ibuf, PASSWDLEN);
+ memset(ibuf, 0, PASSWDLEN);
+ pw[PASSWDLEN] = '\0';
+
+ /* let's do a quick check for the same password */
+ if (memcmp(pw, ibuf + PASSWDLEN, PASSWDLEN) == 0)
+ return AFPERR_PWDSAME;
+
+ /* Set these things up for the conv function */
+ PAM_username = username;
+ PAM_password = pw;
+
+ PAM_error = pam_start("netatalk", username, &PAM_conversation,
+ &lpamh);
+ if (PAM_error != PAM_SUCCESS)
+ return AFPERR_PARAM;
+ pam_set_item(lpamh, PAM_TTY, "afpd");
+ pam_set_item(lpamh, PAM_RHOST, hostname);
+
+ /* we might need to do this as root */
+ uid = geteuid();
+ seteuid(0);
+ PAM_error = pam_authenticate(lpamh,0);
+ if (PAM_error != PAM_SUCCESS) {
+ seteuid(uid);
+ pam_end(lpamh, PAM_error);
+ return AFPERR_NOTAUTH;
+ }
+
+ /* new password */
+ ibuf += PASSWDLEN;
+ PAM_password = ibuf;
+ ibuf[PASSWDLEN] = '\0';
+
+ /* this really does need to be done as root */
+ PAM_error = pam_chauthtok(lpamh, 0);
+ seteuid(uid); /* un-root ourselves. */
+ memset(pw, 0, PASSWDLEN);
+ memset(ibuf, 0, PASSWDLEN);
+ if (PAM_error != PAM_SUCCESS) {
+ pam_end(lpamh, PAM_error);
+ return AFPERR_ACCESS;
+ }
+
+ pam_end(lpamh, 0);
+ return AFP_OK;
+}
+
+
+static int uam_setup(const char *path)
+{
+ if (uam_register(UAM_SERVER_LOGIN, path, "Cleartxt Passwrd",
+ pam_login, NULL, pam_logout) < 0)
+ return -1;
+
+ if (uam_register(UAM_SERVER_CHANGEPW, path, "Cleartxt Passwrd",
+ pam_changepw) < 0) {
+ uam_unregister(UAM_SERVER_LOGIN, "Cleartxt Passwrd");
+ return -1;
+ }
+
+ /*uam_register(UAM_SERVER_PRINTAUTH, path, "Cleartxt Passwrd",
+ pam_printer);*/
+
+ return 0;
+}
+
+static void uam_cleanup(void)
+{
+ uam_unregister(UAM_SERVER_LOGIN, "Cleartxt Passwrd");
+ uam_unregister(UAM_SERVER_CHANGEPW, "Cleartxt Passwrd");
+ /*uam_unregister(UAM_SERVER_PRINTAUTH, "Cleartxt Passwrd"); */
+}
+
+UAM_MODULE_EXPORT struct uam_export uams_clrtxt = {
+ UAM_MODULE_SERVER,
+ UAM_MODULE_VERSION,
+ uam_setup, uam_cleanup
+};
+
+#endif /* USE_PAM */
--- /dev/null
+/* Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifndef NO_CRYPT_H
+#include <crypt.h>
+#endif
+#include <pwd.h>
+#include <syslog.h>
+
+#ifdef SOLARIS
+#define SHADOWPW
+#endif SOLARIS
+
+#ifdef SHADOWPW
+#include <shadow.h>
+#endif SHADOWPW
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+#define PASSWDLEN 8
+
+/* cleartxt login */
+static int passwd_login(void *obj, struct passwd **uam_pwd,
+ char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ struct passwd *pwd;
+#ifdef SHADOWPW
+ struct spwd *sp;
+#endif
+ char *username, *p;
+ int len, ulen;
+
+ *rbuflen = 0;
+
+ if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
+ (void *) &username, &ulen) < 0)
+ return AFPERR_MISC;
+
+ len = (unsigned char) *ibuf++;
+ if ( len > ulen ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy(username, ibuf, len );
+ ibuf += len;
+ username[ len ] = '\0';
+ if ((unsigned long) ibuf & 1) /* pad character */
+ ++ibuf;
+ ibuf[ PASSWDLEN ] = '\0';
+
+ if (( pwd = uam_getname(username, ulen)) == NULL ) {
+ return AFPERR_PARAM;
+ }
+
+ syslog(LOG_INFO, "cleartext login: %s", username);
+ if (uam_checkuser(pwd) < 0)
+ return AFPERR_NOTAUTH;
+
+#ifdef SHADOWPW
+ if (( sp = getspnam( pwd->pw_name )) == NULL ) {
+ syslog( LOG_INFO, "no shadow passwd entry for %s", username);
+ return AFPERR_NOTAUTH;
+ }
+ pwd->pw_passwd = sp->sp_pwdp;
+#endif SHADOWPW
+
+ if (!pwd->pw_passwd)
+ return AFPERR_NOTAUTH;
+
+ *uam_pwd = pwd;
+
+ p = crypt( ibuf, pwd->pw_passwd );
+ if ( strcmp( p, pwd->pw_passwd ) == 0 )
+ return AFP_OK;
+
+ return AFPERR_NOTAUTH;
+}
+
+
+#if 0
+/* change passwd */
+static int passwd_changepw(void *obj, char *username,
+ struct passwd *pwd, char *ibuf,
+ int ibuflen, char *rbuf, int *rbuflen)
+{
+#ifdef SHADOWPW
+ struct spwd *sp;
+#endif
+ char pw[PASSWDLEN + 1], *p;
+ uid_t uid = geteuid();
+
+ if (uam_checkuser(pwd) < 0)
+ return AFPERR_ACCESS;
+
+ /* old password */
+ memcpy(pw, ibuf, PASSWDLEN);
+ memset(ibuf, 0, PASSWDLEN);
+ pw[PASSWDLEN] = '\0';
+
+#ifdef SHADOWPW
+ if (( sp = getspnam( pwd->pw_name )) == NULL ) {
+ syslog( LOG_INFO, "no shadow passwd entry for %s", username);
+ return AFPERR_PARAM;
+ }
+ pwd->pw_passwd = sp->sp_pwdp;
+#endif SHADOWPW
+
+ p = crypt(pw, pwd->pw_passwd );
+ if (strcmp( p, pwd->pw_passwd )) {
+ memset(pw, 0, sizeof(pw));
+ return AFPERR_NOTAUTH;
+ }
+
+ /* new password */
+ ibuf += PASSWDLEN;
+ ibuf[PASSWDLEN] = '\0';
+
+#ifdef SHADOWPW
+#else
+#endif
+ return AFP_OK;
+}
+#endif
+
+static int uam_setup(const char *path)
+{
+ if (uam_register(UAM_SERVER_LOGIN, path,
+ "Cleartxt Passwrd", passwd_login, NULL, NULL) < 0)
+ return -1;
+ /*uam_register(UAM_SERVER_PRINTAUTH, path, "Cleartxt Passwrd",
+ passwd_printer);*/
+ return 0;
+}
+
+static void uam_cleanup(void)
+{
+ uam_unregister(UAM_SERVER_LOGIN, "Cleartxt Passwrd");
+ /*uam_unregister(UAM_SERVER_PRINTAUTH, "Cleartxt Passwrd"); */
+}
+
+UAM_MODULE_EXPORT struct uam_export uams_clrtxt = {
+ UAM_MODULE_SERVER,
+ UAM_MODULE_VERSION,
+ uam_setup, uam_cleanup
+};
--- /dev/null
+/* Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifdef UAM_PGP
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <syslog.h>
+
+#include <bn.h>
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+#define KEYSIZE 16
+#define PASSWDLEN 64
+#define CRYPTBUFLEN (KEYSIZE*2)
+#define CRYPT2BUFLEN (KEYSIZE + PASSWDLEN)
+
+/* hash a number to a 16-bit quantity */
+#define pgphash(a) ((((unsigned long) (a) >> 8) ^ \
+ (unsigned long) (a)) & 0xffff)
+
+/* the secret key */
+static struct passwd *pgppwd;
+
+
+/* pgp passwd */
+static int pgp_login(void *obj, struct passwd **uam_pwd,
+ char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ BIGNUM *bn, *gbn, *pbn;
+ u_int16_t sessid;
+ int len, i;
+ char *name;
+
+ *rbuflen = 0;
+
+ if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &name, &i) < 0)
+ return AFPERR_PARAM;
+
+ len = (unsigned char) *ibuf++;
+ if ( len > i ) {
+ return( AFPERR_PARAM );
+ }
+
+ memcpy(name, ibuf, len );
+ ibuf += len;
+ name[ len ] = '\0';
+ if ((unsigned long) ibuf & 1) /* padding */
+ ++ibuf;
+
+ if (( pgppwd = uam_getname(name, i)) == NULL ) {
+ return AFPERR_PARAM;
+ }
+
+ syslog( LOG_INFO, "pgp login: %s", name);
+ if (uam_checkuser(pgppwd) < 0)
+ return AFPERR_NOTAUTH;
+
+ /* get the challenge */
+ len = (unsigned char) *ibuf++;
+ /* challenge */
+
+ /* get the signature. it's always 16 bytes. */
+ if (uam_afpserver_option(obj, UAM_OPTION_SIGNATURE,
+ (void *) &name, NULL) < 0) {
+ *rbuflen = 0;
+ goto passwd_fail;
+ }
+ memcpy(rbuf + KEYSIZE, name, KEYSIZE);
+
+pgp_fail:
+ return AFPERR_PARAM;
+}
+
+static int pgp_logincont(void *obj, struct passwd **uam_pwd,
+ char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ BIGNUM *bn1, *bn2, *bn3;
+ u_int16_t sessid;
+ char *p;
+
+ *rbuflen = 0;
+
+ /* check for session id */
+ memcpy(&sessid, ibuf, sizeof(sessid));
+ if (sessid != pgphash(obj))
+ return AFPERR_PARAM;
+ ibuf += sizeof(sessid);
+
+ /* use rbuf as scratch space */
+ CAST_cbc_encrypt(ibuf, rbuf, CRYPT2BUFLEN, &castkey,
+ iv, CAST_DECRYPT);
+
+ /* check to make sure that the random number is the same. we
+ * get sent back an incremented random number. */
+ if (!(bn1 = BN_bin2bn(rbuf, KEYSIZE, NULL)))
+ return AFPERR_PARAM;
+
+ if (!(bn2 = BN_bin2bn(randbuf, sizeof(randbuf), NULL))) {
+ BN_free(bn1);
+ return AFPERR_PARAM;
+ }
+
+ /* zero out the random number */
+ memset(rbuf, 0, sizeof(randbuf));
+ memset(randbuf, 0, sizeof(randbuf));
+ rbuf += KEYSIZE;
+
+ if (!(bn3 = BN_new())) {
+ BN_free(bn2);
+ BN_free(bn1);
+ return AFPERR_PARAM;
+ }
+
+ BN_sub(bn3, bn1, bn2);
+ BN_free(bn2);
+ BN_free(bn1);
+
+ /* okay. is it one more? */
+ if (!BN_is_one(bn3)) {
+ BN_free(bn3);
+ return AFPERR_PARAM;
+ }
+ BN_free(bn3);
+
+#ifdef AFS
+ if ( kcheckuser(*uam_pwd, rbuf) == 0) {
+ *uam_pwd = pgppwd;
+ return AFP_OK;
+ }
+#endif AFS
+
+ rbuf[PASSWDLEN] = '\0';
+ p = crypt( rbuf, pgppwd->pw_passwd );
+ memset(rbuf, 0, PASSWDLEN);
+ if ( strcmp( p, pgppwd->pw_passwd ) == 0 ) {
+ *uam_pwd = pgppwd;
+ return AFP_OK;
+ }
+
+ return AFPERR_NOTAUTH;
+}
+
+
+static int uam_setup(const char *path)
+{
+ if (uam_register(UAM_SERVER_LOGIN, path, "PGPuam 1.0",
+ pgp_login, pgp_logincont, NULL) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void uam_cleanup(void)
+{
+ uam_unregister(UAM_SERVER_LOGIN, "PGPuam 1.0");
+}
+
+UAM_MODULE_EXPORT struct uam_export uams_pgp = {
+ UAM_MODULE_SERVER,
+ UAM_MODULE_VERSION,
+ uam_setup, uam_cleanup
+};
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <syslog.h>
+
+#include <netatalk/endian.h>
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+
+#ifdef UAM_RNDNUM
+#include <des.h>
+
+#ifdef USE_CRACKLIB
+#include <crack.h>
+#endif
+
+#ifndef __inline__
+#define __inline__
+#endif
+
+#define PASSWDLEN 8
+
+static C_Block seskey;
+static Key_schedule seskeysched;
+static struct passwd *randpwd;
+static u_int8_t randbuf[8];
+
+/* hash to a 16-bit number. this will generate completely harmless
+ * warnings on 64-bit machines. */
+#define randhash(a) (((((unsigned long) a) >> 8) ^ \
+ ((unsigned long)a)) & 0xffff)
+
+
+/* handle ~/.passwd. courtesy of shirsch@ibm.net. */
+static __inline__ int home_passwd(const struct passwd *pwd,
+ const char *path, const int pathlen,
+ char *passwd, const int len,
+ const int set)
+{
+ struct stat st;
+ int fd, i;
+
+ if ( (fd = open(path, (set) ? O_WRONLY : O_RDONLY)) < 0 ) {
+ syslog( LOG_ERR, "Failed to open %s", path);
+ return AFPERR_ACCESS;
+ }
+
+ if ( fstat( fd, &st ) < 0 )
+ goto home_passwd_fail;
+
+ /* If any of these are true, disallow login:
+ * - not a regular file
+ * - gid or uid don't match user
+ * - anyone else has permissions of any sort
+ */
+ if (!S_ISREG(st.st_mode) || (pwd->pw_uid != st.st_uid) ||
+ (pwd->pw_gid != st.st_gid) ||
+ (st.st_mode & ( S_IRWXG | S_IRWXO )) ) {
+ syslog( LOG_INFO, "Insecure permissions found for %s.", path);
+ goto home_passwd_fail;
+ }
+
+ /* get the password */
+ if (set) {
+ if (write(fd, passwd, len) < 0) {
+ syslog( LOG_ERR, "Failed to write to %s", path );
+ goto home_passwd_fail;
+ }
+ } else {
+ if (read(fd, passwd, len) < 0) {
+ syslog( LOG_ERR, "Failed to read from %s", path );
+ goto home_passwd_fail;
+ }
+
+ /* get rid of pesky characters */
+ for (i = 0; i < len; i++)
+ if ((passwd[i] != ' ') && isspace(passwd[i]))
+ passwd[i] = '\0';
+ }
+
+ close(fd);
+ return AFP_OK;
+
+home_passwd_fail:
+ close(fd);
+ return AFPERR_ACCESS;
+}
+
+
+
+/*
+ * handle /path/afppasswd with an optional key file. we're a lot more
+ * trusting of this file. NOTE: we use our own password entry writing
+ * bits as we want to avoid tromping over global variables. in addition,
+ * we look for a key file and use that if it's there. here are the
+ * formats:
+ * password file:
+ * username:password:last login date:failedcount
+ *
+ * password is just the hex equivalent of either the ASCII password
+ * (if the key file doesn't exist) or the des encrypted password.
+ *
+ * key file:
+ * key (in hex) */
+#define PASSWD_ILLEGAL '*'
+#define unhex(x) (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
+static int afppasswd(const struct passwd *pwd,
+ const char *path, const int pathlen,
+ char *passwd, int len,
+ const int set)
+{
+ u_int8_t key[DES_KEY_SZ*2];
+ char buf[MAXPATHLEN + 1], *p;
+ Key_schedule schedule;
+ FILE *fp;
+ int i, j, keyfd = -1, err = 0;
+ off_t pos;
+
+ if ((fp = fopen(path, (set) ? "r+" : "r")) < 0) {
+ syslog( LOG_ERR, "Failed to open %s", path);
+ return AFPERR_ACCESS;
+ }
+
+ /* open the key file if it exists */
+ strcpy(buf, path);
+ if (pathlen < sizeof(buf) - 5) {
+ strcat(buf, ".key");
+ keyfd = open(buf, O_RDONLY);
+ }
+
+ pos = ftell(fp);
+ memset(buf, 0, sizeof(buf));
+ while (fgets(buf, sizeof(buf), fp)) {
+ if ((p = strchr(buf, ':'))) {
+ if (strncmp(buf, pwd->pw_name, p - buf) == 0) {
+ p++;
+ if (*p == PASSWD_ILLEGAL) {
+ syslog(LOG_INFO, "invalid password entry for %s", pwd->pw_name);
+ err = AFPERR_ACCESS;
+ goto afppasswd_done;
+ }
+ goto afppasswd_found;
+ }
+ }
+ pos = ftell(fp);
+ memset(buf, 0, sizeof(buf));
+ }
+ err = AFPERR_PARAM;
+ goto afppasswd_done;
+
+afppasswd_found:
+ if (!set) {
+ /* convert to binary. */
+ for (i = j = 0; i < sizeof(key); i += 2, j++)
+ p[j] = (unhex(p[i]) << 4) | unhex(p[i + 1]);
+ if (j <= DES_KEY_SZ)
+ memset(p + j, 0, sizeof(key) - j);
+ }
+
+ if (keyfd > -1) {
+ /* read in the hex representation of an 8-byte key */
+ read(keyfd, key, sizeof(key));
+
+ /* convert to binary key */
+ for (i = j = 0; i < strlen(key); i += 2, j++)
+ key[j] = (unhex(key[i]) << 4) | unhex(key[i + 1]);
+ if (j <= DES_KEY_SZ)
+ memset(key + j, 0, sizeof(key) - j);
+ key_sched((C_Block *) key, schedule);
+ memset(key, 0, sizeof(key));
+
+ if (set) {
+ /* NOTE: this takes advantage of the fact that passwd doesn't
+ * get used after this call if it's being set. */
+ ecb_encrypt((C_Block *) passwd, (C_Block *) passwd, schedule,
+ DES_ENCRYPT);
+ } else {
+ /* decrypt the password */
+ ecb_encrypt((C_Block *) p, (C_Block *) p, schedule, DES_DECRYPT);
+ }
+ memset(schedule, 0, sizeof(schedule));
+ }
+
+ if (set) {
+ const unsigned char hextable[] = "0123456789ABCDEF";
+ struct flock lock;
+ int fd = fileno(fp);
+
+ /* convert to hex password */
+ for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
+ key[j] = hextable[(passwd[i] & 0xF0) >> 4];
+ key[j + 1] = hextable[passwd[i] & 0x0F];
+ }
+ memcpy(p, key, sizeof(key));
+
+ /* get exclusive access to the user's password entry. we don't
+ * worry so much on reads. in the worse possible case there, the
+ * user will just need to re-enter their password. */
+ lock.l_type = F_WRLCK;
+ lock.l_start = pos;
+ lock.l_len = 1;
+ lock.l_whence = SEEK_SET;
+
+ fseek(fp, pos, SEEK_SET);
+ fcntl(fd, F_SETLKW, &lock);
+ fwrite(buf, p - buf + sizeof(key), 1, fp);
+ lock.l_type = F_UNLCK;
+ fcntl(fd, F_SETLK, &lock);
+ } else
+ memcpy(passwd, p, len);
+
+ memset(buf, 0, sizeof(buf));
+
+afppasswd_done:
+ if (keyfd > -1)
+ close(keyfd);
+ fclose(fp);
+ return err;
+}
+
+
+/* this sets the uid. it needs to do slightly different things
+ * depending upon whether or not the password is in ~/.passwd
+ * or in a global location */
+static int randpass(const struct passwd *pwd, const char *file,
+ char *passwd, const int len, const int set)
+{
+ int i;
+ uid_t uid = geteuid();
+
+ /* Build pathname to user's '.passwd' file */
+ i = strlen(file);
+ if (*file == '~') {
+ char path[MAXPATHLEN + 1];
+
+ if ( (strlen(pwd->pw_dir) + i - 1) > MAXPATHLEN)
+ return AFPERR_PARAM;
+
+ strcpy(path, pwd->pw_dir );
+ strcat(path, "/" );
+ strcat(path, file + 2);
+ if (!uid)
+ seteuid(pwd->pw_uid); /* change ourselves to the user */
+ i = home_passwd(pwd, path, i, passwd, len, set);
+ if (!uid)
+ seteuid(0); /* change ourselves back to root */
+ return i;
+ }
+
+ if (i > MAXPATHLEN)
+ return AFPERR_PARAM;
+
+ /* handle afppasswd file. we need to make sure that we're root
+ * when we do this. */
+ if (uid)
+ seteuid(0);
+ i = afppasswd(pwd, file, i, passwd, len, set);
+ if (uid)
+ seteuid(uid);
+ return i;
+}
+
+
+/* randnum sends an 8-byte number and uses the user's password to
+ * check against the encrypted reply. */
+static int randnum_login(void *obj, struct passwd **uam_pwd,
+ char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ char *username, *passwdfile;
+ u_int16_t sessid;
+ int len, ulen, err;
+
+ *rbuflen = 0;
+
+ if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
+ (void *) &username, &ulen) < 0)
+ return AFPERR_PARAM;
+
+ len = UAM_PASSWD_FILENAME;
+ if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
+ (void *) &passwdfile, &len) < 0)
+ return AFPERR_PARAM;
+
+ len = (unsigned char) *ibuf++;
+ if ( len > ulen ) {
+ return AFPERR_PARAM;
+ }
+ memcpy(username, ibuf, len );
+ ibuf += len;
+ username[ len ] = '\0';
+ if ((unsigned long) ibuf & 1) /* padding */
+ ++ibuf;
+
+ if (( randpwd = uam_getname(username, ulen)) == NULL )
+ return AFPERR_PARAM; /* unknown user */
+
+ syslog( LOG_INFO, "randnum/rand2num login: %s", username);
+ if (uam_checkuser(randpwd) < 0)
+ return AFPERR_NOTAUTH;
+
+ if ((err = randpass(randpwd, passwdfile, seskey,
+ sizeof(seskey), 0)) != AFP_OK)
+ return err;
+
+ /* get a random number */
+ len = sizeof(randbuf);
+ if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM,
+ (void *) randbuf, &len) < 0)
+ return AFPERR_PARAM;
+
+ /* session id is a hashed version of the obj pointer */
+ sessid = randhash(obj);
+ memcpy(rbuf, &sessid, sizeof(sessid));
+ rbuf += sizeof(sessid);
+ *rbuflen = sizeof(sessid);
+
+ /* send the random number off */
+ memcpy(rbuf, randbuf, sizeof(randbuf));
+ *rbuflen += sizeof(randbuf);
+ return AFPERR_AUTHCONT;
+}
+
+
+/* check encrypted reply. we actually setup the encryption stuff
+ * here as the first part of randnum and rand2num are identical. */
+static int randnum_logincont(void *obj, struct passwd **uam_pwd,
+ char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ u_int16_t sessid;
+
+ *rbuflen = 0;
+
+ memcpy(&sessid, ibuf, sizeof(sessid));
+ if (sessid != randhash(obj))
+ return AFPERR_PARAM;
+
+ ibuf += sizeof(sessid);
+
+ /* encrypt. this saves a little space by using the fact that
+ * des can encrypt in-place without side-effects. */
+ key_sched((C_Block *) seskey, seskeysched);
+ memset(seskey, 0, sizeof(seskey));
+ ecb_encrypt((C_Block *) randbuf, (C_Block *) randbuf,
+ seskeysched, DES_ENCRYPT);
+ memset(seskeysched, 0, sizeof(seskeysched));
+
+ /* test against what the client sent */
+ if (memcmp( randbuf, ibuf, sizeof(randbuf) )) { /* != */
+ memset(randbuf, 0, sizeof(randbuf));
+ return AFPERR_NOTAUTH;
+ }
+
+ memset(randbuf, 0, sizeof(randbuf));
+ *uam_pwd = randpwd;
+ return AFP_OK;
+}
+
+
+/* differences from randnum:
+ * 1) each byte of the key is shifted left one bit
+ * 2) client sends the server a 64-bit number. the server encrypts it
+ * and sends it back as part of the reply.
+ */
+static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
+ char *ibuf, int ibuflen,
+ char *rbuf, int *rbuflen)
+{
+ u_int16_t sessid;
+ int i;
+
+ *rbuflen = 0;
+
+ /* compare session id */
+ memcpy(&sessid, ibuf, sizeof(sessid));
+ if (sessid != randhash(obj))
+ return AFPERR_PARAM;
+
+ ibuf += sizeof(sessid);
+
+ /* shift key elements left one bit */
+ for (i = 0; i < sizeof(seskey); i++)
+ seskey[i] <<= 1;
+
+ /* encrypt randbuf */
+ key_sched((C_Block *) seskey, seskeysched);
+ memset(seskey, 0, sizeof(seskey));
+ ecb_encrypt( (C_Block *) randbuf, (C_Block *) randbuf,
+ seskeysched, DES_ENCRYPT);
+
+ /* test against client's reply */
+ if (memcmp(randbuf, ibuf, sizeof(randbuf))) { /* != */
+ memset(randbuf, 0, sizeof(randbuf));
+ memset(seskeysched, 0, sizeof(seskeysched));
+ return AFPERR_NOTAUTH;
+ }
+ ibuf += sizeof(randbuf);
+ memset(randbuf, 0, sizeof(randbuf));
+
+ /* encrypt client's challenge and send back */
+ ecb_encrypt( (C_Block *) ibuf, (C_Block *) rbuf,
+ seskeysched, DES_ENCRYPT);
+ memset(seskeysched, 0, sizeof(seskeysched));
+ *rbuflen = sizeof(randbuf);
+
+ *uam_pwd = randpwd;
+ return AFP_OK;
+}
+
+/* change password --
+ * NOTE: an FPLogin must already have completed successfully for this
+ * to work.
+ */
+static int randnum_changepw(void *obj, const char *username,
+ struct passwd *pwd, char *ibuf,
+ int ibuflen, char *rbuf, int *rbuflen)
+{
+ char *passwdfile;
+ int err, len;
+
+ if (uam_checkuser(pwd) < 0)
+ return AFPERR_ACCESS;
+
+ len = UAM_PASSWD_FILENAME;
+ if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
+ (void *) &passwdfile, &len) < 0)
+ return AFPERR_PARAM;
+
+ /* old password is encrypted with new password and new password is
+ * encrypted with old. */
+ if ((err = randpass(pwd, passwdfile, seskey,
+ sizeof(seskey), 0)) != AFP_OK)
+ return err;
+
+ /* use old passwd to decrypt new passwd */
+ key_sched((C_Block *) seskey, seskeysched);
+ ibuf += PASSWDLEN; /* new passwd */
+ ibuf[PASSWDLEN] = '\0';
+ ecb_encrypt( (C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
+
+ /* now use new passwd to decrypt old passwd */
+ key_sched((C_Block *) ibuf, seskeysched);
+ ibuf -= PASSWDLEN; /* old passwd */
+ ecb_encrypt((C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
+ if (memcmp(seskey, ibuf, sizeof(seskey)))
+ err = AFPERR_NOTAUTH;
+ else if (memcmp(seskey, ibuf + PASSWDLEN, sizeof(seskey)) == 0)
+ err = AFPERR_PWDSAME;
+#ifdef USE_CRACKLIB
+ else if (FascistCheck(ibuf + PASSWDLEN, _PATH_CRACKLIB))
+ err = AFPERR_PWDPOLCY;
+#endif
+
+ if (!err)
+ err = randpass(pwd, passwdfile, ibuf + PASSWDLEN, sizeof(seskey), 1);
+
+ /* zero out some fields */
+ memset(seskeysched, 0, sizeof(seskeysched));
+ memset(seskey, 0, sizeof(seskey));
+ memset(ibuf, 0, sizeof(seskey)); /* old passwd */
+ memset(ibuf + PASSWDLEN, 0, sizeof(seskey)); /* new passwd */
+
+ if (err)
+ return err;
+
+ return( AFP_OK );
+}
+
+static int uam_setup(const char *path)
+{
+ if (uam_register(UAM_SERVER_LOGIN, path, "Randnum exchange",
+ randnum_login, randnum_logincont, NULL) < 0)
+ return -1;
+ if (uam_register(UAM_SERVER_LOGIN, path, "2-Way Randnum exchange",
+ randnum_login, rand2num_logincont, NULL) < 0) {
+ uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
+ return -1;
+ }
+
+ if (uam_register(UAM_SERVER_CHANGEPW, path, "Randnum Exchange",
+ randnum_changepw) < 0) {
+ uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
+ uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
+ return -1;
+ }
+ /*uam_register(UAM_SERVER_PRINTAUTH, path, "Randnum Exchange",
+ pam_printer);*/
+
+ return 0;
+}
+
+static void uam_cleanup(void)
+{
+ uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
+ uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
+ uam_unregister(UAM_SERVER_CHANGEPW, "Randnum Exchange");
+ /*uam_unregister(UAM_SERVER_PRINTAUTH, "Randnum Exchange");*/
+}
+
+UAM_MODULE_EXPORT struct uam_export uams_randnum = {
+ UAM_MODULE_SERVER,
+ UAM_MODULE_VERSION,
+ uam_setup, uam_cleanup
+};
+
+#endif /* UAM_RNDNUM */
--- /dev/null
+TARGETS= atalk netatalk
+
+INSTALL= install
+
+all :
+
+install : all
+ -mkdir ${DESTDIR}
+ -mkdir ${INCDIR}
+ for i in ${TARGETS}; do \
+ rm -rf ${INCDIR}/$$i ; \
+ mkdir ${INCDIR}/$$i ; \
+ cp $$i/*.h ${INCDIR}/$$i ; \
+ done
+
+clean :
+
+tags :
+
+depend :
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_ADOUBLE_H
+#define _ATALK_ADOUBLE_H
+
+#include <unistd.h>
+
+#if defined(sun) && defined(__svr4__)
+#include </usr/ucbinclude/sys/file.h>
+#else
+#include <sys/file.h>
+#endif
+#include <fcntl.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <netatalk/endian.h>
+
+
+/* XXX: this is the wrong place to put this.
+ * NOTE: as of 2.2.1, linux can't do a sendfile from a socket. */
+#ifdef SENDFILE_FLAVOR_LINUX
+#define HAVE_SENDFILE_READ
+#define HAVE_SENDFILE_WRITE
+#include <asm/unistd.h>
+#ifdef __NR_sendfile
+static inline int sendfile(int fdout, int fdin, off_t *off, size_t count)
+{
+ return syscall(__NR_sendfile, fdout, fdin, off, count);
+}
+#else
+#include <sys/sendfile.h>
+#endif
+#endif
+
+#ifdef SENDFILE_FLAVOR_BSD
+#define HAVE_SENDFILE_READ
+#endif
+
+/*
+ * AppleDouble entry IDs.
+ */
+#define ADEID_DFORK 1
+#define ADEID_RFORK 2
+#define ADEID_NAME 3
+#define ADEID_COMMENT 4
+#define ADEID_ICONBW 5
+#define ADEID_ICONCOL 6
+#define ADEID_FILEI 7 /* v1, replaced by: */
+#define ADEID_FILEDATESI 8 /* this */
+#define ADEID_FINDERI 9
+#define ADEID_MACFILEI 10 /* we don't use this */
+#define ADEID_PRODOSFILEI 11 /* we store prodos info here */
+#define ADEID_MSDOSFILEI 12 /* we don't use this */
+#define ADEID_SHORTNAME 13
+#define ADEID_AFPFILEI 14 /* where the rest of the FILEI info goes */
+#define ADEID_DID 15
+
+#define ADEID_MAX 16
+
+/* magic */
+#define AD_APPLESINGLE_MAGIC 0x00051600
+#define AD_APPLEDOUBLE_MAGIC 0x00051607
+#define AD_MAGIC AD_APPLEDOUBLE_MAGIC
+
+/* version info */
+#define AD_VERSION1 0x00010000
+#define AD_VERSION2 0x00020000
+#define AD_VERSION AD_VERSION1
+
+/* sizes of relevant entry bits */
+#define ADEDLEN_MAGIC 4
+#define ADEDLEN_VERSION 4
+#define ADEDLEN_FILLER 16
+#define ADEDLEN_NENTRIES 2
+
+#define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
+ ADEDLEN_FILLER + ADEDLEN_NENTRIES)
+#define AD_ENTRY_LEN 12 /* size of a single entry header */
+
+/* v1 field widths */
+#define ADEDLEN_NAME 255
+#define ADEDLEN_COMMENT 200
+#define ADEDLEN_FILEI 16
+#define ADEDLEN_FINDERI 32
+
+/* v2 field widths */
+#define ADEDLEN_FILEDATESI 16
+#define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
+#define ADEDLEN_AFPFILEI 4
+#define ADEDLEN_MACFILEI 4
+#define ADEDLEN_PRODOSFILEI 8
+#define ADEDLEN_MSDOSFILEI 2
+#define ADEDLEN_DID 4
+
+
+#define AD_DATASZ1 589
+#define AD_DATASZ2 665 /* v1 + 4 new entries (entry desc. + entry) */
+#define AD_DATASZ_MAX 1024
+#if AD_VERSION == AD_VERSION1
+#define AD_DATASZ AD_DATASZ1 /* hold enough for the entries */
+#else if AD_VERSION == AD_VERSION2
+#define AD_DATASZ AD_DATASZ2
+#endif
+
+/*
+ * The header of the AppleDouble Header File looks like this:
+ *
+ * NAME SIZE
+ * ==== ====
+ * Magic 4
+ * Version 4
+ * Home File System 16 (this becomes filler in ad v2)
+ * Number of Entries 2
+ * Entry Descriptors for each entry:
+ * Entry ID 4
+ * Offset 4
+ * Length 4
+ */
+
+struct ad_entry {
+ u_int32_t ade_off;
+ u_int32_t ade_len;
+};
+
+typedef struct adf_lock_t {
+ struct flock lock;
+ int user;
+ int *refcount; /* handle read locks with multiple users */
+} adf_lock_t;
+
+struct ad_fd {
+ int adf_fd;
+ off_t adf_off;
+ int adf_flags;
+ adf_lock_t *adf_lock;
+ int adf_refcount, adf_lockcount, adf_lockmax;
+};
+
+/* some header protection */
+#define AD_INITED 0xad494e54 /* ad"INT" */
+struct adouble {
+ u_int32_t ad_magic;
+ u_int32_t ad_version;
+ char ad_filler[ 16 ];
+ struct ad_entry ad_eid[ ADEID_MAX ];
+ struct ad_fd ad_df, ad_hf;
+ int ad_flags, ad_inited;
+#ifdef USE_MMAPPED_HEADERS
+ char *ad_data;
+#else
+ char ad_data[AD_DATASZ_MAX];
+#endif
+};
+
+#define ADFLAGS_DF (1<<0)
+#define ADFLAGS_HF (1<<1)
+#define ADFLAGS_DIR (1<<2)
+#define ADFLAGS_NOADOUBLE (1<<3)
+#define ADFLAGS_V1COMPAT (1<<4)
+
+/* lock flags */
+#define ADLOCK_CLR (0)
+#define ADLOCK_RD (1<<0)
+#define ADLOCK_WR (1<<1)
+#define ADLOCK_MASK (ADLOCK_RD | ADLOCK_WR)
+#define ADLOCK_UPGRADE (1<<2)
+#define ADLOCK_FILELOCK (1<<3)
+
+/* we use this so that we can use the same mechanism for both byte
+ * locks and file synchronization locks. i do this by co-opting either
+ * first bits on 32-bit machines or shifting above the last bit on
+ * 64-bit machines. this only matters for the data fork. */
+#if defined(TRY_64BITOFF_T) && (~0UL > 0xFFFFFFFFU)
+/* synchronization locks */
+#define AD_FILELOCK_BASE (0x80000000)
+#define AD_FILELOCK_WR (AD_FILELOCK_BASE + 0)
+#define AD_FILELOCK_RD (AD_FILELOCK_BASE + 1)
+#else
+#define AD_FILELOCK_BASE (0x7FFFFFFE)
+#define AD_FILELOCK_WR (AD_FILELOCK_BASE + 0)
+#define AD_FILELOCK_RD (AD_FILELOCK_BASE + 1)
+#endif
+
+/* time stuff. we overload the bits a little. */
+#define AD_DATE_CREATE 0
+#define AD_DATE_MODIFY 4
+#define AD_DATE_BACKUP 8
+#define AD_DATE_ACCESS 12
+#define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
+ AD_DATE_BACKUP | AD_DATE_ACCESS)
+#define AD_DATE_UNIX (1 << 10)
+#define AD_DATE_START htonl(0x80000000)
+#define AD_DATE_DELTA 946684800
+#define AD_DATE_FROM_UNIX(x) htonl((x) - AD_DATE_DELTA)
+#define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
+
+/* private AFPFileInfo bits */
+#define AD_AFPFILEI_OWNER (1 << 0) /* any owner */
+#define AD_AFPFILEI_GROUP (1 << 1) /* ignore group */
+#define AD_AFPFILEI_BLANKACCESS (1 << 2) /* blank access permissions */
+
+#define ad_dfileno(ad) ((ad)->ad_df.adf_fd)
+#define ad_hfileno(ad) ((ad)->ad_hf.adf_fd)
+#define ad_getversion(ad) ((ad)->ad_version)
+
+#define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
+#define ad_setentrylen(ad,eid,len) \
+ ((ad)->ad_eid[(eid)].ade_len = (len))
+#define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
+#define ad_entry(ad,eid) ((caddr_t)(ad)->ad_data + \
+ (ad)->ad_eid[(eid)].ade_off)
+#define ad_getoflags(ad,adf) (((adf)&ADFLAGS_HF) ? \
+ (ad)->ad_hf.adf_flags : (ad)->ad_df.adf_flags)
+
+/* ad_flush.c */
+extern void ad_rebuild_header __P((struct adouble *));
+extern int ad_flush __P((struct adouble *, int));
+extern int ad_close __P((struct adouble *, int));
+
+/* ad_lock.c */
+extern int ad_flock_lock __P((struct adouble *, const u_int32_t /*eid*/,
+ const int /*type*/, const off_t /*offset*/,
+ const size_t /*len*/, const int /*user*/));
+extern int ad_fcntl_lock __P((struct adouble *, const u_int32_t /*eid*/,
+ const int /*type*/, const off_t /*offset*/,
+ const size_t /*len*/, const int /*user*/));
+extern void ad_fcntl_unlock __P((struct adouble *, const int /*user*/));
+
+extern int ad_flock_tmplock __P((struct adouble *, const u_int32_t /*eid*/,
+ const int /*type*/, const off_t /*offset*/,
+ const size_t /*len*/));
+extern int ad_fcntl_tmplock __P((struct adouble *, const u_int32_t /*eid*/,
+ const int /*type*/, const off_t /*offset*/,
+ const size_t /*len*/));
+
+#ifdef USE_FLOCK_LOCKS
+#define ad_lock ad_flock_lock
+#define ad_tmplock ad_flock_tmplock
+#define ad_unlock(a,b)
+#else
+#define ad_lock ad_fcntl_lock
+#define ad_tmplock ad_fcntl_tmplock
+#define ad_unlock ad_fcntl_unlock
+#endif
+
+/* ad_open.c */
+extern char *ad_path __P((char *, int));
+extern int ad_mode __P((char *, int));
+extern int ad_mkdir __P((char *, int));
+extern int ad_open __P((char *, int, int, int, struct adouble *));
+extern int ad_refresh __P((struct adouble *));
+
+/* ad_read.c/ad_write.c */
+extern ssize_t ad_read __P((struct adouble *, const u_int32_t,
+ const off_t, char *, const size_t));
+extern ssize_t ad_write __P((struct adouble *, const u_int32_t, off_t,
+ const int, const char *, const size_t));
+extern int ad_dtruncate __P((struct adouble *, const size_t));
+extern int ad_rtruncate __P((struct adouble *, const size_t));
+
+/* ad_size.c */
+extern off_t ad_size __P((const struct adouble *, const u_int32_t ));
+
+/* ad_mmap.c */
+extern void *ad_mmapread __P((struct adouble *, const u_int32_t,
+ const off_t, const size_t));
+extern void *ad_mmapwrite __P((struct adouble *, const u_int32_t,
+ const off_t, const int, const size_t));
+#define ad_munmap(buf, len) (munmap((buf), (len)))
+
+/* ad_date.c */
+extern int ad_setdate __P((const struct adouble *, unsigned int, u_int32_t));
+extern int ad_getdate __P((const struct adouble *, unsigned int, u_int32_t *));
+
+/* ad_attr.c */
+extern int ad_setattr __P((const struct adouble *, const u_int16_t));
+extern int ad_getattr __P((const struct adouble *, u_int16_t *));
+
+#ifdef HAVE_SENDFILE_READ
+extern ssize_t ad_readfile __P((const struct adouble *, const int,
+ const int, off_t, const size_t));
+#endif
+
+#if 0
+#ifdef HAVE_SENDFILE_WRITE
+extern ssize_t ad_writefile __P((struct adouble *, const int,
+ const int, off_t, const int, const size_t));
+#endif
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_AEP_H
+#define _ATALK_AEP_H 1
+
+#define AEPOP_REQUEST 1
+#define AEPOP_REPLY 2
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_AFP_H
+#define _ATALK_AFP_H 1
+
+#include <sys/types.h>
+#include <netatalk/endian.h>
+
+typedef u_int16_t AFPUserBytes;
+
+/* protocols */
+#define AFPPROTO_ASP 1
+#define AFPPROTO_DSI 2
+
+/* actual transports. the DSI ones (tcp right now) need to be
+ * kept in sync w/ <atalk/dsi.h>.
+ * convention: AFPTRANS_* = (1 << DSI_*)
+ */
+#define AFPTRANS_NONE 0
+#define AFPTRANS_DDP (1 << 0)
+#define AFPTRANS_TCP (1 << 1)
+#define AFPTRANS_ALL (AFPTRANS_DDP | AFPTRANS_TCP)
+
+/* server flags */
+#define AFPSRVRINFO_COPY (1<<0) /* supports copyfile */
+#define AFPSRVRINFO_PASSWD (1<<1) /* supports change password */
+#define AFPSRVRINFO_NOSAVEPASSWD (1<<2) /* don't allow save password */
+#define AFPSRVRINFO_SRVMSGS (1<<3) /* supports server messages */
+#define AFPSRVRINFO_SRVSIGNATURE (1<<4) /* supports server signature */
+#define AFPSRVRINFO_TCPIP (1<<5) /* supports tcpip */
+#define AFPSRVRINFO_SRVNOTIFY (1<<6) /* supports server notifications */
+#define AFPSRVRINFO_FASTBOZO (1<<15) /* fast copying */
+
+#define AFP_OK 0
+#define AFPERR_ACCESS -5000 /* permission denied */
+#define AFPERR_AUTHCONT -5001 /* logincont */
+#define AFPERR_BADUAM -5002 /* uam doesn't exist */
+#define AFPERR_BADVERS -5003 /* bad afp version number */
+#define AFPERR_BITMAP -5004 /* invalid bitmap */
+#define AFPERR_CANTMOVE -5005 /* can't move file */
+#define AFPERR_DENYCONF -5006 /* file synchronization locks conflict */
+#define AFPERR_DIRNEMPT -5007 /* directory not empty */
+#define AFPERR_DFULL -5008 /* disk full */
+#define AFPERR_EOF -5009 /* end of file -- catsearch and afp_read */
+#define AFPERR_BUSY -5010 /* FileBusy */
+#define AFPERR_FLATVOL -5011 /* volume doesn't support directories */
+#define AFPERR_NOITEM -5012 /* ItemNotFound */
+#define AFPERR_LOCK -5013 /* LockErr */
+#define AFPERR_MISC -5014 /* misc. err */
+#define AFPERR_NLOCK -5015 /* no more locks */
+#define AFPERR_NOSRVR -5016 /* no response by server at that address */
+#define AFPERR_EXIST -5017 /* object already exists */
+#define AFPERR_NOOBJ -5018 /* object not found */
+#define AFPERR_PARAM -5019 /* parameter error */
+#define AFPERR_NORANGE -5020 /* no range lock */
+#define AFPERR_RANGEOVR -5021 /* range overlap */
+#define AFPERR_SESSCLOS -5022 /* session closed */
+#define AFPERR_NOTAUTH -5023 /* user not authenticated */
+#define AFPERR_NOOP -5024 /* command not supported */
+#define AFPERR_BADTYPE -5025 /* object is the wrong type */
+#define AFPERR_NFILE -5026 /* too many files open */
+#define AFPERR_SHUTDOWN -5027 /* server is going down */
+#define AFPERR_NORENAME -5028 /* can't rename */
+#define AFPERR_NODIR -5029 /* couldn't find directory */
+#define AFPERR_ITYPE -5030 /* wrong icon type */
+#define AFPERR_VLOCK -5031 /* volume locked */
+#define AFPERR_OLOCK -5032 /* object locked */
+#define AFPERR_CTNSHRD -5033 /* share point contains a share point */
+#define AFPERR_NOID -5034 /* file thread not found */
+#define AFPERR_EXISTID -5035 /* file already has an id */
+#define AFPERR_DIFFVOL -5036 /* different volume */
+#define AFPERR_CATCHNG -5037 /* catalog has changed */
+#define AFPERR_SAMEOBJ -5038 /* source file == destination file */
+#define AFPERR_BADID -5039 /* non-existent file id */
+#define AFPERR_PWDSAME -5040 /* same password/can't change password */
+#define AFPERR_PWDSHORT -5041 /* password too short */
+#define AFPERR_PWDEXPR -5042 /* password expired */
+#define AFPERR_INSHRD -5043 /* folder being shared is inside a
+ shared folder. may be returned by
+ afpMoveAndRename. */
+#define AFPERR_INTRASH -5044 /* shared folder in trash. */
+#define AFPERR_PWDCHNG -5045 /* password needs to be changed */
+#define AFPERR_PWDPOLCY -5046 /* password fails policy check */
+#define AFPERR_USRLOGIN -5047 /* user already logged on */
+
+/* AFP Attention Codes -- 4 bits */
+#define AFPATTN_SHUTDOWN (1 << 15) /* shutdown/disconnect */
+#define AFPATTN_CRASH (1 << 14) /* server crashed */
+#define AFPATTN_MESG (1 << 13) /* server has message */
+#define AFPATTN_NORECONNECT (1 << 12) /* don't reconnect */
+/* server notification */
+#define AFPATTN_NOTIFY (AFPATTN_MESG | AFPATTN_NORECONNECT)
+
+/* extended bitmap -- 12 bits. volchanged is only useful w/ a server
+ * notification, and time is only useful for shutdown. */
+#define AFPATTN_VOLCHANGED (1 << 0) /* volume has changed */
+#define AFPATTN_TIME(x) ((x) & 0xfff) /* time in minutes */
+
+typedef enum {
+ AFPMESG_LOGIN = 0,
+ AFPMESG_SERVER = 1
+} afpmessage_t;
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_ASP_H
+#define _ATALK_ASP_H 1
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/afp.h>
+#include <atalk/server_child.h>
+
+#define ASP_HDRSIZ 4
+#define ASP_CMDSIZ 578
+
+#define ASP_MAXPACKETS 8
+#define ASP_CMDMAXSIZ (ASP_CMDSIZ + ASP_HDRSIZ)
+#define ASP_DATASIZ (ASP_CMDSIZ*ASP_MAXPACKETS)
+#define ASP_DATAMAXSIZ ((ASP_CMDSIZ + ASP_HDRSIZ)*ASP_MAXPACKETS)
+
+typedef struct ASP {
+ ATP asp_atp;
+ struct sockaddr_at asp_sat;
+ u_int8_t asp_wss;
+ u_int8_t asp_sid;
+ union {
+ struct {
+ char *as_status;
+ int as_slen;
+ } asu_status;
+ u_int16_t asu_seq;
+ } asp_u;
+#define asp_status asp_u.asu_status.as_status
+#define asp_slen asp_u.asu_status.as_slen
+#define asp_seq asp_u.asu_seq
+ int asp_flags;
+ char child, inited, *commands;
+ char cmdbuf[ASP_CMDMAXSIZ];
+ char data[ASP_DATAMAXSIZ];
+ int cmdlen, datalen;
+ size_t read_count, write_count;
+} *ASP;
+
+#define ASPFL_SLS 1
+#define ASPFL_SSS 2
+
+#define ASPFUNC_CLOSE 1
+#define ASPFUNC_CMD 2
+#define ASPFUNC_STAT 3
+#define ASPFUNC_OPEN 4
+#define ASPFUNC_TICKLE 5
+#define ASPFUNC_WRITE 6
+#define ASPFUNC_WRTCONT 7
+#define ASPFUNC_ATTN 8
+
+#define ASPERR_OK 0x0000
+#define ASPERR_BADVERS 0xfbd6
+#define ASPERR_BUFSMALL 0xfbd5
+#define ASPERR_NOSESS 0xfbd4
+#define ASPERR_NOSERV 0xfbd3
+#define ASPERR_PARM 0xfbd2
+#define ASPERR_SERVBUSY 0xfbd1
+#define ASPERR_SESSCLOS 0xfbd0
+#define ASPERR_SIZERR 0xfbcf
+#define ASPERR_TOOMANY 0xfbce
+#define ASPERR_NOACK 0xfbcd
+
+extern ASP asp_init __P((ATP));
+extern void asp_setstatus __P((ASP, char *, const int));
+extern ASP asp_getsession __P((ASP, server_child *, const int));
+extern int asp_close __P((ASP));
+extern int asp_shutdown __P((ASP));
+extern int asp_attention __P((ASP, AFPUserBytes));
+extern int asp_getrequest __P((ASP));
+extern int asp_cmdreply __P((ASP, int));
+extern int asp_wrtcont __P((ASP, char *, int *));
+#define asp_wrtreply(a,b) asp_cmdreply((a), (b))
+extern void asp_kill __P((int));
+extern void asp_tickle __P((ASP, const u_int8_t, struct sockaddr_at *));
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_ATP_H
+#define _ATALK_ATP_H 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+
+/* ATP packet format
+
+ |----------------|
+ | link header |
+ | ... |
+ |----------------|
+ | DDP header |
+ | ... |
+ | type = 3 |
+ |----------------|
+ | control info | --> bits 7,6: function code
+ |----------------| 5: XO bit
+ | bitmap/seq no. | 4: EOM bit
+ |----------------| 3: STS bit
+ | TID (MSB) | 2,1,0: release timer code (ignored under phase I)
+ |----------------|
+ | TID (LSB) |
+ |----------------|
+ | user bytes (4) |
+ |----------------|
+ | data (0-578) |
+ | ... |
+ |----------------|
+*/
+struct atphdr {
+ u_int8_t atphd_ctrlinfo; /* control information */
+ u_int8_t atphd_bitmap; /* bitmap or sequence number */
+ u_int16_t atphd_tid; /* transaction id. */
+};
+
+/* ATP protocol parameters
+*/
+#define ATP_MAXDATA (578+4) /* maximum ATP data size */
+#define ATP_BUFSIZ 587 /* maximum packet size */
+#define ATP_HDRSIZE 5 /* includes DDP type field */
+
+#define ATP_TRELMASK 0x07 /* mask all but TREL */
+#define ATP_RELTIME 30 /* base release timer (in secs) */
+
+#define ATP_TREL30 0x0 /* release time codes */
+#define ATP_TREL1M 0x1 /* these are passed in flags of */
+#define ATP_TREL2M 0x2 /* atp_sreq call, and set in the */
+#define ATP_TREL4M 0x3 /* packet control info. */
+#define ATP_TREL8M 0x4
+
+#define ATP_TRIES_INFINITE -1 /* for atp_sreq, etc */
+
+struct atpxobuf {
+ u_int16_t atpxo_tid;
+ struct timeval atpxo_tv;
+ int atpxo_reltime;
+ struct atpbuf *atpxo_packet[8];
+};
+
+struct atpbuf {
+ struct atpbuf *atpbuf_next; /* next buffer in chain */
+ short atpbuf_dlen; /* data length <= ATP_BUFSIZ */
+ struct sockaddr_at atpbuf_addr; /* net address sent/recvd */
+ union {
+ char atpbuf_data[ ATP_BUFSIZ ]; /* the data */
+ struct atpxobuf atpbuf_xo; /* for XO requests */
+ } atpbuf_info;
+};
+
+struct atp_handle {
+ int atph_socket; /* ddp socket */
+ struct sockaddr_at atph_saddr; /* address */
+ u_int16_t atph_tid; /* last tid used */
+ u_int16_t atph_rtid; /* last received (rreq) */
+ u_int8_t atph_rxo; /* XO flag from last rreq */
+ int atph_rreltime; /* release time (secs) */
+ struct atpbuf *atph_sent; /* packets we send (XO) */
+ struct atpbuf *atph_queue; /* queue of pending packets */
+ int atph_reqtries; /* retry count for request */
+ int atph_reqto; /* retry timeout for request */
+ int atph_rrespcount; /* expected # of responses */
+ u_int8_t atph_rbitmap; /* bitmap for request */
+ struct atpbuf *atph_reqpkt; /* last request packet */
+ struct timeval atph_reqtv; /* when we last sent request */
+ struct atpbuf *atph_resppkt[8]; /* response to request */
+};
+
+typedef struct atp_handle *ATP;
+
+#define atp_sockaddr( h ) (&(h)->atph_saddr)
+#define atp_fileno(x) ((x)->atph_socket)
+
+struct sreq_st {
+ char *atpd_data; /* request data */
+ int atpd_dlen;
+ int atpd_tries; /* max. retry count */
+ int atpd_to; /* retry interval */
+};
+
+struct rres_st {
+ struct iovec *atpd_iov; /* for response */
+ int atpd_iovcnt;
+};
+
+struct rreq_st {
+ char *atpd_data; /* request data */
+ int atpd_dlen;
+};
+
+struct sres_st {
+ struct iovec *atpd_iov; /* for response */
+ int atpd_iovcnt;
+};
+
+struct atp_block {
+ struct sockaddr_at *atp_saddr; /* from/to address */
+ union {
+ struct sreq_st sreqdata;
+#define atp_sreqdata atp_data.sreqdata.atpd_data
+#define atp_sreqdlen atp_data.sreqdata.atpd_dlen
+#define atp_sreqtries atp_data.sreqdata.atpd_tries
+#define atp_sreqto atp_data.sreqdata.atpd_to
+
+ struct rres_st rresdata;
+#define atp_rresiov atp_data.rresdata.atpd_iov
+#define atp_rresiovcnt atp_data.rresdata.atpd_iovcnt
+
+ struct rreq_st rreqdata;
+#define atp_rreqdata atp_data.rreqdata.atpd_data
+#define atp_rreqdlen atp_data.rreqdata.atpd_dlen
+
+ struct sres_st sresdata;
+#define atp_sresiov atp_data.sresdata.atpd_iov
+#define atp_sresiovcnt atp_data.sresdata.atpd_iovcnt
+ } atp_data;
+ u_int8_t atp_bitmap; /* response buffer bitmap */
+};
+
+
+/* flags for ATP options (and control byte)
+*/
+#define ATP_STS (1<<3) /* Send Transaction Status */
+#define ATP_EOM (1<<4) /* End Of Message */
+#define ATP_XO (1<<5) /* eXactly Once mode */
+
+/* function codes
+*/
+#define ATP_FUNCMASK (3<<6) /* mask all but function */
+
+#define ATP_TREQ (1<<6) /* Trans. REQuest */
+#define ATP_TRESP (2<<6) /* Trans. RESPonse */
+#define ATP_TREL (3<<6) /* Trans. RELease */
+
+extern ATP atp_open __P((const u_int8_t,
+ const struct at_addr *));
+extern int atp_close __P((ATP));
+extern int atp_sreq __P((ATP, struct atp_block *, int,
+ u_int8_t));
+extern int atp_rresp __P((ATP, struct atp_block *));
+extern int atp_rsel __P((ATP, struct sockaddr_at *, int));
+extern int atp_rreq __P((ATP, struct atp_block *));
+extern int atp_sresp __P((ATP, struct atp_block *));
+
+#endif
--- /dev/null
+/*
+ * interface for database access to cnids. i do it this way to abstract
+ * things a bit in case we want to change the underlying implementation.
+ */
+
+#ifndef _ATALK_CNID_H
+#define _ATALK_CNID_H 1
+
+#include <sys/cdefs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <netatalk/endian.h>
+
+typedef u_int32_t cnid_t;
+
+/* cnid_open.c */
+extern void *cnid_open __P((const char *));
+
+/* cnid_close.c */
+extern void cnid_close __P((void *));
+
+/* cnid_add.c */
+extern cnid_t cnid_add __P((void *, const struct stat *, const cnid_t,
+ const char *, const int, cnid_t));
+
+/* cnid_get.c */
+extern cnid_t cnid_get __P((void *, const cnid_t, const char *, const int));
+extern char *cnid_resolve __P((void *, cnid_t *));
+extern cnid_t cnid_lookup __P((void *, const struct stat *, const cnid_t,
+ const char *, const int));
+
+/* cnid_update.c */
+extern int cnid_update __P((void *, const cnid_t, const struct stat *,
+ const cnid_t, const char *, int));
+
+/* cnid_delete.c */
+extern int cnid_delete __P((void *, const cnid_t));
+
+/* cnid_nextid.c */
+extern cnid_t cnid_nextid __P((void *));
+
+#endif /* include/atalk/cnid.h */
--- /dev/null
+/*
+ * Copyright (c) 1996 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * NOTE: SunOS 4 and ultrix are pretty much the only reason why there
+ * are checks for EINTR everywhere.
+ */
+
+#include <sys/cdefs.h>
+#include <signal.h>
+
+#ifdef __svr4__
+/*
+ * SunOS 5 (solaris) has SA_RESTART, but no SA_INTERRUPT.
+ */
+#ifndef SA_INTERRUPT
+#define SA_INTERRUPT 0
+#endif
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+extern int flock __P((int, int));
+extern int inet_aton __P((const char *, struct in_addr *));
+#else /* __svr4__ */
+
+#ifdef sun
+/*
+ * SunOS 4 has SA_INTERRUPT, but no SA_RESTART.
+ */
+#ifndef SA_RESTART
+#define SA_RESTART 0
+#endif
+#endif /* sun */
+
+#endif /* __svr4__ */
+
+#ifdef linux
+/*
+ * Linux has SA_RESTART, but no SA_INTERRUPT. Note that the documentation
+ * seems to be wrong on several counts. First, SA_ONESHOT is not the default,
+ * and second, SA_RESTART does what you'd expect (the same as svr4) and has
+ * nothing to do with SA_ONESHOT.
+ */
+#ifndef SA_INTERRUPT
+#define SA_INTERRUPT 0
+#endif /* SA_INTERRUPT */
+#endif /* linux */
+
+#ifdef ultrix
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/*
+ * Here's the really confusing one... Under Ultrix, sigaction() works just
+ * like sigvec(), except that SV_INTERRUPT is always set. Hence, there is
+ * no SA_INTERRUPT flag. Unfortunately, there's also no SA_RESTART, so
+ * there's no way to suppress the interrupt. Sigh.
+ */
+#ifndef SA_INTERRUPT
+#define SA_INTERRUPT 0
+#endif
+#ifndef SA_RESTART
+#define SA_RESTART 0
+#endif
+
+extern char *strdup __P((const char *));
+extern int inet_aton __P((const char *, struct in_addr *));
+#endif /* ultrix */
+
+#ifdef BSD4_4
+#ifndef SA_INTERRUPT
+#define SA_INTERRUPT 0
+#endif
+#endif /* BSD4_4 */
+
+#if defined(ultrix) || defined(_IBMR2) || defined(NEED_GETUSERSHELL)
+extern char *getusershell __P((void));
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_DDP_H
+#define _ATALK_DDP_H 1
+
+#define DDPTYPE_RTMPRD 1
+#define DDPTYPE_NBP 2
+#define DDPTYPE_ATP 3
+#define DDPTYPE_AEP 4
+#define DDPTYPE_RTMPR 5
+#define DDPTYPE_ZIP 6
+#define DDPTYPE_ADSP 7
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved.
+ */
+
+#ifndef _ATALK_DSI_H
+#define _ATALK_DSI_H
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+
+#include <netinet/in.h>
+#include <atalk/afp.h>
+#include <atalk/server_child.h>
+#include <netatalk/endian.h>
+
+/* What a DSI packet looks like:
+ 0 32
+ |-------------------------------|
+ |flags |command| requestID |
+ |-------------------------------|
+ |error code/enclosed data offset|
+ |-------------------------------|
+ |total data length |
+ |-------------------------------|
+ |reserved field |
+ |-------------------------------|
+
+ CONVENTION: anything with a dsi_ prefix is kept in network byte order.
+*/
+
+/* these need to be kept in sync w/ AFPTRANS_* in <atalk/afp.h>.
+ * convention: AFPTRANS_* = (1 << DSI_*) */
+typedef enum {
+ DSI_MIN = 1,
+ DSI_TCPIP = 1,
+ DSI_MAX = 1
+} dsi_proto;
+
+#define DSI_BLOCKSIZ 16
+struct dsi_block {
+ u_int8_t dsi_flags; /* packet type: request or reply */
+ u_int8_t dsi_command; /* command */
+ u_int16_t dsi_requestID; /* request ID */
+ u_int32_t dsi_code; /* error code or data offset */
+ u_int32_t dsi_len; /* total data length */
+ u_int32_t dsi_reserved; /* reserved field */
+};
+
+#define DSI_CMDSIZ 800
+#define DSI_DATASIZ 8192
+/* child and parent processes might interpret a couple of these
+ * differently. */
+typedef struct DSI {
+ dsi_proto protocol;
+ struct dsi_block header;
+ struct sockaddr_in server, client;
+ sigset_t sigblockset;
+ struct itimerval timer, savetimer;
+ u_int32_t attn_quantum, datasize, server_quantum;
+ u_int16_t serverID, clientID;
+ u_int8_t *status, commands[DSI_CMDSIZ], data[DSI_DATASIZ];
+ int statuslen, datalen, cmdlen;
+ size_t read_count, write_count;
+ /* inited = initialized?, child = a child?, noreply = send reply? */
+ char child, inited, noreply;
+ const char *program;
+ int socket, serversock;
+
+ /* protocol specific open/close, send/receive
+ * send/receive fill in the header and use dsi->commands.
+ * write/read just write/read data */
+ pid_t (*proto_open)(struct DSI *);
+ void (*proto_close)(struct DSI *);
+} DSI;
+
+/* DSI flags */
+#define DSIFL_REQUEST 0x00
+#define DSIFL_REPLY 0x01
+#define DSIFL_MAX 0x01
+
+/* DSI session options */
+#define DSIOPT_SERVQUANT 0x00 /* server request quantum */
+#define DSIOPT_ATTNQUANT 0x01 /* attention quantum */
+
+/* DSI Commands */
+#define DSIFUNC_CLOSE 1 /* DSICloseSession */
+#define DSIFUNC_CMD 2 /* DSICommand */
+#define DSIFUNC_STAT 3 /* DSIGetStatus */
+#define DSIFUNC_OPEN 4 /* DSIOpenSession */
+#define DSIFUNC_TICKLE 5 /* DSITickle */
+#define DSIFUNC_WRITE 6 /* DSIWrite */
+#define DSIFUNC_ATTN 8 /* DSIAttention */
+#define DSIFUNC_MAX 8 /* largest command */
+
+/* DSI Error codes: most of these aren't used. */
+#define DSIERR_OK 0x0000
+#define DSIERR_BADVERS 0xfbd6
+#define DSIERR_BUFSMALL 0xfbd5
+#define DSIERR_NOSESS 0xfbd4
+#define DSIERR_NOSERV 0xfbd3
+#define DSIERR_PARM 0xfbd2
+#define DSIERR_SERVBUSY 0xfbd1
+#define DSIERR_SESSCLOS 0xfbd0
+#define DSIERR_SIZERR 0xfbcf
+#define DSIERR_TOOMANY 0xfbce
+#define DSIERR_NOACK 0xfbcd
+
+/* server and client quanta */
+#define DSI_DEFQUANT 2 /* default attention quantum size */
+#define DSI_SERVQUANT_MAX 0xffffffffL /* server quantum */
+#define DSI_SERVQUANT_MIN 0x0004A2E0L /* minimum server quantum */
+#define DSI_SERVQUANT_DEF DSI_SERVQUANT_MIN /* default server quantum */
+
+/* default port number */
+#define DSI_AFPOVERTCP_PORT 548
+
+/* basic initialization: dsi_init.c */
+extern DSI *dsi_init __P((const dsi_proto /*protocol*/,
+ const char * /*program*/,
+ const char * /*host*/, const char * /*address*/,
+ const int /*port*/, const int /*proxy*/,
+ const u_int32_t /* server quantum */));
+extern void dsi_setstatus __P((DSI *, u_int8_t *, const int));
+
+/* in dsi_getsess.c */
+extern DSI *dsi_getsession __P((DSI *, server_child *, const int));
+extern void dsi_kill __P((int));
+
+
+/* DSI Commands: individual files */
+extern void dsi_opensession __P((DSI *));
+extern int dsi_attention __P((DSI *, AFPUserBytes));
+extern int dsi_cmdreply __P((DSI *, const int));
+extern void dsi_tickle __P((DSI *));
+extern void dsi_getstatus __P((DSI *));
+extern void dsi_close __P((DSI *));
+
+/* low-level stream commands -- in dsi_stream.c */
+extern size_t dsi_stream_write __P((DSI *, void *, const size_t));
+extern size_t dsi_stream_read __P((DSI *, void *, const size_t));
+extern int dsi_stream_send __P((DSI *, void *, size_t));
+extern int dsi_stream_receive __P((DSI *, void *, const int, int *));
+
+/* client writes -- dsi_write.c */
+extern size_t dsi_writeinit __P((DSI *, void *, const size_t));
+extern size_t dsi_write __P((DSI *, void *, const size_t));
+extern void dsi_writeflush __P((DSI *));
+#define dsi_wrtreply(a,b) dsi_cmdreply(a,b)
+
+/* client reads -- dsi_read.c */
+extern ssize_t dsi_readinit __P((DSI *, void *, const size_t, const size_t,
+ const int));
+extern ssize_t dsi_read __P((DSI *, void *, const size_t));
+extern void dsi_readdone __P((DSI *));
+
+/* some useful macros */
+#define dsi_serverID(x) ((x)->serverID++)
+#define dsi_send(x) do { \
+ (x)->header.dsi_len = htonl((x)->cmdlen); \
+ dsi_stream_send((x), (x)->commands, (x)->cmdlen); \
+} while (0)
+#define dsi_receive(x) (dsi_stream_receive((x), (x)->commands, \
+ DSI_CMDSIZ, &(x)->cmdlen))
+#endif /* atalk/dsi.h */
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_NBP_H
+#define _ATALK_NBP_H 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+
+struct nbphdr {
+#if BYTE_ORDER == BIG_ENDIAN
+ unsigned nh_op : 4,
+ nh_cnt : 4,
+#else BYTE_ORDER
+ unsigned nh_cnt : 4,
+ nh_op : 4,
+#endif BYTE_ORDER
+ nh_id : 8;
+};
+
+#define SZ_NBPHDR 2
+
+struct nbptuple {
+ u_int16_t nt_net;
+ u_int8_t nt_node;
+ u_int8_t nt_port;
+ u_int8_t nt_enum;
+};
+#define SZ_NBPTUPLE 5
+
+#define NBPSTRLEN 32
+/*
+ * Name Binding Protocol Network Visible Entity
+ */
+struct nbpnve {
+ struct sockaddr_at nn_sat;
+ u_char nn_objlen;
+ char nn_obj[ NBPSTRLEN ];
+ u_char nn_typelen;
+ char nn_type[ NBPSTRLEN ];
+ u_char nn_zonelen;
+ char nn_zone[ NBPSTRLEN ];
+};
+
+/*
+ * Note that NBPOP_ERROR is not standard. As Apple adds more NBPOPs,
+ * we need to check for collisions with our extra values. */
+#define NBPOP_BRRQ 0x1
+#define NBPOP_LKUP 0x2
+#define NBPOP_LKUPREPLY 0x3
+#define NBPOP_FWD 0x4
+#define NBPOP_RGSTR 0x7
+#define NBPOP_UNRGSTR 0x8
+#define NBPOP_CONFIRM 0x9
+#define NBPOP_OK 0xa /* NBPOP_STATUS_REPLY */
+#define NBPOP_CLOSE_NOTE 0xb
+
+#define NBPOP_ERROR 0xf
+
+#define NBPMATCH_NOGLOB (1<<1)
+#define NBPMATCH_NOZONE (1<<2)
+
+extern int nbp_name __P((const char *, char **, char **, char **));
+extern int nbp_lookup __P((const char *, const char *, const char *,
+ struct nbpnve *, const int,
+ const struct at_addr *));
+extern int nbp_rgstr __P((struct sockaddr_at *,
+ const char *, const char *, const char *));
+extern int nbp_unrgstr __P((const char *, const char *, const char *,
+ const struct at_addr *));
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * this provides a generic interface to the ddp layer. with this, we
+ * should be able to interact with any appletalk stack that allows
+ * direct access to the ddp layer. right now, only os x server's ddp
+ * layer and the generic socket based interfaces are understood.
+ */
+
+#ifndef _ATALK_NETDDP_H
+#define _ATALK_NETDDP_H 1
+
+#include <sys/cdefs.h>
+#include <sys/socket.h>
+#include <netatalk/at.h>
+
+extern int netddp_open __P((struct sockaddr_at *, struct sockaddr_at *));
+
+#if !defined(NO_DDP) && defined(MACOSX_SERVER)
+extern int netddp_sendto __P((int, void *, int, unsigned int,
+ const struct sockaddr *, unsigned int));
+extern int netddp_recvfrom __P((int, void *, int, unsigned int,
+ struct sockaddr *, unsigned int *));
+#define netddp_close(a) ddp_close(a)
+#else
+#include <unistd.h>
+#include <sys/types.h>
+
+#define netddp_close(a) close(a)
+#define netddp_sendto sendto
+#define netddp_recvfrom recvfrom
+#endif
+
+#endif /* netddp.h */
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_PAP_H
+#define _ATALK_PAP_H 1
+
+#define PAP_OPEN 1
+#define PAP_OPENREPLY 2
+#define PAP_READ 3
+#define PAP_DATA 4
+#define PAP_TICKLE 5
+#define PAP_CLOSE 6
+#define PAP_CLOSEREPLY 7
+#define PAP_SENDSTATUS 8
+#define PAP_STATUS 9
+
+#define PAP_MAXDATA 512
+#define PAP_MAXQUANTUM 8
+
+#endif
--- /dev/null
+#ifndef ATALK_PATHS_H
+#define ATALK_PATHS_H 1
+
+/* we need a way of concatenating strings */
+#ifdef __STDC__
+#ifdef HAVE_BROKEN_CPP
+#define BROKEN_ECHO(a) a
+#define ATALKPATHCAT(a,b) BROKEN_ECHO(a)##BROKEN_ECHO(b)
+#else
+#define ATALKPATHCAT(a,b) a##b
+#endif
+#else
+#define ATALKPATHCAT(a,b) a/**/b
+#endif
+
+
+/* lock file path. this should be re-organized a bit. */
+#ifndef _PATH_LOCKDIR
+#ifdef BSD4_4
+#ifdef MACOSX_SERVER
+#define _PATH_LOCKDIR "/var/run/"
+#else
+#define _PATH_LOCKDIR "/var/spool/lock/"
+#endif
+#else
+#ifdef linux
+#define _PATH_LOCKDIR "/var/lock/"
+#else
+#define _PATH_LOCKDIR "/var/spool/locks/"
+#endif /* linux */
+#endif /* BSD4_4 */
+#endif
+
+/*
+ * papd paths
+ */
+#define _PATH_PAPDPRINTCAP "/etc/printcap"
+#ifdef ultrix
+#define _PATH_PAPDSPOOLDIR "/usr/spool/lpd"
+#else ultrix
+#define _PATH_PAPDSPOOLDIR "/var/spool/lpd"
+#endif ultrix
+#ifdef BSD4_4
+#define _PATH_DEVPRINTER "/var/run/printer"
+#else BSD4_4
+#define _PATH_DEVPRINTER "/dev/printer"
+#endif BSD4_4
+
+/*
+ * atalkd paths
+ */
+#define _PATH_ATALKDEBUG "/tmp/atalkd.debug"
+#define _PATH_ATALKDTMP "atalkd.tmp"
+#define _PATH_ATALKDLOCK ATALKPATHCAT(_PATH_LOCKDIR,"atalkd")
+
+/*
+ * psorder paths
+ */
+#define _PATH_TMPPAGEORDER "/tmp/psorderXXXXXX"
+#define _PATH_PAPDLOCK ATALKPATHCAT(_PATH_LOCKDIR,"papd")
+
+/*
+ * afpd paths
+ */
+#define _PATH_AFPTKT "/tmp/AFPtktXXXXXX"
+#define _PATH_AFPDLOCK ATALKPATHCAT(_PATH_LOCKDIR,"afpd")
+
+#endif /* atalk/paths.h */
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_RTMP_H
+#define _ATALK_RTMP_H 1
+
+#include <netatalk/endian.h>
+
+#define RTMPROP_REQUEST 1
+
+struct rtmpent {
+ u_int16_t re_net;
+ u_int8_t re_hops;
+};
+
+#define RTMPHOPS_MAX 15
+#define RTMPHOPS_POISON 31
+
+struct rtmprdhdr {
+ u_int16_t rrdh_snet;
+ u_int8_t rrdh_idlen;
+ u_int8_t rrdh_id;
+};
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved.
+ */
+
+#ifndef _ATALK_SERVER_CHILD_H
+#define _ATALK_SERVER_CHILD_H 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <netatalk/endian.h>
+
+/* useful stuff for child processes. most of this is hidden in
+ * server_child.c to ease changes in implementation */
+
+#define CHILD_NFORKS 2
+#define CHILD_ASPFORK 0
+#define CHILD_PAPFORK 0
+#define CHILD_DSIFORK 1
+
+typedef struct server_child {
+ void *fork;
+ int count, nsessions, nforks;
+} server_child;
+
+/* server_child.c */
+extern server_child *server_child_alloc __P((const int, const int));
+extern int server_child_add __P((server_child *, const int, const pid_t));
+extern int server_child_remove __P((server_child *, const int, const pid_t));
+extern void server_child_free __P((server_child *));
+
+extern void server_child_kill __P((server_child *, const int, const int));
+extern void server_child_setup __P((server_child *, const int, void (*)()));
+extern void server_child_handler __P((server_child *));
+
+#endif
--- /dev/null
+/* Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef UAM_H
+#define UAM_H 1
+
+#include <sys/cdefs.h>
+#include <pwd.h>
+#include <stdarg.h>
+
+/* just a label for exported bits */
+#define UAM_MODULE_EXPORT
+
+/* type of uam */
+#define UAM_MODULE_SERVER 1
+#define UAM_MODULE_CLIENT 2
+
+/* in case something drastic has to change */
+#define UAM_MODULE_VERSION 1
+
+/* things for which we can have uams */
+#define UAM_SERVER_LOGIN (1 << 0)
+#define UAM_SERVER_CHANGEPW (1 << 1)
+#define UAM_SERVER_PRINTAUTH (1 << 2)
+
+/* options */
+#define UAM_OPTION_USERNAME (1 << 0) /* get space for username */
+#define UAM_OPTION_GUEST (1 << 1) /* get guest user */
+#define UAM_OPTION_PASSWDOPT (1 << 2) /* get the password file */
+#define UAM_OPTION_SIGNATURE (1 << 3) /* get server signature */
+#define UAM_OPTION_RANDNUM (1 << 4) /* request a random number */
+#define UAM_OPTION_HOSTNAME (1 << 5) /* get host name */
+#define UAM_OPTION_COOKIE (1 << 6) /* cookie handle */
+
+/* some password options. you pass these in the length parameter and
+ * get back the corresponding option. not all of these are implemented. */
+#define UAM_PASSWD_FILENAME (1 << 0)
+#define UAM_PASSWD_MINLENGTH (1 << 1)
+#define UAM_PASSWD_MAXFAIL (1 << 2) /* not implemented yet. */
+#define UAM_PASSWD_EXPIRETIME (1 << 3) /* not implemented yet. */
+
+/* i'm doing things this way because os x server's dynamic linker
+ * support is braindead. it also allows me to do a little versioning. */
+struct uam_export {
+ int uam_type, uam_version;
+ int (*uam_setup)(const char *);
+ void (*uam_cleanup)(void);
+};
+
+/* register and unregister uams with these functions */
+extern int uam_register __P((const int, const char *, const char *, ...));
+extern void uam_unregister __P((const int, const char *));
+
+/* helper functions */
+extern struct passwd *uam_getname __P((char *, const int));
+extern int uam_checkuser __P((const struct passwd *));
+
+/* afp helper functions */
+extern int uam_afp_read __P((void *, char *, int *,
+ int (*)(void *, void *, const int)));
+extern int uam_afpserver_option __P((void *, const int, void *, int *));
+
+/* switch.c */
+#define UAM_AFPSERVER_PREAUTH (0)
+#define UAM_AFPSERVER_POSTAUTH (1 << 0)
+
+extern int uam_afpserver_action __P((const int /*id*/, const int /*switch*/,
+ int (**)(), int (**)()));
+
+#endif
--- /dev/null
+#ifndef _ATALK_UTIL_H
+#define _ATALK_UTIL_H 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <netatalk/at.h>
+
+extern unsigned const char _diacasemap[], _dialowermap[];
+
+extern char **getifacelist(void);
+extern void freeifacelist(char **);
+
+
+#define diatolower(x) _dialowermap[(x)]
+#define diatoupper(x) _diacasemap[(x)]
+extern int atalk_aton __P((char *, struct at_addr *));
+extern void bprint __P((char *, int));
+extern int strdiacasecmp __P((const unsigned char *, const unsigned char *));
+extern int strndiacasecmp __P((const unsigned char *, const unsigned char *,
+ int));
+extern pid_t server_lock __P((char * /*program*/, char * /*file*/,
+ int /*debug*/));
+#define server_unlock(x) (unlink(x))
+
+#ifdef NO_DLFCN_H
+extern void *mod_open __P((const char *));
+extern void *mod_symbol __P((void *, const char *));
+extern void mod_close __P((void *));
+#define mod_error() ""
+#else
+#include <dlfcn.h>
+#ifndef RTLD_NOW
+#define RTLD_NOW 1
+#endif
+#define mod_open(a) dlopen(a, RTLD_NOW)
+#ifndef DLSYM_PREPEND_UNDERSCORE
+#define mod_symbol(a, b) dlsym(a, b)
+#else
+extern void *mod_symbol __P((void *, const char *));
+#endif
+#define mod_error() dlerror()
+#define mod_close(a) dlclose(a)
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+
+#ifndef _ATALK_ZIP_H
+#define _ATALK_ZIP_H 1
+
+#include <netatalk/endian.h>
+
+struct ziphdr {
+ u_int8_t zh_op;
+ u_int8_t zh_cnt;
+#define zh_count zh_cnt
+#define zh_zero zh_cnt
+#define zh_flags zh_cnt
+};
+
+struct zipreplent {
+ u_int16_t zre_net;
+ u_int8_t zre_zonelen;
+};
+
+#define ZIPOP_QUERY 1
+#define ZIPOP_REPLY 2
+#define ZIPOP_TAKEDOWN 3 /* XXX */
+#define ZIPOP_BRINGUP 4 /* XXX */
+#define ZIPOP_GNI 5
+#define ZIPOP_GNIREPLY 6
+#define ZIPOP_NOTIFY 7
+#define ZIPOP_EREPLY 8
+#define ZIPOP_GETMYZONE 7
+#define ZIPOP_GETZONELIST 8
+#define ZIPOP_GETLOCALZONES 9
+
+#define ZIPGNI_INVALID 0x80
+#define ZIPGNI_ONEZONE 0x20
+
+#endif
--- /dev/null
+ALL= asp dsi atp nbp adouble util compat netddp
+# cnid
+LIBS= asp/asplib atp/atplib nbp/nbplib adouble/adoublelib \
+ dsi/dsilib util/utillib compat/compatlib netddp/netddplib
+# cnid/cnidlib
+PLIBS= asp/asplib_p atp/atplib_p nbp/nbplib_p adouble/adoublelib_p \
+ dsi/dsilib_p util/utillib_p compat/compatlib_p \
+ netddp/netddplib
+# cnid/cnidlib_p
+TAGSFILE=tags
+CC=cc
+INSTALL=install
+RANLIB=ranlib
+
+all: libatalk.a
+
+libatalk.a: ${LIBS}
+ rm -rf tmp
+ -mkdir tmp
+# touch tmp/________64ELEL_
+ for i in ${ALL}; do (cd tmp; ar x ../$$i/$${i}lib); done
+ (cd tmp; ar cr libatalk.a *.o; ${RANLIB} libatalk.a)
+ mv tmp/libatalk.a libatalk.a
+ ${RANLIB} libatalk.a
+ rm -rf tmp tmp_p
+ -mkdir tmp_p
+# touch tmp_p/________64ELEL_
+ for i in ${ALL}; do (cd tmp_p; ar x ../$$i/$${i}lib_p); done
+ (cd tmp_p; ar cr libatalk_p.a *.o; ${RANLIB} libatalk_p.a)
+ mv tmp_p/libatalk_p.a libatalk_p.a
+ ${RANLIB} libatalk_p.a
+ rm -rf tmp_p
+
+${ALL}: FRC
+ if [ ! -f /usr/lib/librpcsvc.a -a ! -f /lib/librpcsvc.a ]; then \
+ RPCSVCDEFS=-DNEED_RQUOTA; \
+ fi; \
+ cd $@; ${MAKE} ${MFLAGS} CC="${CC}" DEFS="${DEFS}" \
+ RPCSVCDEFS="$${RPCSVCDEFS}" \
+ OPTOPTS="${OPTOPTS}" TCPWRAPDIR="${TCPWRAPDIR}" DB2DIR="${DB2DIR}"
+
+asp/asplib: asp
+dsi/dsilib: dsi
+atp/atplib: atp
+nbp/nbplib: nbp
+adouble/adoublelib: adouble
+util/utillib: util
+compat/compatlib: compat
+cnid/cnidlib: cnid
+netddp/netddplib: netddp
+
+FRC:
+
+tags:
+ for i in ${ALL}; do \
+ (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" \
+ TAGSFILE=../${TAGSFILE} tags); \
+ done
+
+install: all
+ -mkdir ${LIBDIR}
+ ${INSTALL} -c libatalk.a ${LIBDIR}/libatalk.a
+ (cd ${LIBDIR}; ranlib ${LIBDIR}/libatalk.a)
+ ${INSTALL} -c libatalk_p.a ${LIBDIR}/libatalk_p.a
+ (cd ${LIBDIR}; ranlib ${LIBDIR}/libatalk_p.a)
+
+clean:
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+ done
+ rm -f *[Ee]rrs libatalk.a libatalk_p.a
+ rm -rf tmp tmp_p
+
+depend:
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} DEFS=${DEFS} depend); \
+ done
--- /dev/null
+SRC = ad_open.c ad_flush.c ad_read.c ad_write.c ad_size.c ad_mmap.c \
+ ad_lock.c ad_date.c ad_attr.c ad_sendfile.c
+OBJ = ad_open.o ad_flush.o ad_read.o ad_write.o ad_size.o ad_mmap.o \
+ ad_lock.o ad_date.o ad_attr.o ad_sendfile.o
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH}
+TAGSFILE= tags
+CC= cc
+
+all : profiled adoublelib
+
+profiled:
+ -mkdir profiled
+
+adoublelib adoublelib_p : ${OBJ}
+ @echo "building profiled adoublelib"
+ @cd profiled; ar cru ../adoublelib_p ${OBJ}
+ @echo "building normal adoublelib"
+ @ar cru adoublelib ${OBJ}
+
+.c.o :
+ ${CC} -p ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- profiled/$*.o
+ ${CC} ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- $*.o
+
+clean :
+ rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+ rm -f adoublelib adoublelib_p
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+#include <string.h>
+#include <atalk/adouble.h>
+
+#define FILEIOFF_ATTR 14
+#define AFPFILEIOFF_ATTR 2
+
+int ad_getattr(const struct adouble *ad, u_int16_t *attr)
+{
+ if (ad->ad_version == AD_VERSION1)
+ memcpy(attr, ad_entry(ad, ADEID_FILEI) + FILEIOFF_ATTR,
+ sizeof(u_int16_t));
+ else if (ad->ad_version == AD_VERSION2)
+ memcpy(attr, ad_entry(ad, ADEID_AFPFILEI) + AFPFILEIOFF_ATTR,
+ sizeof(u_int16_t));
+ else
+ return -1;
+
+ return 0;
+}
+
+int ad_setattr(const struct adouble *ad, const u_int16_t attr)
+{
+ if (ad->ad_version == AD_VERSION1)
+ memcpy(ad_entry(ad, ADEID_FILEI) + FILEIOFF_ATTR, &attr,
+ sizeof(attr));
+ else if (ad->ad_version == AD_VERSION2)
+ memcpy(ad_entry(ad, ADEID_AFPFILEI) + AFPFILEIOFF_ATTR, &attr,
+ sizeof(attr));
+ else
+ return -1;
+
+ return 0;
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atalk/adouble.h>
+
+int ad_setdate(const struct adouble *ad,
+ unsigned int dateoff, u_int32_t date)
+{
+ int xlate = (dateoff & AD_DATE_UNIX);
+
+ dateoff &= AD_DATE_MASK;
+ if (xlate)
+ date = AD_DATE_FROM_UNIX(date);
+
+ if (ad->ad_version == AD_VERSION1) {
+ if (dateoff > AD_DATE_BACKUP)
+ return -1;
+ memcpy(ad_entry(ad, ADEID_FILEI) + dateoff, &date, sizeof(date));
+
+ } else if (ad->ad_version == AD_VERSION2) {
+ if (dateoff > AD_DATE_ACCESS)
+ return -1;
+ memcpy(ad_entry(ad, ADEID_FILEDATESI) + dateoff, &date, sizeof(date));
+
+ } else
+ return -1;
+
+ return 0;
+}
+
+int ad_getdate(const struct adouble *ad,
+ unsigned int dateoff, u_int32_t *date)
+{
+ int xlate = (dateoff & AD_DATE_UNIX);
+
+ dateoff &= AD_DATE_MASK;
+ if (ad->ad_version == AD_VERSION1) {
+ if (dateoff > AD_DATE_BACKUP)
+ return -1;
+ memcpy(date, ad_entry(ad, ADEID_FILEI) + dateoff, sizeof(u_int32_t));
+
+ } else if (ad->ad_version == AD_VERSION2) {
+ if (dateoff > AD_DATE_ACCESS)
+ return -1;
+ memcpy(date, ad_entry(ad, ADEID_FILEDATESI) + dateoff, sizeof(u_int32_t));
+
+ } else
+ return -1;
+
+ if (xlate)
+ *date = AD_DATE_TO_UNIX(*date);
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+
+#include "ad_private.h"
+
+/* rebuild the header */
+void ad_rebuild_header(struct adouble *ad)
+{
+ u_int32_t eid;
+ u_int16_t nent;
+ char *buf, *nentp;
+
+ /*
+ * Rebuild any header information that might have changed.
+ */
+ buf = ad->ad_data;
+ ad->ad_magic = htonl( ad->ad_magic );
+ memcpy(buf, &ad->ad_magic, sizeof( ad->ad_magic ));
+ ad->ad_magic = ntohl( ad->ad_magic );
+ buf += sizeof( ad->ad_magic );
+
+ ad->ad_version = htonl( ad->ad_version );
+ memcpy(buf, &ad->ad_version, sizeof( ad->ad_version ));
+ ad->ad_version = ntohl( ad->ad_version );
+ buf += sizeof( ad->ad_version );
+ memcpy(buf, ad->ad_filler, sizeof( ad->ad_filler ));
+ buf += sizeof( ad->ad_filler );
+
+ nentp = buf;
+ buf += sizeof( nent );
+ for ( eid = 0, nent = 0; eid < ADEID_MAX; eid++ ) {
+ if ( ad->ad_eid[ eid ].ade_off == 0 ) {
+ continue;
+ }
+ eid = htonl( eid );
+ memcpy(buf, &eid, sizeof( eid ));
+ eid = ntohl( eid );
+ buf += sizeof( eid );
+ ad->ad_eid[ eid ].ade_off = htonl( ad->ad_eid[ eid ].ade_off );
+ memcpy(buf, &ad->ad_eid[ eid ].ade_off,
+ sizeof( ad->ad_eid[ eid ].ade_off ));
+ ad->ad_eid[ eid ].ade_off = ntohl( ad->ad_eid[ eid ].ade_off );
+ buf += sizeof( ad->ad_eid[ eid ].ade_off );
+ ad->ad_eid[ eid ].ade_len = htonl( ad->ad_eid[ eid ].ade_len );
+ memcpy(buf, &ad->ad_eid[ eid ].ade_len,
+ sizeof( ad->ad_eid[ eid ].ade_len ));
+ ad->ad_eid[ eid ].ade_len = ntohl( ad->ad_eid[ eid ].ade_len );
+ buf += sizeof( ad->ad_eid[ eid ].ade_len );
+ nent++;
+ }
+ nent = htons( nent );
+ memcpy(nentp, &nent, sizeof( nent ));
+}
+
+
+int ad_flush( ad, adflags )
+ struct adouble *ad;
+ int adflags;
+{
+#ifndef USE_MMAPPED_HEADERS
+ int len;
+#endif
+
+ if (( adflags & ADFLAGS_HF ) && ( ad->ad_hf.adf_flags & O_RDWR )) {
+ /* sync our header */
+ ad_rebuild_header(ad);
+
+#ifdef USE_MMAPPED_HEADERS
+ /* now sync it */
+#ifdef MS_SYNC
+ msync(ad->ad_data, ad_getentryoff(ad, ADEID_RFORK),
+ MS_SYNC | MS_INVALIDATE);
+#else
+ msync(ad->ad_data, ad_getentryoff(ad, ADEID_RFORK));
+#endif
+
+#else
+ if ( ad->ad_hf.adf_off != 0 ) {
+ if ( lseek( ad->ad_hf.adf_fd, 0L, SEEK_SET ) < 0L ) {
+ return( -1 );
+ }
+ ad->ad_hf.adf_off = 0;
+ }
+
+ /* now flush it out */
+ len = ad_getentryoff(ad, ADEID_RFORK);
+ if (write( ad->ad_hf.adf_fd, ad->ad_data, len) != len) {
+ if ( errno == 0 ) {
+ errno = EIO;
+ }
+ return( -1 );
+ }
+ ad->ad_hf.adf_off = len;
+#endif
+ }
+
+ return( 0 );
+}
+
+/* use refcounts so that we don't have to re-establish fcntl locks. */
+int ad_close( ad, adflags )
+ struct adouble *ad;
+ int adflags;
+{
+ int err = 0;
+
+ if (( adflags & ADFLAGS_DF ) && ad->ad_df.adf_fd != -1 &&
+ !(--ad->ad_df.adf_refcount)) {
+ if ( close( ad->ad_df.adf_fd ) < 0 ) {
+ err = -1;
+ }
+ ad->ad_df.adf_fd = -1;
+ adf_lock_free(&ad->ad_df);
+ }
+
+ if (( adflags & ADFLAGS_HF ) && ad->ad_hf.adf_fd != -1 &&
+ !(--ad->ad_hf.adf_refcount)) {
+#ifdef USE_MMAPPED_HEADERS
+ if (ad->ad_data != MAP_FAILED)
+ munmap(ad->ad_data, ad_getentryoff(ad, ADEID_RFORK));
+#endif
+ if ( close( ad->ad_hf.adf_fd ) < 0 ) {
+ err = -1;
+ }
+ ad->ad_hf.adf_fd = -1;
+ adf_lock_free(&ad->ad_hf);
+ }
+
+ return( err );
+}
--- /dev/null
+/*
+ * Copyright (c) 1998,1999 Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT for more information.
+ *
+ * Byte-range locks. This uses either whole-file flocks to fake byte
+ * locks or fcntl-based actual byte locks. Because fcntl locks are
+ * process-oriented, we need to keep around a list of file descriptors
+ * that refer to the same file. Currently, this doesn't serialize access
+ * to the locks. as a result, there's the potential for race conditions.
+ *
+ * TODO: fix the race when reading/writing.
+ * keep a pool of both locks and reference counters around so that
+ * we can save on mallocs. we should also use a tree to keep things
+ * sorted.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <atalk/adouble.h>
+
+#include "ad_private.h"
+
+/* translate between ADLOCK styles and specific locking mechanisms */
+#define XLATE_FLOCK(type) ((type) == ADLOCK_RD ? LOCK_SH : \
+((type) == ADLOCK_WR ? LOCK_EX : \
+ ((type) == ADLOCK_CLR ? LOCK_UN : -1)))
+
+#define XLATE_FCNTL_LOCK(type) ((type) == ADLOCK_RD ? F_RDLCK : \
+((type) == ADLOCK_WR ? F_WRLCK : \
+ ((type) == ADLOCK_CLR ? F_UNLCK : -1)))
+
+#define OVERLAP(a,alen,b,blen) ((!(alen) && (a) <= (b)) || \
+ (!(blen) && (b) <= (a)) || \
+ ((((a) + (alen)) > (b)) && \
+ (((b) + (blen)) > (a))))
+
+
+/* allocation for lock regions. we allocate aggressively and shrink
+ * only in large chunks. */
+#define ARRAY_BLOCK_SIZE 10
+#define ARRAY_FREE_DELTA 100
+
+/* remove a lock and compact space if necessary */
+static __inline__ void adf_freelock(struct ad_fd *ad, const int i)
+{
+ adf_lock_t *lock = ad->adf_lock + i;
+
+ if (--(*lock->refcount) < 1) {
+ free(lock->refcount);
+ lock->lock.l_type = F_UNLCK;
+ fcntl(ad->adf_fd, F_SETLK, &lock->lock); /* unlock */
+ }
+
+ ad->adf_lockcount--;
+
+ /* move another lock into the empty space */
+ if (i < ad->adf_lockcount) {
+ memcpy(lock, lock + ad->adf_lockcount - i, sizeof(adf_lock_t));
+ }
+
+ /* free extra cruft if we go past a boundary. we always want to
+ * keep at least some stuff around for allocations. this wastes
+ * a bit of space to save time on reallocations. */
+ if ((ad->adf_lockmax > ARRAY_FREE_DELTA) &&
+ (ad->adf_lockcount + ARRAY_FREE_DELTA < ad->adf_lockmax)) {
+ struct adf_lock_t *tmp;
+
+ tmp = (struct adf_lock_t *)
+ realloc(ad->adf_lock, sizeof(adf_lock_t)*
+ (ad->adf_lockcount + ARRAY_FREE_DELTA));
+ if (tmp) {
+ ad->adf_lock = tmp;
+ ad->adf_lockmax = ad->adf_lockcount + ARRAY_FREE_DELTA;
+ }
+ }
+}
+
+
+/* this needs to deal with the following cases:
+ * 1) user is the only user of the lock
+ * 2) user shares a read lock with another user
+ *
+ * i converted to using arrays of locks. everytime a lock
+ * gets removed, we shift all of the locks down.
+ */
+static __inline__ void adf_unlock(struct ad_fd *ad, int fd, const int user)
+{
+ adf_lock_t *lock = ad->adf_lock;
+ int i;
+
+ for (i = 0; i < ad->adf_lockcount; i++) {
+ if (lock[i].user == user) {
+ /* we're really going to delete this lock. note: read locks
+ are the only ones that allow refcounts > 1 */
+ adf_freelock(ad, i);
+ i--; /* we shifted things down, so we need to backtrack */
+ }
+ }
+}
+
+/* relock any byte lock that overlaps off/len. unlock everything
+ * else. */
+static __inline__ void adf_relockrange(struct ad_fd *ad, int fd,
+ const off_t off, const size_t len)
+{
+ adf_lock_t *lock = ad->adf_lock;
+ int i;
+
+ for (i = 0; i < ad->adf_lockcount; i++) {
+ if (OVERLAP(off, len, lock[i].lock.l_start, lock[i].lock.l_len))
+ fcntl(fd, F_SETLK, &lock[i].lock);
+ }
+}
+
+
+/* find a byte lock that overlaps off/len for a particular user */
+static __inline__ int adf_findlock(struct ad_fd *ad,
+ const int user, const int type,
+ const off_t off,
+ const size_t len)
+{
+ adf_lock_t *lock = ad->adf_lock;
+ int i;
+
+ for (i = 0; i < ad->adf_lockcount; i++) {
+ if ((((type & ADLOCK_RD) && (lock[i].lock.l_type == F_RDLCK)) ||
+ ((type & ADLOCK_WR) && (lock[i].lock.l_type == F_WRLCK))) &&
+ (lock[i].user == user) &&
+ OVERLAP(off, len, lock[i].lock.l_start, lock[i].lock.l_len)) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
+/* search other user lock lists */
+static __inline__ int adf_findxlock(struct ad_fd *ad,
+ const int user, const int type,
+ const off_t off,
+ const size_t len)
+{
+ adf_lock_t *lock = ad->adf_lock;
+ int i;
+
+ for (i = 0; i < ad->adf_lockcount; i++) {
+ if ((((type & ADLOCK_RD) && (lock[i].lock.l_type == F_RDLCK)) ||
+ ((type & ADLOCK_WR) && (lock[i].lock.l_type == F_WRLCK))) &&
+ (lock[i].user != user) &&
+ OVERLAP(off, len, lock[i].lock.l_start, lock[i].lock.l_len))
+ return i;
+ }
+ return -1;
+}
+
+/* okay, this needs to do the following:
+ * 1) check current list of locks. error on conflict.
+ * 2) apply the lock. error on conflict with another process.
+ * 3) update the list of locks this file has.
+ *
+ * NOTE: this treats synchronization locks a little differently. we
+ * do the following things for those:
+ * 1) if the header file exists, all the locks go in the beginning
+ * of that.
+ * 2) if the header file doesn't exist, we stick the locks
+ * in the locations specified by AD_FILELOCK_RD/WR.
+ */
+#define LOCK_RSRC_RD (0)
+#define LOCK_RSRC_WR (1)
+#define LOCK_DATA_RD (2)
+#define LOCK_DATA_WR (3)
+int ad_fcntl_lock(struct adouble *ad, const u_int32_t eid, const int type,
+ const off_t off, const size_t len, const int user)
+{
+ struct flock lock;
+ struct ad_fd *adf;
+ adf_lock_t *adflock, *oldlock;
+ int i;
+
+ lock.l_start = off;
+ if (eid == ADEID_DFORK) {
+ if ((type & ADLOCK_FILELOCK) && (ad_hfileno(ad) != -1)) {
+ adf = &ad->ad_hf;
+ if (off == AD_FILELOCK_WR)
+ lock.l_start = LOCK_DATA_WR;
+ else if (off == AD_FILELOCK_RD)
+ lock.l_start = LOCK_DATA_RD;
+ } else
+ adf = &ad->ad_df;
+
+ } else { /* rfork */
+ adf = &ad->ad_hf;
+ if (type & ADLOCK_FILELOCK) {
+ if (off == AD_FILELOCK_WR)
+ lock.l_start = LOCK_RSRC_WR;
+ else if (off == AD_FILELOCK_RD)
+ lock.l_start = LOCK_RSRC_RD;
+ } else
+ lock.l_start += ad_getentryoff(ad, eid);
+ }
+
+ lock.l_type = XLATE_FCNTL_LOCK(type & ADLOCK_MASK);
+
+ /* see if it's locked by another user.
+ * NOTE: this guarantees that any existing locks must be at most
+ * read locks. we use ADLOCK_WR/RD because F_RD/WRLCK aren't
+ * guaranteed to be ORable. */
+ if (adf_findxlock(adf, user, ADLOCK_WR |
+ ((type & ADLOCK_WR) ? ADLOCK_RD : 0),
+ lock.l_start, len) > -1) {
+ errno = EACCES;
+ return -1;
+ }
+
+ /* look for any existing lock that we may have */
+ i = adf_findlock(adf, user, ADLOCK_RD | ADLOCK_WR, lock.l_start, len);
+ adflock = (i < 0) ? NULL : adf->adf_lock + i;
+
+ /* here's what we check for:
+ 1) we're trying to re-lock a lock, but we didn't specify an update.
+ 2) we're trying to free only part of a lock.
+ 3) we're trying to free a non-existent lock. */
+ if ((!adflock && (lock.l_type == F_UNLCK)) ||
+ (adflock && !(type & ADLOCK_UPGRADE) &&
+ ((lock.l_type != F_UNLCK) || (adflock->lock.l_start != lock.l_start) ||
+ (adflock->lock.l_len != len)))) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ lock.l_whence = SEEK_SET;
+ lock.l_len = len;
+
+ /* now, update our list of locks */
+ /* clear the lock */
+ if (lock.l_type == F_UNLCK) {
+ adf_freelock(adf, i);
+ return 0;
+ }
+
+ /* attempt to lock the file. */
+ if (fcntl(adf->adf_fd, F_SETLK, &lock) < 0)
+ return -1;
+
+ /* we upgraded this lock. */
+ if (adflock && (type & ADLOCK_UPGRADE)) {
+ memcpy(&adflock->lock, &lock, sizeof(lock));
+ return 0;
+ }
+
+ /* it wasn't an upgrade */
+ oldlock = NULL;
+ if ((lock.l_type = F_RDLCK) &&
+ ((i = adf_findxlock(adf, user, ADLOCK_RD, lock.l_start, len)) > -1)) {
+ oldlock = adf->adf_lock + i;
+ }
+
+ /* no more space. this will also happen if lockmax == lockcount == 0 */
+ if (adf->adf_lockmax == adf->adf_lockcount) {
+ adf_lock_t *tmp = (adf_lock_t *)
+ realloc(adf->adf_lock, sizeof(adf_lock_t)*
+ (adf->adf_lockmax + ARRAY_BLOCK_SIZE));
+ if (!tmp)
+ goto fcntl_lock_err;
+ adf->adf_lock = tmp;
+ adf->adf_lockmax += ARRAY_BLOCK_SIZE;
+ }
+ adflock = adf->adf_lock + adf->adf_lockcount;
+
+ /* fill in fields */
+ memcpy(&adflock->lock, &lock, sizeof(lock));
+ adflock->user = user;
+ if (oldlock)
+ adflock->refcount = oldlock->refcount;
+ else if ((adflock->refcount = calloc(1, sizeof(int))) == NULL) {
+ goto fcntl_lock_err;
+ }
+
+ (*adflock->refcount)++;
+ adf->adf_lockcount++;
+ return 0;
+
+fcntl_lock_err:
+ lock.l_type = F_UNLCK;
+ fcntl(adf->adf_fd, F_SETLK, &lock);
+ return -1;
+}
+
+
+/* with temp locks, we don't need to distinguish within the same
+ * process as everything is single-threaded. in addition, if
+ * multi-threading gets added, it will only be in a few areas. */
+int ad_fcntl_tmplock(struct adouble *ad, const u_int32_t eid, const int type,
+ const off_t off, const size_t len)
+{
+ struct flock lock;
+ struct ad_fd *adf;
+ int err;
+
+ lock.l_start = off;
+ if (eid == ADEID_DFORK) {
+ adf = &ad->ad_df;
+ } else {
+ adf = &ad->ad_hf;
+ lock.l_start += ad_getentryoff(ad, eid);
+ }
+ lock.l_type = XLATE_FCNTL_LOCK(type & ADLOCK_MASK);
+ lock.l_whence = SEEK_SET;
+ lock.l_len = len;
+
+ /* okay, we might have ranges byte-locked. we need to make sure that
+ * we restore the appropriate ranges once we're done. so, we check
+ * for overlap on an unlock and relock.
+ * XXX: in the future, all the byte locks will be sorted and contiguous.
+ * we just want to upgrade all the locks and then downgrade them
+ * here. */
+ err = fcntl(adf->adf_fd, F_SETLK, &lock);
+ if (!err && (lock.l_type == F_UNLCK))
+ adf_relockrange(adf, adf->adf_fd, lock.l_start, len);
+
+ return err;
+}
+
+
+void ad_fcntl_unlock(struct adouble *ad, const int user)
+{
+ if (ad->ad_df.adf_fd != -1) {
+ adf_unlock(&ad->ad_df, ad->ad_df.adf_fd, user);
+ }
+ if (ad->ad_hf.adf_fd != -1) {
+ adf_unlock(&ad->ad_hf, ad->ad_hf.adf_fd, user);
+ }
+}
+
+/* byte-range locks. ad_lock is used by afp_bytelock and afp_openfork
+ * to establish locks. both ad_lock and ad_tmplock take 0, 0, 0 to
+ * signify locking of the entire file. in the absence of working
+ * byte-range locks, this will default to file-wide flock-style locks.
+ */
+int ad_flock_lock(struct adouble *ad, const u_int32_t eid, const int type,
+ const off_t off, const size_t len, const int user)
+{
+ int err, lock_type;
+
+ lock_type = XLATE_FLOCK(type & ADLOCK_MASK);
+ if (eid == ADEID_DFORK) {
+ if ((err = flock(ad_dfileno(ad), lock_type | LOCK_NB)) == 0)
+ ad->ad_df.adf_lockcount = lock_type;
+ } else if ((err = flock(ad_hfileno(ad), lock_type | LOCK_NB)) == 0)
+ ad->ad_hf.adf_lockcount = lock_type;
+
+ if (err) {
+ if ((EWOULDBLOCK != EAGAIN) && (errno == EWOULDBLOCK))
+ errno = EAGAIN;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* ad_tmplock is used by afpd to lock actual read/write operations.
+ * it saves the current lock state before attempting to lock to prevent
+ * mixups. if byte-locks don't exist, it will lock the entire file with
+ * an flock. we can be a little smart here by just upgrading/downgrading
+ * locks. */
+int ad_flock_tmplock(struct adouble *ad, const u_int32_t eid, const int type,
+ const off_t off, const size_t len)
+{
+ int fd, oldlock, lock_type;
+
+ if (eid == ADEID_DFORK) {
+ oldlock = ad->ad_df.adf_lockcount;
+ fd = ad_dfileno(ad);
+ } else {
+ oldlock = ad->ad_hf.adf_lockcount;
+ fd = ad_hfileno(ad);
+ }
+
+ /* if we already have a write lock, we don't need to do anything */
+ if (oldlock == LOCK_EX) {
+ return 0;
+ }
+
+ /* if we have a read lock, upgrade it if necessary */
+ lock_type = XLATE_FLOCK(type & ADLOCK_MASK);
+ if (oldlock == LOCK_SH) {
+ if (lock_type == LOCK_EX)
+ return flock(fd, LOCK_EX | LOCK_NB);
+ else if (lock_type == LOCK_UN) /* reset it */
+ return flock(fd, LOCK_SH | LOCK_NB);
+ else /* do nothing */
+ return 0;
+ }
+
+ /* if we don't already have a lock, just do it. */
+ return flock(fd, lock_type | LOCK_NB);
+}
+
+
--- /dev/null
+/* ad_mmap provides interfaces to memory mapped files. as this is the
+ * case, we don't have to deal w/ temporary buffers such as
+ * ad_data. the ad_mmap routines are designed to not interact w/ the
+ * ad_read/ad_write routines to avoid confusion.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <atalk/adouble.h>
+
+#include "ad_private.h"
+
+static __inline__ void *ad_mmap(const size_t length, const int prot,
+ const int flags, const int fd,
+ const off_t offset)
+{
+ return mmap(0, length, prot, flags, fd, offset);
+}
+
+/* this just sets things up for mmap. as mmap can handle offsets,
+ * we need to reset the file position before handing it off */
+void *ad_mmapread(struct adouble *ad, const u_int32_t eid,
+ const off_t off, const size_t buflen)
+{
+ /* data fork */
+ if ( eid == ADEID_DFORK ) {
+ if ( lseek( ad->ad_df.adf_fd, 0, SEEK_SET ) < 0 ) {
+ perror( "df lseek" );
+ return (void *) -1;
+ }
+ ad->ad_df.adf_off = 0;
+ return ad_mmap(buflen, PROT_READ | PROT_WRITE, MAP_PRIVATE,
+ ad->ad_df.adf_fd, off);
+
+ }
+
+ /* resource fork */
+ if ( lseek( ad->ad_hf.adf_fd, 0, SEEK_SET ) < 0 ) {
+ perror( "hf lseek" );
+ return (void *) -1;
+ }
+ ad->ad_hf.adf_off = 0;
+ return ad_mmap(buflen, PROT_READ | PROT_WRITE, MAP_PRIVATE,
+ ad->ad_hf.adf_fd, ad->ad_eid[eid].ade_off + off);
+}
+
+
+/* to do writeable mmaps correctly, we actually need to make sure that
+ * the file to be mapped is large enough. that's what all the initial
+ * mess is for. */
+void *ad_mmapwrite(struct adouble *ad, const u_int32_t eid,
+ off_t off, const int end, const size_t buflen)
+{
+ struct stat st;
+
+ /* data fork */
+ if ( eid == ADEID_DFORK ) {
+ if ( fstat( ad->ad_df.adf_fd, &st ) < 0 ) {
+ return (void *) -1;
+ }
+
+ if ( end ) {
+ off = st.st_size - off;
+ }
+
+ /* make sure the file is large enough */
+ if (st.st_size < buflen + off)
+ ftruncate(ad->ad_df.adf_fd, buflen + off);
+
+ if ( lseek( ad->ad_df.adf_fd, 0, SEEK_SET ) < 0 ) {
+ return (void *) -1;
+ }
+ ad->ad_df.adf_off = 0;
+ return ad_mmap(buflen, PROT_READ | PROT_WRITE, MAP_SHARED,
+ ad->ad_df.adf_fd, off);
+ }
+
+
+ if ( fstat( ad->ad_hf.adf_fd, &st ) < 0 ) {
+ return (void *) -1;
+ }
+
+ if ( end ) {
+ off = ad->ad_eid[ eid ].ade_len - off;
+ }
+
+ off += ad->ad_eid[eid].ade_off;
+
+ /* make sure the file is large enough */
+ if (st.st_size < buflen + off)
+ ftruncate(ad->ad_hf.adf_fd, buflen + off);
+
+ if ( lseek( ad->ad_hf.adf_fd, 0, SEEK_SET ) < 0 ) {
+ return (void *) -1;
+ }
+ ad->ad_hf.adf_off = 0;
+ return ad_mmap(buflen, PROT_READ | PROT_WRITE, MAP_SHARED,
+ ad->ad_hf.adf_fd, off);
+}
--- /dev/null
+/*
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+
+#include "ad_private.h"
+
+#ifndef MAX
+#define MAX(a, b) ((a) < (b) ? (b) : (a))
+#endif
+
+/*
+ * AppleDouble entry default offsets.
+ * The layout looks like this:
+ *
+ * this is the v1 layout:
+ * 255 200 16 32 N
+ * | NAME | COMMENT | FILEI | FINDERI | RFORK |
+ *
+ * we need to change it to look like this:
+ *
+ * v2 layout:
+ * field length (in bytes)
+ * NAME 255
+ * COMMENT 200
+ * FILEDATESI 16 replaces FILEI
+ * FINDERI 32
+ * DID 4 new
+ * AFPFILEI 4 new
+ * SHORTNAME 12 8.3 new
+ * RFORK N
+ *
+ * so, all we need to do is replace FILEI with FILEDATESI, move RFORK,
+ * and add in the new fields.
+ *
+ * NOTE: the HFS module will need similar modifications to interact with
+ * afpd correctly.
+ */
+
+#define ADEDOFF_MAGIC (0)
+#define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
+#define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
+#define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
+
+/* initial lengths of some of the fields */
+#define ADEDLEN_INIT 0
+
+#define ADEID_NUM_V1 5
+#define ADEDOFF_NAME_V1 (AD_HEADER_LEN + ADEID_NUM_V1*AD_ENTRY_LEN)
+#define ADEDOFF_COMMENT_V1 (ADEDOFF_NAME_V1 + ADEDLEN_NAME)
+#define ADEDOFF_FILEI (ADEDOFF_COMMENT_V1 + ADEDLEN_COMMENT)
+#define ADEDOFF_FINDERI_V1 (ADEDOFF_FILEI + ADEDLEN_FILEI)
+#define ADEDOFF_RFORK_V1 (ADEDOFF_FINDERI_V1 + ADEDLEN_FINDERI)
+
+/* i stick things in a slightly different order than their eid order in
+ * case i ever want to separate RootInfo behaviour from the rest of the
+ * stuff. */
+#define ADEID_NUM_V2 9
+#define ADEDOFF_NAME_V2 (AD_HEADER_LEN + ADEID_NUM_V2*AD_ENTRY_LEN)
+#define ADEDOFF_COMMENT_V2 (ADEDOFF_NAME_V2 + ADEDLEN_NAME)
+#define ADEDOFF_FILEDATESI (ADEDOFF_COMMENT_V2 + ADEDLEN_COMMENT)
+#define ADEDOFF_FINDERI_V2 (ADEDOFF_FILEDATESI + ADEDLEN_FILEDATESI)
+#define ADEDOFF_DID (ADEDOFF_FINDERI_V2 + ADEDLEN_FINDERI)
+#define ADEDOFF_AFPFILEI (ADEDOFF_DID + ADEDLEN_DID)
+#define ADEDOFF_SHORTNAME (ADEDOFF_AFPFILEI + ADEDLEN_AFPFILEI)
+#define ADEDOFF_PRODOSFILEI (ADEDOFF_SHORTNAME + ADEDLEN_SHORTNAME)
+#define ADEDOFF_RFORK_V2 (ADEDOFF_PRODOSFILEI + ADEDLEN_PRODOSFILEI)
+
+
+
+/* we keep local copies of a bunch of stuff so that we can initialize things
+ * correctly. */
+
+/* Bits in the finderinfo data.
+ * see etc/afpd/{directory.c,file.c} for the finderinfo structure
+ * layout. */
+#define FINDERINFO_CUSTOMICON 0x4
+#define FINDERINFO_CLOSEDVIEW 0x100
+
+/* offsets in finderinfo */
+#define FINDERINFO_FRTYPEOFF 0
+#define FINDERINFO_FRCREATOFF 4
+#define FINDERINFO_FRFLAGOFF 8
+#define FINDERINFO_FRVIEWOFF 14
+
+/* invisible bit for dot files */
+#define ATTRBIT_INVISIBLE (1 << 0)
+#define FINDERINFO_INVISIBLE (1 << 14)
+
+/* this is to prevent changing timezones from causing problems with
+ localtime volumes. the screw-up is 30 years. we use a delta of 5
+ years. */
+#define TIMEWARP_DELTA 157680000
+
+
+struct entry {
+ u_int32_t id, offset, len;
+};
+
+#if AD_VERSION == AD_VERSION1
+static const struct entry entry_order[] = {
+ {ADEID_NAME, ADEDOFF_NAME_V1, ADEDLEN_INIT},
+ {ADEID_COMMENT, ADEDOFF_COMMENT_V1, ADEDLEN_INIT},
+ {ADEID_FILEI, ADEDOFF_FILEI, ADEDLEN_FILEI},
+ {ADEID_FINDERI, ADEDOFF_FINDERI_V1, ADEDLEN_FINDERI},
+ {ADEID_RFORK, ADEDOFF_RFORK_V1, ADEDLEN_INIT},
+ {0, 0, 0}
+};
+#else if AD_VERSION == AD_VERSION2
+static const struct entry entry_order[] = {
+ {ADEID_NAME, ADEDOFF_NAME_V2, ADEDLEN_INIT},
+ {ADEID_COMMENT, ADEDOFF_COMMENT_V2, ADEDLEN_INIT},
+ {ADEID_FILEDATESI, ADEDOFF_FILEDATESI, ADEDLEN_FILEDATESI},
+ {ADEID_FINDERI, ADEDOFF_FINDERI_V2, ADEDLEN_FINDERI},
+ {ADEID_DID, ADEDOFF_DID, ADEDLEN_DID},
+ {ADEID_AFPFILEI, ADEDOFF_AFPFILEI, ADEDLEN_AFPFILEI},
+ {ADEID_SHORTNAME, ADEDOFF_SHORTNAME, ADEDLEN_INIT},
+ {ADEID_PRODOSFILEI, ADEDOFF_PRODOSFILEI, ADEDLEN_PRODOSFILEI},
+ {ADEID_RFORK, ADEDOFF_RFORK_V2, ADEDLEN_INIT},
+ {0, 0, 0}
+};
+#endif
+
+#if AD_VERSION == AD_VERSION2
+
+
+static __inline__ int ad_v1tov2(struct adouble *ad, const char *path)
+{
+ struct stat st;
+ struct timeval tv;
+ u_int16_t attr;
+ char *buf;
+ int fd, off;
+
+ /* check to see if we should convert this header. */
+ if (!path || (ad->ad_version != AD_VERSION1))
+ return 0;
+
+ /* convert from v1 to v2. what does this mean?
+ * 1) change FILEI into FILEDATESI
+ * 2) create space for SHORTNAME, AFPFILEI, DID, and PRODOSI
+ * 3) move FILEI attributes into AFPFILEI
+ * 4) initialize ACCESS field of FILEDATESI.
+ *
+ * so, we need 4*12 (entry ids) + 12 (shortname) + 4 (afpfilei) +
+ * 4 (did) + 8 (prodosi) = 76 more bytes. */
+
+#define SHIFTDATA (AD_DATASZ2 - AD_DATASZ1)
+
+ /* bail if we can't get a lock */
+ if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0) < 0)
+ goto bail_err;
+
+ if ((fd = open(path, O_RDWR)) < 0)
+ goto bail_lock;
+
+ if (gettimeofday(&tv, NULL) < 0)
+ goto bail_lock;
+
+ if (fstat(fd, &st) ||
+ ftruncate(fd, st.st_size + SHIFTDATA) < 0) {
+ goto bail_open;
+ }
+
+ /* last place for failure. */
+ if ((void *) (buf = (char *)
+ mmap(NULL, st.st_size + SHIFTDATA,
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) ==
+ MAP_FAILED) {
+ goto bail_truncate;
+ }
+
+ off = ad->ad_eid[ADEID_RFORK].ade_off;
+
+#ifdef USE_MMAPPED_HEADERS
+ /* okay, unmap our old ad header and point it to our local copy */
+ munmap(ad->ad_data, off);
+ ad->ad_data = buf;
+#endif
+
+ /* move the RFORK. this assumes that the RFORK is at the end */
+ memmove(buf + off + SHIFTDATA, buf + off,
+ ad->ad_eid[ADEID_RFORK].ade_len);
+
+ /* now, fix up our copy of the header */
+ memset(ad->ad_filler, 0, sizeof(ad->ad_filler));
+
+ /* replace FILEI with FILEDATESI */
+ ad_getattr(ad, &attr);
+ ad->ad_eid[ADEID_FILEDATESI].ade_off = ADEDOFF_FILEDATESI;
+ ad->ad_eid[ADEID_FILEDATESI].ade_len = ADEDLEN_FILEDATESI;
+ ad->ad_eid[ADEID_FILEI].ade_off = 0;
+ ad->ad_eid[ADEID_FILEI].ade_len = 0;
+
+ /* add in the new entries */
+ ad->ad_eid[ADEID_DID].ade_off = ADEDOFF_DID;
+ ad->ad_eid[ADEID_DID].ade_len = ADEDLEN_DID;
+ ad->ad_eid[ADEID_AFPFILEI].ade_off = ADEDOFF_AFPFILEI;
+ ad->ad_eid[ADEID_AFPFILEI].ade_len = ADEDLEN_AFPFILEI;
+ ad->ad_eid[ADEID_SHORTNAME].ade_off = ADEDOFF_SHORTNAME;
+ ad->ad_eid[ADEID_SHORTNAME].ade_len = ADEDLEN_INIT;
+ ad->ad_eid[ADEID_PRODOSFILEI].ade_off = ADEDOFF_PRODOSFILEI;
+ ad->ad_eid[ADEID_PRODOSFILEI].ade_len = ADEDLEN_PRODOSFILEI;
+
+ /* shift the old entries (NAME, COMMENT, FINDERI, RFORK) */
+ ad->ad_eid[ADEID_NAME].ade_off = ADEDOFF_NAME_V2;
+ ad->ad_eid[ADEID_COMMENT].ade_off = ADEDOFF_COMMENT_V2;
+ ad->ad_eid[ADEID_FINDERI].ade_off = ADEDOFF_FINDERI_V2;
+ ad->ad_eid[ADEID_RFORK].ade_off = ADEDOFF_RFORK_V2;
+
+ /* switch to v2 */
+ ad->ad_version = AD_VERSION2;
+
+ /* move our data buffer to make space for the new entries. */
+ memmove(buf + ADEDOFF_NAME_V2, buf + ADEDOFF_NAME_V1,
+ ADEDOFF_RFORK_V1 - ADEDOFF_NAME_V1);
+
+ /* now, fill in the space with appropriate stuff. we're
+ operating as a v2 file now. */
+ ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, tv.tv_sec);
+ memset(ad_entry(ad, ADEID_DID), 0, ADEDLEN_DID);
+ memset(ad_entry(ad, ADEID_AFPFILEI), 0, ADEDLEN_AFPFILEI);
+ ad_setattr(ad, attr);
+ memset(ad_entry(ad, ADEID_SHORTNAME), 0, ADEDLEN_SHORTNAME);
+ memset(ad_entry(ad, ADEID_PRODOSFILEI), 0, ADEDLEN_PRODOSFILEI);
+
+ /* rebuild the header and cleanup */
+ ad_rebuild_header(ad);
+ munmap(buf, st.st_size + SHIFTDATA);
+ close(fd);
+ ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
+
+#ifdef USE_MMAPPED_HEADERS
+ /* now remap our header */
+ ad->ad_data = mmap(NULL, ADEDOFF_RFORK_V2, PROT_READ | PROT_WRITE,
+ (ad_getoflags(ad, ADFLAGS_HF) & O_RDWR) ? MAP_SHARED :
+ MAP_PRIVATE, ad->ad_hf.adf_fd, 0);
+ if (ad->ad_data == MAP_FAILED)
+ goto bail_err;
+#endif
+
+ return 0;
+
+bail_truncate:
+ ftruncate(fd, st.st_size);
+bail_open:
+ close(fd);
+bail_lock:
+ ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
+bail_err:
+ return -1;
+}
+#endif
+
+
+/* read in the entries */
+static __inline__ void parse_entries(struct adouble *ad, char *buf,
+ u_int16_t nentries)
+{
+ u_int32_t eid, len, off;
+
+ /* now, read in the entry bits */
+ for (; nentries > 0; nentries-- ) {
+ memcpy(&eid, buf, sizeof( eid ));
+ eid = ntohl( eid );
+ buf += sizeof( eid );
+ memcpy(&off, buf, sizeof( off ));
+ off = ntohl( off );
+ buf += sizeof( off );
+ memcpy(&len, buf, sizeof( len ));
+ len = ntohl( len );
+ buf += sizeof( len );
+
+ if ( 0 < eid && eid < ADEID_MAX ) {
+ ad->ad_eid[ eid ].ade_off = off;
+ ad->ad_eid[ eid ].ade_len = len;
+ } else {
+ syslog( LOG_DEBUG, "ad_refresh: nentries %hd eid %d\n",
+ nentries, eid );
+ }
+ }
+}
+
+
+/* this reads enough of the header so that we can figure out all of
+ * the entry lengths and offsets. once that's done, we just read/mmap
+ * the rest of the header in.
+ *
+ * NOTE: we're assuming that the resource fork is kept at the end of
+ * the file. also, mmapping won't work for the hfs fs until it
+ * understands how to mmap header files. */
+static __inline__ int ad_header_read(struct adouble *ad)
+{
+#ifdef USE_MMAPPED_HEADERS
+ char buf[AD_ENTRY_LEN*ADEID_MAX];
+#else
+ char *buf = ad->ad_data;
+#endif
+ u_int16_t nentries;
+ int len;
+ static int warning = 0;
+
+ /* read the header */
+ if ( ad->ad_hf.adf_off != 0 ) {
+ if ( lseek( ad->ad_hf.adf_fd, 0L, SEEK_SET ) < 0L ) {
+ return( -1 );
+ }
+ ad->ad_hf.adf_off = 0;
+ }
+
+ if (read( ad->ad_hf.adf_fd, buf, AD_HEADER_LEN) != AD_HEADER_LEN) {
+ if ( errno == 0 ) {
+ errno = EIO;
+ }
+ return( -1 );
+ }
+ ad->ad_hf.adf_off = AD_HEADER_LEN;
+
+ memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
+ memcpy(&ad->ad_version, buf + ADEDOFF_VERSION,
+ sizeof( ad->ad_version ));
+
+ /* tag broken v1 headers. just assume they're all right.
+ * we detect two cases: null magic/version
+ * byte swapped magic/version
+ * XXX: in the future, you'll need the v1compat flag.
+ * (ad->ad_flags & ADFLAGS_V1COMPAT) */
+ if (!ad->ad_magic && !ad->ad_version) {
+ if (!warning) {
+ syslog(LOG_DEBUG, "notice: fixing up null v1 magic/version.");
+ warning++;
+ }
+ ad->ad_magic = AD_MAGIC;
+ ad->ad_version = AD_VERSION1;
+
+ } else if ((ad->ad_magic == AD_MAGIC) &&
+ (ad->ad_version == AD_VERSION1)) {
+ if (!warning) {
+ syslog(LOG_DEBUG, "notice: fixing up byte-swapped v1 magic/version.");
+ warning++;
+ }
+
+ } else {
+ ad->ad_magic = ntohl( ad->ad_magic );
+ ad->ad_version = ntohl( ad->ad_version );
+ }
+
+ if ((ad->ad_magic != AD_MAGIC) || ((ad->ad_version != AD_VERSION1)
+#if AD_VERSION == AD_VERSION2
+ && (ad->ad_version != AD_VERSION2)
+#endif
+ )) {
+ errno = EIO;
+ syslog(LOG_DEBUG, "ad_open: can't parse AppleDouble header.");
+ return -1;
+ }
+
+ memcpy(ad->ad_filler, buf + ADEDOFF_FILLER, sizeof( ad->ad_filler ));
+ memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
+ nentries = ntohs( nentries );
+
+ /* read in all the entry headers. if we have more than the
+ * maximum, just hope that the rfork is specified early on. */
+ len = nentries*AD_ENTRY_LEN;
+#ifdef USE_MMAPPED_HEADERS
+ if (len > sizeof(buf))
+ len = sizeof(buf);
+#else
+ if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
+ len = sizeof(ad->ad_data) - AD_HEADER_LEN;
+ buf += AD_HEADER_LEN;
+#endif
+ if (read(ad->ad_hf.adf_fd, buf, len) != len) {
+ if (errno == 0)
+ errno = EIO;
+ syslog(LOG_DEBUG, "ad_header_read: can't read entry info.");
+ return -1;
+ }
+ ad->ad_hf.adf_off += len;
+
+ /* figure out all of the entry offsets and lengths. if we aren't
+ * able to read a resource fork entry, bail. */
+ parse_entries(ad, buf, nentries);
+ if (!ad_getentryoff(ad, ADEID_RFORK)
+#ifndef USE_MMAPPED_HEADERS
+ || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
+#endif
+ ) {
+ syslog(LOG_DEBUG, "ad_header_read: problem with rfork entry offset.");
+ return -1;
+ }
+
+ /* read/mmap up to the beginning of the resource fork. */
+#ifdef USE_MMAPPED_HEADERS
+ ad->ad_data = mmap(NULL, ad_getentryoff(ad, ADEID_RFORK),
+ PROT_READ | PROT_WRITE,
+ (ad_getoflags(ad, ADFLAGS_HF) & O_RDWR) ? MAP_SHARED :
+ MAP_PRIVATE, ad->ad_hf.adf_fd, 0);
+ if (ad->ad_data == MAP_FAILED)
+ return -1;
+#else
+ buf += len;
+ len = ad_getentryoff(ad, ADEID_RFORK) - ad->ad_hf.adf_off;
+ if (read(ad->ad_hf.adf_fd, buf, len) != len) {
+ if (errno == 0)
+ errno = EIO;
+ syslog(LOG_DEBUG, "ad_header_read: can't read in entries.");
+ return -1;
+ }
+#endif
+
+ /* fix up broken dates */
+ if (ad->ad_version == AD_VERSION1) {
+ struct stat st;
+ int32_t aint;
+
+ if (fstat(ad->ad_hf.adf_fd, &st) < 0) {
+ return 1; /* fail silently */
+ }
+
+ /* check to see if the ad date is wrong. just see if we have
+ * a modification date in the future. */
+ if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) &&
+ (aint > TIMEWARP_DELTA + st.st_mtime)) {
+ ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA);
+ ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint);
+ ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA);
+ ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint);
+ ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA);
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Put the .AppleDouble where it needs to be:
+ *
+ * / a/.AppleDouble/b
+ * a/b
+ * \ b/.AppleDouble/.Parent
+ */
+char *
+ad_path( path, adflags )
+ char *path;
+ int adflags;
+{
+ static char pathbuf[ MAXPATHLEN + 1];
+ char c, *slash, buf[MAXPATHLEN + 1];
+
+ strncpy(buf, path, MAXPATHLEN);
+ if ( adflags & ADFLAGS_DIR ) {
+ strncpy( pathbuf, buf, MAXPATHLEN );
+ if ( *buf != '\0' ) {
+ strcat( pathbuf, "/" );
+ }
+ slash = ".Parent";
+ } else {
+ if (( slash = strrchr( buf, '/' )) != NULL ) {
+ c = *++slash;
+ *slash = '\0';
+ strncpy( pathbuf, buf, MAXPATHLEN);
+ *slash = c;
+ } else {
+ pathbuf[ 0 ] = '\0';
+ slash = buf;
+ }
+ }
+ strncat( pathbuf, ".AppleDouble/", MAXPATHLEN - strlen(pathbuf));
+ strncat( pathbuf, slash, MAXPATHLEN - strlen(pathbuf));
+
+ return( pathbuf );
+}
+
+/*
+ * Support inherited protection modes for AppleDouble files. The supplied
+ * mode is ANDed with the parent directory's mask value in lieu of "umask",
+ * and that value is returned.
+ */
+
+#define DEFMASK 7700 /* be conservative */
+
+int
+ad_mode( path, mode )
+ char *path;
+ int mode;
+{
+ static char modebuf[ MAXPATHLEN + 1];
+ struct stat stbuf;
+ char *slash;
+
+ if ( mode == 0 ) {
+ return( mode ); /* save on syscalls */
+ }
+
+ if ( strlen( path ) >= MAXPATHLEN ) {
+ return( mode & DEFMASK ); /* can't do it */
+ }
+
+ /*
+ * For a path with directories in it, remove the final component
+ * (path or subdirectory name) to get the name we want to stat.
+ * For a path which is just a filename, use "." instead.
+ */
+ strcpy( modebuf, path );
+ if (( slash = strrchr( modebuf, '/' )) != NULL ) {
+ *slash = '\0'; /* remove pathname component */
+ } else {
+ modebuf[0] = '.'; /* use current directory */
+ modebuf[1] = '\0';
+ }
+
+ if ( stat( modebuf, &stbuf ) != 0 ) {
+ return( mode & DEFMASK ); /* bail out... can't stat dir? */
+ }
+
+ return( mode & stbuf.st_mode );
+}
+
+/*
+ * Use mkdir() with mode bits taken from ad_mode().
+ */
+int
+ad_mkdir( path, mode )
+ char *path;
+ int mode;
+{
+ return mkdir( path, ad_mode( path, mode ) );
+}
+
+
+/*
+ * It's not possible to open the header file O_RDONLY -- the read
+ * will fail and return an error. this refcounts things now.
+ */
+int ad_open( path, adflags, oflags, mode, ad )
+ char *path;
+ int adflags, oflags, mode;
+ struct adouble *ad;
+{
+ const struct entry *eid;
+ struct stat st;
+ char *slash, *ad_p;
+ int hoflags, admode;
+ u_int16_t ashort;
+
+ if (ad->ad_inited != AD_INITED) {
+ ad_dfileno(ad) = -1;
+ ad_hfileno(ad) = -1;
+ adf_lock_init(&ad->ad_df);
+ adf_lock_init(&ad->ad_hf);
+#ifdef USE_MMAPPED_HEADERS
+ ad->ad_data = MAP_FAILED;
+#endif
+ ad->ad_inited = AD_INITED;
+ }
+
+ if (adflags & ADFLAGS_DF) {
+ if (ad_dfileno(ad) == -1) {
+ if (( ad->ad_df.adf_fd =
+ open( path, oflags, ad_mode( path, mode ) )) < 0 ) {
+ return( -1 );
+ }
+ ad->ad_df.adf_off = 0;
+ ad->ad_df.adf_flags = oflags;
+ }
+ ad->ad_df.adf_refcount++;
+ }
+
+ if (adflags & ADFLAGS_HF) {
+ if (ad_hfileno(ad) == -1) {
+ ad_p = ad_path( path, adflags );
+ admode = ad_mode( ad_p, mode );
+
+ hoflags = oflags & ~O_CREAT;
+ if (( ad->ad_hf.adf_fd = open( ad_p, hoflags, admode )) < 0 ) {
+ if ( errno == ENOENT && hoflags != oflags ) {
+ /*
+ * We're expecting to create a new adouble header file,
+ * here.
+ */
+ errno = 0;
+ if (( ad->ad_hf.adf_fd = open( ad_p, oflags,
+ admode )) < 0 ) {
+ /*
+ * Probably .AppleDouble doesn't exist, try to
+ * mkdir it.
+ */
+ if ((errno == ENOENT) &&
+ ((adflags & ADFLAGS_NOADOUBLE) == 0)) {
+ if (( slash = strrchr( ad_p, '/' )) == NULL ) {
+ ad_close( ad, adflags );
+ return( -1 );
+ }
+ *slash = '\0';
+ errno = 0;
+ if ( ad_mkdir( ad_p, 0777 ) < 0 ) {
+ ad_close( ad, adflags );
+ return( -1 );
+ }
+ *slash = '/';
+ if (( ad->ad_hf.adf_fd =
+ open( ad_p, oflags, ad_mode( ad_p, mode) )) < 0 ) {
+ ad_close( ad, adflags );
+ return( -1 );
+ }
+ } else {
+ ad_close( ad, adflags );
+ return( -1 );
+ }
+ }
+ ad->ad_hf.adf_flags = oflags;
+ } else {
+ ad_close( ad, adflags );
+ return( -1 );
+ }
+ } else if ((fstat(ad->ad_hf.adf_fd, &st) == 0) &&
+ (st.st_size == 0)) {
+ /* for 0 length files, treat them as new. */
+ ad->ad_hf.adf_flags = oflags;
+ } else {
+ ad->ad_hf.adf_flags = hoflags;
+ }
+ ad->ad_hf.adf_off = 0;
+
+ /*
+ * This is a new adouble header file. Initialize the structure,
+ * instead of reading it.
+ */
+ memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
+ if ( ad->ad_hf.adf_flags & ( O_TRUNC | O_CREAT )) {
+ struct timeval tv;
+
+ ad->ad_magic = AD_MAGIC;
+ ad->ad_version = AD_VERSION;
+ memset(ad->ad_filler, 0, sizeof( ad->ad_filler ));
+
+#ifdef USE_MMAPPED_HEADERS
+ /* truncate the header file and mmap it. */
+ ftruncate(ad->ad_hf.adf_fd, AD_DATASZ);
+ ad->ad_data = mmap(NULL, AD_DATASZ, PROT_READ | PROT_WRITE,
+ MAP_SHARED, ad->ad_hf.adf_fd, 0);
+ if (ad->ad_data == MAP_FAILED) {
+ ad_close(ad, adflags);
+ return -1;
+ }
+#else
+ memset(ad->ad_data, 0, sizeof(ad->ad_data));
+#endif
+
+ eid = entry_order;
+ while (eid->id) {
+ ad->ad_eid[eid->id].ade_off = eid->offset;
+ ad->ad_eid[eid->id].ade_len = eid->len;
+ eid++;
+ }
+
+ /* put something sane in the directory finderinfo */
+ if (adflags & ADFLAGS_DIR) {
+ /* set default view */
+ ashort = htons(FINDERINFO_CLOSEDVIEW);
+ memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF,
+ &ashort, sizeof(ashort));
+ } else {
+ /* set default creator/type fields */
+ memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,
+ "TEXT", 4);
+ memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,
+ "UNIX", 4);
+ }
+
+ /* make things invisible */
+ if ((*path == '.') && strcmp(path, ".") && strcmp(path, "..")) {
+ ashort = htons(ATTRBIT_INVISIBLE);
+ ad_setattr(ad, ashort);
+ ashort = htons(FINDERINFO_INVISIBLE);
+ memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF,
+ &ashort, sizeof(ashort));
+ }
+
+ if (gettimeofday(&tv, NULL) < 0) {
+ ad_close(ad, adflags);
+ return -1;
+ }
+
+ /* put something sane in the date fields */
+ ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, tv.tv_sec);
+ ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, tv.tv_sec);
+ ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, tv.tv_sec);
+ ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
+
+ } else {
+ /*
+ * Read the adouble header in and parse it.
+ */
+ if ((ad_header_read( ad ) < 0)
+#if AD_VERSION == AD_VERSION2
+ || (ad_v1tov2(ad, ad_p) < 0)
+#endif
+ ) {
+ ad_close( ad, adflags );
+ return( -1 );
+ }
+ }
+ }
+ ad->ad_hf.adf_refcount++;
+ }
+
+ return( 0 );
+}
+
+/* to do this with mmap, we need the hfs fs to understand how to mmap
+ header files. */
+int ad_refresh(struct adouble *ad)
+{
+#ifdef USE_MMAPPED_HEADERS
+ off_t off;
+#endif
+
+ if (ad->ad_hf.adf_fd < -1)
+ return -1;
+
+#ifdef USE_MMAPPED_HEADERS
+ if (ad->ad_data == MAP_FAILED)
+ return -1;
+
+ /* re-read the header */
+ off = ad_getentryoff(ad, ADEID_RFORK);
+ memcpy(&nentries, ad->ad_data + ADEDOFF_NENTRIES, sizeof(nentries));
+ nentries = ntohs(nentries);
+ parse_entries(ad, ad->ad_data + AD_HEADER_LEN, nentries);
+
+ /* check to see if something screwy happened */
+ if (!ad_getentryoff(ad, ADEID_RFORK))
+ return -1;
+
+ /* if there's a length discrepancy, remap the header. this shouldn't
+ * really ever happen. */
+ if (off != ad_getentryoff(ad, ADEID_RFORK)) {
+ char *buf = ad->ad_data;
+ buf = ad->ad_data;
+ ad->ad_data = mmap(NULL, ad_getentryoff(ad, ADEID_RFORK),
+ PROT_READ | PROT_WRITE,
+ (ad_getoflags(ad, ADFLAGS_HF) & O_RDWR) ?
+ MAP_SHARED : MAP_PRIVATE, ad->ad_hf.adf_fd, 0);
+ if (ad->ad_data == MAP_FAILED) {
+ ad->ad_data = buf;
+ return -1;
+ }
+ munmap(buf, off);
+ }
+ return 0;
+
+#else
+ return ad_header_read(ad);
+#endif
+}
--- /dev/null
+#ifndef LIBATALK_ADOUBLE_AD_PRIVATE_H
+#define LIBATALK_ADOUBLE_AD_PRIVATE_H 1
+
+#include <atalk/adouble.h>
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *) -1)
+#endif
+
+#ifndef __inline__
+#define __inline__
+#endif
+
+/* this is so that we can keep lists of fds referencing the same file
+ * around. that way, we can honor locks created by the same process
+ * with the same file. */
+#ifdef USE_FLOCK_LOCKS
+#define adf_lock_init(a)
+#define adf_lock_free(a)
+#else
+
+#define adf_lock_init(a) do { \
+ (a)->adf_lockmax = (a)->adf_lockcount = 0; \
+ (a)->adf_lock = NULL; \
+} while (0)
+
+#define adf_lock_free(a) do { \
+ if (!(a)->adf_lock) \
+ break; \
+ free((a)->adf_lock); \
+ adf_lock_init(a); \
+} while (0)
+#endif
+
+#endif /* libatalk/adouble/ad_private.h */
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <atalk/adouble.h>
+#include <unistd.h>
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+/* XXX: this would probably benefit from pread.
+ * locks have to be checked before each stream of consecutive
+ * ad_reads to prevent a denial in the middle from causing
+ * problems. */
+ssize_t ad_read( ad, eid, off, buf, buflen)
+ struct adouble *ad;
+ const u_int32_t eid;
+ off_t off;
+ char *buf;
+ const size_t buflen;
+{
+ ssize_t cc;
+
+ /* We're either reading the data fork (and thus the data file)
+ * or we're reading anything else (and thus the header file). */
+ if ( eid == ADEID_DFORK ) {
+ if ( ad->ad_df.adf_off != off ) {
+ if ( lseek( ad->ad_df.adf_fd, (off_t) off, SEEK_SET ) < 0 ) {
+ return( -1 );
+ }
+ ad->ad_df.adf_off = off;
+ }
+ if (( cc = read( ad->ad_df.adf_fd, buf, buflen )) < 0 ) {
+ return( -1 );
+ }
+ ad->ad_df.adf_off += cc;
+ } else {
+ cc = ad->ad_eid[eid].ade_off + off;
+
+#ifdef USE_MMAPPED_HEADERS
+ if (eid != ADEID_RFORK) {
+ memcpy(buf, ad->ad_data + cc, buflen);
+ cc = buflen;
+ goto ad_read_done;
+ }
+#endif
+ if ( ad->ad_hf.adf_off != cc ) {
+ if ( lseek( ad->ad_hf.adf_fd, (off_t) cc, SEEK_SET ) < 0 ) {
+ return( -1 );
+ }
+ ad->ad_hf.adf_off = cc;
+ }
+
+ if (( cc = read( ad->ad_hf.adf_fd, buf, buflen )) < 0 ) {
+ return( -1 );
+ }
+
+#ifndef USE_MMAPPED_HEADERS
+ /*
+ * We've just read in bytes from the disk that we read earlier
+ * into ad_data. If we're going to write this buffer out later,
+ * we need to update ad_data.
+ */
+ if (ad->ad_hf.adf_off < ad_getentryoff(ad, ADEID_RFORK)) {
+ if ( ad->ad_hf.adf_flags & O_RDWR ) {
+ memcpy(buf, ad->ad_data + ad->ad_hf.adf_off,
+ MIN(sizeof( ad->ad_data ) - ad->ad_hf.adf_off, cc));
+ } else {
+ memcpy(ad->ad_data + ad->ad_hf.adf_off, buf,
+ MIN(sizeof( ad->ad_data ) - ad->ad_hf.adf_off, cc));
+ }
+ }
+ ad->ad_hf.adf_off += cc;
+#else
+ad_read_done:
+#endif
+ }
+
+ return( cc );
+}
--- /dev/null
+/*
+ * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ *
+ * NOTE: the following uses the fact that sendfile() only exists on
+ * machines with SA_RESTART behaviour. this is all very machine specific.
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <atalk/adouble.h>
+
+#include <syslog.h>
+
+#include "ad_private.h"
+
+static int _ad_sendfile_dummy;
+
+#if defined(HAVE_SENDFILE_READ) || defined(HAVE_SENDFILE_WRITE)
+static __inline__ int ad_sendfile_init(const struct adouble *ad,
+ const int eid, off_t *off,
+ const int end)
+{
+ int fd;
+
+ if (end)
+ *off = ad_size(ad, eid) - *off;
+
+ if (eid == ADEID_DFORK) {
+ fd = ad_dfileno(ad);
+ } else {
+ *off += ad_getentryoff(ad, eid);
+ fd = ad_hfileno(ad);
+ }
+
+ return fd;
+}
+#endif
+
+
+/* read from adouble file and write to socket. sendfile doesn't change
+ * the file pointer position. */
+#ifdef HAVE_SENDFILE_READ
+ssize_t ad_readfile(const struct adouble *ad, const int eid,
+ const int sock, off_t off, const size_t len)
+{
+ off_t cc;
+ int fd;
+
+ fd = ad_sendfile_init(ad, eid, &off, 0);
+#ifdef __linux__
+ cc = sendfile(sock, fd, &off, len);
+#endif
+
+#ifdef BSD4_4
+ if (sendfile(fd, sock, off, len, NULL, &cc, 0) < 0)
+ return -1;
+#endif
+
+ return cc;
+}
+#endif
+
+#if 0
+#ifdef HAVE_SENDFILE_WRITE
+/* read from a socket and write to an adouble file */
+ssize_t ad_writefile(struct adouble *ad, const int eid,
+ const int sock, off_t off, const int end,
+ const size_t len)
+{
+#ifdef __linux__
+ ssize_t cc;
+ int fd;
+
+ fd = ad_sendfile_init(ad, eid, &off, end);
+ if ((cc = sendfile(fd, sock, &off, len)) < 0)
+ return -1;
+
+ if ((eid != ADEID_DFORK) && (off > ad_getentrylen(ad, eid)))
+ ad_setentrylen(ad, eid, off);
+
+ return cc;
+#endif
+}
+#endif
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ *
+ * if we could depend upon inline functions, this would be one.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <syslog.h>
+
+#include <atalk/adouble.h>
+
+off_t ad_size(const struct adouble *ad, const u_int32_t eid)
+{
+ if (eid == ADEID_DFORK) {
+ struct stat st;
+
+ if (fstat(ad_dfileno(ad), &st) < 0)
+ return 0;
+ return st.st_size;
+ }
+
+ return ad_getentrylen(ad, eid);
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1995 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <atalk/adouble.h>
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+/* XXX: this would benefit from pwrite.
+ * locking has to be checked before each stream of consecutive
+ * ad_writes to prevent a lock in the middle from causing problems.
+ */
+ssize_t ad_write( ad, eid, off, end, buf, buflen )
+ struct adouble *ad;
+ const u_int32_t eid;
+ off_t off;
+ const int end;
+ const char *buf;
+ const size_t buflen;
+{
+ struct stat st;
+ ssize_t cc;
+
+ if ( eid == ADEID_DFORK ) {
+ if ( end ) {
+ if ( fstat( ad->ad_df.adf_fd, &st ) < 0 ) {
+ return( -1 );
+ }
+ off = st.st_size - off;
+ }
+
+ if ( ad->ad_df.adf_off != off ) {
+ if ( lseek( ad->ad_df.adf_fd, (off_t) off, SEEK_SET ) < 0 ) {
+ return( -1 );
+ }
+ ad->ad_df.adf_off = off;
+ }
+ cc = write( ad->ad_df.adf_fd, buf, buflen );
+ if ( cc < 0 ) {
+ return( -1 );
+ }
+ ad->ad_df.adf_off += cc;
+ } else {
+ if ( end ) {
+ off = ad->ad_eid[ eid ].ade_len - off;
+ }
+ cc = ad->ad_eid[eid].ade_off + off;
+
+#ifdef USE_MMAPPED_HEADERS
+ if (eid != ADEID_RFORK) {
+ memcpy(ad->ad_data + cc, buf, buflen);
+ cc = buflen;
+ goto ad_write_done;
+ }
+#endif
+
+ if ( ad->ad_hf.adf_off != cc ) {
+ if ( lseek( ad->ad_hf.adf_fd, (off_t) cc, SEEK_SET ) < 0 ) {
+ return( -1 );
+ }
+ ad->ad_hf.adf_off = cc;
+ }
+
+ if ((cc = write( ad->ad_hf.adf_fd, buf, buflen )) < 0)
+ return( -1 );
+ ad->ad_hf.adf_off += cc;
+
+#ifndef USE_MMAPPED_HEADERS
+ /* sync up our internal buffer */
+ if (ad->ad_hf.adf_off < ad_getentryoff(ad, ADEID_RFORK))
+ memcpy(ad->ad_data + ad->ad_hf.adf_off, buf,
+ MIN(sizeof(ad->ad_data) - ad->ad_hf.adf_off, cc));
+#else
+ad_write_done:
+#endif
+ if ( ad->ad_eid[ eid ].ade_len < off + cc ) {
+ ad->ad_eid[ eid ].ade_len = off + cc;
+ }
+ }
+
+ return( cc );
+}
+
+/* set locks here */
+int ad_rtruncate( ad, size )
+ struct adouble *ad;
+ const size_t size;
+{
+ int err;
+
+ if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0) < 0)
+ return -2;
+
+ if ( ftruncate( ad->ad_hf.adf_fd,
+ size + ad->ad_eid[ ADEID_RFORK ].ade_off ) < 0 ) {
+ err = errno;
+ ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
+ errno = err;
+ return( -1 );
+ }
+
+ ad->ad_eid[ ADEID_RFORK ].ade_len = size;
+ if ( lseek( ad->ad_hf.adf_fd, ad->ad_eid[ADEID_RFORK].ade_off,
+ SEEK_SET ) < 0 ) {
+ err = errno;
+ ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
+ errno = err;
+ return( -1 );
+ }
+
+ ad->ad_hf.adf_off = ad->ad_eid[ADEID_RFORK].ade_off;
+ ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
+ return( 0 );
+}
+
+int ad_dtruncate(ad, size)
+ struct adouble *ad;
+ const size_t size;
+{
+ int err;
+
+ if (ad_tmplock(ad, ADEID_DFORK, ADLOCK_WR, 0, 0) < 0)
+ return -2;
+
+ if (ftruncate(ad->ad_df.adf_fd, size) < 0) {
+ err = errno;
+ ad_tmplock(ad, ADEID_DFORK, ADLOCK_CLR, 0, 0);
+ errno = err;
+ } else
+ ad_tmplock(ad, ADEID_DFORK, ADLOCK_CLR, 0, 0);
+
+ return 0;
+}
--- /dev/null
+SRC = asp_attn.c asp_close.c asp_cmdreply.c asp_getreq.c \
+ asp_getsess.c asp_init.c asp_write.c asp_shutdown.c \
+ asp_tickle.c
+OBJ = asp_attn.o asp_close.o asp_cmdreply.o asp_getreq.o \
+ asp_getsess.o asp_init.o asp_write.o asp_shutdown.o \
+ asp_tickle.o
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH}
+TAGSFILE= tags
+CC= cc
+
+all : profiled asplib
+
+profiled:
+ -mkdir profiled
+
+asplib asplib_p : ${OBJ}
+ @echo "building profiled asplib"
+ @cd profiled; ar cru ../asplib_p ${OBJ}
+ @echo "building normal asplib"
+ @ar cru asplib ${OBJ}
+
+.c.o :
+ ${CC} -p ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- profiled/$*.o
+ ${CC} ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- $*.o
+
+clean :
+ rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+ rm -f asplib asplib_p
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+#include <atalk/afp.h>
+
+/* attentions can get sent at any time. as a consequence, we don't
+ * want to touch anything that might be used elsewhere. */
+int asp_attention(ASP asp, AFPUserBytes flags)
+{
+ char cmds[ASP_HDRSIZ], data[ASP_HDRSIZ];
+ struct sockaddr_at sat;
+ struct atp_block atpb;
+ struct iovec iov[ 1 ];
+
+ cmds[0] = ASPFUNC_ATTN;
+ cmds[1] = asp->asp_sid;
+ flags = htons(flags);
+ memcpy(cmds + 2, &flags, sizeof(flags));
+
+ sat = asp->asp_sat;
+ sat.sat_port = asp->asp_wss;
+ atpb.atp_saddr = &sat;
+ atpb.atp_sreqdata = cmds;
+ atpb.atp_sreqdlen = sizeof(cmds);
+ atpb.atp_sreqto = 2;
+ atpb.atp_sreqtries = 5;
+
+ if ( atp_sreq( asp->asp_atp, &atpb, 1, 0 ) < 0 ) {
+ syslog( LOG_ERR, "atp_sreq: %m" );
+ return -1;
+ }
+
+ iov[ 0 ].iov_base = data;
+ iov[ 0 ].iov_len = sizeof( data );
+ atpb.atp_rresiov = iov;
+ atpb.atp_rresiovcnt = sizeof( iov )/sizeof( iov[ 0 ] );
+ if ( atp_rresp( asp->asp_atp, &atpb ) < 0 ) {
+ syslog( LOG_ERR, "atp_rresp: %m" );
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+struct asp_child {
+ int ac_pid;
+ int ac_state;
+ struct sockaddr_at ac_sat;
+};
+
+#define ACSTATE_DEAD 0
+#define ACSTATE_OK 1
+#define ACSTATE_BAD 7
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+
+int asp_close( asp )
+ ASP asp;
+{
+ struct atp_block atpb;
+ struct iovec iov[ 1 ];
+ int err = 0;
+
+ memset(asp->commands, 0, sizeof(u_int32_t));
+
+ atpb.atp_saddr = &asp->asp_sat;
+ iov[ 0 ].iov_base = asp->commands;
+ iov[ 0 ].iov_len = sizeof(u_int32_t);
+ atpb.atp_sresiov = iov;
+ atpb.atp_sresiovcnt = 1;
+
+ if (atp_sresp( asp->asp_atp, &atpb ) < 0)
+ err = -1;
+
+ if (atp_close( asp->asp_atp ) < 0)
+ err = -1;
+
+ free( asp );
+ return err;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+
+#if defined(BSD) || defined(BSD4_3)
+#define memmove(a, b, n) bcopy((b), (a), (n))
+#endif
+
+int asp_cmdreply( asp, result)
+ ASP asp;
+ int result;
+{
+ struct iovec iov[ ASP_MAXPACKETS ];
+ struct atp_block atpb;
+ int iovcnt, buflen;
+ char *buf;
+
+ /* unpack data into a format that atp likes. it needs to get
+ * 4-byte headers prepended before each ASP_CMDSIZ chunk. */
+ buf = (char *) asp->data;
+ buflen = asp->datalen;
+ asp->write_count += buflen;
+ result = htonl(result);
+
+ iovcnt = 0;
+ do {
+ iov[ iovcnt ].iov_base = buf;
+ memmove(buf + ASP_HDRSIZ, buf, buflen);
+
+ if ( iovcnt == 0 ) {
+ memcpy( iov[ iovcnt ].iov_base, &result, ASP_HDRSIZ );
+ } else {
+ memset( iov[ iovcnt ].iov_base, 0, ASP_HDRSIZ );
+ }
+
+ if ( buflen > ASP_CMDSIZ ) {
+ buf += ASP_CMDMAXSIZ;
+ buflen -= ASP_CMDSIZ;
+ iov[ iovcnt ].iov_len = ASP_CMDMAXSIZ;
+ } else {
+ iov[ iovcnt ].iov_len = buflen + ASP_HDRSIZ;
+ buflen = 0;
+ }
+ iovcnt++;
+ } while ( buflen > 0 );
+
+ atpb.atp_saddr = &asp->asp_sat;
+ atpb.atp_sresiov = iov;
+ atpb.atp_sresiovcnt = iovcnt;
+ if ( atp_sresp( asp->asp_atp, &atpb ) < 0 ) {
+ return( -1 );
+ }
+ asp->asp_seq++;
+
+ return( 0 );
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ *
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+
+int asp_getrequest(ASP asp)
+{
+ struct atp_block atpb;
+ u_int16_t seq;
+
+ asp->asp_sat.sat_port = ATADDR_ANYPORT;
+ atpb.atp_saddr = &asp->asp_sat;
+ atpb.atp_rreqdata = asp->cmdbuf;
+ atpb.atp_rreqdlen = sizeof(asp->cmdbuf);
+
+ if ( atp_rreq( asp->asp_atp, &atpb ) < 0 ) {
+ return( -1 );
+ }
+
+ asp->cmdlen = atpb.atp_rreqdlen - 4;
+ asp->read_count += asp->cmdlen;
+ memcpy( &seq, asp->cmdbuf + 2, sizeof(seq));
+ seq = ntohs( seq );
+
+ if ((asp->cmdbuf[0] != ASPFUNC_CLOSE) && (seq != asp->asp_seq)) {
+ return( -2 );
+ }
+ if ( asp->cmdbuf[1] != asp->asp_sid ) {
+ return( -3 );
+ }
+
+ return( asp->cmdbuf[0] ); /* the command */
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1996 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <syslog.h>
+#include <errno.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <netatalk/at.h>
+#include <atalk/compat.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+
+#include <atalk/server_child.h>
+#include "asp_child.h"
+
+static ASP server_asp;
+static struct server_child *children = NULL;
+static struct asp_child **asp_ac = NULL;
+
+/* send tickles and check tickle status of connections
+ * thoughts on using a hashed list:
+ * + child_cleanup, finding slots
+ * - tickle_handler, freeing, tickles
+ * if setup for a large number of connections,
+ * + space: if actual connections < potential
+ * - space: actual connections ~ potential
+ */
+static void tickle_handler()
+{
+ int sid;
+
+ /* check status */
+ for (sid = 0; sid < children->nsessions; sid++) {
+ if (asp_ac[sid] == NULL || asp_ac[sid]->ac_state == ACSTATE_DEAD)
+ continue;
+
+ if (++asp_ac[sid]->ac_state >= ACSTATE_BAD) {
+ /* kill. if already dead, just continue */
+ if (kill( asp_ac[ sid ]->ac_pid, SIGTERM) == 0)
+ syslog( LOG_INFO, "asp_alrm: %d timed out",
+ asp_ac[ sid ]->ac_pid );
+
+ asp_ac[ sid ]->ac_state = ACSTATE_DEAD;
+ continue;
+ }
+
+ /* send off a tickle */
+ asp_tickle(server_asp, sid, &asp_ac[sid]->ac_sat);
+ }
+}
+
+static void child_cleanup(const pid_t pid)
+{
+ int i;
+
+ for (i = 0; i < children->nsessions; i++)
+ if (asp_ac[i] && (asp_ac[i]->ac_pid == pid)) {
+ asp_ac[i]->ac_state = ACSTATE_DEAD;
+ break;
+ }
+}
+
+
+/* kill children */
+void asp_kill(int sig)
+{
+ if (children)
+ server_child_kill(children, CHILD_ASPFORK, sig);
+}
+
+
+/*
+ * This call handles open, tickle, and getstatus requests. On a
+ * successful open, it forks a child process.
+ * It returns an ASP to the child and parent and NULL if there is
+ * an error.
+ */
+ASP asp_getsession(ASP asp, server_child *server_children,
+ const int tickleval)
+{
+ struct sigaction action;
+ struct itimerval timer;
+ struct sockaddr_at sat;
+ struct atp_block atpb;
+ ATP atp;
+ struct iovec iov[ 8 ];
+ pid_t pid;
+ int i, sid;
+ u_int16_t asperr;
+
+ if (!asp->inited) {
+ if (!(children = server_children))
+ return NULL;
+
+ if ((asp_ac = (struct asp_child **)
+ calloc(server_children->nsessions, sizeof(struct asp_child *)))
+ == NULL)
+ return NULL;
+
+ server_asp = asp;
+
+ /* install cleanup pointer */
+ server_child_setup(children, CHILD_ASPFORK, child_cleanup);
+
+ /* install tickle handler */
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = tickle_handler;
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = SA_RESTART;
+
+ timer.it_interval.tv_sec = timer.it_value.tv_sec = tickleval;
+ timer.it_interval.tv_usec = timer.it_value.tv_usec = 0;
+ if ((sigaction(SIGALRM, &action, NULL) < 0) ||
+ (setitimer(ITIMER_REAL, &timer, NULL) < 0)) {
+ free(asp_ac);
+ return NULL;
+ }
+
+ asp->inited = 1;
+ }
+
+ memset( &sat, 0, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+ sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ sat.sat_family = AF_APPLETALK;
+ sat.sat_addr.s_net = ATADDR_ANYNET;
+ sat.sat_addr.s_node = ATADDR_ANYNODE;
+ sat.sat_port = ATADDR_ANYPORT;
+ atpb.atp_saddr = &sat;
+ atpb.atp_rreqdata = asp->cmdbuf;
+ atpb.atp_rreqdlen = sizeof( asp->cmdbuf );
+ while ( atp_rreq( asp->asp_atp, &atpb ) < 0 ) {
+ if ( errno == EINTR || errno == EAGAIN ) {
+ continue;
+ }
+ return( NULL );
+ }
+
+ switch ( asp->cmdbuf[ 0 ] ) {
+ case ASPFUNC_TICKLE:
+ sid = asp->cmdbuf[1];
+ if ((asp_ac[sid] != NULL) && (asp_ac[sid]->ac_state != ACSTATE_DEAD))
+ asp_ac[sid]->ac_state = ACSTATE_OK;
+ break;
+
+ case ASPFUNC_STAT :
+#ifdef EBUG
+ printf( "asp stat\n" );
+#endif EBUG
+ if ( asp->asp_slen > 0 ) {
+ asp->cmdbuf[0] = 0;
+ memcpy( asp->cmdbuf + 4, asp->asp_status, asp->asp_slen );
+ iov[ 0 ].iov_base = asp->cmdbuf;
+ iov[ 0 ].iov_len = 4 + asp->asp_slen;
+ atpb.atp_sresiov = iov;
+ atpb.atp_sresiovcnt = 1;
+ atp_sresp( asp->asp_atp, &atpb );
+ }
+ break;
+
+ case ASPFUNC_OPEN :
+ if (children->count < children->nsessions) {
+
+ /* find a slot */
+ for (sid = 0; sid < children->nsessions; sid++) {
+ if (asp_ac[sid] == NULL)
+ break;
+
+ if (asp_ac[sid]->ac_state == ACSTATE_DEAD) {
+ free(asp_ac[sid]);
+ asp_ac[sid] = NULL;
+ break;
+ }
+ }
+
+ if ((atp = atp_open(ATADDR_ANYPORT,
+ &(atp_sockaddr(asp->asp_atp)->sat_addr))) == NULL)
+ return NULL;
+
+ switch ((pid = fork())) {
+ case 0 : /* child */
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+ /* free/close some things */
+ for (i = 0; i < children->nsessions; i++ ) {
+ if ( asp_ac[i] != NULL )
+ free( asp_ac[i] );
+ }
+ free(asp_ac);
+
+ server_child_free(children);
+ children = NULL;
+ atp_close(asp->asp_atp);
+
+ asp->child = 1;
+ asp->asp_atp = atp;
+ asp->asp_sat = sat;
+ asp->asp_wss = asp->cmdbuf[1];
+ asp->asp_seq = 0;
+ asp->asp_sid = sid;
+ asp->asp_flags = ASPFL_SSS;
+ return asp;
+
+ case -1 : /* error */
+ asp->cmdbuf[ 0 ] = 0;
+ asp->cmdbuf[ 1 ] = 0;
+ asperr = ASPERR_SERVBUSY;
+ break;
+
+ default : /* parent process */
+ switch (server_child_add(children, CHILD_ASPFORK, pid)) {
+ case 0: /* added child */
+ if ((asp_ac[sid] = (struct asp_child *)
+ malloc(sizeof(struct asp_child)))) {
+ asp_ac[sid]->ac_pid = pid;
+ asp_ac[sid]->ac_state = ACSTATE_OK;
+ asp_ac[sid]->ac_sat = sat;
+ asp_ac[sid]->ac_sat.sat_port = asp->cmdbuf[1];
+
+ asp->cmdbuf[0] = atp_sockaddr(atp)->sat_port;
+ asp->cmdbuf[1] = sid;
+ asperr = ASPERR_OK;
+ break;
+ } /* fall through if malloc fails */
+ case -1: /* bad error */
+ kill(pid, SIGQUIT);
+ break;
+ default: /* non-fatal error */
+ break;
+ }
+ atp_close(atp);
+ break;
+ }
+
+ } else {
+ asp->cmdbuf[0] = asp->cmdbuf[1] = 0;
+ asperr = ASPERR_SERVBUSY;
+ }
+
+ memcpy( asp->cmdbuf + 2, &asperr, sizeof(asperr));
+ iov[ 0 ].iov_base = asp->cmdbuf;
+ iov[ 0 ].iov_len = 4;
+ atpb.atp_sresiov = iov;
+ atpb.atp_sresiovcnt = 1;
+ atp_sresp( asp->asp_atp, &atpb );
+ break;
+
+ default:
+ syslog(LOG_INFO, "ASPUnknown %d", asp->cmdbuf[0]);
+ break;
+ }
+
+ return asp;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+ASP asp_init( atp )
+ ATP atp;
+{
+ ASP asp;
+
+ if (( asp = (struct ASP *)calloc(1, sizeof( struct ASP ))) == NULL ) {
+ return( NULL );
+ }
+
+ asp->asp_atp = atp;
+#ifdef BSD4_4
+ asp->asp_sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ asp->asp_sat.sat_family = AF_APPLETALK;
+ asp->asp_sat.sat_addr.s_net = ATADDR_ANYNET;
+ asp->asp_sat.sat_addr.s_node = ATADDR_ANYNODE;
+ asp->asp_sat.sat_port = ATADDR_ANYPORT;
+ asp->asp_status = NULL;
+ asp->asp_slen = 0;
+ asp->asp_sid = 0;
+ asp->asp_flags = ASPFL_SLS;
+ asp->cmdlen = asp->datalen = 0;
+ asp->read_count = asp->write_count = 0;
+ asp->commands = asp->cmdbuf + 4;
+
+ return( asp );
+}
+
+void asp_setstatus( asp, status, slen )
+ ASP asp;
+ char *status;
+ const int slen;
+{
+ asp->asp_status = status;
+ asp->asp_slen = slen;
+}
--- /dev/null
+/*
+ * Copyright (c) 1996 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+
+int asp_shutdown( asp )
+ ASP asp;
+{
+ struct atp_block atpb;
+ struct iovec iov;
+ char *p;
+ u_int16_t seq;
+ u_int8_t oport;
+
+
+ p = asp->commands;
+ *p++ = ASPFUNC_CLOSE;
+ *p++ = asp->asp_sid;
+ seq = 0;
+ memcpy( p, &seq, sizeof(seq));
+ p += sizeof(seq);
+
+ oport = asp->asp_sat.sat_port;
+ atpb.atp_saddr = &asp->asp_sat;
+ atpb.atp_saddr->sat_port = asp->asp_wss;
+ atpb.atp_sreqdata = asp->commands;
+ atpb.atp_sreqdlen = p - asp->commands;
+ atpb.atp_sreqto = 2;
+ atpb.atp_sreqtries = 5;
+
+ if ( atp_sreq( asp->asp_atp, &atpb, 1, ATP_XO ) < 0 ) {
+ asp->asp_sat.sat_port = oport;
+ return( -1 );
+ }
+
+ iov.iov_base = asp->commands;
+ iov.iov_len = ASP_CMDSIZ;
+ atpb.atp_rresiov = &iov;
+ atpb.atp_rresiovcnt = 1;
+
+ if ( atp_rresp( asp->asp_atp, &atpb ) < 0 ) {
+ asp->asp_sat.sat_port = oport;
+ return( -1 );
+ }
+ asp->asp_sat.sat_port = oport;
+
+ return( 0 );
+}
--- /dev/null
+#include <syslog.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+
+/* send off a tickle */
+void asp_tickle(ASP asp, const u_int8_t sid, struct sockaddr_at *sat)
+{
+ struct atp_block atpb;
+ char buf[ASP_HDRSIZ];
+
+ buf[ 0 ] = ASPFUNC_TICKLE;
+ buf[ 1 ] = sid;
+ buf[ 2 ] = buf[ 3 ] = 0;
+
+ atpb.atp_saddr = sat;
+ atpb.atp_sreqdata = buf;
+ atpb.atp_sreqdlen = sizeof(buf);
+ atpb.atp_sreqto = 0;
+ atpb.atp_sreqtries = 1;
+ if ( atp_sreq( asp->asp_atp, &atpb, 0, 0 ) < 0 ) {
+ syslog( LOG_ERR, "atp_sreq: %m" );
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+
+#if defined(BSD) || defined(BSD4_3)
+#define memmove(a, b, n) bcopy((b), (a), (n))
+#endif
+
+int asp_wrtcont(ASP asp, char *buf, int *buflen)
+{
+ struct iovec iov[ ASP_MAXPACKETS ];
+ struct atp_block atpb;
+ char *p;
+ int iovcnt = ASP_MAXPACKETS;
+ u_int16_t blen, seq;
+ u_int8_t oport;
+
+ p = buf;
+ *p++ = ASPFUNC_WRTCONT;
+ *p++ = asp->asp_sid;
+ seq = htons( asp->asp_seq );
+ memcpy( p, &seq, sizeof(seq));
+ p += sizeof(seq);
+ blen = htons(*buflen);
+ memcpy( p, &blen, sizeof(blen));
+ p += sizeof(blen);
+
+ for ( iovcnt = 0; iovcnt < ASP_MAXPACKETS; iovcnt++ ) {
+ iov[iovcnt].iov_base = buf + iovcnt*ASP_CMDMAXSIZ;
+ iov[ iovcnt ].iov_len = ASP_CMDMAXSIZ;
+ }
+
+ oport = asp->asp_sat.sat_port;
+ atpb.atp_saddr = &asp->asp_sat;
+ atpb.atp_saddr->sat_port = asp->asp_wss;
+ atpb.atp_sreqdata = buf;
+ atpb.atp_sreqdlen = p - buf;
+ atpb.atp_sreqto = 2;
+ atpb.atp_sreqtries = 5;
+
+ if ( atp_sreq( asp->asp_atp, &atpb, iovcnt, ATP_XO ) < 0 ) {
+ asp->asp_sat.sat_port = oport;
+ return( -1 );
+ }
+ asp->write_count += atpb.atp_sreqdlen;
+
+ atpb.atp_rresiov = iov;
+ atpb.atp_rresiovcnt = iovcnt;
+ if ( atp_rresp( asp->asp_atp, &atpb ) < 0 ) {
+ asp->asp_sat.sat_port = oport;
+ return( -1 );
+ }
+
+ asp->asp_sat.sat_port = oport;
+
+ /* get rid of the 4-byte headers */
+ p = buf;
+ for ( iovcnt = 0; iovcnt < atpb.atp_rresiovcnt; iovcnt++ ) {
+ memmove(p, (char *) iov[ iovcnt ].iov_base + ASP_HDRSIZ,
+ iov[ iovcnt ].iov_len - ASP_HDRSIZ );
+ p += ( iov[ iovcnt ].iov_len - ASP_HDRSIZ );
+ }
+
+ *buflen = p - buf;
+ asp->read_count += *buflen;
+ return 0;
+}
--- /dev/null
+SRC= atp_bufs.c atp_close.c atp_open.c atp_packet.c atp_rreq.c \
+ atp_rresp.c atp_rsel.c atp_sreq.c atp_sresp.c
+OBJ= atp_bufs.o atp_close.o atp_open.o atp_packet.o atp_rreq.o \
+ atp_rresp.o atp_rsel.o atp_sreq.o atp_sresp.o
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH} # -DDROP_ATPTREL -DEBUG -DDROPPACKETS -g
+TAGSFILE= tags
+CC= cc
+
+all: profiled atplib
+
+profiled:
+ -mkdir profiled
+
+atplib atplib_p : ${OBJ}
+ @echo "building profiled atplib"
+ @cd profiled; ar cru ../atplib_p ${OBJ}
+ @echo "building normal atplib"
+ @ar cru atplib ${OBJ}
+
+.c.o :
+ ${CC} -p ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- profiled/$*.o
+ ${CC} ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- $*.o
+
+clean :
+ rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+ rm -f atplib atplib_p
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#define BPLEN 48
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+char hexdig[] = "0123456789abcdef";
+
+bprint( data, len )
+ char *data;
+ int len;
+{
+ char out[ BPLEN ];
+ int i = 0;
+
+ memset( out, 0, BPLEN );
+ for ( ;; ) {
+ if ( len < 1 ) {
+ printf( "\t%s\n", ( i == 0 ) ? "(end)" : out );
+ break;
+ }
+
+ if ( isgraph( (unsigned char)*data )) {
+ out[ i ] = ' ';
+ out[ i+1 ] = *data;
+ } else {
+ out[ i ] = hexdig[ ( *data & 0xf0 ) >> 4 ];
+ out[ i+1 ] = hexdig[ *data & 0x0f ];
+ }
+ i += 2;
+ len--;
+ data++;
+
+ if ( i > BPLEN - 2 ) {
+ printf( "\t%s\n", out );
+ memset( out, 0, BPLEN );
+ i = 0;
+ continue;
+ }
+ out[ i++ ] = ' ';
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+/*
+ * Our own memory maintenance for atp
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include "atp_internals.h"
+
+#define N_MORE_BUFS 10
+
+static struct atpbuf *free_list = NULL; /* free buffers */
+
+#ifdef EBUG
+static int numbufs = 0;
+#endif EBUG
+
+/* only call this when the free_list is empty...
+ * N_MORE_BUFS must be >= one
+*/
+static int more_bufs(void)
+{
+ int i;
+ char *mem;
+ struct atpbuf *bp;
+
+ /* get the whole chunk in one malloc call
+ */
+ if (( mem = malloc( N_MORE_BUFS * sizeof( struct atpbuf ))) == NULL ) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ /* now split into separate bufs
+ */
+ bp = free_list = (struct atpbuf *) mem;
+ for ( i = 1; i < N_MORE_BUFS; ++i ) {
+ bp->atpbuf_next = (struct atpbuf *) ( mem += sizeof( struct atpbuf ));
+ bp = bp->atpbuf_next;
+ }
+ bp->atpbuf_next = NULL;
+
+ return 0;
+}
+
+
+#ifdef EBUG
+void atp_print_bufuse( ah, s )
+ ATP ah;
+ char *s;
+{
+ struct atpbuf *bp;
+ int i, sentcount, incount, respcount;
+
+ sentcount = 0;
+ for ( bp = ah->atph_sent; bp != NULL; bp = bp->atpbuf_next ) {
+ ++sentcount;
+ for ( i = 0; i < 8; ++i ) {
+ if ( bp->atpbuf_info.atpbuf_xo.atpxo_packet[ i ] != NULL ) {
+ ++sentcount;
+ }
+ }
+ }
+
+ if ( ah->atph_reqpkt != NULL ) {
+ ++sentcount;
+ }
+
+
+ incount = 0;
+ for ( bp = ah->atph_queue; bp != NULL; bp = bp->atpbuf_next, ++incount );
+
+ respcount = 0;
+ for ( i = 0; i < 8; ++i ) {
+ if ( ah->atph_resppkt[ i ] != NULL ) {
+ ++respcount;
+ }
+ }
+
+ printf( "<%d> %s: bufs total %d sent %d incoming %d req %d resp %d\n",
+ getpid(), s, numbufs, sentcount, incount,
+ ( ah->atph_reqpkt != NULL ) ? 1: 0, respcount );
+}
+#endif EBUG
+
+
+struct atpbuf *atp_alloc_buf(void)
+{
+ struct atpbuf *bp;
+
+ if ( free_list == NULL && more_bufs() ) return NULL;
+
+ bp = free_list;
+ free_list = free_list->atpbuf_next;
+#ifdef EBUG
+ ++numbufs;
+#endif EBUG
+ return bp;
+}
+
+
+int atp_free_buf( bp )
+ struct atpbuf *bp;
+{
+ if ( bp == NULL ) {
+ return -1;
+ }
+ bp->atpbuf_next = free_list;
+ free_list = bp;
+#ifdef EBUG
+ --numbufs;
+#endif EBUG
+ return 0;
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1997 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netatalk/at.h>
+#include <atalk/netddp.h>
+#include <atalk/atp.h>
+#include "atp_internals.h"
+#ifdef EBUG
+#include <stdio.h>
+#endif EBUG
+
+int atp_close( ah )
+ ATP ah;
+{
+ struct atpbuf *cq;
+ int i;
+
+ /* remove from list of open atp sockets & discard queued data
+ */
+#ifdef EBUG
+ print_bufuse( ah, "atp_close");
+#endif EBUG
+
+ while ( ah->atph_queue != NULL ) {
+ cq = ah->atph_queue;
+ ah->atph_queue = cq->atpbuf_next;
+ atp_free_buf( cq );
+ }
+
+ while ( ah->atph_sent != NULL ) {
+ cq = ah->atph_sent;
+ for ( i = 0; i < 8; ++i ) {
+ if ( cq->atpbuf_info.atpbuf_xo.atpxo_packet[ i ] != NULL ) {
+ atp_free_buf( cq->atpbuf_info.atpbuf_xo.atpxo_packet[ i ] );
+ }
+ }
+ ah->atph_sent = cq->atpbuf_next;
+ atp_free_buf( cq );
+ }
+
+ if ( ah->atph_reqpkt != NULL ) {
+ atp_free_buf( ah->atph_reqpkt );
+ ah->atph_reqpkt = NULL;
+ }
+
+ for ( i = 0; i < 8; ++i ) {
+ if ( ah->atph_resppkt[ i ] != NULL ) {
+ atp_free_buf( ah->atph_resppkt[ i ] );
+ ah->atph_resppkt[ i ] = NULL;
+ }
+ }
+
+#ifdef EBUG
+ print_bufuse( ah, "atp_close end");
+#endif EBUG
+
+ i = ah->atph_socket;
+ atp_free_buf( (struct atpbuf *) ah );
+
+ if (netddp_close(i) < 0)
+ return -1;
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef ATP_INTERNALS_H
+#define ATP_INTERNALS_H 1
+
+#include <sys/cdefs.h>
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <atalk/atp.h>
+
+/*
+ * masks for matching incoming packets
+ */
+#define ATP_FUNCANY ATP_TREQ | ATP_TRESP | ATP_TREL
+#define ATP_TIDANY 0xffff
+
+/* in atp_bufs.c */
+extern struct atpbuf *atp_alloc_buf __P((void));
+extern void atp_print_bufuse __P((ATP, char *));
+extern int atp_free_buf __P((struct atpbuf *));
+
+/* in atp_packet.c */
+extern int at_addr_eq __P((struct sockaddr_at *,
+ struct sockaddr_at *));
+extern void atp_build_req_packet __P((struct atpbuf *, u_int16_t,
+ u_int8_t, struct atp_block *));
+extern void atp_build_resp_packet __P((struct atpbuf *, u_int16_t,
+ u_int8_t, struct atp_block *,
+ u_int8_t));
+extern int atp_recv_atp __P((ATP, struct sockaddr_at *,
+ u_int8_t *, u_int16_t, char *,
+ int));
+#ifdef EBUG
+extern void atp_print_addr __P((char *, struct sockaddr_at *));
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h>
+
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+
+#include <atalk/netddp.h>
+#include <atalk/ddp.h>
+#include <atalk/atp.h>
+
+#include "atp_internals.h"
+
+ATP atp_open(port, saddr)
+ u_int8_t port;
+ const struct at_addr *saddr;
+{
+ struct sockaddr_at addr;
+ int s;
+ ATP atp;
+ struct timeval tv;
+ int pid;
+
+#ifdef EBUG
+ printf( "<%d> atp_open\n", getpid());
+#endif
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sat_port = port;
+ if (saddr)
+ memcpy(&addr.sat_addr, saddr, sizeof(struct at_addr));
+ if ((s = netddp_open(&addr, NULL)) < 0)
+ return NULL;
+
+ if (( atp = (ATP) atp_alloc_buf()) == NULL ) {
+ netddp_close(s);
+ return NULL;
+ }
+
+ /* initialize the atp handle */
+ memset(atp, 0, sizeof( struct atp_handle ));
+ memcpy(&atp->atph_saddr, &addr, sizeof(addr));
+
+ atp->atph_socket = s;
+ atp->atph_reqto = -1;
+ gettimeofday( &tv, (struct timezone *) 0 );
+ pid = getpid();
+ atp->atph_tid = tv.tv_sec ^ ((( pid << 8 ) & 0xff00 ) | ( pid >> 8 ));
+
+#ifdef EBUG
+srandom( tv.tv_sec );
+#endif
+
+ return atp;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+
+#include <atalk/netddp.h>
+#include <atalk/ddp.h>
+#include <atalk/atp.h>
+#include <atalk/util.h>
+
+#include "atp_internals.h"
+
+/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature. */
+#ifndef SOCKLEN_T
+#define SOCKLEN_T unsigned int
+#endif
+
+#ifdef EBUG
+#include <stdio.h>
+
+static void print_func( ctrlinfo )
+ u_int8_t ctrlinfo;
+{
+ switch ( ctrlinfo & ATP_FUNCMASK ) {
+ case ATP_TREQ:
+ printf( "TREQ" );
+ break;
+ case ATP_TRESP:
+ printf( "TRESP" );
+ break;
+ case ATP_TREL:
+ printf( "ANY/TREL" );
+ break;
+ case ATP_TIDANY:
+ printf( "*" );
+ break;
+ default:
+ printf( "%x", ctrlinfo & ATP_FUNCMASK );
+ }
+}
+
+static void dump_packet( buf, len )
+ char *buf;
+ int len;
+{
+ int i;
+
+ for ( i = 0; i < len; ++i ) {
+ printf( "%x-%c ", buf[i], buf[i] );
+ }
+ putchar( '\n' );
+}
+
+void atp_print_addr( s, saddr )
+ char *s;
+ struct sockaddr_at *saddr;
+{
+ printf( "%s ", s );
+ saddr->sat_family == AF_APPLETALK ? printf( "at." ) :
+ printf( "%d.", saddr->sat_family );
+ saddr->sat_addr.s_net == ATADDR_ANYNET ? printf( "*." ) :
+ printf( "%d.", ntohs( saddr->sat_addr.s_net ));
+ saddr->sat_addr.s_node == ATADDR_ANYNODE ? printf( "*." ) :
+ printf( "%d.", saddr->sat_addr.s_node );
+ saddr->sat_port == ATADDR_ANYPORT ? printf( "*" ) :
+ printf( "%d", saddr->sat_port );
+}
+#endif
+
+
+void atp_build_req_packet( pktbuf, tid, ctrl, atpb )
+ struct atpbuf *pktbuf;
+ u_int16_t tid;
+ u_int8_t ctrl;
+ struct atp_block *atpb;
+{
+ struct atphdr hdr;
+
+ /* fill in the packet fields
+ */
+ hdr.atphd_ctrlinfo = ctrl;
+ hdr.atphd_bitmap = atpb->atp_bitmap;
+ hdr.atphd_tid = htons( tid );
+ *(pktbuf->atpbuf_info.atpbuf_data) = DDPTYPE_ATP;
+ memcpy(pktbuf->atpbuf_info.atpbuf_data + 1, &hdr, sizeof( struct atphdr ));
+ memcpy(pktbuf->atpbuf_info.atpbuf_data + ATP_HDRSIZE,
+ atpb->atp_sreqdata, atpb->atp_sreqdlen );
+
+ /* set length
+ */
+ pktbuf->atpbuf_dlen = ATP_HDRSIZE + atpb->atp_sreqdlen;
+}
+
+void atp_build_resp_packet( pktbuf, tid, ctrl, atpb, seqnum )
+ struct atpbuf *pktbuf;
+ u_int16_t tid;
+ u_int8_t ctrl;
+ struct atp_block *atpb;
+ u_int8_t seqnum;
+{
+ struct atphdr hdr;
+
+ /* fill in the packet fields */
+ *(pktbuf->atpbuf_info.atpbuf_data) = DDPTYPE_ATP;
+ hdr.atphd_ctrlinfo = ctrl;
+ hdr.atphd_bitmap = seqnum;
+ hdr.atphd_tid = htons( tid );
+ memcpy(pktbuf->atpbuf_info.atpbuf_data + 1, &hdr,
+ sizeof( struct atphdr ));
+ memcpy(pktbuf->atpbuf_info.atpbuf_data + ATP_HDRSIZE,
+ atpb->atp_sresiov[ seqnum ].iov_base,
+ atpb->atp_sresiov[ seqnum ].iov_len );
+
+ /* set length
+ */
+ pktbuf->atpbuf_dlen = ATP_HDRSIZE + atpb->atp_sresiov[ seqnum ].iov_len;
+}
+
+
+int
+atp_recv_atp( ah, fromaddr, func, tid, rbuf, wait )
+ ATP ah;
+ struct sockaddr_at *fromaddr;
+ u_int8_t *func;
+ u_int16_t tid;
+ char *rbuf;
+ int wait;
+{
+/*
+ Receive a packet from address fromaddr of the correct function type
+ and with the correct tid. fromaddr = AT_ANY... and function == ATP_TYPEANY
+ and tid == ATP_TIDANY can be used to wildcard match.
+
+ recv_atp returns the length of the packet received (or -1 if error)
+ The function code for the packet received is returned in *func (ATP_TREQ or
+ ATP_TRESP).
+*/
+ struct atpbuf *pq, *cq;
+ struct atphdr ahdr;
+ u_int16_t rfunc;
+ u_int16_t rtid;
+ int i;
+ int dlen = -1;
+ int recvlen;
+ struct sockaddr_at faddr;
+ SOCKLEN_T faddrlen;
+ struct atpbuf *inbuf;
+
+ tid = htons( tid );
+
+ /* first check the queue
+ */
+#ifdef EBUG
+ atp_print_bufuse( ah, "recv_atp checking queue" );
+#endif
+ for ( pq = NULL, cq = ah->atph_queue; cq != NULL;
+ pq = cq, cq = cq->atpbuf_next ) {
+ memcpy(&ahdr, cq->atpbuf_info.atpbuf_data + 1,
+ sizeof( struct atphdr ));
+ rfunc = ahdr.atphd_ctrlinfo & ATP_FUNCMASK;
+#ifdef EBUG
+ printf( "<%d> checking", getpid());
+ printf( " tid=%hu func=", ntohs( ahdr.atphd_tid ));
+ print_func( rfunc );
+ atp_print_addr( " from", &cq->atpbuf_addr );
+ putchar( '\n' );
+#endif
+ if ((( tid & ahdr.atphd_tid ) == ahdr.atphd_tid ) &&
+ (( *func & rfunc ) == rfunc )
+ && at_addr_eq( fromaddr, &cq->atpbuf_addr )) {
+ break;
+ }
+ }
+ if ( cq != NULL ) {
+ /* we found one in the queue -- copy to rbuf
+ */
+ dlen = cq->atpbuf_dlen;
+ *func = rfunc;
+ memcpy( fromaddr, &cq->atpbuf_addr, sizeof( struct sockaddr_at ));
+ memcpy( rbuf, cq->atpbuf_info.atpbuf_data, cq->atpbuf_dlen );
+
+ /* remove packet from queue and free buffer
+ */
+ if ( pq == NULL ) {
+ ah->atph_queue = NULL;
+ } else {
+ pq->atpbuf_next = cq->atpbuf_next;
+ }
+ atp_free_buf( cq );
+ return( dlen );
+ }
+
+ /* we need to get it the net -- call on ddp to receive a packet
+ */
+#ifdef EBUG
+ printf( "<%d>", getpid());
+ atp_print_addr( " waiting on address", &ah->atph_saddr );
+ printf( "\nfor tid=%hu func=", ntohs( tid ));
+ print_func( *func );
+ atp_print_addr( " from", fromaddr );
+ putchar( '\n' );
+#endif
+
+ do {
+#ifdef EBUG
+ fflush( stdout );
+#endif
+ faddrlen = sizeof( struct sockaddr_at );
+ memset( &faddr, 0, sizeof( struct sockaddr_at ));
+
+ if (( recvlen = netddp_recvfrom( ah->atph_socket, rbuf,
+ ATP_BUFSIZ, 0,
+ (struct sockaddr *) &faddr,
+ &faddrlen )) < 0 ) {
+ return -1;
+ }
+ memcpy( &ahdr, rbuf + 1, sizeof( struct atphdr ));
+ if ( recvlen >= ATP_HDRSIZE && *rbuf == DDPTYPE_ATP) {
+ /* this is a valid ATP packet -- check for a match */
+ rfunc = ahdr.atphd_ctrlinfo & ATP_FUNCMASK;
+ rtid = ahdr.atphd_tid;
+#ifdef EBUG
+ printf( "<%d> got tid=%hu func=", getpid(), ntohs( rtid ));
+ print_func( rfunc );
+ atp_print_addr( " from", &faddr );
+ putchar( '\n' );
+ bprint( rbuf, recvlen );
+#endif
+ if ( rfunc == ATP_TREL ) {
+ /* remove response from sent list */
+ for ( pq = NULL, cq = ah->atph_sent; cq != NULL;
+ pq = cq, cq = cq->atpbuf_next ) {
+ if ( at_addr_eq( &faddr, &cq->atpbuf_addr ) &&
+ cq->atpbuf_info.atpbuf_xo.atpxo_tid == ntohs( rtid ))
+ break;
+ }
+ if ( cq != NULL ) {
+#ifdef EBUG
+ printf( "<%d> releasing transaction %hu\n", getpid(), ntohs( rtid ));
+#endif
+ if ( pq == NULL ) {
+ ah->atph_sent = cq->atpbuf_next;
+ } else {
+ pq->atpbuf_next = cq->atpbuf_next;
+ }
+ for ( i = 0; i < 8; ++i ) {
+ if ( cq->atpbuf_info.atpbuf_xo.atpxo_packet[ i ]
+ != NULL ) {
+ atp_free_buf ( cq->atpbuf_info.atpbuf_xo.atpxo_packet[ i ] );
+ }
+ }
+ atp_free_buf( cq );
+ }
+
+ } else if ((( tid & rtid ) == rtid ) &&
+ (( *func & rfunc ) == rfunc ) &&
+ at_addr_eq( fromaddr, &faddr )) { /* got what we wanted */
+ *func = rfunc;
+ dlen = recvlen;
+ memcpy( fromaddr, &faddr, sizeof( struct sockaddr_at ));
+
+ } else {
+ /* add packet to incoming queue */
+#ifdef EBUG
+ printf( "<%d> queuing incoming...\n", getpid() );
+#endif
+ if (( inbuf = atp_alloc_buf()) == NULL ) {
+ return -1;
+ }
+ memcpy( &inbuf->atpbuf_addr, &faddr,
+ sizeof( struct sockaddr_at ));
+ inbuf->atpbuf_next = ah->atph_queue;
+ inbuf->atpbuf_dlen = recvlen;
+ memcpy( inbuf->atpbuf_info.atpbuf_data, rbuf, recvlen );
+ }
+ }
+ if ( !wait && dlen < 0 ) {
+ return( 0 );
+ }
+
+ } while ( dlen < 0 );
+
+ return( dlen );
+}
+
+
+int at_addr_eq( paddr, saddr )
+ struct sockaddr_at *paddr; /* primary address */
+ struct sockaddr_at *saddr; /* secondary address */
+{
+/* compare two atalk addresses -- only check the non-zero fields
+ of paddr against saddr.
+ return zero if not equal, non-zero if equal
+*/
+ return (( paddr->sat_port == ATADDR_ANYPORT || paddr->sat_port == saddr->sat_port )
+ && ( paddr->sat_addr.s_net == ATADDR_ANYNET ||
+ paddr->sat_addr.s_net == saddr->sat_addr.s_net )
+ && ( paddr->sat_addr.s_node == ATADDR_ANYNODE ||
+ paddr->sat_addr.s_node == saddr->sat_addr.s_node ));
+}
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include <netinet/in.h>
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <atalk/atp.h>
+
+#include "atp_internals.h"
+
+
+/* wait for a tranasaction service request
+*/
+int atp_rreq( ah, atpb )
+ ATP ah; /* open atp handle */
+ struct atp_block *atpb; /* parameter block */
+{
+ struct atpbuf *req_buf; /* for receiving request packet */
+ struct atphdr req_hdr; /* request header overlay */
+ struct sockaddr_at faddr; /* sender's address */
+ int recvlen; /* length of received packet */
+ u_int16_t tid;
+ int rc;
+ u_int8_t func;
+
+#ifdef EBUG
+ atp_print_bufuse( ah, "atp_rreq" );
+#endif
+
+ while (( rc = atp_rsel( ah, atpb->atp_saddr, ATP_TREQ )) == 0 ) {
+ ;
+ }
+
+ if ( rc != ATP_TREQ ) {
+#ifdef EBUG
+ printf( "<%d> atp_rreq: atp_rsel returns err %d\n", getpid(), rc );
+#endif EBUG
+ return( rc );
+ }
+
+ /* allocate a buffer for receiving request
+ */
+ if (( req_buf = atp_alloc_buf()) == NULL ) {
+ return -1;
+ }
+
+ memcpy( &faddr, atpb->atp_saddr, sizeof( struct sockaddr_at ));
+ func = ATP_TREQ;
+ if (( recvlen = atp_recv_atp( ah, &faddr, &func, ATP_TIDANY,
+ req_buf->atpbuf_info.atpbuf_data, 1 )) < 0 ) {
+ atp_free_buf( req_buf );
+ return -1;
+ }
+
+ memcpy( &req_hdr, req_buf->atpbuf_info.atpbuf_data + 1,
+ sizeof( struct atphdr ));
+ tid = ntohs( req_hdr.atphd_tid );
+
+ ah->atph_rtid = tid;
+ if (( ah->atph_rxo = req_hdr.atphd_ctrlinfo & ATP_XO ) != 0 ) {
+ ah->atph_rreltime = ATP_RELTIME *
+ ( 1 << ( req_hdr.atphd_ctrlinfo & ATP_TRELMASK ));
+ }
+
+ memcpy( atpb->atp_saddr, &faddr, sizeof( struct sockaddr_at ));
+
+ if ( recvlen - ATP_HDRSIZE > atpb->atp_rreqdlen ) {
+ atp_free_buf( req_buf );
+ errno = EMSGSIZE;
+ return -1;
+ }
+
+ atpb->atp_rreqdlen = recvlen - ATP_HDRSIZE;
+ memcpy( atpb->atp_rreqdata,
+ req_buf->atpbuf_info.atpbuf_data + ATP_HDRSIZE,
+ recvlen - ATP_HDRSIZE );
+ atpb->atp_bitmap = req_hdr.atphd_bitmap;
+ atp_free_buf( req_buf );
+ return( 0 );
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <sys/uio.h>
+
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/util.h>
+
+#ifdef EBUG
+#include <stdio.h>
+#endif
+
+#include "atp_internals.h"
+
+int
+atp_rresp( ah, atpb )
+ ATP ah; /* open atp handle */
+ struct atp_block *atpb; /* parameter block */
+{
+ int len, i, rc;
+
+#ifdef EBUG
+ atp_print_bufuse( ah, "atp_rresp" );
+#endif
+ /* check parameters
+ */
+ if ( atpb->atp_rresiovcnt <= 0 || atpb->atp_rresiovcnt > 8 ) {
+ errno = EINVAL;
+ return( -1 );
+ }
+
+ while (( rc = atp_rsel( ah, atpb->atp_saddr, ATP_TRESP )) == 0 ) {
+ ;
+ }
+
+ if ( rc != ATP_TRESP ) {
+ return( rc );
+ }
+
+ for ( i = 0; i < 8; ++i ) {
+ if ( ah->atph_resppkt[ i ] == NULL ) {
+ break;
+ }
+ len = ah->atph_resppkt[ i ]->atpbuf_dlen - ATP_HDRSIZE;
+ if ( i > atpb->atp_rresiovcnt - 1 ||
+ len > atpb->atp_rresiov[ i ].iov_len ) {
+ errno = EMSGSIZE;
+ return( -1 );
+ }
+#ifdef EBUG
+ fprintf( stderr, "atp_rresp copying %d bytes packet %d\n",
+ len, i );
+ bprint( (char *)ah->atph_resppkt[ i ]->atpbuf_info.atpbuf_data,
+ len + ATP_HDRSIZE );
+#endif
+ memcpy(atpb->atp_rresiov[ i ].iov_base,
+ ah->atph_resppkt[ i ]->atpbuf_info.atpbuf_data + ATP_HDRSIZE,
+ len );
+ atpb->atp_rresiov[ i ].iov_len = len;
+ atp_free_buf( ah->atph_resppkt[ i ] );
+ ah->atph_resppkt[ i ] = NULL;
+ }
+ atpb->atp_rresiovcnt = i;
+
+ return( 0 );
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1997 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <signal.h>
+#include <errno.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+
+#include <atalk/netddp.h>
+#include <atalk/compat.h>
+#include <atalk/atp.h>
+#include <atalk/util.h>
+
+#include "atp_internals.h"
+
+#ifdef DROP_ATPTREL
+static int release_count = 0;
+#endif DROP_ATPTREL
+
+
+static int
+resend_request( ah )
+ ATP ah;
+{
+ /*
+ * update bitmap and send request packet
+ */
+ struct atphdr req_hdr;
+
+#ifdef EBUG
+ printf( "\n<%d> resend_request: resending %d byte request packet",
+ getpid(), ah->atph_reqpkt->atpbuf_dlen );
+ atp_print_addr( " to", &ah->atph_reqpkt->atpbuf_addr );
+ putchar( '\n' );
+ bprint( ah->atph_reqpkt->atpbuf_info.atpbuf_data,
+ ah->atph_reqpkt->atpbuf_dlen );
+#endif
+
+ memcpy( &req_hdr, ah->atph_reqpkt->atpbuf_info.atpbuf_data + 1,
+ sizeof( struct atphdr ));
+ req_hdr.atphd_bitmap = ah->atph_rbitmap;
+ memcpy( ah->atph_reqpkt->atpbuf_info.atpbuf_data + 1, &req_hdr,
+ sizeof( struct atphdr ));
+
+ gettimeofday( &ah->atph_reqtv, (struct timezone *)0 );
+ if ( netddp_sendto( ah->atph_socket,
+ ah->atph_reqpkt->atpbuf_info.atpbuf_data,
+ ah->atph_reqpkt->atpbuf_dlen, 0,
+ (struct sockaddr *) &ah->atph_reqpkt->atpbuf_addr,
+ sizeof( struct sockaddr_at )) != ah->atph_reqpkt->atpbuf_dlen ) {
+ return( -1 );
+ }
+
+ if ( ah->atph_reqtries > 0 ) {
+ --(ah->atph_reqtries);
+ }
+
+ return( 0 );
+}
+
+int
+atp_rsel( ah, faddr, func )
+ ATP ah; /* open atp handle */
+ struct sockaddr_at *faddr; /* address to receive from */
+ int func; /* which function(s) to wait for;
+ 0 means request or response */
+{
+ struct atpbuf *abuf, *pb, *cb;
+ struct atphdr req_hdr, resp_hdr;
+ fd_set fds;
+ int i, recvlen, requesting, mask, c;
+ u_int8_t rfunc;
+ u_int16_t tid;
+ struct timeval tv;
+ struct sockaddr_at saddr;
+
+#ifdef EBUG
+ atp_print_bufuse( ah, "atp_rsel at top" );
+#endif
+ if ( func == 0 ) {
+ func = ATP_FUNCANY;
+ }
+
+ requesting = ( func & ATP_TRESP ) && ah->atph_rrespcount > 0 &&
+ ( ah->atph_reqtries > 0 || ah->atph_reqtries == ATP_TRIES_INFINITE );
+
+ if ( requesting && ah->atph_rbitmap == 0 ) {
+ /*
+ * we already have a complete atp response; just return
+ */
+ return( ATP_TRESP );
+ }
+
+ if (( abuf = atp_alloc_buf()) == NULL ) {
+ return( -1 );
+ }
+
+ if ( requesting ) {
+#ifdef EBUG
+ printf( "<%d> atp_rsel: request pending\n", getpid());
+#endif
+ gettimeofday( &tv, (struct timezone *)0 );
+ if ( tv.tv_sec - ah->atph_reqtv.tv_sec > ah->atph_reqto ) {
+ if ( resend_request( ah ) < 0 ) {
+ atp_free_buf( abuf );
+ return( -1 );
+ }
+ }
+ }
+
+ for ( ;; ) {
+ rfunc = func;
+ if ( requesting ) {
+ FD_ZERO( &fds );
+ FD_SET( ah->atph_socket, &fds );
+ tv.tv_sec = ah->atph_reqto;
+ tv.tv_usec = 0;
+ if (( c = select( ah->atph_socket + 1, &fds, NULL, NULL,
+ &tv )) < 0 ) {
+ atp_free_buf( abuf );
+ return( -1 );
+ }
+ if ( c == 0 || FD_ISSET( ah->atph_socket, &fds ) == 0 ) {
+ recvlen = -1;
+ errno = EINTR;
+ goto timeout;
+ }
+ }
+ memcpy( &saddr, faddr, sizeof( struct sockaddr_at ));
+#ifdef EBUG
+ printf( "<%d> atp_rsel calling recv_atp,", getpid());
+ atp_print_addr( " accepting from: ", &saddr );
+ putchar( '\n' );
+#endif EBUG
+ if (( recvlen = atp_recv_atp( ah, &saddr, &rfunc, ATP_TIDANY,
+ abuf->atpbuf_info.atpbuf_data, 0 )) >= 0 ) {
+ break; /* we received something */
+ }
+
+timeout :
+ if ( !requesting || errno != EINTR ) {
+ break; /* error */
+ }
+
+ if ( ah->atph_reqtries <= 0 &&
+ ah->atph_reqtries != ATP_TRIES_INFINITE ) {
+ errno = ETIMEDOUT;
+ break;
+ }
+
+ if ( resend_request( ah ) < 0 ) {
+ break; /* error */
+ }
+ }
+
+ if ( recvlen <= 0 ) { /* error */
+ atp_free_buf( abuf );
+ return( recvlen );
+ }
+
+#ifdef EBUG
+ printf( "<%d> atp_rsel: rcvd %d bytes", getpid(), recvlen );
+ atp_print_addr( " from: ", &saddr );
+ putchar( '\n' );
+ bprint( abuf->atpbuf_info.atpbuf_data, recvlen );
+#endif
+
+ abuf->atpbuf_dlen = recvlen;
+ memcpy( &resp_hdr, abuf->atpbuf_info.atpbuf_data + 1,
+ sizeof( struct atphdr ));
+
+ if ( rfunc == ATP_TREQ ) {
+ /*
+ * we got a request: check to see if it is a duplicate (XO)
+ * while we are at it, we expire old XO responses from sent list
+ */
+ memcpy( &req_hdr, abuf->atpbuf_info.atpbuf_data + 1,
+ sizeof( struct atphdr ));
+ tid = ntohs( req_hdr.atphd_tid );
+ gettimeofday( &tv, (struct timezone *)0 );
+ for ( pb = NULL, cb = ah->atph_sent; cb != NULL;
+ pb = cb, cb = cb->atpbuf_next ) {
+#ifdef EBUG
+ printf( "<%d>", getpid());
+ atp_print_addr( " examining", &cb->atpbuf_addr );
+ printf( " %hu", cb->atpbuf_info.atpbuf_xo.atpxo_tid );
+ atp_print_addr( " (looking for", &saddr );
+ printf( " %hu)\n", tid );
+#endif
+ if ( tv.tv_sec - cb->atpbuf_info.atpbuf_xo.atpxo_tv.tv_sec
+ > cb->atpbuf_info.atpbuf_xo.atpxo_reltime ) {
+ /* discard expired response */
+#ifdef EBUG
+ printf( "<%d> expiring tid %hu\n", getpid(),
+ cb->atpbuf_info.atpbuf_xo.atpxo_tid );
+#endif
+ if ( pb == NULL ) {
+ ah->atph_sent = cb->atpbuf_next;
+ } else {
+ pb->atpbuf_next = cb->atpbuf_next;
+ }
+
+ for ( i = 0; i < 8; ++i ) {
+ if ( cb->atpbuf_info.atpbuf_xo.atpxo_packet[ i ]
+ != NULL ) {
+ atp_free_buf( cb->atpbuf_info.atpbuf_xo.atpxo_packet[ i ] );
+ }
+ }
+ atp_free_buf( cb );
+
+ if (( cb = pb ) == NULL )
+ break;
+
+ } else if ( at_addr_eq( &saddr, &cb->atpbuf_addr )) {
+ if ( cb->atpbuf_info.atpbuf_xo.atpxo_tid == tid ) {
+ break;
+ }
+ }
+ }
+
+ if ( cb != NULL ) {
+#ifdef EBUG
+ printf( "<%d> duplicate request -- re-sending XO resp\n",
+ getpid());
+#endif
+ /* matches an old response -- just re-send and reset expire */
+ cb->atpbuf_info.atpbuf_xo.atpxo_tv = tv;
+ for ( i = 0; i < 8; ++i ) {
+ if ( cb->atpbuf_info.atpbuf_xo.atpxo_packet[i] != NULL &&
+ req_hdr.atphd_bitmap & ( 1 << i )) {
+ netddp_sendto( ah->atph_socket,
+ cb->atpbuf_info.atpbuf_xo.atpxo_packet[i]->atpbuf_info.atpbuf_data,
+ cb->atpbuf_info.atpbuf_xo.atpxo_packet[i]->atpbuf_dlen,
+ 0, (struct sockaddr *) &saddr, sizeof( struct sockaddr_at));
+ }
+ }
+ }
+
+ if ( cb == NULL ) {
+ /* new request -- queue it and return */
+ memcpy( &abuf->atpbuf_addr, &saddr, sizeof( struct sockaddr_at ));
+ memcpy( faddr, &saddr, sizeof( struct sockaddr_at ));
+ abuf->atpbuf_next = ah->atph_queue;
+ ah->atph_queue = abuf;
+ return( ATP_TREQ );
+ } else {
+ atp_free_buf( abuf );
+ return( 0 );
+ }
+ }
+
+ /*
+ * we got a response: update bitmap
+ */
+ memcpy( &req_hdr, ah->atph_reqpkt->atpbuf_info.atpbuf_data + 1,
+ sizeof( struct atphdr ));
+ if ( requesting && ah->atph_rbitmap & ( 1<<resp_hdr.atphd_bitmap )
+ && req_hdr.atphd_tid == resp_hdr.atphd_tid ) {
+ ah->atph_rbitmap &= ~( 1<<resp_hdr.atphd_bitmap );
+
+ if ( ah->atph_resppkt[ resp_hdr.atphd_bitmap ] != NULL ) {
+ atp_free_buf( ah->atph_resppkt[ resp_hdr.atphd_bitmap ] );
+ }
+ ah->atph_resppkt[ resp_hdr.atphd_bitmap ] = abuf;
+
+ /* if End Of Message, clear all higher bitmap bits
+ */
+ if ( resp_hdr.atphd_ctrlinfo & ATP_EOM ) {
+#ifdef EBUG
+ printf( "<%d> EOM -- seq num %d current bitmap %d\n",
+ getpid(), resp_hdr.atphd_bitmap, ah->atph_rbitmap );
+#endif
+ mask = 1 << resp_hdr.atphd_bitmap;
+ ah->atph_rbitmap &= ( mask | (mask-1) );
+ }
+
+ /* if Send Trans. Status, send updated request
+ */
+ if ( resp_hdr.atphd_ctrlinfo & ATP_STS ) {
+#ifdef EBUG
+ puts( "STS" );
+#endif
+ req_hdr.atphd_bitmap = ah->atph_rbitmap;
+ memcpy(ah->atph_reqpkt->atpbuf_info.atpbuf_data + 1,
+ &req_hdr, sizeof( struct atphdr ));
+ if ( netddp_sendto( ah->atph_socket,
+ ah->atph_reqpkt->atpbuf_info.atpbuf_data,
+ ah->atph_reqpkt->atpbuf_dlen, 0,
+ (struct sockaddr *) &ah->atph_reqpkt->atpbuf_addr,
+ sizeof( struct sockaddr_at )) !=
+ ah->atph_reqpkt->atpbuf_dlen ) {
+ atp_free_buf( abuf );
+ return( -1 );
+ }
+ }
+ } else {
+ /*
+ * we are not expecting this response -- toss it
+ */
+ atp_free_buf( abuf );
+#ifdef EBUG
+ printf( "atp_rsel: ignoring resp bm=%x tid=%d (expected %x/%d)\n",
+ resp_hdr.atphd_bitmap, ntohs( resp_hdr.atphd_tid ),
+ ah->atph_rbitmap, ah->atph_tid );
+#endif EBUG
+ }
+
+ if ( !ah->atph_rbitmap && ( req_hdr.atphd_ctrlinfo & ATP_XO )) {
+ /*
+ * successful completion - send release
+ * the release consists of DDP type byte + ATP header + 4 user bytes
+ */
+ req_hdr.atphd_ctrlinfo = ATP_TREL;
+ memcpy( ah->atph_reqpkt->atpbuf_info.atpbuf_data + 1, &req_hdr,
+ sizeof( struct atphdr ));
+ memset( ah->atph_reqpkt->atpbuf_info.atpbuf_data + ATP_HDRSIZE, 0, 4 );
+ ah->atph_reqpkt->atpbuf_dlen = sizeof( struct atphdr ) + ATP_HDRSIZE;
+#ifdef EBUG
+ printf( "<%d> sending TREL", getpid() );
+ bprint( ah->atph_reqpkt->atpbuf_info.atpbuf_data,
+ ah->atph_reqpkt->atpbuf_dlen );
+#endif
+#ifdef DROP_ATPTREL
+ if (( ++release_count % 10 ) != 0 ) {
+#endif DROP_ATPTREL
+ netddp_sendto( ah->atph_socket,
+ ah->atph_reqpkt->atpbuf_info.atpbuf_data,
+ ah->atph_reqpkt->atpbuf_dlen, 0,
+ (struct sockaddr *) &ah->atph_reqpkt->atpbuf_addr,
+ sizeof( struct sockaddr_at));
+#ifdef DROP_ATPTREL
+ }
+#endif DROP_ATPTREL
+ }
+
+ if ( ah->atph_rbitmap != 0 ) {
+ if ( ah->atph_reqtries > 0
+ || ah->atph_reqtries == ATP_TRIES_INFINITE ) {
+ return( 0 );
+ } else {
+ errno = ETIMEDOUT;
+ return( -1 );
+ }
+ }
+
+ memcpy( faddr, &saddr, sizeof( struct sockaddr_at ));
+ return( ATP_TRESP );
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <signal.h>
+
+#include <netinet/in.h>
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+
+#include <atalk/netddp.h>
+#include <atalk/atp.h>
+#include <atalk/util.h>
+
+#include "atp_internals.h"
+
+int
+atp_sreq( ah, atpb, respcount, flags )
+ ATP ah; /* open atp handle */
+ struct atp_block *atpb; /* parameter block */
+ int respcount; /* buffers available for response */
+ u_int8_t flags; /* ATP_XO, ATP_TREL?? */
+{
+ struct atpbuf *req_buf;
+ int i;
+
+#ifdef EBUG
+ atp_print_bufuse( ah, "atp_sreq" );
+#endif
+
+ /* check parameters
+ */
+ if ( atpb->atp_sreqdlen < 4 || atpb->atp_sreqdlen > ATP_MAXDATA
+ || ( respcount < 0 ) || ( respcount > 8 )
+ || ( atpb->atp_sreqto < 0 ) || (( atpb->atp_sreqtries < 1 )
+ && ( atpb->atp_sreqtries != ATP_TRIES_INFINITE ))) {
+ errno = EINVAL;
+ return -1;
+ }
+ /* clean up any packet fragments left from last request
+ */
+ for ( i = 0; i < 8; ++i ) {
+ if ( ah->atph_resppkt[ i ] != NULL ) {
+ atp_free_buf( ah->atph_resppkt[ i ] );
+ ah->atph_resppkt[ i ] = NULL;
+ }
+ }
+
+ /* generate bitmap, tid and ctrlinfo
+ */
+ atpb->atp_bitmap = ( 1 << respcount ) - 1;
+
+ /* allocate a new buffer and build request packet
+ */
+ if (( req_buf = atp_alloc_buf()) == NULL ) {
+ return( -1 );
+ }
+ atp_build_req_packet( req_buf, ah->atph_tid++, flags | ATP_TREQ, atpb );
+ memcpy( &req_buf->atpbuf_addr, atpb->atp_saddr,
+ sizeof( struct sockaddr_at ));
+
+ /* send the initial request
+ */
+#ifdef EBUG
+ printf( "\n<%d> atp_sreq: sending a %d byte packet ", getpid(),
+ req_buf->atpbuf_dlen );
+ atp_print_addr( " to", atpb->atp_saddr );
+ putchar( '\n' );
+ bprint( req_buf->atpbuf_info.atpbuf_data, req_buf->atpbuf_dlen );
+#endif
+
+ gettimeofday( &ah->atph_reqtv, (struct timezone *)0 );
+#ifdef DROPPACKETS
+if (( random() % 3 ) != 2 ) {
+#endif
+ if ( netddp_sendto( ah->atph_socket, req_buf->atpbuf_info.atpbuf_data,
+ req_buf->atpbuf_dlen, 0, (struct sockaddr *) atpb->atp_saddr,
+ sizeof( struct sockaddr_at )) != req_buf->atpbuf_dlen ) {
+ atp_free_buf( req_buf );
+ return( -1 );
+ }
+#ifdef DROPPACKETS
+} else printf( "<%d> atp_sreq: dropped request\n", getpid() );
+#endif
+
+ if ( atpb->atp_sreqto != 0 ) {
+ if ( ah->atph_reqpkt != NULL ) {
+ atp_free_buf( ah->atph_reqpkt );
+ }
+ ah->atph_reqto = atpb->atp_sreqto;
+ if ( atpb->atp_sreqtries == ATP_TRIES_INFINITE ) {
+ ah->atph_reqtries = ATP_TRIES_INFINITE;
+ } else {
+ /* we already sent one */
+ ah->atph_reqtries = atpb->atp_sreqtries - 1;
+ }
+ ah->atph_reqpkt = req_buf;
+ ah->atph_rbitmap = ( 1 << respcount ) - 1;
+ ah->atph_rrespcount = respcount;
+ } else {
+ atp_free_buf( req_buf );
+ ah->atph_rrespcount = 0;
+ }
+
+ return( 0 );
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+
+#include <atalk/netddp.h>
+#include <atalk/atp.h>
+#include <atalk/util.h>
+
+#include "atp_internals.h"
+
+/* send a transaction response
+*/
+int atp_sresp( ah, atpb )
+ ATP ah; /* open atp handle */
+ struct atp_block *atpb; /* parameter block */
+{
+ int i;
+ u_int8_t ctrlinfo;
+ struct atpbuf *resp_buf;
+ struct atpbuf *save_buf;
+
+#ifdef EBUG
+ atp_print_bufuse( ah, "atp_sresp" );
+#endif
+
+ /* check parameters
+ */
+ for ( i = atpb->atp_sresiovcnt - 1; i >= 0; --i ) {
+ if ( atpb->atp_sresiov[ i ].iov_len > ATP_MAXDATA )
+ break;
+ }
+ if ( i >= 0 || atpb->atp_sresiovcnt < 1 || atpb->atp_sresiovcnt > 8 ) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* allocate a new buffer for reponse packet construction
+ */
+ if (( resp_buf = atp_alloc_buf()) == NULL ) {
+ return -1;
+ }
+
+ /* send all the response packets
+ * also need to attach list to ah for dup. detection (if XO)
+ */
+#ifdef EBUG
+ printf( "<%d> preparing to send %s response tid=%hu ", getpid(),
+ ah->atph_rxo ? "XO" : "", ah->atph_rtid );
+ atp_print_addr( " to", atpb->atp_saddr );
+ putchar( '\n' );
+#endif
+ if ( ah->atph_rxo ) {
+ if (( save_buf = atp_alloc_buf()) == NULL ) {
+ return -1;
+ }
+ for ( i = 0; i < 8;
+ save_buf->atpbuf_info.atpbuf_xo.atpxo_packet[ i++ ] = NULL );
+ }
+ for ( i = 0; i < atpb->atp_sresiovcnt; ++i ) {
+ ctrlinfo = ATP_TRESP;
+#ifdef STS_RESPONSES
+ ctrlinfo |= ATP_STS;
+#endif STS_RESPONSES
+ if ( i == atpb->atp_sresiovcnt-1 ) {
+ ctrlinfo |= ATP_EOM;
+ }
+ atp_build_resp_packet( resp_buf, ah->atph_rtid, ctrlinfo, atpb, i );
+
+ if ( ah->atph_rxo ) {
+ save_buf->atpbuf_info.atpbuf_xo.atpxo_packet[i] = resp_buf;
+ }
+#ifdef DROPPACKETS
+if (( random() % 3 ) != 2 ) {
+#endif
+#ifdef EBUG
+printf( "<%d> sending packet tid=%hu serial no.=%d\n", getpid(),
+ ah->atph_rtid, i );
+bprint( resp_buf->atpbuf_info.atpbuf_data, resp_buf->atpbuf_dlen );
+#endif
+ if ( netddp_sendto( ah->atph_socket, resp_buf->atpbuf_info.atpbuf_data,
+ resp_buf->atpbuf_dlen, 0, (struct sockaddr *) atpb->atp_saddr,
+ sizeof( struct sockaddr_at )) != resp_buf->atpbuf_dlen ) {
+ if ( ah->atph_rxo ) {
+ for ( ; i >= 0; --i ) {
+ atp_free_buf( save_buf->atpbuf_info.atpbuf_xo.atpxo_packet[ i ] );
+ }
+ atp_free_buf( save_buf );
+ }
+ return -1;
+ }
+#ifdef DROPPACKETS
+} else printf( "<%d> atp_sresp: dropped serial no. %d\n", getpid(), i );
+#endif
+ /* allocate a buffer for next packet (if XO mode)
+ */
+ if ( ah->atph_rxo && ( resp_buf = atp_alloc_buf()) == NULL ) {
+ return -1;
+ }
+ }
+ atp_free_buf( resp_buf );
+ if ( ah->atph_rxo ) {
+ /* record timestamp, tid, release time, and destination address
+ */
+ gettimeofday( &save_buf->atpbuf_info.atpbuf_xo.atpxo_tv,
+ (struct timezone *) 0 );
+ save_buf->atpbuf_info.atpbuf_xo.atpxo_tid = ah->atph_rtid;
+ save_buf->atpbuf_info.atpbuf_xo.atpxo_reltime = ah->atph_rreltime;
+ memcpy( &save_buf->atpbuf_addr, atpb->atp_saddr,
+ sizeof( struct sockaddr_at ));
+
+ /* add to list of packets we have sent
+ */
+ save_buf->atpbuf_next = ah->atph_sent;
+ ah->atph_sent = save_buf;
+#ifdef EBUG
+printf( "<%d> saved XO response\n", getpid());
+#endif
+ }
+ return 0;
+}
--- /dev/null
+SRC = cnid_open.c cnid_close.c cnid_add.c cnid_get.c cnid_delete.c \
+ cnid_update.c cnid_resolve.c cnid_lookup.c cnid_nextid.c
+OBJ = cnid_open.o cnid_close.o cnid_add.o cnid_get.o cnid_delete.o \
+ cnid_update.o cnid_resolve.o cnid_lookup.o cnid_nextid.o
+
+INCPATH= -I../../include -I${DB2DIR}/include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH}
+
+TAGSFILE= tags
+CC= cc
+
+all : profiled cnidlib
+
+profiled:
+ -mkdir profiled
+
+cnidlib cnidlib_p : ${OBJ}
+ @echo "building profiled cnidlib"
+ @cd profiled; ar cru ../cnidlib_p ${OBJ}
+ @echo "building normal cnidlib"
+ @ar cru cnidlib ${OBJ}
+
+.c.o :
+ ${CC} -p ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- profiled/$*.o
+ ${CC} ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- $*.o
+
+clean :
+ rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+ rm -f cnidlib cnidlib_p
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
--- /dev/null
+the catalog database keeps track of three mappings:
+ CNID -> dev/ino and did/name
+ dev/ino -> CNID
+ did/name -> CNID
+
+dev/ino is used to keep track of magically moved files. did/name is
+for quick lookups of CNIDs.
+
+NOTE: the database will append a nul byte to the end of name. in
+addition, name should be given as it appears on disk. this allows the
+creation of cnid updating/cleaning programs that don't have to deal
+with knowing what the particular codepage is.
+
+here's the ritual:
+ 1) open a volume. call cnid_open.
+ 2) every time you need a CNID, call cnid_add(). it will
+ automatically look for an existing cnid and add a new one
+ if one isn't already there. you can pass a hint if you
+ want. the only use this has right now is to enable
+ consistency between AFP and HFS. in the future, it would
+ allow people to write conversion utilities that
+ pre-instantiate a database without needing to re-assign
+ CNIDs.
+ 3) if you want to just look for a CNID without automatically
+ adding one in, you have two choices:
+ a) cnid_resolve takes a CNID, returns name, and
+ over-writes the CNID given with the parent DID. this
+ is good for FPResolveID.
+ b) cnid_lookup returns a CNID corresponding to the
+ dev/ino,did/name keys. it will auto-update the catalog
+ database if there's a discrepancy.
+ NOTE: cnid_add calls this before adding a new CNID.
+ 4) when you delete a file or directory, you need to call
+ cnid_delete with the CNID for that file/directory.
+ 5) if you're just moving a CNID, call cnid_move with the CNID
+ and the new dev/ino,did/name pairs.
+ 6) call cnid_close when closing the volume.
--- /dev/null
+/*
+ * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * cnid_add (db, dev, ino, did, name, hint):
+ * add a name to the CNID database. we use both dev/ino and did/name
+ * to keep track of things. */
+#include <stdio.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include <db.h>
+#include <netatalk/endian.h>
+
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+#include <atalk/util.h>
+
+#include "cnid_private.h"
+
+/* add an entry to the CNID databases. we do this as a transaction
+ * to prevent messiness. */
+static int add_cnid(CNID_private *db, DBT *key, DBT *data)
+{
+ DBT altkey, altdata;
+ DB_TXN *tid;
+ DB_TXNMGR *txnp;
+
+ txnp = db->dbenv.tx_info;
+ memset(&altkey, 0, sizeof(altkey));
+ memset(&altdata, 0, sizeof(altdata));
+
+retry:
+ if (errno = txn_begin(txnp, NULL, &tid)) {
+ return errno;
+ }
+
+ /* main database */
+ if (errno = db->db_cnid->put(db->db_cnid, tid,
+ key, data, DB_NOOVERWRITE)) {
+ txn_abort(tid);
+ if (errno == EAGAIN)
+ goto retry;
+
+ return errno;
+ }
+
+ /* dev/ino database */
+ altkey.data = data->data;
+ altkey.size = CNID_DEVINO_LEN;
+ altdata.data = key->data;
+ altdata.size = key->size;
+ if ((errno = db->db_devino->put(db->db_devino, tid,
+ &altkey, &altdata, 0))) {
+ txn_abort(tid);
+ if (errno == EAGAIN)
+ goto retry;
+
+ return errno;
+ }
+
+ /* did/name database */
+ altkey.data = data->data + CNID_DEVINO_LEN;
+ altkey.size = data->size - CNID_DEVINO_LEN;
+ if (errno = db->db_didname->put(db->db_didname, tid,
+ &altkey, &altdata, 0)) {
+ txn_abort(tid);
+ if (errno == EAGAIN)
+ goto retry;
+
+ return errno;
+ }
+
+ return txn_commit(tid);
+}
+
+/* 0 is not a valid cnid. this will do a cnid_lookup beforehand and
+ return that cnid if it exists. */
+cnid_t cnid_add(void *CNID, const struct stat *st,
+ const cnid_t did, const char *name, const int len,
+ cnid_t hint)
+{
+ CNID_private *db;
+ DBT key, data;
+ struct flock lock;
+ cnid_t id, save;
+
+
+ if (!(db = CNID) || !st || !name)
+ return 0;
+
+ /* just do a lookup if RootInfo is read-only. */
+ if (db->flags & (CNIDFLAG_ROOTINFO_RO | CNIDFLAG_DB_RO))
+ return cnid_lookup(db, st, did, name, len);
+
+ /* initialize everything */
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ /* acquire a lock on RootInfo. as the cnid database is the only user
+ * of RootInfo, we just use our own locks.
+ *
+ * NOTE: we lock it here to serialize access to the database. */
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = ad_getentryoff(&db->rootinfo, ADEID_DID);
+ lock.l_len = ad_getentrylen(&db->rootinfo, ADEID_DID);
+ if (fcntl(ad_hfileno(&db->rootinfo), F_SETLKW, &lock) < 0) {
+ syslog(LOG_ERR, "cnid_add: can't establish lock: %m");
+ goto cleanup_err;
+ }
+
+ /* if it's already stored, just return it */
+ if ((id = cnid_lookup(db, st, did, name, len))) {
+ hint = id;
+ goto cleanup_unlock;
+ }
+
+ /* just set hint, and the key will change. */
+ key.data = &hint;
+ key.size = sizeof(hint);
+
+ if ((data.data =
+ make_cnid_data(st, did, name, len)) == NULL) {
+ syslog(LOG_ERR, "cnid_add: path name too long.");
+ goto cleanup_unlock;
+ }
+ data.size = CNID_HEADER_LEN + len + 1;
+
+ /* start off with the hint. it should be in network byte order.
+ * we need to make sure that somebody doesn't add in restricted
+ * cnid's to the database. */
+ if (ntohl(hint) >= CNID_START) {
+ /* if the key doesn't exist, add it in. don't fiddle with nextID. */
+ errno = add_cnid(db, &key, &data);
+ switch (errno) {
+ case DB_KEYEXIST: /* need to use RootInfo after all. */
+ break;
+ default:
+ syslog(LOG_ERR, "cnid_add: unable to add CNID(%x)", hint);
+ hint = 0;
+ /* fall through */
+ case 0:
+ goto cleanup_unlock;
+ }
+ }
+
+ /* no need to refresh the header file */
+ memcpy(&hint, ad_entry(&db->rootinfo, ADEID_DID), sizeof(hint));
+
+ /* search for a new id. we keep the first id around to check for
+ * wrap-around. NOTE: i do it this way so that we can go back and
+ * fill in holes. */
+ save = id = ntohl(hint);
+ while (errno = add_cnid(db, &key, &data)) {
+ /* don't use any of the special CNIDs */
+ if (++id < CNID_START)
+ id = CNID_START;
+
+ if ((errno != DB_KEYEXIST) || (save == id)) {
+ syslog(LOG_ERR, "cnid_add: unable to add CNID(%x)", hint);
+ hint = 0;
+ goto cleanup_unlock;
+ }
+ hint = htonl(id);
+ }
+
+ /* update RootInfo with the next id. */
+ id = htonl(++id);
+ memcpy(ad_entry(&db->rootinfo, ADEID_DID), &id, sizeof(id));
+ ad_flush(&db->rootinfo, ADFLAGS_HF);
+
+cleanup_unlock:
+ lock.l_type = F_UNLCK;
+ fcntl(ad_hfileno(&db->rootinfo), F_SETLK, &lock);
+ return hint;
+
+cleanup_err:
+ return 0;
+}
--- /dev/null
+#include <unistd.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <db.h>
+#include <errno.h>
+
+#include <atalk/cnid.h>
+
+#include "cnid_private.h"
+
+void cnid_close(void *CNID)
+{
+ CNID_private *db;
+
+ if (!(db = CNID))
+ return;
+
+ /* flush the transaction log and delete the log file if we can. */
+ if ((db->lockfd > -1) && ((db->flags & CNIDFLAG_DB_RO) == 0)) {
+ struct flock lock;
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = lock.l_len = 0;
+ if (fcntl(db->lockfd, F_SETLK, &lock) == 0) {
+ DB_TXNMGR *txnp;
+ char **list, **first;
+
+ txnp = db->dbenv.tx_info;
+ errno = txn_checkpoint(txnp, 0, 0);
+ while (errno == DB_INCOMPLETE)
+ errno = txn_checkpoint(txnp, 0, 0);
+
+ /* we've checkpointed, so clean up the log files.
+ * NOTE: any real problems will make log_archive return an error. */
+ chdir(db->dbenv.db_log_dir ? db->dbenv.db_log_dir : db->dbenv.db_home);
+ if (!log_archive(db->dbenv.lg_info, &first, DB_ARCH_LOG, NULL)) {
+ list = first;
+ while (*list) {
+ if (truncate(*list, 0) < 0)
+ syslog(LOG_INFO, "cnid_close: failed to truncate %s: %m", *list);
+ list++;
+ }
+ free(first);
+ }
+ }
+ }
+
+ db->db_didname->close(db->db_didname, 0);
+ db->db_devino->close(db->db_devino, 0);
+ db->db_cnid->close(db->db_cnid, 0);
+ db_appexit(&db->dbenv);
+ if (db->lockfd > -1)
+ close(db->lockfd); /* this will also close any lock we have. */
+ ad_close(&db->rootinfo, ADFLAGS_HF);
+ free(db);
+}
--- /dev/null
+/*
+ * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * cnid_delete: delete a CNID from the database
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include <db.h>
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+
+#include "cnid_private.h"
+
+int cnid_delete(void *CNID, const cnid_t id)
+{
+ CNID_private *db;
+ DBT key, data;
+ DB_TXN *tid;
+ DB_TXNMGR *txnp;
+
+ if (!(db = CNID) || !id || (db->flags & CNIDFLAG_DB_RO))
+ return -1;
+
+ txnp = db->dbenv.tx_info;
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+
+retry:
+ if (errno = txn_begin(txnp, NULL, &tid)) {
+ return errno;
+ }
+
+ /* get from main database */
+ key.data = &id;
+ key.size = sizeof(id);
+ if (errno = db->db_cnid->get(db->db_cnid, tid, &key, &data, 0)) {
+ txn_abort(tid);
+ switch (errno) {
+ case EAGAIN:
+ goto retry;
+
+ case DB_NOTFOUND:
+ syslog(LOG_INFO, "cnid_delete: CNID(%x) not in database", id);
+ return 0;
+ default:
+ syslog(LOG_ERR, "cnid_delete: can't delete entry");
+ return errno;
+ }
+ }
+
+ /* now delete from dev/ino database */
+ key.data = data.data;
+ key.size = CNID_DEVINO_LEN;
+ if (errno = db->db_devino->del(db->db_devino, tid, &key, 0)) {
+ if (errno == EAGAIN) {
+ txn_abort(tid);
+ goto retry;
+ }
+
+ /* be silent if there isn't an entry */
+ if (errno != DB_NOTFOUND) {
+ txn_abort(tid);
+ goto abort_err;
+ }
+ }
+
+ /* get data from the did/name database */
+ /* free from did/macname, did/shortname, and did/longname databases */
+
+ /* delete from did/name database */
+ key.data = data.data + CNID_DEVINO_LEN;
+ key.size = data.size - CNID_DEVINO_LEN;
+ if (errno = db->db_didname->del(db->db_didname, tid, &key, 0)) {
+ if (errno == EAGAIN) {
+ txn_abort(tid);
+ goto retry;
+ }
+
+ /* be silent if there isn't an entry */
+ if (errno != DB_NOTFOUND) {
+ txn_abort(tid);
+ goto abort_err;
+ }
+ }
+
+ /* now delete from main database */
+ key.data = &id;
+ key.size = sizeof(id);
+ if (errno = db->db_cnid->del(db->db_cnid, tid, &key, 0)) {
+ txn_abort(tid);
+ if (errno == EAGAIN) {
+ goto retry;
+ }
+ goto abort_err;
+ }
+
+ return txn_commit(tid);
+
+abort_err:
+ syslog(LOG_ERR, "cnid_del: unable to delete CNID(%x)", id);
+ return errno;
+}
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <errno.h>
+
+#include <db.h>
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+
+#include "cnid_private.h"
+
+/* return CNID for a given did/name */
+cnid_t cnid_get(void *CNID, const cnid_t did, const char *name,
+ const int len)
+{
+ char start[CNID_DID_LEN + MAXPATHLEN + 1], *buf;
+ CNID_private *db;
+ DBT key, data;
+ cnid_t id;
+
+ if (!(db = CNID) || (len > MAXPATHLEN))
+ return 0;
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ buf = start;
+ memcpy(buf, &did, sizeof(did));
+ buf += sizeof(did);
+ memcpy(buf, name, len);
+ *(buf + len) = '\0'; /* make sure to nul terminate. */
+ key.data = start;
+ key.size = CNID_DID_LEN + len + 1;
+
+ while (errno = db->db_didname->get(db->db_didname, NULL,
+ &key, &data, 0)) {
+ if (errno == EAGAIN)
+ continue;
+
+ if (errno != DB_NOTFOUND)
+ syslog(LOG_ERR, "cnid_get: can't get CNID(%u:%s)", did, name);
+
+ return 0;
+ }
+
+ memcpy(&id, data.data, sizeof(id));
+ return id;
+}
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <errno.h>
+
+#include <db.h>
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+
+#include "cnid_private.h"
+
+#define LOGFILEMAX 100 /* kbytes */
+#define CHECKTIMEMAX 30 /* minutes */
+
+/* this returns the cnid corresponding to a particular file. it will
+ also fix up the various databases if there's a problem. */
+cnid_t cnid_lookup(void *CNID,
+ const struct stat *st, const cnid_t did,
+ const char *name, const int len)
+{
+ char *buf;
+ CNID_private *db;
+ DBT key, devdata, diddata;
+ DB_TXNMGR *txnp;
+ int devino = 1, didname = 1;
+ cnid_t id = 0;
+
+ if (!(db = CNID) || !st || !name)
+ return 0;
+
+ /* do a little checkpointing if necessary. i stuck it here as
+ * cnid_lookup gets called when we do directory lookups. only do
+ * this if we're using a read-write database. */
+ if ((db->flags & CNIDFLAG_DB_RO) == 0) {
+ txnp = db->dbenv.tx_info;
+ errno = txn_checkpoint(txnp, LOGFILEMAX, CHECKTIMEMAX);
+ while (errno == DB_INCOMPLETE)
+ errno = txn_checkpoint(txnp, 0, 0);
+ }
+
+ if ((buf = make_cnid_data(st, did, name, len)) == NULL) {
+ syslog(LOG_ERR, "cnid_lookup: path name too long");
+ return 0;
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&devdata, 0, sizeof(devdata));
+
+ /* look for a CNID. we have two options: dev/ino or did/name. if we
+ only get a match on one of them, that means a file has moved. */
+ key.data = buf; /* dev/ino is the first part of the buffer */
+ key.size = CNID_DEVINO_LEN;
+ while (errno = db->db_devino->get(db->db_devino, NULL,
+ &key, &devdata, 0)) {
+ if (errno == EAGAIN)
+ continue;
+
+ if (errno == DB_NOTFOUND) {
+ devino = 0;
+ break;
+ }
+
+ syslog(LOG_ERR, "cnid_lookup: can't get CNID(%u/%u)",
+ st->st_dev, st->st_ino);
+ return 0;
+ }
+
+ /* did/name is right afterwards. */
+ key.data = buf + CNID_DEVINO_LEN;
+ key.size = CNID_DID_LEN + len + 1;
+ memset(&diddata, 0, sizeof(diddata));
+ while (errno = db->db_didname->get(db->db_didname, NULL,
+ &key, &diddata, 0)) {
+ if (errno == EAGAIN)
+ continue;
+
+ if (errno == DB_NOTFOUND) {
+ didname = 0;
+ break;
+ }
+
+ syslog(LOG_ERR, "cnid_lookup: can't get CNID(%u:%s)",
+ did, name);
+ return 0;
+ }
+
+ /* set id. honor did/name over dev/ino as dev/ino isn't necessarily
+ * 1-1. */
+ if (didname) {
+ memcpy(&id, diddata.data, sizeof(id));
+ } else if (devino) {
+ memcpy(&id, devdata.data, sizeof(id));
+ }
+
+ /* either entries in both databases exist or neither of them do. */
+ if ((devino && didname) || !(devino || didname))
+ return id;
+
+ /* fix up the databases */
+ cnid_update(db, id, st, did, name, len);
+ return id;
+}
--- /dev/null
+/*
+ * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * deal with metadata
+ *
+ */
--- /dev/null
+#define CNID_META_CNID_LEN 4
+#define CNID_META_MDATE_LEN 4 /* space for 8 */
+#define CNID_META_CDATE_LEN 4 /* space for 8 */
+#define CNID_META_BDATE_LEN 4 /* ditto */
+#define CNID_META_ADATE_LEN 4 /* ditto */
+#define CNID_META_AFPI_LEN 4 /* plus permission bits */
+#define CNID_META_FINDERI_LEN 32
+#define CNID_META_PRODOSI_LEN 8
+#define CNID_META_RFORKLEN_LEN 4 /* space for 8 */
+#define CNID_META_MACNAME_LEN 32 /* maximum size */
+#define CNID_META_SHORTNAME_LEN 12 /* max size (8.3) */
+#define CNID_META_FILLER_LEN 4
+
+#define CNID_META_CNID_OFF 0
+#define CNID_META_MDATE_OFF (CNID_META_CNID_OFF + CNID_META_CNID_LEN + \
+ CNID_META_FILLER_LEN)
+#define CNID_META_CDATE_OFF (CNID_META_MDATE_OFF + CNID_META_MDATE_LEN + \
+ CNID_META_FILLER_LEN)
+#define CNID_META_BDATE_OFF (CNID_META_CDATE_OFF + CNID_META_CDATE_LEN + \
+ CNID_META_FILLER_LEN)
+#define CNID_META_ADATE_OFF (CNID_META_BDATE_OFF + CNID_META_BDATE_LEN + \
+ CNID_META_FILLER_LEN)
+#define CNID_META_AFPI_OFF (CNID_META_ADATE_OFF + CNID_META_ADATE_LEN)
+#define CNID_META_FINDERI_OFF (CNID_META_AFPI_OFF + CNID_META_AFPI_LEN)
+#define CNID_META_PRODOSI_OFF (CNID_META_FINDERI_OFF + CNID_META_FINDERI_LEN)
+#define CNID_META_RFORKLEN_OFF (CNID_META_PRODOSI_OFF + CNID_META_PRODOSI_LEN)
+#define CNID_META_MACNAME_OFF (CNID_META_RFORKLEN_OFF + \
+ CNID_META_RFORKLEN_LEN)
+#define CNID_META_SHORTNAME_OFF (CNID_META_MACNAME_OFF +
+
+
+#define cnid_meta_clear(a)
+#define cnid_meta_get(id)
+
+#define cnid_meta_cnid(a)
+#define cnid_meta_modifydate(a)
+#define cnid_meta_createdate(a)
+#define cnid_meta_backupdate(a)
+#define cnid_meta_accessdate(a)
+#define cnid_meta_afpi(a)
+#define cnid_meta_finderi(a)
+#define cnid_meta_prodosi(a)
+#define cnid_meta_rforklen(a)
+#define cnid_meta_macname(a)
+#define cnid_meta_shortname(a)
+#define cnid_meta_longname(a)
+
--- /dev/null
+#include <db.h>
+
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+
+#include <syslog.h>
+
+#include "cnid_private.h"
+
+/* return the next id. we use the fact that ad files are memory
+ * mapped. */
+cnid_t cnid_nextid(void *CNID)
+{
+ CNID_private *db;
+ cnid_t id;
+
+ if (!(db = CNID))
+ return 0;
+
+ memcpy(&id, ad_entry(&db->rootinfo, ADEID_DID), sizeof(id));
+ return id;
+}
--- /dev/null
+/*
+ * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * CNID database support.
+ *
+ * here's the deal:
+ * 1) afpd already caches did's.
+ * 2) the database stores cnid's as both did/name and dev/ino pairs.
+ * 3) RootInfo holds the value of the NextID.
+ * 4) the cnid database gets called in the following manner --
+ * start a database:
+ * cnid = cnid_open(root_dir);
+ *
+ * allocate a new id:
+ * newid = cnid_add(cnid, dev, ino, parent did,
+ * name, id); id is a hint for a specific id. pass 0 if you don't
+ * care. if the id is already assigned, you won't get what you
+ * requested.
+ *
+ * given an id, get a did/name and dev/ino pair.
+ * name = cnid_get(cnid, &id); given an id, return the corresponding
+ * info.
+ * return code = cnid_delete(cnid, id); delete an entry.
+ *
+ * with AFP, CNIDs 0-2 have special meanings. here they are:
+ * 0 -- invalid cnid
+ * 1 -- parent of root directory (handled by afpd)
+ * 2 -- root directory (handled by afpd)
+ *
+ * so, CNID_START begins at 3.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <syslog.h>
+
+#include <db.h>
+
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+#include <atalk/util.h>
+
+#include "cnid_private.h"
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define ROOTINFO "RootInfo"
+#define ROOTINFO_LEN 8
+
+#define DBHOME ".AppleDB"
+#define DBCNID "cnid.db"
+#define DBDEVINO "devino.db"
+#define DBDIDNAME "didname.db" /* did/full name mapping */
+#define DBSHORTNAME "shortname.db" /* did/8+3 mapping */
+#define DBMACNAME "macname.db" /* did/31 mapping */
+#define DBLONGNAME "longname.db" /* did/unicode mapping */
+#define DBLOCKFILE "/cnid.lock"
+
+#define DBHOMELEN 8
+#define DBLEN 10
+
+/* we version the did/name database so that we can change the format
+ * if necessary. the key is in the form of a did/name pair. in this case,
+ * we use 0/0. */
+#define DBVERSION_KEY "\0\0\0\0\0"
+#define DBVERSION_KEYLEN 5
+#define DBVERSION1 0x00000001U
+#define DBVERSION DBVERSION1
+
+#define DBOPTIONS (DB_CREATE | DB_INIT_MPOOL | DB_INIT_LOCK | \
+DB_INIT_LOG | DB_INIT_TXN | DB_TXN_NOSYNC | DB_RECOVER)
+
+#define MAXITER 0xFFFF /* maximum number of simultaneously open CNID
+ * databases. */
+
+/* the first compare that's always done. */
+static __inline__ int compare_did(const DBT *a, const DBT *b)
+{
+ u_int32_t dida, didb;
+
+ memcpy(&dida, a->data, sizeof(dida));
+ memcpy(&didb, b->data, sizeof(didb));
+ return dida - didb;
+}
+
+/* sort did's and then names. this is for unix paths.
+ * i.e., did/unixname lookups. */
+static int compare_unix(const DBT *a, const DBT *b)
+{
+ u_int8_t *sa, *sb;
+ int len, ret;
+
+ /* sort by did */
+ if (ret = compare_did(a, b))
+ return ret;
+
+ sa = a->data + 4; /* shift past did */
+ sb = b->data + 4;
+ for (len = MIN(a->size, b->size); len-- > 4; sa++, sb++)
+ if (ret = (*sa - *sb))
+ return ret; /* sort by lexical ordering */
+ return a->size - b->size; /* sort by length */
+}
+
+/* sort did's and then names. this is for macified paths (i.e.,
+ * did/macname, and did/shortname. i think did/longname needs a
+ * unicode table to work. also, we can't use strdiacasecmp as that
+ * returns a match if a < b. */
+static int compare_mac(const DBT *a, const DBT *b)
+{
+ u_int8_t *sa, *sb;
+ int len, ret;
+
+ /* sort by did */
+ if (ret = compare_did(a, b))
+ return ret;
+
+ sa = a->data + 4;
+ sb = b->data + 4;
+ for (len = MIN(a->size, b->size); len-- > 4; sa++, sb++)
+ if (ret = (_diacasemap[*sa] - _diacasemap[*sb]))
+ return ret; /* sort by lexical ordering */
+ return a->size - b->size; /* sort by length */
+}
+
+
+/* for unicode names -- right now it's the same as compare_mac. */
+#define compare_unicode(a, b) compare_mac((a), (b))
+
+void *cnid_open(const char *dir)
+{
+ struct stat st;
+ struct flock lock;
+ char path[MAXPATHLEN + 1];
+ CNID_private *db;
+ DB_INFO dbi;
+ DBT key, data;
+ int open_flag, len;
+
+ if (!dir)
+ return NULL;
+
+ /* this checks both RootInfo and .AppleDB */
+ if ((len = strlen(dir)) > (MAXPATHLEN - DBLEN - 1)) {
+ syslog(LOG_ERR, "cnid_open: path too large");
+ return NULL;
+ }
+
+ if ((db = (CNID_private *) calloc(1, sizeof(CNID_private))) == NULL) {
+ syslog(LOG_ERR, "cnid_open: unable to allocate memory");
+ return NULL;
+ }
+ db->magic = CNID_DB_MAGIC;
+
+ strcpy(path, dir);
+ if (path[len - 1] != '/') {
+ strcat(path, "/");
+ len++;
+ }
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+
+ /* we create and initialize RootInfo if it doesn't exist. */
+ strcat(path, ROOTINFO);
+ if (ad_open(path, ADFLAGS_HF, O_RDWR, 0666, &db->rootinfo) < 0) {
+ cnid_t id;
+
+ /* see if we can open it read-only. if it's read-only, we can't
+ * add CNIDs. */
+ memset(&db->rootinfo, 0, sizeof(db->rootinfo));
+ if (ad_open(path, ADFLAGS_HF, O_RDONLY, 0666, &db->rootinfo) == 0) {
+ db->flags = CNIDFLAG_ROOTINFO_RO;
+ syslog(LOG_INFO, "cnid_open: read-only RootInfo");
+ goto mkdir_appledb;
+ }
+
+ /* create the file */
+ memset(&db->rootinfo, 0, sizeof(db->rootinfo));
+ if (ad_open(path, ADFLAGS_HF, O_CREAT | O_RDWR, 0666,
+ &db->rootinfo) < 0) {
+ syslog(LOG_ERR, "cnid_open: ad_open(RootInfo)");
+ goto fail_db;
+ }
+
+ /* lock the RootInfo file. this and cnid_add are the only places
+ * that should fiddle with RootInfo. */
+ lock.l_start = ad_getentryoff(&db->rootinfo, ADEID_DID);
+ lock.l_len = ad_getentrylen(&db->rootinfo, ADEID_DID);
+ if (fcntl(ad_hfileno(&db->rootinfo), F_SETLKW, &lock) < 0) {
+ syslog(LOG_ERR, "cnid_open: can't establish lock: %m");
+ goto fail_adouble;
+ }
+
+ /* store the beginning CNID */
+ id = htonl(CNID_START);
+ memcpy(ad_entry(&db->rootinfo, ADEID_DID), &id, sizeof(id));
+ ad_flush(&db->rootinfo, ADFLAGS_HF);
+
+ /* unlock it */
+ lock.l_type = F_UNLCK;
+ fcntl(ad_hfileno(&db->rootinfo), F_SETLK, &lock);
+ lock.l_type = F_WRLCK;
+ }
+
+mkdir_appledb:
+ strcpy(path + len, DBHOME);
+ if ((stat(path, &st) < 0) && (ad_mkdir(path, 0777) < 0)) {
+ syslog(LOG_ERR, "cnid_open: mkdir failed");
+ goto fail_adouble;
+ }
+
+ /* search for a byte lock. this allows us to cleanup the log files
+ * at cnid_close() in a clean fashion.
+ *
+ * NOTE: this won't work if multiple volumes for the same user refer
+ * to the same directory. */
+ strcat(path, DBLOCKFILE);
+ if ((db->lockfd = open(path, O_RDWR | O_CREAT, 0666)) > -1) {
+ lock.l_start = 0;
+ lock.l_len = 1;
+ while (fcntl(db->lockfd, F_SETLK, &lock) < 0) {
+ if (++lock.l_start > MAXITER) {
+ syslog(LOG_INFO, "cnid_open: can't establish logfile cleanup lock.");
+ close(db->lockfd);
+ db->lockfd = -1;
+ break;
+ }
+ }
+ }
+
+ path[len + DBHOMELEN] = '\0';
+ open_flag = DB_CREATE;
+ /* try a full-blown transactional environment */
+ if (db_appinit(path, NULL, &db->dbenv, DBOPTIONS)) {
+
+ /* try with a shared memory pool */
+ memset(&db->dbenv, 0, sizeof(db->dbenv));
+ if (db_appinit(path, NULL, &db->dbenv, DB_INIT_MPOOL)) {
+
+ /* try without any options. */
+ memset(&db->dbenv, 0, sizeof(db->dbenv));
+ if (db_appinit(path, NULL, &db->dbenv, 0)) {
+ syslog(LOG_ERR, "cnid_open: db_appinit failed");
+ goto fail_lock;
+ }
+ }
+ db->flags |= CNIDFLAG_DB_RO;
+ open_flag = DB_RDONLY;
+ syslog(LOG_INFO, "cnid_open: read-only CNID database");
+ }
+
+ memset(&dbi, 0, sizeof(dbi));
+
+ /* did/name reverse mapping. we use a btree for this one. */
+ dbi.bt_compare = compare_unix;
+ if (db_open(DBDIDNAME, DB_BTREE, open_flag, 0666, &db->dbenv, &dbi,
+ &db->db_didname)) {
+ goto fail_appinit;
+ }
+
+ /* check for version. this way we can update the database if we need
+ to change the format in any way. */
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = DBVERSION_KEY;
+ key.len = DBVERSION_KEY_LEN;
+ while (errno = db->db_didname->get(db->db_didname, NULL, &key, &data, 0)) {
+ switch (errno) {
+ case EAGAIN:
+ continue;
+
+ case DB_NOTFOUND:
+ u_int32_t version = htonl(DBVERSION);
+
+ data.data = &version;
+ data.len = sizeof(version);
+dbversion_retry:
+ if (db->db_didname->put(db->db_didname, NULL, &key, &data,
+ DB_NOOVERWRITE))
+ if (errno == EAGAIN)
+ goto dbversion_retry;
+ break;
+ default:
+ /* uh oh. something bad happened. bail. */
+ db->db_didname->close(db->db_didname, 0);
+ goto fail_appinit;
+ }
+ }
+
+ /* XXX: in the future, we might check for version number here. */
+#if 0
+ memcpy(&version, data.data, sizeof(version));
+ if (version != htonl(DBVERSION)) {
+ /* fix up stuff */
+ }
+#endif
+
+ /* did/macname mapping. btree this one. */
+ dbi.bt_compare = compare_mac;
+ if (db_open(DBMACNAME, DB_BTREE, open_flag, 0666, &db->dbenv, &dbi,
+ &db->db_macname)) {
+ db->db_didname->close(db->db_didname, 0);
+ goto fail_appinit;
+ }
+
+ /* did/shortname mapping */
+ if (db_open(DBSHORTNAME, DB_BTREE, open_flag, 0666, &db->dbenv, &dbi,
+ &db->db_shortname)) {
+ db->db_didname->close(db->db_didname, 0);
+ db->db_macname->close(db->db_macname, 0);
+ goto fail_appinit;
+ }
+
+ /* did/longname mapping */
+ dbi.bt_compare = compare_unicode;
+ if (db_open(DBLONGNAME, DB_BTREE, open_flag, 0666, &db->dbenv, &dbi,
+ &db->db_longname)) {
+ db->db_didname->close(db->db_didname, 0);
+ db->db_macname->close(db->db_macname, 0);
+ db->db_shortname->close(db->db_shortname, 0);
+ goto fail_appinit;
+ }
+
+ /* dev/ino reverse mapping. we hash this one. */
+ dbi.bt_compare = NULL;
+ if (db_open(DBDEVINO, DB_HASH, open_flag, 0666, &db->dbenv, &dbi,
+ &db->db_devino)) {
+ db->db_didname->close(db->db_didname, 0);
+ db->db_macname->close(db->db_macname, 0);
+ db->db_shortname->close(db->db_shortname, 0);
+ db->db_longname->close(db->db_longname, 0);
+ goto fail_appinit;
+ }
+
+ /* main cnid database. we hash this one as well. */
+ if (db_open(DBCNID, DB_HASH, open_flag, 0666, &db->dbenv, &dbi,
+ &db->db_cnid)) {
+ db->db_didname->close(db->db_didname, 0);
+ db->db_macname->close(db->db_macname, 0);
+ db->db_shortname->close(db->db_shortname, 0);
+ db->db_longname->close(db->db_longname, 0);
+ db->db_devino->close(db->db_devino, 0);
+ goto fail_appinit;
+ }
+ return db;
+
+fail_appinit:
+ syslog(LOG_ERR, "cnid_open: db_open failed");
+ db_appexit(&db->dbenv);
+
+fail_lock:
+ if (db->lockfd > -1)
+ close(db->lockfd);
+
+fail_adouble:
+ ad_close(&db->rootinfo, ADFLAGS_HF);
+
+fail_db:
+ free(db);
+ return NULL;
+}
--- /dev/null
+#ifndef LIBATALK_CNID_PRIVATE_H
+#define LIBATALK_CNID_PRIVATE_H 1
+
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <db.h>
+
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+
+#define CNID_DB_MAGIC 0x434E4944U /* CNID */
+#define CNID_DATA_MAGIC 0x434E4945U /* CNIE */
+
+#define CNID_DEVINO_LEN 8
+#define CNID_DID_LEN 4
+#define CNID_HEADER_LEN (CNID_DEVINO_LEN + CNID_DID_LEN)
+
+#define CNID_START 3
+
+#define CNIDFLAG_ROOTINFO_RO (1 << 0)
+#define CNIDFLAG_DB_RO (1 << 1)
+
+typedef struct CNID_private {
+ u_int32_t magic;
+ DB *db_cnid, *db_didname, *db_devino,
+ *db_shortname, *db_macname, *db_longname;
+ DB_ENV dbenv;
+ struct adouble rootinfo;
+ int lockfd, flags;
+} CNID_private;
+
+/* on-disk data format (in network byte order where appropriate) --
+ * db_cnid: (key: cnid)
+ * name size (in bytes)
+ * dev 4
+ * ino 4
+ * did 4
+ * unix name strlen(name) + 1
+ *
+ * db_didname: (key: did/unix name)
+ * -- this also caches the bits of .AppleDouble used by FPGetFilDirParam
+ * so that we don't have to open the header file.
+ * NOTE: FPCatSearch has to search through all of the directories as
+ * this stuff doesn't get entered until needed.
+ * if the entire volume is in the database, though, we can use
+ * cursor operations to make this faster.
+ *
+ * version number is stored with did/name key of 0/0
+ *
+ * cnid 4
+ * modfiller 4 (dates only use 4 bytes right now, but we leave space
+ * moddate 4 for 8. moddate is also used to keep this info
+ * createfiller 4 up-to-date.)
+ * createdate 4
+ * backfiller 4
+ * backupdate 4
+ * accfiller 4 (unused)
+ * accdate 4 (unused)
+ * AFP info 4 (stores a couple permission bits as well)
+ * finder info 32
+ * prodos info 8
+ * rforkfiller 4
+ * rforklen 4
+ * macname 32 (nul-terminated)
+ * shortname 12 (nul-terminated)
+ * longname longnamelen (nul-terminated)
+ * ---------------
+ * 132 bytes + longnamelen
+ *
+ * db_devino: (key: dev/ino)
+ * -- this is only used for consistency checks and isn't 1-1
+ * cnid 4
+ *
+ * these correspond to the different path types. longname is for the
+ * 255 unicode character names (path type == ?), macname is for the
+ * 32-character names (path type == 2), and shortname is for the
+ * 8+3-character names (path type == 1).
+ *
+ * db_longname: (key: did/longname)
+ * name namelen = strlen(name) + 1
+ *
+ * db_macname: (key: did/macname)
+ * name namelen = strlen(name) + 1
+ *
+ * db_shortname: (key: did/shortname)
+ * name namelen = strlen(name) + 1
+ */
+
+#ifndef __inline__
+#define __inline__
+#endif
+
+/* construct db_cnid data. NOTE: this is not re-entrant. */
+static __inline__ char *make_cnid_data(const struct stat *st,
+ const cnid_t did,
+ const char *name, const int len)
+{
+ static char start[CNID_HEADER_LEN + MAXPATHLEN + 1];
+ u_int32_t i;
+
+ if (len > MAXPATHLEN)
+ return NULL;
+
+ i = htonl(st->st_dev);
+ memcpy(start, &i, sizeof(i));
+ i = htonl(st->st_ino);
+ memcpy(start + sizeof(i), &i, sizeof(i));
+ /* did is already in network byte order */
+ memcpy(start + CNID_DEVINO_LEN, &did, sizeof(did));
+ memcpy(start + CNID_HEADER_LEN, name, len);
+ *(buf + len) = '\0';
+ buf += len + 1;
+
+ return start;
+}
+
+#endif /* atalk/cnid/cnid_private.h */
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <errno.h>
+
+#include <db.h>
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+
+#include "cnid_private.h"
+
+/* return the did/name pair corresponding to a CNID. */
+char *cnid_resolve(void *CNID, cnid_t *id)
+{
+ CNID_private *db;
+ DBT key, data;
+
+ if (!(db = CNID) || !id || !(*id))
+ return NULL;
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ key.data = id;
+ key.size = sizeof(*id);
+ while (errno = db->db_cnid->get(db->db_cnid, NULL, &key, &data, 0)) {
+ if (errno == EAGAIN)
+ continue;
+
+ if (errno != DB_NOTFOUND)
+ syslog(LOG_ERR, "cnid_resolve: can't get did/name");
+
+ *id = 0;
+ return NULL;
+ }
+
+ memcpy(id, data.data + CNID_DEVINO_LEN, sizeof(*id));
+ return data.data + CNID_HEADER_LEN;
+}
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include <db.h>
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+
+#include "cnid_private.h"
+
+
+/* cnid_update: takes the given cnid and updates the metadata. to
+ handle the did/name data, there are a bunch of functions to get
+ and set the various fields. */
+int cnid_update(void *CNID, cnid_t id, const struct stat *st,
+ const cnid_t did, const char *name, const int len,
+ const char *info, const int infolen)
+{
+ CNID_private *db;
+ DBT key, data, altdata;
+ DB_TXN *tid;
+ DB_TXNMGR *txnp;
+
+ if (!(db = CNID) || !id || !st || !name || (db->flags & CNIDFLAG_DB_RO))
+ return -1;
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ memset(&altdata, 0, sizeof(altdata));
+ txnp = db->dbenv.tx_info;
+
+ /* begin a transaction */
+retry:
+ if (errno = txn_begin(txnp, NULL, &tid)) {
+ return errno;
+ }
+
+ /* get the old info */
+ key.data = &id;
+ key.size = sizeof(id);
+ if (errno = db->db_cnid->get(db->db_cnid, tid, &key, &data, 0)) {
+ txn_abort(tid);
+ if (errno == EAGAIN)
+ goto retry;
+ goto update_err;
+ }
+
+ /* delete the old dev/ino mapping */
+ key.data = data.data;
+ key.size = CNID_DEVINO_LEN;
+ if (errno = db->db_devino->del(db->db_devino, tid, &key, 0)) {
+ if (errno == EAGAIN) {
+ txn_abort(tid);
+ goto retry;
+ }
+
+ /* silently fail on a non-existent entry */
+ if (errno != DB_NOTFOUND) {
+ txn_abort(tid);
+ goto update_err;
+ }
+ }
+
+ /* delete the old did/name mapping */
+ key.data = data.data + CNID_DEVINO_LEN;
+ key.size = data.size - CNID_DEVINO_LEN;
+ if (errno = db->db_didname->del(db->db_didname, tid, &key, 0)) {
+ if (errno == EAGAIN) {
+ txn_abort(tid);
+ goto retry;
+ }
+
+ /* silently fail on a non-existent entry */
+ if (errno != DB_NOTFOUND) {
+ txn_abort(tid);
+ goto update_err;
+ }
+ }
+
+ /* delete the old aliases if necessary */
+
+
+ /* make a new entry */
+ data.data = make_cnid_data(st, did, name, len);
+ data.size = CNID_HEADER_LEN + len + 1;
+
+ /* put a new dev/ino mapping in */
+ key.data = data.data;
+ key.size = CNID_DEVINO_LEN;
+ altdata.data = &id;
+ altdata.size = sizeof(id);
+ if (errno = db->db_devino->put(db->db_devino, tid, &key, &altdata, 0)) {
+ txn_abort(tid);
+ if (errno == EAGAIN) {
+ goto retry;
+ }
+ goto update_err;
+ }
+
+ /* put a new did/name mapping in */
+ key.data = data.data + CNID_DEVINO_LEN;
+ key.size = data.size - CNID_DEVINO_LEN;
+ if (errno = db->db_didname->put(db->db_didname, tid, &key, &altdata, 0)) {
+ txn_abort(tid);
+ if (errno == EAGAIN) {
+ goto retry;
+ }
+ goto update_err;
+ }
+
+ /* update the old CNID with the new info */
+ key.data = &id;
+ key.size = sizeof(id);
+ if (errno = db->db_cnid->put(db->db_cnid, tid, &key, &data, 0)) {
+ txn_abort(tid);
+ if (errno == EAGAIN) {
+ goto retry;
+ }
+ goto update_err;
+ }
+
+ /* end transaction */
+ return txn_commit(tid);
+
+update_err:
+ syslog(LOG_ERR, "cnid_update: can't update CNID(%x)", id);
+ return -1;
+}
--- /dev/null
+SRC= mktemp.c getusershell.c strcasecmp.c strstr.c flock.c strdup.c \
+ inet_aton.c rquota_xdr.c
+OBJ= mktemp.o getusershell.o strcasecmp.o strstr.o flock.o strdup.o \
+ inet_aton.o rquota_xdr.o
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${RPCSVCDEFS} ${OPTOPTS} ${INCPATH}
+TAGSFILE= tags
+CC= cc
+
+all : profiled compatlib
+
+profiled:
+ -mkdir profiled
+
+compatlib compatlib_p : ${OBJ}
+ @echo "building profiled compatlib"
+ @cd profiled; ar cru ../compatlib_p ${OBJ}
+ @echo "building normal compatlib"
+ @ar cru compatlib ${OBJ}
+
+.c.o :
+ ${CC} -p ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- profiled/$*.o
+ ${CC} ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- $*.o
+
+clean :
+ rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+ rm -f compatlib compatlib_p
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/*
+ * Copyright (c) 1996 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+static int _flock_dummy;
+
+# if defined( sun ) && defined( __svr4__ )
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include </usr/ucbinclude/sys/file.h>
+
+int flock( fd, operation )
+ int fd;
+ int operation;
+{
+ flock_t l;
+ int rc, op;
+
+ if ( operation & LOCK_NB ) {
+ op = F_SETLK;
+ } else {
+ op = F_SETLKW;
+ }
+
+ if ( operation & LOCK_EX ) {
+ l.l_type = F_WRLCK;
+ }
+
+ if ( operation & LOCK_SH ) {
+ l.l_type = F_RDLCK;
+ }
+
+ if ( operation & LOCK_UN ) {
+ l.l_type = F_UNLCK;
+ }
+
+ l.l_whence = 0;
+ l.l_start = 0;
+ l.l_len = 0;
+
+ if (( rc = fcntl( fd, F_SETLK, &l )) < 0 ) {
+ if ( errno == EAGAIN || errno == EACCES ) {
+ errno = EWOULDBLOCK;
+ }
+ }
+ return( rc );
+}
+# endif sun __svr4__
--- /dev/null
+/*
+ * Copyright (c) 1985 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement: ``This product includes software
+ * developed by the University of California, Berkeley and its contributors''
+ * in the documentation or other materials provided with the distribution
+ * and in all advertising materials mentioning features or use of this
+ * software. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)getusershell.c 5.6 (Berkeley) 6/1/90";
+#endif /* LIBC_SCCS and not lint */
+
+static int _getusershell_dummy;
+
+#if defined(ultrix) || defined(_IBMR2) || defined(NEED_GETUSERSHELL)
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#define SHELLS "/etc/shells"
+
+/*
+ * Do not add local shells here. They should be added in /etc/shells
+ */
+static char *okshells[] = {
+ "/bin/sh", "/bin/csh",
+#ifdef _IBMR2
+ "/bin/ksh",
+#endif _IBMR2
+ 0
+};
+
+static char **shells, *strings;
+static char **curshell = NULL;
+extern char **initshells();
+
+/*
+ * Get a list of shells from SHELLS, if it exists.
+ */
+char *
+getusershell()
+{
+ char *ret;
+
+ if (curshell == NULL)
+ curshell = initshells();
+ ret = *curshell;
+ if (ret != NULL)
+ curshell++;
+ return (ret);
+}
+
+endusershell()
+{
+
+ if (shells != NULL)
+ free((char *)shells);
+ shells = NULL;
+ if (strings != NULL)
+ free(strings);
+ strings = NULL;
+ curshell = NULL;
+}
+
+setusershell()
+{
+
+ curshell = initshells();
+}
+
+static char **
+initshells()
+{
+ register char **sp, *cp;
+ register FILE *fp;
+ struct stat statb;
+ extern char *malloc(), *calloc();
+
+ if (shells != NULL)
+ free((char *)shells);
+ shells = NULL;
+ if (strings != NULL)
+ free(strings);
+ strings = NULL;
+ if ((fp = fopen(SHELLS, "r")) == (FILE *)0)
+ return(okshells);
+ if (fstat(fileno(fp), &statb) == -1) {
+ (void)fclose(fp);
+ return(okshells);
+ }
+ if ((strings = malloc((unsigned)statb.st_size)) == NULL) {
+ (void)fclose(fp);
+ return(okshells);
+ }
+ shells = (char **)calloc((unsigned)statb.st_size / 3, sizeof (char *));
+ if (shells == NULL) {
+ (void)fclose(fp);
+ free(strings);
+ strings = NULL;
+ return(okshells);
+ }
+ sp = shells;
+ cp = strings;
+ while (fgets(cp, MAXPATHLEN + 1, fp) != NULL) {
+ while (*cp != '#' && *cp != '/' && *cp != '\0')
+ cp++;
+ if (*cp == '#' || *cp == '\0')
+ continue;
+ *sp++ = cp;
+ while (!isspace(*cp) && *cp != '#' && *cp != '\0')
+ cp++;
+ *cp++ = '\0';
+ }
+ *sp = (char *)0;
+ (void)fclose(fp);
+ return (shells);
+}
+
+# endif ultrix
--- /dev/null
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+static int _inet_aton_dummy;
+
+#if defined(ultrix) || (defined(sun) && defined(__svr4__))
+#ifndef INADDR_NONE
+#define INADDR_NONE ((unsigned) 0xffffffff)
+#endif
+
+int inet_aton(const char *name, struct in_addr *addr)
+{
+ if ((addr->s_addr = inet_addr(name)) == htonl(INADDR_NONE))
+ return 0;
+
+ return 1;
+}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement: ``This product includes software
+ * developed by the University of California, Berkeley and its contributors''
+ * in the documentation or other materials provided with the distribution
+ * and in all advertising materials mentioning features or use of this
+ * software. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)mktemp.c 5.9 (Berkeley) 6/1/90";
+#endif /* LIBC_SCCS and not lint */
+
+static int _mktemp_dummy;
+
+# ifdef ultrix
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+
+mkstemp(path)
+ char *path;
+{
+ int fd;
+
+ return (_gettemp(path, &fd) ? fd : -1);
+}
+
+char *
+mktemp(path)
+ char *path;
+{
+ return(_gettemp(path, (int *)NULL) ? path : (char *)NULL);
+}
+
+static
+_gettemp(path, doopen)
+ char *path;
+ register int *doopen;
+{
+ extern int errno;
+ register char *start, *trv;
+ struct stat sbuf;
+ u_int pid;
+
+ pid = getpid();
+ for (trv = path; *trv; ++trv); /* extra X's get set to 0's */
+ while (*--trv == 'X') {
+ *trv = (pid % 10) + '0';
+ pid /= 10;
+ }
+
+ /*
+ * check the target directory; if you have six X's and it
+ * doesn't exist this runs for a *very* long time.
+ */
+ for (start = trv + 1;; --trv) {
+ if (trv <= path)
+ break;
+ if (*trv == '/') {
+ *trv = '\0';
+ if (stat(path, &sbuf))
+ return(0);
+ if (!S_ISDIR(sbuf.st_mode)) {
+ errno = ENOTDIR;
+ return(0);
+ }
+ *trv = '/';
+ break;
+ }
+ }
+
+ for (;;) {
+ if (doopen) {
+ if ((*doopen =
+ open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
+ return(1);
+ if (errno != EEXIST)
+ return(0);
+ }
+ else if (stat(path, &sbuf))
+ return(errno == ENOENT ? 1 : 0);
+
+ /* tricky little algorithm for backward compatibility */
+ for (trv = start;;) {
+ if (!*trv)
+ return(0);
+ if (*trv == 'z')
+ *trv++ = 'a';
+ else {
+ if (isdigit(*trv))
+ *trv = 'a';
+ else
+ ++*trv;
+ break;
+ }
+ }
+ }
+ /*NOTREACHED*/
+}
+
+# endif ultrix
--- /dev/null
+/* taken from the quota-1.55 used on linux. here's the bsd copyright:
+ *
+ * Copyright (c) 1980, 1990 Regents of the University of California. All
+ * rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by Robert Elz at
+ * The University of Melbourne.
+ */
+
+#include <stdio.h> /* to get __GNU_LIBRARY__ */
+
+static int _xdr_rquota_dummy;
+
+/* list of machines that don't have these functions:
+ solaris
+ linux libc5
+*/
+#if defined(NEED_RQUOTA) || (defined(sun) && defined(__svr4__)) || \
+(defined(__GNU_LIBRARY__) && __GNU_LIBRARY__ < 6)
+
+#include <rpc/rpc.h>
+#include <rpcsvc/rquota.h>
+
+bool_t
+xdr_getquota_args(xdrs, objp)
+ XDR *xdrs;
+ getquota_args *objp;
+{
+ if (!xdr_string(xdrs, &objp->gqa_pathp, RQ_PATHLEN)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->gqa_uid)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+bool_t
+xdr_rquota(xdrs, objp)
+ XDR *xdrs;
+ rquota *objp;
+{
+ if (!xdr_int(xdrs, &objp->rq_bsize)) {
+ return (FALSE);
+ }
+ if (!xdr_bool(xdrs, &objp->rq_active)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->rq_bhardlimit)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->rq_bsoftlimit)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->rq_curblocks)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->rq_fhardlimit)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->rq_fsoftlimit)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->rq_curfiles)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->rq_btimeleft)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->rq_ftimeleft)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+
+
+bool_t
+xdr_gqr_status(xdrs, objp)
+ XDR *xdrs;
+ gqr_status *objp;
+{
+ if (!xdr_enum(xdrs, (enum_t *)objp)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+bool_t
+xdr_getquota_rslt(xdrs, objp)
+ XDR *xdrs;
+ getquota_rslt *objp;
+{
+ if (!xdr_gqr_status(xdrs, &objp->status)) {
+ return (FALSE);
+ }
+ switch (objp->status) {
+ case Q_OK:
+ if (!xdr_rquota(xdrs, &objp->getquota_rslt_u.gqr_rquota)) {
+ return (FALSE);
+ }
+ break;
+ case Q_NOQUOTA:
+ break;
+ case Q_EPERM:
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement: ``This product includes software
+ * developed by the University of California, Berkeley and its contributors''
+ * in the documentation or other materials provided with the distribution
+ * and in all advertising materials mentioning features or use of this
+ * software. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+static int _strcasecmp_dummy;
+
+# if defined( ibm032 )
+
+#include <sys/types.h>
+#include <string.h>
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)strcasecmp.c 5.9 (Berkeley) 6/1/90";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * This array is designed for mapping upper and lower case letter
+ * together for a case independent comparison. The mappings are
+ * based upon ascii character sequences.
+ */
+static u_char charmap[] = {
+ '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
+ '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
+ '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
+ '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
+ '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
+ '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
+ '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
+ '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
+ '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+ '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+ '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+ '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
+ '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+ '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+ '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+ '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
+ '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
+ '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
+ '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
+ '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
+ '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
+ '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
+ '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
+ '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
+ '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307',
+ '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317',
+ '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327',
+ '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337',
+ '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+ '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+ '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+ '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
+};
+
+int
+strcasecmp(s1, s2)
+ char *s1, *s2;
+{
+ register u_char *cm = charmap,
+ *us1 = (u_char *)s1,
+ *us2 = (u_char *)s2;
+
+ while (cm[*us1] == cm[*us2++])
+ if (*us1++ == '\0')
+ return (0);
+ return (cm[*us1] - cm[*--us2]);
+}
+
+int
+strncasecmp(s1, s2, n)
+ char *s1, *s2;
+ register size_t n;
+{
+ if (n != 0) {
+ register u_char *cm = charmap,
+ *us1 = (u_char *)s1,
+ *us2 = (u_char *)s2;
+
+ do {
+ if (cm[*us1] != cm[*us2++])
+ return (cm[*us1] - cm[*--us2]);
+ if (*us1++ == '\0')
+ break;
+ } while (--n != 0);
+ }
+ return (0);
+}
+
+# endif ibm032
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int _strdup_dummy;
+
+#ifdef ultrix
+char *strdup(const char *string)
+{
+ char *new;
+
+ if (new = (char *) malloc(strlen(string) + 1))
+ strcpy(new, string);
+
+ return new;
+}
+#endif
--- /dev/null
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strstr.c 5.2 (Berkeley) 1/26/91";
+#endif /* LIBC_SCCS and not lint */
+
+static int _strstr_dummy;
+
+# if defined(ibm032) || (defined(sun) && defined(i386))
+#ifdef sun
+#define const
+#endif sun
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Find the first occurrence of find in s.
+ */
+char *
+strstr(s, find)
+ register const char *s, *find;
+{
+ register char c, sc;
+ register size_t len;
+
+ if ((c = *find++) != 0) {
+ len = strlen(find);
+ do {
+ do {
+ if ((sc = *s++) == 0)
+ return (0);
+ } while (sc != c);
+ } while (strncmp(s, find, len) != 0);
+ s--;
+ }
+ return ((char *)s);
+}
+# endif ibm03 sun i386
--- /dev/null
+SRC = dsi_attn.c dsi_close.c dsi_cmdreply.c dsi_getsess.c \
+ dsi_getstat.c dsi_init.c dsi_opensess.c dsi_read.c \
+ dsi_tcp.c dsi_tickle.c dsi_write.c dsi_stream.c
+OBJ = dsi_attn.o dsi_close.o dsi_cmdreply.o dsi_getsess.o \
+ dsi_getstat.o dsi_init.o dsi_opensess.o dsi_read.o \
+ dsi_tcp.o dsi_tickle.o dsi_write.o dsi_stream.o
+
+INCPATH= -I../../include ${TCPWRAPINCPATH}
+CFLAGS = ${DEFS} ${OPTOPTS} ${INCPATH} ${TCPWRAPDEFS}
+TAGSFILE= tags
+CC= cc
+
+all :
+ if [ x"${TCPWRAPDIR}" != x ]; then \
+ TCPWRAPDEFS="-DTCPWRAP"; \
+ if [ "${TCPWRAPDIR}" != "/usr" ]; then \
+ TCPWRAPINCPATH="-I${TCPWRAPDIR}/include"; \
+ fi; \
+ fi; \
+ ${MAKE} ${MFLAGS} CC="${CC}" DEFS="${DEFS}" \
+ OPTOPTS="${OPTOPTS}" \
+ TCPWRAPINCPATH="$${TCPWRAPINCPATH}" TCPWRAPDEFS="$${TCPWRAPDEFS}" \
+ dsilib
+
+profiled:
+ -mkdir profiled
+
+dsilib dsilib_p : profiled ${OBJ}
+ @echo "building profiled dsilib"
+ @cd profiled; ar cru ../dsilib_p ${OBJ}
+ @echo "building normal dsilib"
+ @ar cru dsilib ${OBJ}
+
+.c.o :
+ ${CC} -p ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- profiled/$*.o
+ ${CC} ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- $*.o
+
+clean :
+ rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+ rm -f dsilib dsilib_p
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
--- /dev/null
+SIGNALS AND WRITES TO CLIENT:
+because AFP/TCP uses a streaming protocol, we need to make sure that
+writes to the client are atomic. notably, signal handlers which write
+data can't interrupt data currently being written. in addition, some
+functions get called from the signal handlers or can write partial
+packets. we need to SIG_BLOCK/SIG_SETMASK those functions instead of
+SIG_BLOCK/SIG_UNBLOCK'ing them. furthermore, certain functions which
+write to the client can get called at any time. to avoid corruption,
+we need to make sure that these functions DO NOT touch any common-use
+buffers.
+
+signals that send data to the client and should block other signals
+(afp_dsi.c):
+ SIGALRM (tickle handler)
+ SIGHUP (attention)
+ SIGTERM (attention)
+
+functions which need SIG_BLOCK/SIG_SETMASK: dsi_send, dsi_attention
+functions which can use SIG_BLOCK/SIG_UNBLOCK: dsi_read
+
+functions which need their own buffers: dsi_attention, dsi_tickle
+
+
+PERFORMANCE TWEAKING:
+sending complete packets or the header and a partial packet to the
+client should always be handled by proto_send. for dsi_tcp.c,
+proto_send will coalesce the header and data by using writev if
+USE_WRITEV is defined. in addition, appleshare sessions often involve
+the sending and receiving of many small packets. as a consequence, i
+use TCP_NODELAY to speed up the turnaround time.
+
+because dsi_read can send incomplete packets, proto_send should not
+use the length parameter to specify the dsi_len field in the
+header. instead, anything that uses proto_send needs to specify
+dsi_len (in network byte order) before calling proto_send. the
+dsi_send() macro already does this.
+
+functions that need to specify .dsi_len: dsi_readinit, dsi_cmdreply
+
+mmap doesn't actually help things that much, so i don't use it.
+
+to reduce the amount of tickles generated on a slow link, i actually
+turn off SIGALRM for the duration of a "known" large file transfer
+(i.e., dsi_read/write).
+
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+
+#include <atalk/dsi.h>
+#include <atalk/afp.h>
+#include <netatalk/endian.h>
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/* send an attention. this may get called at any time, so we can't use
+ * DSI buffers to send one. */
+int dsi_attention(DSI *dsi, AFPUserBytes flags)
+{
+ /* header + AFPUserBytes */
+ char block[DSI_BLOCKSIZ + sizeof(AFPUserBytes)];
+ sigset_t oldset;
+ u_int32_t len, nlen;
+ u_int16_t id;
+
+ id = htons(dsi_serverID(dsi));
+ flags = htons(flags);
+ len = MIN(sizeof(flags), dsi->attn_quantum);
+ nlen = htonl(len);
+
+ memset(block, 0, sizeof(block));
+ block[0] = DSIFL_REQUEST; /* sending a request */
+ block[1] = DSIFUNC_ATTN; /* it's an attention */
+ memcpy(block + 2, &id, sizeof(id));
+ /* code = 0 */
+ memcpy(block + 8, &nlen, sizeof(nlen));
+ memcpy(block + 16, &flags, sizeof(flags));
+ /* reserved = 0 */
+
+ /* send an attention */
+ sigprocmask(SIG_BLOCK, &dsi->sigblockset, &oldset);
+ len = dsi_stream_write(dsi, block, DSI_BLOCKSIZ + len);
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+
+ return len;
+}
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <atalk/dsi.h>
+#include <netatalk/endian.h>
+
+void dsi_close(DSI *dsi)
+{
+ /* server generated. need to set all the fields. */
+ dsi->header.dsi_flags = DSIFL_REQUEST;
+ dsi->header.dsi_command = DSIFUNC_CLOSE;
+ dsi->header.dsi_requestID = htons(dsi_serverID(dsi));
+ dsi->header.dsi_code = dsi->header.dsi_reserved = htonl(0);
+ dsi->cmdlen = 0;
+
+ dsi_send(dsi);
+ dsi->proto_close(dsi);
+ free(dsi);
+}
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <atalk/dsi.h>
+#include <netatalk/endian.h>
+
+/* this assumes that the reply follows right after the command, saving
+ * on a couple assignments. specifically, command, requestID, and
+ * reserved field are assumed to already be set. */
+int dsi_cmdreply(DSI *dsi, const int err)
+{
+ dsi->header.dsi_flags = DSIFL_REPLY;
+ /*dsi->header.dsi_command = DSIFUNC_CMD;*/
+ dsi->header.dsi_len = htonl(dsi->datalen);
+ dsi->header.dsi_code = htonl(err);
+
+ return dsi_stream_send(dsi, dsi->data, dsi->datalen);
+}
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include <atalk/dsi.h>
+#include <atalk/server_child.h>
+
+static server_child *children = NULL;
+
+void dsi_kill(int sig)
+{
+ if (children)
+ server_child_kill(children, CHILD_DSIFORK, sig);
+}
+
+/* hand off the command. return child connection to the main program */
+DSI *dsi_getsession(DSI *dsi, server_child *serv_children,
+ const int tickleval)
+{
+ pid_t pid;
+
+ /* do a couple things on first entry */
+ if (!dsi->inited) {
+ if (!(children = serv_children))
+ return NULL;
+ dsi->inited = 1;
+ }
+
+ switch (pid = dsi->proto_open(dsi)) {
+ case -1:
+ /* if we fail, just return. it might work later */
+ syslog(LOG_ERR, "dsi_getsess: %m");
+ return dsi;
+
+ case 0: /* child. mostly handled below. */
+ dsi->child = 1;
+ break;
+
+ default: /* parent */
+ /* using SIGQUIT is hokey, but the child might not have
+ * re-established its signal handler for SIGTERM yet. */
+ if (server_child_add(children, CHILD_DSIFORK, pid) < 0) {
+ syslog(LOG_ERR, "dsi_getsess: %m");
+ dsi->header.dsi_flags = DSIFL_REPLY;
+ dsi->header.dsi_code = DSIERR_SERVBUSY;
+ dsi_send(dsi);
+ dsi->header.dsi_code = DSIERR_OK;
+ kill(pid, SIGQUIT);
+ }
+
+ dsi->proto_close(dsi);
+ return dsi;
+ }
+
+ /* child: check number of open connections. this is one off the
+ * actual count. */
+ if ((children->count >= children->nsessions) &&
+ (dsi->header.dsi_command == DSIFUNC_OPEN)) {
+ syslog(LOG_INFO, "dsi_getsess: too many connections");
+ dsi->header.dsi_flags = DSIFL_REPLY;
+ dsi->header.dsi_code = DSIERR_TOOMANY;
+ dsi_send(dsi);
+ exit(1);
+ }
+
+ /* get rid of some stuff */
+ close(dsi->serversock);
+ server_child_free(children);
+ children = NULL;
+
+ switch (dsi->header.dsi_command) {
+ case DSIFUNC_STAT: /* send off status and return */
+ {
+ /* OpenTransport 1.1.2 bug workaround:
+ *
+ * OT code doesn't currently handle close sockets well. urk.
+ * the workaround: wait for the client to close its
+ * side. timeouts prevent indefinite resource use.
+ */
+
+ static struct timeval timeout = {120, 0};
+ fd_set readfds;
+
+ dsi_getstatus(dsi);
+
+ FD_ZERO(&readfds);
+ FD_SET(dsi->socket, &readfds);
+ free(dsi);
+ select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
+ exit(0);
+ }
+ break;
+
+ case DSIFUNC_OPEN: /* setup session */
+ /* set up the tickle timer */
+ dsi->timer.it_interval.tv_sec = dsi->timer.it_value.tv_sec = tickleval;
+ dsi->timer.it_interval.tv_usec = dsi->timer.it_value.tv_usec = 0;
+ signal(SIGPIPE, SIG_IGN); /* we catch these ourselves */
+ dsi_opensession(dsi);
+ return dsi;
+ break;
+
+ default: /* just close */
+ syslog(LOG_INFO, "DSIUnknown %d", dsi->header.dsi_command);
+ dsi->proto_close(dsi);
+ exit(1);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <atalk/dsi.h>
+#include <netatalk/endian.h>
+
+/* return the status and then delete the connection. most of the
+ * fields are already set. */
+void dsi_getstatus(DSI *dsi)
+{
+ dsi->header.dsi_flags = DSIFL_REPLY;
+ /*dsi->header.dsi_command = DSIFUNC_STAT;*/
+
+ memcpy(dsi->commands, dsi->status, dsi->statuslen);
+ dsi->cmdlen = dsi->statuslen;
+ dsi_send(dsi);
+}
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <atalk/dsi.h>
+#include "dsi_private.h"
+
+DSI *dsi_init(const dsi_proto protocol, const char *program,
+ const char *hostname, const char *address,
+ const int port, const int proxy, const u_int32_t quantum)
+{
+ DSI *dsi;
+
+ if ((dsi = (DSI *) calloc(1, sizeof(DSI))) == NULL) {
+ return( NULL );
+ }
+ dsi->attn_quantum = DSI_DEFQUANT; /* default quantum size */
+ dsi->server_quantum = quantum; /* default server quantum */
+ dsi->program = program;
+
+ /* signals to block. we actually disable timers for "known"
+ * large transfers (i.e., dsi_read/write). */
+ sigemptyset(&dsi->sigblockset);
+ sigaddset(&dsi->sigblockset, SIGTERM);
+ sigaddset(&dsi->sigblockset, SIGHUP);
+ sigaddset(&dsi->sigblockset, SIGALRM);
+
+ switch (protocol) {
+ /* currently the only transport protocol that exists for dsi */
+ case DSI_TCPIP:
+ if (!dsi_tcp_init(dsi, hostname, address, port, proxy)) {
+ free(dsi);
+ dsi = NULL;
+ }
+ break;
+
+ default: /* unknown protocol */
+ free(dsi);
+ dsi = NULL;
+ break;
+ }
+
+ return dsi;
+}
+
+void dsi_setstatus(DSI *dsi, u_int8_t *status, const int slen)
+{
+ dsi->status = status;
+ dsi->statuslen = slen;
+}
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <atalk/dsi.h>
+
+/* OpenSession. set up the connection */
+void dsi_opensession(DSI *dsi)
+{
+ u_int32_t i = 0; /* this serves double duty. it must be 4-bytes long */
+
+ /* parse options */
+ while (i < dsi->cmdlen) {
+ switch (dsi->commands[i++]) {
+ case DSIOPT_ATTNQUANT:
+ memcpy(&dsi->attn_quantum, dsi->commands + i + 1, dsi->commands[i]);
+ dsi->attn_quantum = ntohl(dsi->attn_quantum);
+
+ case DSIOPT_SERVQUANT: /* just ignore these */
+ default:
+ i += dsi->commands[i] + 1; /* forward past length tag + length */
+ break;
+ }
+ }
+
+ /* let the client know the server quantum. we don't use the
+ * max server quantum due to a bug in appleshare client 3.8.6. */
+ dsi->header.dsi_flags = DSIFL_REPLY;
+ /* dsi->header.dsi_command = DSIFUNC_OPEN;*/
+ dsi->cmdlen = 2 + sizeof(i); /* length of data. dsi_send uses it. */
+ dsi->commands[0] = DSIOPT_SERVQUANT;
+ dsi->commands[1] = sizeof(i);
+ i = htonl(( dsi->server_quantum < DSI_SERVQUANT_MIN ||
+ dsi->server_quantum > DSI_SERVQUANT_MAX ) ?
+ DSI_SERVQUANT_DEF : dsi->server_quantum);
+ memcpy(dsi->commands + 2, &i, sizeof(i));
+
+ dsi_send(dsi);
+}
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#ifndef _DSI_PRIVATE_H
+#define _DSI_PRIVATE_H 1
+
+/* this header handles interactions between protocol-specific code and
+ * dsi initialization. only dsi_init.c and dsi_<proto>.c should
+ * include it.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <netatalk/endian.h>
+
+extern int dsi_tcp_init __P((DSI *, const char * /*host*/,
+ const char * /*address*/,
+ const u_int16_t /*port*/,
+ const int /*proxy*/));
+
+#endif /* _DSI_PRIVATE_H */
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <atalk/dsi.h>
+
+#ifndef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/* streaming i/o for afp_read. this is all from the perspective of the
+ * client. it basically does the reverse of dsi_write. on first entry,
+ * it will send off the header plus whatever is in its command
+ * buffer. it returns the amount of stuff still to be read
+ * (constrained by the buffer size). */
+ssize_t dsi_readinit(DSI *dsi, void *buf, const size_t buflen,
+ const size_t size, const int err)
+{
+ const struct itimerval none = {{0, 0}, {0, 0}};
+
+ dsi->noreply = 1; /* we will handle our own replies */
+ dsi->header.dsi_flags = DSIFL_REPLY;
+ /*dsi->header.dsi_command = DSIFUNC_CMD;*/
+ dsi->header.dsi_len = htonl(size);
+ dsi->header.dsi_code = htonl(err);
+
+ sigprocmask(SIG_BLOCK, &dsi->sigblockset, NULL);
+ setitimer(ITIMER_REAL, &none, &dsi->savetimer);
+ if (dsi_stream_send(dsi, buf, buflen)) {
+ dsi->datasize = size - buflen;
+ return min(dsi->datasize, buflen);
+ }
+
+ return -1; /* error */
+}
+
+void dsi_readdone(DSI *dsi)
+{
+ setitimer(ITIMER_REAL, &dsi->savetimer, NULL);
+ sigprocmask(SIG_UNBLOCK, &dsi->sigblockset, NULL);
+}
+
+/* send off the data */
+ssize_t dsi_read(DSI *dsi, void *buf, const size_t buflen)
+{
+ size_t len = dsi_stream_write(dsi, buf, buflen);
+
+ if (len == buflen) {
+ dsi->datasize -= len;
+ return min(dsi->datasize, buflen);
+ }
+
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (c) 1998 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ *
+ * this file provides the following functions:
+ * dsi_stream_write: just write a bunch of bytes.
+ * dsi_stream_read: just read a bunch of bytes.
+ * dsi_stream_send: send a DSI header + data.
+ * dsi_stream_receive: read a DSI header + data.
+ */
+
+#define USE_WRITEV
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#ifdef USE_WRITEV
+#include <sys/uio.h>
+#endif
+#include <syslog.h>
+
+#include <atalk/dsi.h>
+#include <netatalk/endian.h>
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+
+
+/* write raw data. return actual bytes read. checks against EINTR
+ * aren't necessary if all of the signals have SA_RESTART
+ * specified. */
+size_t dsi_stream_write(DSI *dsi, void *data, const size_t length)
+{
+ size_t written;
+ ssize_t len;
+
+ written = 0;
+ while (written < length) {
+ if (((len = write(dsi->socket, (u_int8_t *) data + written,
+ length - written)) == -1 && errno == EINTR) ||
+ !len)
+ continue;
+
+ if (len < 0) {
+ syslog(LOG_ERR, "dsi_stream_write: %m");
+ break;
+ }
+
+ written += len;
+ }
+
+ dsi->write_count += written;
+ return written;
+}
+
+/* read raw data. return actual bytes read. this will wait until
+ * it gets length bytes */
+size_t dsi_stream_read(DSI *dsi, void *data, const size_t length)
+{
+ size_t stored;
+ ssize_t len;
+
+ stored = 0;
+ while (stored < length) {
+ if ((len = read(dsi->socket, (u_int8_t *) data + stored,
+ length - stored)) == -1 && errno == EINTR)
+ continue;
+
+ if (len > 0)
+ stored += len;
+ else {/* eof or error */
+ syslog(LOG_ERR, "dsi_stream_read(%d): %m", len);
+ break;
+ }
+ }
+
+ dsi->read_count += stored;
+ return stored;
+}
+
+
+/* write data. 0 on failure. this assumes that dsi_len will never
+ * cause an overflow in the data buffer. */
+int dsi_stream_send(DSI *dsi, void *buf, size_t length)
+{
+ char block[DSI_BLOCKSIZ];
+ sigset_t oldset;
+#ifdef USE_WRITEV
+ struct iovec iov[2];
+ size_t towrite;
+ ssize_t len;
+#endif
+
+ block[0] = dsi->header.dsi_flags;
+ block[1] = dsi->header.dsi_command;
+ memcpy(block + 2, &dsi->header.dsi_requestID,
+ sizeof(dsi->header.dsi_requestID));
+ memcpy(block + 4, &dsi->header.dsi_code, sizeof(dsi->header.dsi_code));
+ memcpy(block + 8, &dsi->header.dsi_len, sizeof(dsi->header.dsi_len));
+ memcpy(block + 12, &dsi->header.dsi_reserved,
+ sizeof(dsi->header.dsi_reserved));
+
+ /* block signals */
+ sigprocmask(SIG_BLOCK, &dsi->sigblockset, &oldset);
+
+ if (!length) { /* just write the header */
+ length = (dsi_stream_write(dsi, block, sizeof(block)) == sizeof(block));
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ return length; /* really 0 on failure, 1 on success */
+ }
+
+#ifdef USE_WRITEV
+ iov[0].iov_base = block;
+ iov[0].iov_len = sizeof(block);
+ iov[1].iov_base = buf;
+ iov[1].iov_len = length;
+
+ towrite = sizeof(block) + length;
+ dsi->write_count += towrite;
+ while (towrite > 0) {
+ if (((len = writev(dsi->socket, iov, 2)) == -1 && errno == EINTR) ||
+ !len)
+ continue;
+
+ if (len == towrite) /* wrote everything out */
+ break;
+ else if (len < 0) { /* error */
+ syslog(LOG_ERR, "dsi_stream_send: %m");
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ return 0;
+ }
+
+ towrite -= len;
+ if (towrite > length) { /* skip part of header */
+ iov[0].iov_base = (char *) iov[0].iov_base + len;
+ iov[0].iov_len -= len;
+ } else { /* skip to data */
+ if (iov[0].iov_len) {
+ len -= iov[0].iov_len;
+ iov[0].iov_len = 0;
+ }
+ iov[1].iov_base = (char *) iov[1].iov_base + len;
+ iov[1].iov_len -= len;
+ }
+ }
+
+#else
+ /* write the header then data */
+ if ((dsi_stream_write(dsi, block, sizeof(block)) != sizeof(block)) ||
+ (dsi_stream_write(dsi, buf, length) != length)) {
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ return 0;
+ }
+#endif
+
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ return 1;
+}
+
+
+/* read data. function on success. 0 on failure. data length gets
+ * stored in length variable. this should really use size_t's, but
+ * that would require changes elsewhere. */
+int dsi_stream_receive(DSI *dsi, void *buf, const int ilength,
+ int *rlength)
+{
+ char block[DSI_BLOCKSIZ];
+
+ /* read in the header */
+ if (dsi_stream_read(dsi, block, sizeof(block)) != sizeof(block))
+ return 0;
+
+ dsi->header.dsi_flags = block[0];
+ dsi->header.dsi_command = block[1];
+ memcpy(&dsi->header.dsi_requestID, block + 2,
+ sizeof(dsi->header.dsi_requestID));
+ memcpy(&dsi->header.dsi_code, block + 4, sizeof(dsi->header.dsi_code));
+ memcpy(&dsi->header.dsi_len, block + 8, sizeof(dsi->header.dsi_len));
+ memcpy(&dsi->header.dsi_reserved, block + 12,
+ sizeof(dsi->header.dsi_reserved));
+ dsi->clientID = ntohs(dsi->header.dsi_requestID);
+
+ /* make sure we don't over-write our buffers. */
+ *rlength = min(ntohl(dsi->header.dsi_len), ilength);
+ if (dsi_stream_read(dsi, buf, *rlength) != *rlength)
+ return 0;
+
+ return block[1];
+}
--- /dev/null
+/*
+ * Copyright (c) 1997, 1998 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ *
+ * this provides both proto_open() and proto_close() to account for
+ * protocol specific initialization and shutdown procedures. all the
+ * read/write stuff is done in dsi_stream.c. */
+
+#define USE_TCP_NODELAY
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <signal.h>
+#include <syslog.h>
+
+#ifdef __svr4__
+#include <sys/sockio.h>
+#endif
+
+#ifdef TCPWRAP
+#include <tcpd.h>
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+#endif
+
+#include <atalk/dsi.h>
+#include <atalk/compat.h>
+#include <atalk/util.h>
+#include <netatalk/endian.h>
+#include "dsi_private.h"
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+
+#ifndef DSI_TCPMAXPEND
+#define DSI_TCPMAXPEND 20 /* max # of pending connections */
+#endif
+
+#ifndef DSI_TCPTIMEOUT
+#define DSI_TCPTIMEOUT 120 /* timeout in seconds for connections */
+#endif
+
+
+/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature. */
+#ifndef SOCKLEN_T
+#define SOCKLEN_T unsigned int
+#endif
+
+static void dsi_tcp_close(DSI *dsi)
+{
+ if (dsi->socket == -1)
+ return;
+
+ close(dsi->socket);
+ dsi->socket = -1;
+}
+
+/* alarm handler for tcp_open */
+static void timeout_handler()
+{
+ syslog(LOG_ERR, "dsi_tcp_open: connection timed out");
+ exit(1);
+}
+
+/* accept the socket and do a little sanity checking */
+static int dsi_tcp_open(DSI *dsi)
+{
+ pid_t pid;
+ SOCKLEN_T len;
+
+ len = sizeof(dsi->client);
+ dsi->socket = accept(dsi->serversock, (struct sockaddr *) &dsi->client,
+ &len);
+
+#ifdef TCPWRAP
+ {
+ struct request_info req;
+ request_init(&req, RQ_DAEMON, dsi->program, RQ_FILE, dsi->socket, NULL);
+ fromhost(&req);
+ if (!hosts_access(&req)) {
+ syslog(deny_severity, "refused connect from %s", eval_client(&req));
+ close(dsi->socket);
+ errno = ECONNREFUSED;
+ dsi->socket = -1;
+ }
+ }
+#endif
+
+ if (dsi->socket < 0)
+ return -1;
+
+ if ((pid = fork()) == 0) { /* child */
+ static const struct itimerval timer = {{0, 0}, {DSI_TCPTIMEOUT, 0}};
+ struct sigaction newact, oldact;
+ u_int8_t block[DSI_BLOCKSIZ];
+ size_t stored;
+
+ /* reset a couple signals */
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+
+ /* install an alarm to deal with non-responsive connections */
+ memset(&newact, 0, sizeof(newact));
+ newact.sa_handler = timeout_handler;
+ if ((sigaction(SIGALRM, &newact, &oldact) < 0) ||
+ (setitimer(ITIMER_REAL, &timer, NULL) < 0)) {
+ syslog(LOG_ERR, "dsi_tcp_open: %m");
+ exit(1);
+ }
+
+ /* read in commands. this is similar to dsi_receive except
+ * for the fact that we do some sanity checking to prevent
+ * delinquent connections from causing mischief. */
+
+ /* read in the first two bytes */
+ dsi_stream_read(dsi, block, 2);
+ if ((block[0] > DSIFL_MAX) || (block[1] > DSIFUNC_MAX)) {
+ syslog(LOG_ERR, "dsi_tcp_open: invalid header");
+ exit(1);
+ }
+
+ /* read in the rest of the header */
+ stored = 2;
+ while (stored < DSI_BLOCKSIZ) {
+ len = dsi_stream_read(dsi, block + stored, sizeof(block) - stored);
+ if (len > 0)
+ stored += len;
+ else {
+ syslog(LOG_ERR, "dsi_tcp_open: stream_read: %m");
+ exit(1);
+ }
+ }
+
+ dsi->header.dsi_flags = block[0];
+ dsi->header.dsi_command = block[1];
+ memcpy(&dsi->header.dsi_requestID, block + 2,
+ sizeof(dsi->header.dsi_requestID));
+ memcpy(&dsi->header.dsi_code, block + 4, sizeof(dsi->header.dsi_code));
+ memcpy(&dsi->header.dsi_len, block + 8, sizeof(dsi->header.dsi_len));
+ memcpy(&dsi->header.dsi_reserved, block + 12,
+ sizeof(dsi->header.dsi_reserved));
+ dsi->clientID = ntohs(dsi->header.dsi_requestID);
+
+ /* make sure we don't over-write our buffers. */
+ dsi->cmdlen = min(ntohl(dsi->header.dsi_len), DSI_CMDSIZ);
+
+ stored = 0;
+ while (stored < dsi->cmdlen) {
+ len = dsi_stream_read(dsi, dsi->commands + stored, dsi->cmdlen - stored);
+ if (len > 0)
+ stored += len;
+ else {
+ syslog(LOG_ERR, "dsi_tcp_open: stream_read: %m");
+ exit(1);
+ }
+ }
+
+ /* restore signal */
+ sigaction(SIGALRM, &oldact, NULL);
+
+ syslog(LOG_INFO,"ASIP session:%u(%d) from %s:%u(%d)",
+ ntohs(dsi->server.sin_port), dsi->serversock,
+ inet_ntoa(dsi->client.sin_addr), ntohs(dsi->client.sin_port),
+ dsi->socket);
+ }
+
+ /* send back our pid */
+ return pid;
+}
+
+/* this needs to accept passed in addresses */
+int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address,
+ const u_int16_t ipport, const int proxy)
+{
+ struct servent *service;
+ struct hostent *host;
+ int port;
+
+ dsi->protocol = DSI_TCPIP;
+
+ /* create a socket */
+ if (proxy)
+ dsi->serversock = -1;
+ else if ((dsi->serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
+ return 0;
+
+ /* find port */
+ if (ipport)
+ port = htons(ipport);
+ else if ((service = getservbyname("afpovertcp", "tcp")))
+ port = service->s_port;
+ else
+ port = htons(DSI_AFPOVERTCP_PORT);
+
+ /* find address */
+ if (!address)
+ dsi->server.sin_addr.s_addr = htonl(INADDR_ANY);
+ else if (inet_aton(address, &dsi->server.sin_addr) == 0) {
+ syslog(LOG_INFO, "dsi_tcp: invalid address (%s)", address);
+ return 0;
+ }
+
+ dsi->server.sin_family = AF_INET;
+ dsi->server.sin_port = port;
+
+ if (!proxy) {
+ /* this deals w/ quick close/opens */
+#ifdef SO_REUSEADDR
+ port = 1;
+ setsockopt(dsi->serversock, SOL_SOCKET, SO_REUSEADDR, &port, sizeof(port));
+#endif
+
+#ifdef USE_TCP_NODELAY
+#ifndef SOL_TCP
+#define SOL_TCP IPPROTO_TCP
+#endif
+ port = 1;
+ setsockopt(dsi->serversock, SOL_TCP, TCP_NODELAY, &port, sizeof(port));
+#endif
+
+ /* now, bind the socket and set it up for listening */
+ if ((bind(dsi->serversock, (struct sockaddr *) &dsi->server,
+ sizeof(dsi->server)) < 0) ||
+ (listen(dsi->serversock, DSI_TCPMAXPEND) < 0)) {
+ close(dsi->serversock);
+ return 0;
+ }
+ }
+
+ /* get real address for GetStatus. we'll go through the list of
+ * interfaces if necessary. */
+ if (!address) {
+ if ((host = gethostbyname(hostname))) /* we can resolve the name */
+ dsi->server.sin_addr.s_addr = ((struct in_addr *) host->h_addr)->s_addr;
+ else {
+ char **start, **list;
+ struct ifreq ifr;
+
+ /* get it from the interface list */
+ start = list = getifacelist();
+ while (list && *list) {
+ strncpy(ifr.ifr_name, *list, sizeof(ifr.ifr_name));
+ list++;
+
+#ifndef IFF_SLAVE
+#define IFF_SLAVE 0
+#endif
+ if (ioctl(dsi->serversock, SIOCGIFFLAGS, &ifr) < 0)
+ continue;
+
+ if (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_SLAVE))
+ continue;
+
+ if ((ifr.ifr_flags & IFF_UP) == 0)
+ continue;
+
+ if (ioctl(dsi->serversock, SIOCGIFADDR, &ifr) < 0)
+ continue;
+
+ dsi->server.sin_addr.s_addr =
+ ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr;
+ syslog(LOG_INFO, "dsi_tcp: Can't resolve hostname (%s).\n"
+ "%s on interface %s will be used instead.", hostname,
+ inet_ntoa(dsi->server.sin_addr), ifr.ifr_name);
+ goto iflist_done;
+
+ }
+ syslog(LOG_INFO, "dsi_tcp (Chooser will not select afp/tcp)\n\
+Check to make sure %s is in /etc/hosts and the correct domain is in\n\
+/etc/resolv.conf: %m", hostname);
+
+iflist_done:
+ if (start)
+ freeifacelist(start);
+ }
+ }
+
+ /* everything's set up. now point protocol specific functions to
+ * tcp versions */
+ dsi->proto_open = dsi_tcp_open;
+ dsi->proto_close = dsi_tcp_close;
+ return 1;
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include <signal.h>
+
+#include <atalk/dsi.h>
+#include <netatalk/endian.h>
+
+/* server generated tickles. as this is only called by the tickle handler,
+ * we don't need to block signals. well, actually, we might get it during
+ * a SIGHUP. */
+void dsi_tickle(DSI *dsi)
+{
+ char block[DSI_BLOCKSIZ];
+ sigset_t oldset;
+ u_int16_t id;
+
+ id = htons(dsi_serverID(dsi));
+
+ memset(block, 0, sizeof(block));
+ block[0] = DSIFL_REQUEST;
+ block[1] = DSIFUNC_TICKLE;
+ memcpy(block + 2, &id, sizeof(id));
+ /* code = len = reserved = 0 */
+
+ sigprocmask(SIG_BLOCK, &dsi->sigblockset, &oldset);
+ dsi_stream_write(dsi, block, DSI_BLOCKSIZ);
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ *
+ * 7 Oct 1997 added checks for 0 data.
+ */
+
+/* this streams writes */
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <atalk/dsi.h>
+#include <netatalk/endian.h>
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/* initialize relevant things for dsi_write. this returns the amount
+ * of data in the data buffer. the interface has been reworked to allow
+ * for arbitrary buffers. */
+size_t dsi_writeinit(DSI *dsi, void *buf, const size_t buflen)
+{
+ const struct itimerval none = {{0, 0}, {0, 0}};
+ size_t len, header;
+
+ /* figure out how much data we have. do a couple checks for 0
+ * data */
+ header = ntohl(dsi->header.dsi_code);
+ dsi->datasize = header ? ntohl(dsi->header.dsi_len) - header : 0;
+ if (dsi->datasize > 0) {
+ len = MIN(sizeof(dsi->commands) - header, dsi->datasize);
+
+ /* write last part of command buffer into buf */
+ memcpy(buf, dsi->commands + header, len);
+
+ /* recalculate remaining data */
+ dsi->datasize -= len;
+ } else
+ len = 0;
+
+ /* deal with signals. i'm doing it this way to ensure that we don't
+ * get confused if a writeflush on zero remaining data is, for some
+ * reason, needed. */
+ sigprocmask(SIG_BLOCK, &dsi->sigblockset, NULL);
+ setitimer(ITIMER_REAL, &none, &dsi->savetimer);
+ return len;
+}
+
+/* fill up buf and then return. this should be called repeatedly
+ * until all the data has been read. i block alarm processing
+ * during the transfer to avoid sending unnecessary tickles. */
+size_t dsi_write(DSI *dsi, void *buf, const size_t buflen)
+{
+ size_t length;
+
+ if (((length = MIN(buflen, dsi->datasize)) > 0) &&
+ ((length = dsi_stream_read(dsi, buf, length)) > 0)) {
+ dsi->datasize -= length;
+ return length;
+ }
+
+ setitimer(ITIMER_REAL, &dsi->savetimer, NULL);
+ sigprocmask(SIG_UNBLOCK, &dsi->sigblockset, NULL);
+ return 0;
+}
+
+/* flush any unread buffers. */
+void dsi_writeflush(DSI *dsi)
+{
+ size_t length;
+
+ while (dsi->datasize > 0) {
+ length = dsi_stream_read(dsi, dsi->data,
+ MIN(sizeof(dsi->data), dsi->datasize));
+ if (length > 0)
+ dsi->datasize -= length;
+ else
+ break;
+ }
+
+ setitimer(ITIMER_REAL, &dsi->savetimer, NULL);
+ sigprocmask(SIG_UNBLOCK, &dsi->sigblockset, NULL);
+}
--- /dev/null
+SRC = nbp_util.c nbp_lkup.c nbp_rgstr.c nbp_unrgstr.c
+OBJ = nbp_util.o nbp_lkup.o nbp_rgstr.o nbp_unrgstr.o
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH}
+TAGSFILE= tags
+CC= cc
+
+all : profiled nbplib
+
+profiled:
+ -mkdir profiled
+
+nbplib nbplib_p : ${OBJ}
+ @echo "building profiled nbplib"
+ @cd profiled; ar cru ../nbplib_p ${OBJ}
+ @echo "building normal nbplib"
+ @ar cru nbplib ${OBJ}
+
+.c.o :
+ ${CC} -p ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- profiled/$*.o
+ ${CC} ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- $*.o
+
+clean :
+ rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+ rm -f nbplib nbplib_p
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef NBP_CONF_H
+#define NBP_CONF_H 1
+
+#include <sys/cdefs.h>
+#include <atalk/nbp.h>
+
+extern char nbp_send[ 1024 ];
+extern char nbp_recv[ 1024 ];
+extern u_char nbp_port;
+extern unsigned char nbp_id;
+
+
+int nbp_parse __P((char *, struct nbpnve *, int));
+int nbp_match __P((struct nbpnve *, struct nbpnve *, int));
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1997 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <netatalk/ddp.h>
+#include <atalk/compat.h>
+#include <atalk/nbp.h>
+#include <atalk/netddp.h>
+#include <atalk/ddp.h>
+
+#include <netdb.h>
+#include <errno.h>
+
+#include "nbp_conf.h"
+
+/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature. */
+#ifndef SOCKLEN_T
+#define SOCKLEN_T unsigned int
+#endif
+
+int nbp_lookup( obj, type, zone, nn, nncnt, ataddr )
+ const char *obj, *type, *zone;
+ struct nbpnve *nn;
+ int nncnt;
+ const struct at_addr *ataddr;
+{
+ struct sockaddr_at addr, from;
+ struct timeval tv, tv_begin, tv_end;
+ fd_set fds;
+ struct nbpnve nve;
+ struct nbphdr nh;
+ struct nbptuple nt;
+ struct servent *se;
+ char *data = nbp_send;
+ SOCKLEN_T namelen;
+ int s, cnt, tries, sc, cc, i, c;
+
+ memset(&addr, 0, sizeof(addr));
+ memset(&from, 0, sizeof(from));
+ if (ataddr)
+ memcpy(&addr.sat_addr, ataddr, sizeof(struct at_addr));
+ if ((s = netddp_open(&addr, &from)) < 0)
+ return -1;
+
+ *data++ = DDPTYPE_NBP;
+#ifdef MACOSX_SERVER
+ nh.nh_op = from.sat_addr.s_node ? NBPOP_BRRQ : NBPOP_LKUP;
+#else
+ nh.nh_op = NBPOP_BRRQ;
+#endif
+
+ nh.nh_cnt = 1;
+ nh.nh_id = ++nbp_id;
+ memcpy( data, &nh, SZ_NBPHDR );
+ data += SZ_NBPHDR;
+
+ memset(&nt, 0, sizeof(nt));
+ nt.nt_net = addr.sat_addr.s_net;
+ nt.nt_node = addr.sat_addr.s_node;
+ nt.nt_port = addr.sat_port;
+
+ memcpy( data, &nt, SZ_NBPTUPLE);
+ data += SZ_NBPTUPLE;
+
+ if ( obj ) {
+ if (( cc = strlen( obj )) > NBPSTRLEN ) goto lookup_err;
+ *data++ = cc;
+ memcpy( data, obj, cc );
+ data += cc;
+ } else {
+ *data++ = 1;
+ *data++ = '='; /* match anything */
+ }
+
+ if ( type ) {
+ if (( cc = strlen( type )) > NBPSTRLEN ) goto lookup_err;
+ *data++ = cc;
+ memcpy( data, type, cc );
+ data += cc;
+ } else {
+ *data++ = 1;
+ *data++ = '='; /* match anything */
+ }
+
+ if ( zone ) {
+ if (( cc = strlen( zone )) > NBPSTRLEN ) goto lookup_err;
+ *data++ = cc;
+ memcpy( data, zone, cc );
+ data += cc;
+ } else {
+ *data++ = 1;
+ *data++ = '*'; /* default zone */
+ }
+
+ if ( nbp_port == 0 ) {
+ if (( se = getservbyname( "nbp", "ddp" )) == NULL ) {
+ nbp_port = 2;
+ } else {
+ nbp_port = ntohs( se->s_port );
+ }
+ }
+
+#ifdef MACOSX_SERVER
+ if (from.sat_addr.s_node) {
+ memcpy(&addr.sat_addr, &from.sat_addr, sizeof(addr.sat_addr));
+ } else {
+ addr.sat_addr.s_net = ATADDR_ANYNET;
+ addr.sat_addr.s_node = ATADDR_BCAST;
+ }
+#endif
+ addr.sat_port = nbp_port;
+
+ cnt = 0;
+ tries = 3;
+ sc = data - nbp_send;
+ while ( tries > 0 ) {
+ if ( netddp_sendto( s, nbp_send, sc, 0, (struct sockaddr *)&addr,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ goto lookup_err;
+ }
+
+ tv.tv_sec = 2L;
+ tv.tv_usec = 0;
+
+ for (;;) {
+ FD_ZERO( &fds );
+ FD_SET( s, &fds );
+ if ( gettimeofday( &tv_begin, NULL ) < 0 ) {
+ goto lookup_err;
+ }
+ if (( c = select( s + 1, &fds, NULL, NULL, &tv )) < 0 ) {
+ goto lookup_err;
+ }
+ if ( c == 0 || FD_ISSET( s, &fds ) == 0 ) {
+ break;
+ }
+ if ( gettimeofday( &tv_end, NULL ) < 0 ) {
+ goto lookup_err;
+ }
+ if ( tv_begin.tv_usec > tv_end.tv_sec ) {
+ tv_end.tv_usec += 1000000;
+ tv_end.tv_sec -= 1;
+ }
+ if (( tv.tv_usec -= ( tv_end.tv_usec - tv_begin.tv_usec )) < 0 ) {
+ tv.tv_usec += 1000000;
+ tv.tv_sec -= 1;
+ }
+ if (( tv.tv_sec -= ( tv_end.tv_sec - tv_begin.tv_sec )) < 0 ) {
+ break;
+ }
+
+ namelen = sizeof( struct sockaddr_at );
+ if (( cc = netddp_recvfrom( s, nbp_recv, sizeof( nbp_recv ), 0,
+ (struct sockaddr *)&from, &namelen )) < 0 ) {
+ goto lookup_err;
+ }
+
+ data = nbp_recv;
+ if ( *data++ != DDPTYPE_NBP ) {
+ continue;
+ }
+ cc--;
+
+ memcpy( &nh, data, SZ_NBPHDR );
+ data += SZ_NBPHDR;
+ if ( nh.nh_op != NBPOP_LKUPREPLY ) {
+ continue;
+ }
+ cc -= SZ_NBPHDR;
+
+ while (( i = nbp_parse( data, &nve, cc )) >= 0 ) {
+ data += cc - i;
+ cc = i;
+ /*
+ * Check to see if nve is already in nn. If not,
+ * put it in, and increment cnt.
+ */
+ for ( i = 0; i < cnt; i++ ) {
+ if ( nbp_match( &nve, &nn[ i ],
+ NBPMATCH_NOZONE|NBPMATCH_NOGLOB )) {
+ break;
+ }
+ }
+ if ( i == cnt ) {
+ nn[ cnt++ ] = nve;
+ }
+ if ( cnt == nncnt ) {
+ tries = 0;
+ break;
+ }
+ }
+ if ( cnt == nncnt ) {
+ tries = 0;
+ break;
+ }
+ }
+ tries--;
+ }
+
+ netddp_close(s);
+ errno = 0;
+ return( cnt );
+
+lookup_err:
+ netddp_close(s);
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <atalk/nbp.h>
+#include <atalk/ddp.h>
+#include <atalk/netddp.h>
+
+#include <netdb.h>
+#include "nbp_conf.h"
+
+/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature. */
+#ifndef SOCKLEN_T
+#define SOCKLEN_T unsigned int
+#endif
+
+int nbp_rgstr( sat, obj, type, zone )
+ struct sockaddr_at *sat;
+ const char *obj, *type, *zone;
+{
+ struct sockaddr_at to;
+ struct nbpnve nn;
+ struct nbphdr nh;
+ struct nbptuple nt;
+ struct timeval timeout;
+ fd_set readfd;
+ struct servent *se;
+ char *data;
+ int s, cc;
+ SOCKLEN_T namelen;
+
+ if ( nbp_lookup( obj, type, zone, &nn, 1, &sat->sat_addr ) > 0 ) {
+ errno = EADDRINUSE;
+ return( -1 );
+ }
+
+ memset(&to, 0, sizeof(to));
+ if ((s = netddp_open(&to, NULL)) < 0)
+ return -1;
+
+ data = nbp_send;
+ *data++ = DDPTYPE_NBP;
+ nh.nh_op = NBPOP_RGSTR;
+ nh.nh_cnt = 1;
+ nh.nh_id = ++nbp_id;
+ memcpy( data, &nh, SZ_NBPHDR );
+ data += SZ_NBPHDR;
+
+ memset(&nt, 0, sizeof(nt));
+ nt.nt_net = sat->sat_addr.s_net;
+ nt.nt_node = sat->sat_addr.s_node;
+ nt.nt_port = sat->sat_port;
+ memcpy( data, &nt, SZ_NBPTUPLE);
+ data += SZ_NBPTUPLE;
+
+ if ( obj ) {
+ if (( cc = strlen( obj )) > NBPSTRLEN ) return( -1 );
+ *data++ = cc;
+ memcpy( data, obj, cc );
+ data += cc;
+ } else {
+ *data++ = 0;
+ }
+
+ if ( type ) {
+ if (( cc = strlen( type )) > NBPSTRLEN ) return( -1 );
+ *data++ = cc;
+ memcpy( data, type, cc );
+ data += cc;
+ } else {
+ *data++ = 0;
+ }
+
+ if ( zone ) {
+ if (( cc = strlen( zone )) > NBPSTRLEN ) return( -1 );
+ *data++ = cc;
+ memcpy( data, zone, cc );
+ data += cc;
+ } else {
+ *data++ = 1;
+ *data++ = '*'; /* default zone */
+ }
+
+
+ if ( nbp_port == 0 ) {
+ if (( se = getservbyname( "nbp", "ddp" )) == NULL ) {
+ nbp_port = 2;
+ } else {
+ nbp_port = ntohs( se->s_port );
+ }
+ }
+ to.sat_port = nbp_port;
+
+ if ( netddp_sendto( s, nbp_send, data - nbp_send, 0,
+ (struct sockaddr *)&to,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ goto register_err;
+ }
+
+ FD_ZERO( &readfd );
+ FD_SET( s, &readfd );
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+ if (( cc = select( s + 1, &readfd, 0, 0, &timeout )) < 0 ) {
+ goto register_err;
+ }
+ if ( cc == 0 ) {
+ errno = ETIMEDOUT;
+ goto register_err;
+ }
+
+ namelen = sizeof( struct sockaddr_at );
+ if (( cc = netddp_recvfrom( s, nbp_recv, sizeof( nbp_recv ), 0,
+ (struct sockaddr *)&to, &namelen )) < 0 ) {
+ goto register_err;
+ }
+
+ netddp_close( s );
+
+ data = nbp_recv;
+ if ( *data++ != DDPTYPE_NBP ) {
+ return( -1 );
+ }
+ memcpy( &nh, data, SZ_NBPHDR );
+ if ( nh.nh_op != NBPOP_OK ) {
+ return -1;
+ }
+ return( 0 );
+
+register_err:
+ netddp_close(s);
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1997 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/nbp.h>
+#include <atalk/netddp.h>
+#include <atalk/ddp.h>
+
+#include <netdb.h>
+#include "nbp_conf.h"
+
+/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature. */
+#ifndef SOCKLEN_T
+#define SOCKLEN_T unsigned int
+#endif
+
+int nbp_unrgstr( obj, type, zone, addr )
+ const char *obj, *type, *zone;
+ const struct at_addr *addr;
+{
+ struct sockaddr_at to;
+ struct nbphdr nh;
+ struct timeval timeout;
+ fd_set readfd;
+ struct servent *se;
+ char *data;
+ int s, cc;
+ SOCKLEN_T namelen;
+
+
+ memset(&to, 0, sizeof(to));
+ if ((s = netddp_open(&to, NULL)) < 0)
+ return -1;
+
+ data = nbp_send;
+ *data++ = DDPTYPE_NBP;
+ nh.nh_op = NBPOP_UNRGSTR;
+ nh.nh_cnt = 1;
+ nh.nh_id = ++nbp_id;
+ memcpy( data, &nh, SZ_NBPHDR );
+ data += SZ_NBPHDR;
+
+ memset(data, 0, SZ_NBPTUPLE);
+ data += SZ_NBPTUPLE;
+
+ if ( obj ) {
+ if (( cc = strlen( obj )) > NBPSTRLEN ) return( -1 );
+ *data++ = cc;
+ memcpy( data, obj, cc );
+ data += cc;
+ } else {
+ *data++ = 0;
+ }
+
+ if ( type ) {
+ if (( cc = strlen( type )) > NBPSTRLEN ) return( -1 );
+ *data++ = cc;
+ memcpy( data, type, cc );
+ data += cc;
+ } else {
+ *data++ = 0;
+ }
+
+ if ( zone ) {
+ if (( cc = strlen( zone )) > NBPSTRLEN ) return( -1 );
+ *data++ = cc;
+ memcpy( data, zone, cc );
+ data += cc;
+ } else {
+ *data++ = 0;
+ }
+
+ memset( &to, 0, sizeof( struct sockaddr_at ));
+ to.sat_family = AF_APPLETALK;
+ if (addr)
+ memcpy(&to.sat_addr, addr, sizeof(struct at_addr));
+#ifdef BSD4_4
+ to.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+
+ if ( nbp_port == 0 ) {
+ if (( se = getservbyname( "nbp", "ddp" )) == NULL ) {
+ nbp_port = 2;
+ } else {
+ nbp_port = ntohs( se->s_port );
+ }
+ }
+ to.sat_port = nbp_port;
+
+ if ( netddp_sendto( s, nbp_send, data - nbp_send, 0,
+ (struct sockaddr *)&to,
+ sizeof( struct sockaddr_at )) < 0 ) {
+ goto unregister_err;
+ }
+
+ FD_ZERO( &readfd );
+ FD_SET( s, &readfd );
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+ if (( cc = select( s + 1, &readfd, 0, 0, &timeout )) < 0 ) {
+ goto unregister_err;
+ }
+ if ( cc == 0 ) {
+ errno = ETIMEDOUT;
+ goto unregister_err;
+ }
+
+ namelen = sizeof( struct sockaddr_at );
+ if (( cc = netddp_recvfrom( s, nbp_recv, sizeof( nbp_recv ), 0,
+ (struct sockaddr *)&to, &namelen )) < 0 ) {
+ goto unregister_err;
+ }
+ netddp_close( s );
+
+ data = nbp_recv;
+ if ( *data++ != DDPTYPE_NBP ) {
+ return( -1 );
+ }
+ memcpy( &nh, data, SZ_NBPHDR );
+ if ( nh.nh_op != NBPOP_OK ) {
+ return( -1 );
+ }
+ return( 0 );
+
+unregister_err:
+ netddp_close(s);
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1997 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+
+#include <atalk/nbp.h>
+#include <atalk/ddp.h>
+#include <atalk/util.h>
+
+#include "nbp_conf.h"
+
+char nbp_send[ 1024 ];
+char nbp_recv[ 1024 ];
+u_char nbp_port = 0;
+unsigned char nbp_id = 0;
+
+int nbp_parse( data, nn, len )
+ char *data;
+ struct nbpnve *nn;
+ int len;
+{
+ struct nbptuple nt;
+
+ memcpy( &nt, data, SZ_NBPTUPLE);
+ data += SZ_NBPTUPLE;
+ len -= SZ_NBPTUPLE;
+ if ( len < 0 ) {
+ return( -1 );
+ }
+
+#ifdef BSD4_4
+ nn->nn_sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ nn->nn_sat.sat_family = AF_APPLETALK;
+ nn->nn_sat.sat_addr.s_net = nt.nt_net;
+ nn->nn_sat.sat_addr.s_node = nt.nt_node;
+ nn->nn_sat.sat_port = nt.nt_port;
+
+ nn->nn_objlen = *data++;
+ len -= nn->nn_objlen + 1;
+ if ( len < 0 ) {
+ return( -1 );
+ }
+ if ( nn->nn_objlen > NBPSTRLEN ) {
+ return( -1 );
+ }
+ memcpy( nn->nn_obj, data, nn->nn_objlen );
+ data += nn->nn_objlen;
+
+ nn->nn_typelen = *data++;
+ len -= nn->nn_typelen + 1;
+ if ( len < 0 ) {
+ return( -1 );
+ }
+ if ( nn->nn_typelen > NBPSTRLEN ) {
+ return( 1 );
+ }
+ memcpy( nn->nn_type, data, nn->nn_typelen );
+
+ data += nn->nn_typelen;
+ nn->nn_zonelen = *data++;
+ len -= nn->nn_zonelen + 1;
+ if ( len < 0 ) {
+ return( -1 );
+ }
+ if ( nn->nn_zonelen > NBPSTRLEN ) {
+ return( 1 );
+ }
+ memcpy( nn->nn_zone, data, nn->nn_zonelen );
+
+ return( len );
+}
+
+#define NBPM_OBJ (1<<1)
+#define NBPM_TYPE (1<<2)
+#define NBPM_ZONE (1<<3)
+
+int nbp_match( n1, n2, flags )
+ struct nbpnve *n1, *n2;
+ int flags;
+{
+ int match = 0;
+
+ if ( flags & NBPMATCH_NOZONE ) {
+ match |= NBPM_ZONE;
+ }
+
+ if ( !( flags & NBPMATCH_NOGLOB )) {
+ if ( n1->nn_objlen == 1 && n1->nn_obj[0] == '=' ) {
+ match |= NBPM_OBJ;
+ }
+ if ( n1->nn_typelen == 1 && n1->nn_type[0] == '=' ) {
+ match |= NBPM_TYPE;
+ }
+ }
+
+ if ( !( match & NBPM_OBJ )) {
+ if ( n1->nn_objlen != n2->nn_objlen ||
+ strndiacasecmp( n1->nn_obj, n2->nn_obj, n1->nn_objlen )) {
+ return( 0 );
+ }
+ }
+ if ( !( match & NBPM_TYPE )) {
+ if ( n1->nn_typelen != n2->nn_typelen ||
+ strndiacasecmp( n1->nn_type, n2->nn_type, n1->nn_typelen )) {
+ return( 0 );
+ }
+ }
+ if ( !( match & NBPM_ZONE )) {
+ if ( n1->nn_zonelen != n2->nn_zonelen ||
+ strndiacasecmp( n1->nn_zone, n2->nn_zone, n1->nn_zonelen )) {
+ return( 0 );
+ }
+ }
+
+ return( 1 );
+}
+
+int nbp_name( name, objp, typep, zonep )
+ const char *name;
+ char **objp, **typep, **zonep;
+{
+ static char buf[ 32 + 1 + 32 + 1 + 32 + 1 ];
+ char *p;
+
+ if ( name ) {
+ if ( strlen( name ) + 1 > sizeof( buf )) {
+ return( -1 );
+ }
+ strcpy( buf, name );
+
+ if (( p = strrchr( buf, '@' )) != NULL ) {
+ *p++ = '\0';
+ *zonep = p;
+ }
+ if (( p = strrchr( buf, ':' )) != NULL ) {
+ *p++ = '\0';
+ *typep = p;
+ }
+ if ( *buf != '\0' ) {
+ *objp = buf;
+ }
+ }
+
+ return( 0 );
+}
--- /dev/null
+SRC= netddp_open.c netddp_sendto.c netddp_recvfrom.c
+OBJ= netddp_open.o netddp_sendto.o netddp_recvfrom.o
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH}
+TAGSFILE= tags
+CC= cc
+
+all: profiled netddplib
+
+profiled:
+ -mkdir profiled
+
+netddplib netddplib_p : ${OBJ}
+ @echo "building profiled netddplib"
+ @cd profiled; ar cru ../netddplib_p ${OBJ}
+ @echo "building normal netddplib"
+ @ar cru netddplib ${OBJ}
+
+.c.o :
+ ${CC} -p ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- profiled/$*.o
+ ${CC} ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- $*.o
+
+clean :
+ rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+ rm -f netddplib netddplib_p
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/*
+ * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * open a ddp socket and return the port and address assigned. return
+ * various address info if requested as well.
+ */
+
+static int _netddp_open_dummy;
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#ifdef MACOSX_SERVER
+#include <at/appletalk.h>
+#include <at/ddp.h>
+#endif
+
+#include <netatalk/at.h>
+#include <atalk/netddp.h>
+
+int netddp_open(struct sockaddr_at *addr, struct sockaddr_at *bridge)
+{
+ int s;
+
+#ifdef MACOSX_SERVER
+ at_inet_t address, baddress;
+
+ if ((s = ddp_open(addr ? &addr->sat_port : NULL)) < 0)
+ return -1;
+
+ if (!addr)
+ return s;
+
+ if (rtmp_netinfo(s, &address, &baddress) < 0) {
+ ddp_close(s);
+ return -1;
+ }
+
+ memcpy(&addr->sat_addr.s_net, &address.net, sizeof(addr->sat_addr.s_net));
+ addr->sat_addr.s_node = address.node;
+ addr->sat_port = address.socket;
+ if (bridge) {
+ memcpy(&bridge->sat_addr.s_net, &baddress.net,
+ sizeof(bridge->sat_addr.s_net));
+ bridge->sat_addr.s_node = baddress.node;
+ bridge->sat_port = baddress.socket;
+ }
+#else
+ int len;
+
+ if ((s = socket( AF_APPLETALK, SOCK_DGRAM, 0 )) < 0)
+ return -1;
+
+ if (!addr)
+ return s;
+
+ addr->sat_family = AF_APPLETALK;
+ /* rest of address should be initialized by the caller */
+ if (bind(s, (struct sockaddr *) addr, sizeof( struct sockaddr_at )) < 0 ) {
+ close(s);
+ return -1;
+ }
+
+ /* get the real address from the kernel */
+ len = sizeof( struct sockaddr_at);
+ if ( getsockname( s, (struct sockaddr *) addr, &len ) != 0 ) {
+ close(s);
+ return -1;
+ }
+#endif
+
+ return s;
+}
--- /dev/null
+/*
+ * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * receive data.
+ */
+
+static int _netddp_recvfrom_dummy;
+
+#ifndef NO_DDP
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <errno.h>
+
+#ifdef MACOSX_SERVER
+#include <at/appletalk.h>
+#include <at/ddp.h>
+#endif
+
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <netatalk/ddp.h>
+#include <atalk/netddp.h>
+
+#ifndef MAX
+#define MAX(a, b) ((a) < (b) ? (b) : (a))
+#endif
+
+#ifdef MACOSX_SERVER
+int netddp_recvfrom(int fd, void *buf, int buflen, unsigned int dummy,
+ struct sockaddr *addr, unsigned int *addrlen)
+{
+ ssize_t i;
+ struct ddpehdr ddphdr;
+ struct sockaddr_at *sat = (struct sockaddr_at *) addr;
+ struct iovec iov[2];
+
+ iov[0].iov_base = (void *) &ddphdr;
+ iov[0].iov_len = sizeof(ddphdr);
+ iov[1].iov_base = buf;
+ iov[1].iov_len = buflen;
+
+ while ((i = readv(fd, iov, 2)) < 0) {
+ if (errno != EINTR)
+ return -1;
+ }
+
+ if (addr) {
+ sat->sat_addr.s_net = ddphdr.deh_snet;
+ sat->sat_addr.s_node = ddphdr.deh_snode;
+ sat->sat_port = ddphdr.deh_sport;
+ }
+
+ return MAX(0, i - sizeof(ddphdr));
+}
+
+#endif /* os x server */
+#endif /* no ddp */
--- /dev/null
+/*
+ * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * send data.
+ */
+
+static int _netddp_sendto_dummy;
+
+#ifndef NO_DDP
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <errno.h>
+
+#ifdef MACOSX_SERVER
+#include <at/appletalk.h>
+#include <at/ddp.h>
+#endif
+
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <netatalk/ddp.h>
+#include <atalk/netddp.h>
+
+#ifndef MAX
+#define MAX(a, b) ((a) < (b) ? (b) : (a))
+#endif
+
+#ifdef MACOSX_SERVER
+int netddp_sendto(int fd, void *buf, int buflen, unsigned int dummy,
+ const struct sockaddr *addr, unsigned int addrlen)
+{
+ ssize_t i;
+ struct ddpehdr ddphdr;
+ const struct sockaddr_at *sat = (const struct sockaddr_at *) addr;
+ struct iovec iov[2];
+
+ iov[0].iov_base = (void *) &ddphdr;
+ iov[0].iov_len = sizeof(ddphdr);
+ iov[1].iov_base = buf;
+ iov[1].iov_len = buflen;
+
+ if (!addr)
+ return -1;
+
+ memset(&ddphdr, 0, sizeof(ddphdr));
+ ddphdr.deh_len = htons(sizeof(ddphdr) + buflen);
+ ddphdr.deh_dnet = sat->sat_addr.s_net;
+ ddphdr.deh_dnode = sat->sat_addr.s_node;
+ ddphdr.deh_dport = sat->sat_port;
+ while ((i = writev(fd, iov, 2)) < 0) {
+ if (errno != EINTR)
+ return -1;
+ }
+
+ return MAX(0, i - sizeof(ddphdr));
+}
+
+#endif /* os x server */
+#endif /* no ddp */
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+struct pap_child {
+ int ac_pid;
+ int ac_state;
+ struct sockaddr_at ac_sat;
+};
+
+#define ACSTATE_DEAD 0
+#define ACSTATE_OK 1
+#define ACSTATE_BAD 7
--- /dev/null
+/* close the connection */
+
+int pap_close(PAP pap)
+{
+ struct atp_block atpb;
+ struct iovec iov;
+ unsigned char buf[PAP_HDRSIZ];
+ int err = -1;
+
+ buf[ 0 ] = pap->pap_connid;
+ buf[ 1 ] = PAP_CLOSE;
+ buf[ 2 ] = buf[ 3 ] = 0;
+
+ atpb.atp_saddr = &pap->pap_sat;
+ atpb.atp_sreqdata = buf;
+ atpb.atp_sreqdlen = sizeof(buf); /* bytes in CloseConn request */
+ atpb.atp_sreqto = 2; /* retry timer */
+ atpb.atp_sreqtries = 5; /* retry count */
+ if (atp_sreq( atp, &atpb, 1, ATP_XO ) < 0) {
+ goto close_done;
+ }
+
+ /* check for CloseConnReply */
+ iov.iov_base = pap->pap_data;
+ iov.iov_len = sizeof( pap->pap_data );
+ atpb.atp_rresiov = &iov;
+ atpb.atp_rresiovcnt = 1;
+ if ( atp_rresp( pap->pap_atp, &atpb ) < 0 ) {
+ goto close_done;
+ }
+
+ /* sanity */
+ if ( iov.iov_len != 4 || pap->pap_data[ 0 ] != pap->pap_connid ||
+ pap->pap_data[ 1 ] != PAP_CLOSEREPLY ) {
+ syslog(LOG_ERR, "pap_close: Bad response!");
+ goto close_done;
+ }
+ err = 0;
+
+close_done:
+ atp_close(pap->pap_atp);
+ free(pap);
+ return err;
+}
--- /dev/null
+
+PAP pap_init(ATP atp)
+{
+ PAP pap;
+
+ if ((pap = (struct PAP *) calloc(1, sizeof(struct PAP))) == NULL)
+ return NULL;
+
+ pap->pap_atp = atp;
+#ifdef BSD4_4
+ pap->pap_sat.sat_len = sizeof(struct sockaddr_at);
+#endif
+ pap->pap_sat.sat_family = AF_APPLETALK;
+ pap->pap_sat.sat_addr.s_net = ATADDR_ANYNET;
+ pap->pap_sat.sat_addr.s_node = ATADDR_ANYNODE;
+ pap->pap_sat.sat_port = ATADDR_ANYPORT;
+ pap->pap_status = NULL;
+ pap->pap_slen = 0;
+ pap->pap_sid = 0;
+ pap->pap_flags = PAPFL_SLS;
+ pap->cmdlen = pap->datalen = 0;
+ pap->read_count = pap->write_count = 0;
+
+ return( pap );
+}
--- /dev/null
+/* moved over from bin/pap/pap.c */
+
+static struct {
+ PAP pap;
+ int tickle;
+ pid_t pid;
+} client;
+
+static void tickle_handler()
+{
+ if (client.tickle++ < 4)
+ pap_tickle(client.pap, client.pap->pap_connid, &client.pap->pap_sat);
+ else {
+ kill(client.pid, SIGTERM);
+ syslog(LOG_ERR, "pap_alarm: connection timed out.");
+ exit(1);
+ }
+}
+
+
+PAP pap_open(PAP pap, struct nbpnve *nn, u_int8_t quantum, int flags)
+{
+ struct atp_block atpb;
+ struct timeval stv, tv;
+ u_int16_t waiting;
+ pid_t pid;
+
+ if (!pap->inited) {
+ pap->pap_connid = getpid() & 0xff;
+ pap->cmdbuf[ 0 ] = pap->pap_connid;
+ pap->cmdbuf[ 1 ] = PAP_OPEN;
+ pap->cmdbuf[ 2 ] = pap->cmdbuf[ 3 ] = 0;
+ pap->cmdbuf[ 4 ] = atp_sockaddr( pap->pap_atp )->sat_port;
+ pap->cmdbuf[ 5 ] = quantum; /* flow quantum */
+
+ if ( gettimeofday( &stv, NULL ) < 0 ) {
+ perror( "gettimeofday" );
+ return NULL;
+ }
+
+ for (;;) {
+ if ( flags & PAPFLAG_CUTS ) {
+ waiting = 0xffff;
+ } else {
+ if ( gettimeofday( &tv, NULL ) < 0 ) {
+ perror( "gettimeofday" );
+ return NULL;
+ }
+ waiting = htons( tv.tv_sec - stv.tv_sec );
+ }
+
+ memcpy(pap->cmdbuf + 6, &waiting, sizeof( waiting ));
+ atpb.atp_saddr = &nn->nn_sat;
+ atpb.atp_sreqdata = pap->cmdbuf;
+ atpb.atp_sreqdlen = 8; /* bytes in OpenConn request */
+ atpb.atp_sreqto = 2; /* retry timer */
+ atpb.atp_sreqtries = 5; /* retry count */
+ if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
+ perror( "atp_sreq" );
+ return NULL;
+ }
+
+ iov.iov_base = pap->data;
+ iov.iov_len = sizeof( rbuf );
+ atpb.atp_rresiov = &iov;
+ atpb.atp_rresiovcnt = 1;
+ if ( atp_rresp( atp, &atpb ) < 0 ) {
+ perror( "atp_rresp" );
+ if ( connattempts-- <= 0 ) {
+ fprintf( stderr, "Can't connect!\n" );
+ return NULL;
+ }
+ continue;
+ }
+
+ /* sanity */
+ if ( iov.iov_len < 8 || pap->data[ 0 ] != pap->pap_connid ||
+ pap->data[ 1 ] != PAP_OPENREPLY ) {
+ fprintf( stderr, "Bad response!\n" );
+ continue; /* This is weird, since TIDs must match... */
+ }
+
+ if ( isatty( 1 )) {
+ printf( "%.*s\n", iov.iov_len - 9, iov.iov_base + 9 );
+ }
+ updatestatus( iov.iov_base + 9, iov.iov_len - 9 );
+
+ bcopy( &rbuf[ 6 ], &result, sizeof( result ));
+ if ( result != 0 ) {
+ sleep( 2 );
+ } else {
+ memcpy(&pap->pap_sat, &nn.nn_sat, sizeof( struct sockaddr_at ));
+ pap->pap_sat.sat_port = rbuf[ 4 ];
+ pap->pap_quantum = rbuf[ 5 ];
+ break;
+ }
+ }
+
+ if ( isatty( 1 )) {
+ printf( "Connected to %.*s:%.*s@%.*s.\n",
+ nn.nn_objlen, nn.nn_obj,
+ nn.nn_typelen, nn.nn_type,
+ nn.nn_zonelen, nn.nn_zone );
+ }
+
+ /* open a second atp connection */
+ if (( atp = atp_open( 0 )) == NULL ) {
+ perror( "atp_open" );
+ return NULL;
+ }
+
+ client.pap = pap;
+ client.pid = pid;
+ pap->inited = 1;
+ }
+
+ /* wait around for tickles */
+
+}
--- /dev/null
+/* taken from bin/pap/pap.c */
+
+int pap_read(PAP pap)
+{
+ struct atp_block atpb;
+ u_int16_t seq;
+
+ pap->cmdbuf[0] = pap->pap_connid;
+ pap->cmdbuf[1] = PAP_READ;
+ if ( ++pap->pap_seq == 0 )
+ pap->pap_seq = 1;
+
+ seq = htons( pap->pap_seq );
+ memcpy(pap->cmdbuf + 2, &seq, sizeof(seq));
+ atpb.atp_saddr = &pap->pap_sat;
+ atpb.atp_sreqdata = pap->cmdbuf;
+ atpb.atp_sreqdlen = sizeof(pap->cmdbuf); /* bytes in SendData request */
+ atpb.atp_sreqto = 15; /* retry timer */
+ atpb.atp_sreqtries = ATP_TRIES_INFINITE; /* retry count */
+ if ( atp_sreq( pap->pap_atp, &atpb, pap->oquantum, ATP_XO ) < 0 ) {
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null
+
+
+int pap_sendstatus(PAP pap)
+{
+ struct atp_block atpb;
+ u_int8_t buf[PAP_HDRSIZ];
+
+ buf[ 0 ] = 0;
+ buf[ 1 ] = PAP_SENDSTATUS;
+ buf[ 2 ] = buf[ 3 ] = 0;
+ atpb.atp_saddr =&pap->pap_sat;
+ atpb.atp_sreqdata = buf;
+ atpb.atp_sreqdlen = 4; /* bytes in SendStatus request */
+ atpb.atp_sreqto = 2; /* retry timer */
+ atpb.atp_sreqtries = 5; /* retry count */
+ if ( atp_sreq( satp, &atpb, 1, 0 ) < 0 ) {
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1996 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <errno.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <netatalk/at.h>
+#include <atalk/compat.h>
+#include <atalk/atp.h>
+#include <atalk/pap.h>
+
+#include <atalk/server_child.h>
+#include "pap_child.h"
+
+static PAP server_pap;
+static struct server_child *children = NULL;
+static struct pap_child **pap_ac = NULL;
+
+/* send tickles and check tickle status of connections */
+static void tickle_handler()
+{
+ int sid;
+
+ /* check status */
+ for (sid = 0; sid < children->nsessions; sid++) {
+ if (pap_ac[sid] == NULL || pap_ac[sid]->ac_state == ACSTATE_DEAD)
+ continue;
+
+ if (++pap_ac[sid]->ac_state >= ACSTATE_BAD) {
+ /* kill. if already dead, just continue */
+ if (kill( pap_ac[ sid ]->ac_pid, SIGTERM) == 0)
+ syslog( LOG_INFO, "pap_alrm: %d timed out",
+ pap_ac[ sid ]->ac_pid );
+
+ pap_ac[ sid ]->ac_state = ACSTATE_DEAD;
+ continue;
+ }
+
+ /* send off a tickle */
+ pap_tickle(server_pap, sid, &pap_ac[sid]->ac_sat);
+ }
+}
+
+static void child_cleanup(const pid_t pid)
+{
+ int i;
+
+ for (i = 0; i < children->nsessions; i++)
+ if (pap_ac[i] && (pap_ac[i]->ac_pid == pid)) {
+ pap_ac[i]->ac_state = ACSTATE_DEAD;
+ break;
+ }
+}
+
+
+/* kill children */
+void pap_kill(int sig)
+{
+ if (children)
+ server_child_kill(children, CHILD_PAPFORK, sig);
+}
+
+
+/*
+ * This call handles open, tickle, and getstatus requests. On a
+ * successful open, it forks a child process.
+ * It returns an PAP to the child and parent and NULL if there is
+ * an error.
+ */
+PAP pap_slinit(PAP pap, server_child *server_children,
+ const int tickleval)
+{
+ struct sigaction action;
+ struct itimerval timer;
+ struct sockaddr_at sat;
+ struct atp_block atpb;
+ ATP atp;
+ struct iovec iov[ 8 ];
+ pid_t pid;
+ int i, sid;
+ u_short paperr;
+
+ if (!pap->inited) {
+ if (!(children = server_children))
+ return NULL;
+
+ if ((pap_ac = (struct pap_child **)
+ calloc(server_children->nsessions, sizeof(struct pap_child *)))
+ == NULL)
+ return NULL;
+
+ server_pap = pap;
+
+ /* install cleanup pointer */
+ server_child_setup(children, CHILD_PAPFORK, child_cleanup);
+
+ /* install tickle handler */
+ action.sa_handler = tickle_handler;
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = SA_RESTART;
+
+ timer.it_interval.tv_sec = timer.it_value.tv_sec = tickleval;
+ timer.it_interval.tv_usec = timer.it_value.tv_usec = 0;
+ if ((setitimer(ITIMER_REAL, &timer, NULL) < 0) ||
+ (sigaction(SIGALRM, &action, NULL) < 0)) {
+ exit(1);
+ }
+
+ pap->inited = 1;
+ }
+
+ memset( &sat, 0, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+ sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ sat.sat_family = AF_APPLETALK;
+ sat.sat_addr.s_net = ATADDR_ANYNET;
+ sat.sat_addr.s_node = ATADDR_ANYNODE;
+ sat.sat_port = ATADDR_ANYPORT;
+ atpb.atp_saddr = &sat;
+ atpb.atp_rreqdata = pap->cmdbuf;
+ atpb.atp_rreqdlen = sizeof( pap->cmdbuf );
+ while ( atp_rreq( pap->pap_atp, &atpb ) < 0 ) {
+ if ( errno == EINTR || errno == EAGAIN ) {
+ continue;
+ }
+ return( NULL );
+ }
+
+ switch ( pap->cmdbuf[ 0 ] ) {
+ case PAPFUNC_TICKLE:
+ sid = pap->cmdbuf[1];
+ if ((pap_ac[sid] != NULL) && (pap_ac[sid]->ac_state != ACSTATE_DEAD))
+ pap_ac[sid]->ac_state = ACSTATE_OK;
+ break;
+
+ case PAPFUNC_STAT:
+#ifdef EBUG
+ printf( "pap stat\n" );
+#endif EBUG
+ if ( pap->pap_slen > 0 ) {
+ pap->cmdbuf[0] = 0;
+ bcopy( pap->pap_status, pap->cmdbuf + 4, pap->pap_slen );
+ iov[ 0 ].iov_base = pap->cmdbuf;
+ iov[ 0 ].iov_len = 4 + pap->pap_slen;
+ atpb.atp_sresiov = iov;
+ atpb.atp_sresiovcnt = 1;
+ atp_sresp( pap->pap_atp, &atpb );
+ }
+ break;
+
+ case PAPFUNC_OPEN :
+ if (children->count < children->nsessions) {
+
+ /* find a slot */
+ for (sid = 0; sid < children->nsessions; sid++) {
+ if (pap_ac[sid] == NULL)
+ break;
+
+ if (pap_ac[sid]->ac_state == ACSTATE_DEAD) {
+ free(pap_ac[sid]);
+ pap_ac[sid] = NULL;
+ break;
+ }
+ }
+
+ if ((atp = atp_open(0)) == NULL)
+ return NULL;
+
+ switch (pid = fork()) {
+ case 0 : /* child */
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+ /* free/close some things */
+ for (i = 0; i < children->nsessions; i++ ) {
+ if ( pap_ac[i] != NULL )
+ free( pap_ac[i] );
+ }
+ free(pap_ac);
+
+ server_child_free(children);
+ children = NULL;
+ atp_close(pap->pap_atp);
+
+ pap->child = 1;
+ pap->pap_atp = atp;
+ pap->pap_sat = sat;
+ pap->pap_wss = pap->cmdbuf[1];
+ pap->pap_seq = 0;
+ pap->pap_sid = sid;
+ pap->pap_flags = PAPFL_SSS;
+ return pap;
+
+ case -1 : /* error */
+ pap->cmdbuf[ 0 ] = 0;
+ pap->cmdbuf[ 1 ] = 0;
+ paperr = PAPERR_SERVBUSY;
+ break;
+
+ default : /* parent process */
+ switch (server_child_add(children, CHILD_PAPFORK, pid)) {
+ case 0: /* added child */
+ if (pap_ac[sid] = (struct pap_child *)
+ malloc(sizeof(struct pap_child))) {
+ pap_ac[sid]->ac_pid = pid;
+ pap_ac[sid]->ac_state = ACSTATE_OK;
+ pap_ac[sid]->ac_sat = sat;
+ pap_ac[sid]->ac_sat.sat_port = pap->cmdbuf[1];
+
+ pap->cmdbuf[0] = atp_sockaddr(atp)->sat_port;
+ pap->cmdbuf[1] = sid;
+ paperr = PAPERR_OK;
+ break;
+ } /* fall through if malloc fails */
+ case -1: /* bad error */
+ kill(pid, SIGQUIT);
+ break;
+ default: /* non-fatal error */
+ break;
+ }
+ atp_close(atp);
+ break;
+ }
+
+ } else {
+ pap->cmdbuf[0] = pap->cmdbuf[1] = 0;
+ paperr = PAPERR_SERVBUSY;
+ }
+
+ bcopy( &paperr, pap->cmdbuf + 2, sizeof( u_short ));
+ iov[ 0 ].iov_base = pap->cmdbuf;
+ iov[ 0 ].iov_len = 4;
+ atpb.atp_sresiov = iov;
+ atpb.atp_sresiovcnt = 1;
+ atp_sresp( pap->pap_atp, &atpb );
+ break;
+
+ default:
+ syslog(LOG_INFO, "PAPUnknown %d", pap->cmdbuf[0]);
+ break;
+ }
+
+ return pap;
+}
--- /dev/null
+/* send a tickle */
+void pap_tickle(PAP pap, const u_int8_t connid, struct sockaddr_at *sat)
+{
+ struct atp_block atpb;
+ u_int8_t buf[PAP_HDRSIZ];
+
+ buf[ 0 ] = connid;
+ buf[ 1 ] = PAP_TICKLE;
+ buf[ 2 ] = buf[ 3 ] = 0;
+
+ atpb.atp_saddr = sat;
+ atpb.atp_sreqdata = buf;
+ atpb.atp_sreqdlen = sizeof(buf); /* bytes in Tickle request */
+ atpb.atp_sreqto = 0; /* retry timer */
+ atpb.atp_sreqtries = 1; /* retry count */
+ if ( atp_sreq( pap->pap_atp, &atpb, 0, 0 ) < 0 ) {
+ syslog(LOG_ERR, "atp_sreq: %m");
+ }
+}
--- /dev/null
+SRC= atalk_addr.c strdicasecmp.c server_child.c server_lock.c module.c \
+ bprint.c getiface.c
+OBJ= atalk_addr.o strdicasecmp.o server_child.o server_lock.o module.o \
+ bprint.o getiface.o
+
+INCPATH= -I../../include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH}
+TAGSFILE= tags
+CC= cc
+
+all : profiled utillib
+
+profiled:
+ -mkdir profiled
+
+utillib utillib_p : ${OBJ}
+ @echo "building profiled utillib"
+ @cd profiled; ar cru ../utillib_p ${OBJ}
+ @echo "building normal utillib"
+ @ar cru utillib ${OBJ}
+
+.c.o :
+ ${CC} -p ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- profiled/$*.o
+ ${CC} ${CFLAGS} -c $*.c
+ -ld -r -o $*.o- $*.o
+ mv $*.o- $*.o
+
+clean :
+ rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+ rm -f utillib utillib_p
+
+tags : ${SRC}
+ cwd=`pwd`; \
+ for i in ${SRC}; do \
+ ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+ done
+
+depend :
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+#include <sys/types.h>
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <atalk/util.h>
+#include <ctype.h>
+
+/*
+ * Check whether "cp" is a valid ascii representation
+ * of an AppleTalk address and convert to a binary address.
+ * Examples of accepted forms are (in decimal, net of 4321,
+ * node of 65):
+ *
+ * 4321.65
+ * 0x10E1.41
+ * 16.225.65
+ * 0x10.E1.41
+ *
+ * If hex is used, and the first digit is one of A-F, the leading
+ * 0x is redundant. Returns 1 if the address is valid, 0 if not.
+ *
+ * Unlike Internet addresses, AppleTalk addresses can have leading
+ * 0's. This means that we can't support octal addressing.
+ */
+
+int atalk_aton( cp, addr )
+ char *cp;
+ struct at_addr *addr;
+{
+ u_int32_t val, base, n;
+ char c;
+
+ val = 0; base = 10;
+ if ( *cp == '0' && ( *++cp == 'x' || *cp == 'X' )) {
+ base = 16, cp++;
+ }
+ if ( !isdigit( *cp ) && isxdigit( *cp )) {
+ base = 16;
+ }
+
+ for ( n = 0;; n++ ) {
+ while (( c = *cp ) != '\0') {
+ if ( isascii( c ) && isdigit( c )) {
+ val = (val * base) + (c - '0');
+ cp++;
+ continue;
+ }
+
+ if ( base == 16 && isascii( c ) && isxdigit( c )) {
+ val = ( val << 4 ) + ( c + 10 - ( islower( c ) ? 'a' : 'A' ));
+ cp++;
+ continue;
+ }
+ break;
+ }
+
+ if ( c != '.' && c != '\0' ) {
+ return( 0 );
+ }
+
+ switch ( n ) {
+ case 0:
+ if ( addr ) {
+ if ( val > 65535 ) {
+ return( 0 );
+ }
+ addr->s_net = val;
+ }
+ if ( *cp++ ) {
+ val = 0;
+ } else {
+ break;
+ }
+ continue;
+
+ case 2:
+ if ( addr ) {
+ if ( addr->s_net > 255 ) {
+ return( 0 );
+ }
+ addr->s_net <<= 8;
+ addr->s_net += addr->s_node;
+ }
+ /*FALLTHROUGH*/
+
+ case 1:
+ if ( addr ) {
+ if ( val > 255 ) {
+ return( 0 );
+ }
+ addr->s_node = val;
+ }
+ if ( *cp++ ) {
+ val = 0;
+ } else {
+ break;
+ }
+ continue;
+
+ default:
+ return( 0 );
+ }
+ break;
+ }
+
+ if ( n < 1 ) {
+ return( 0 );
+ }
+ if ( addr ) {
+ addr->s_net = htons( addr->s_net );
+ }
+ return (1);
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <atalk/util.h>
+
+static const char hexdig[] = "0123456789ABCDEF";
+
+#define BPXLEN 50
+#define BPALEN 18
+
+void bprint( data, len )
+ char *data;
+ int len;
+{
+ char xout[ BPXLEN ], aout[ BPALEN ];
+ int i;
+
+ memset( xout, 0, BPXLEN );
+ memset( aout, 0, BPALEN );
+
+ for ( i = 0; len; len--, data++, i++ ) {
+ if ( i == 16 ) {
+ printf( "%-48s\t%-16s\n", xout, aout );
+ memset( xout, 0, BPXLEN );
+ memset( aout, 0, BPALEN );
+ i = 0;
+ }
+
+ if (isprint( (unsigned char)*data )) {
+ aout[ i ] = *data;
+ } else {
+ aout[ i ] = '.';
+ }
+
+ xout[ (i*3) ] = hexdig[ ( *data & 0xf0 ) >> 4 ];
+ xout[ (i*3) + 1 ] = hexdig[ *data & 0x0f ];
+ xout[ (i*3) + 2 ] = ' ';
+ }
+
+ if ( i )
+ printf( "%-48s\t%-16s\n", xout, aout );
+
+ printf("(end)\n");
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999-2000 Adrian Sun.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <net/if.h>
+#include <errno.h>
+
+#ifdef __svr4__
+#include <sys/sockio.h>
+#endif
+
+#include <atalk/util.h>
+
+/* allocation size for interface list. */
+#define IFACE_NUM 5
+
+/* we leave all of the ioctl's to the application */
+static int addname(char **list, int *i, int *length, const char *name)
+
+{
+ /* if we've run out of room, allocate some more. just return
+ * the present list if we can't. */
+ if (*i >= *length) {
+ char **new = realloc(list, sizeof(char **)*(*length + IFACE_NUM));
+
+ if (!new) /* just break if we can't allocate anything */
+ return -1;
+ *length += IFACE_NUM;
+ }
+
+ if ((list[*i] = strdup(name)) == NULL)
+ return -1;
+
+ (*i)++;
+ list[*i] = NULL; /* zero out the next entry */
+ return 0;
+}
+
+
+static int getifaces(const int sockfd, char **list, int *length)
+{
+#ifdef HAVE_IFNAMEINDEX
+ struct if_nameindex *ifstart, *ifs;
+ int i = 0;
+
+ if (!list || *length < 1)
+ return 0;
+
+ ifs = ifstart = if_nameindex();
+ while (ifs && ifs->if_name) {
+ /* just bail if there's a problem */
+ if (addname(list, &i, length, ifs->if_name) < 0)
+ break;
+ ifs++;
+ }
+
+ if_freenameindex(ifstart);
+ return i;
+
+#else
+ struct ifconf ifc;
+ struct ifreq ifrs[ 64 ], *ifr, *nextifr;
+ int ifrsize, i = 0;
+
+ if (!list || *length < 1)
+ return 0;
+
+ memset( &ifc, 0, sizeof( struct ifconf ));
+ ifc.ifc_len = sizeof( ifrs );
+ memset( ifrs, 0, sizeof( ifrs ));
+ ifc.ifc_buf = (caddr_t)ifrs;
+ if ( ioctl( sockfd, SIOCGIFCONF, &ifc ) < 0 ) {
+ return 0;
+ }
+
+ for ( ifr = ifc.ifc_req; ifc.ifc_len >= sizeof( struct ifreq );
+ ifc.ifc_len -= ifrsize, ifr = nextifr ) {
+#ifdef BSD4_4
+ ifrsize = sizeof(ifr->ifr_name) +
+ (ifr->ifr_addr.sa_len > sizeof(struct sockaddr)
+ ? ifr->ifr_addr.sa_len : sizeof(struct sockaddr));
+#else BSD4_4
+ ifrsize = sizeof( struct ifreq );
+#endif BSD4_4
+ nextifr = (struct ifreq *)((caddr_t)ifr + ifrsize );
+
+ /* just bail if there's a problem */
+ if (addname(list, &i, length, ifr->ifr_name) < 0)
+ break;
+ }
+ return i;
+#endif
+}
+
+
+/*
+ * Get interfaces from the kernel. we keep an extra null entry to signify
+ * the end of the interface list.
+ */
+char **getifacelist()
+{
+ char **list = (char **) malloc(sizeof(char **)*(IFACE_NUM + 1));
+ char **new;
+ int length = IFACE_NUM, i, fd;
+
+ if (!list)
+ return NULL;
+
+ if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
+ return NULL;
+
+ if ((i = getifaces(fd, list, &length)) == 0) {
+ free(list);
+ close(fd);
+ return NULL;
+ }
+ close(fd);
+
+ if ((i < length) &&
+ (new = (char **) realloc(list, sizeof(char **)*(i + 1))))
+ return new;
+
+ return list;
+}
+
+
+/* go through and free the interface list */
+void freeifacelist(char **ifacelist)
+{
+ char *value, **list = ifacelist;
+
+ if (!ifacelist)
+ return;
+
+ while (value = *list++) {
+ free(value);
+ }
+
+ free(ifacelist);
+}
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include <atalk/util.h>
+
+static int _mod_dummy;
+
+#ifdef NO_DLFCN_H
+#ifdef MACOSX_SERVER
+#include <mach-o/dyld.h>
+
+void *mod_open(const char *path)
+{
+ NSObjectFileImage file;
+
+ if (NSCreateObjectFileImageFromFile(path, &file) !=
+ NSObjectFileImageSuccess)
+ return NULL;
+ return NSLinkModule(file, path, TRUE);
+}
+
+void *mod_symbol(void *module, const char *name)
+{
+ NSSymbol symbol;
+ char *underscore;
+
+ if ((underscore = (char *) malloc(strlen(name) + 2)) == NULL)
+ return NULL;
+ strcpy(underscore, "_");
+ strcat(underscore, name);
+ symbol = NSLookupAndBindSymbol(underscore);
+ free(underscore);
+
+ return NSAddressOfSymbol(symbol);
+}
+
+void mod_close(void *module)
+{
+ NSUnLinkModule(module, FALSE);
+}
+#endif
+
+#else
+
+#ifdef DLSYM_PREPEND_UNDERSCORE
+#include <dlfcn.h>
+
+void *mod_symbol(void *module, const char *name)
+{
+ void *symbol;
+ char *underscore;
+
+ if (!module)
+ return NULL;
+
+ if ((underscore = (char *) malloc(strlen(name) + 2)) == NULL)
+ return NULL;
+
+ strcpy(underscore, "_");
+ strcat(underscore, name);
+ symbol = dlsym(module, underscore);
+ free(underscore);
+
+ return symbol;
+}
+#endif /* DLSYM_PREPEND_UNDERSCORE */
+#endif /* NO_DLFCN */
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ *
+ *
+ * handle inserting, removing, and freeing of children.
+ * this does it via a hash table. it incurs some overhead over
+ * a linear append/remove in total removal and kills, but it makes
+ * single-entry removals a fast operation. as total removals occur during
+ * child initialization and kills during server shutdown, this is
+ * probably a win for a lot of connections and unimportant for a small
+ * number of connections.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <atalk/server_child.h>
+
+#ifndef __inline__
+#define __inline__
+#endif
+
+/* hash/child functions: hash OR's pid */
+#define CHILD_HASHSIZE 32
+#define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
+
+struct server_child_data {
+ pid_t pid;
+ struct server_child_data **prevp, *next;
+};
+
+typedef struct server_child_fork {
+ struct server_child_data *table[CHILD_HASHSIZE];
+ void (*cleanup)(const pid_t);
+} server_child_fork;
+
+
+static __inline__ void hash_child(struct server_child_data **htable,
+ struct server_child_data *child)
+{
+ struct server_child_data **table;
+
+ table = &htable[HASH(child->pid)];
+ if ((child->next = *table) != NULL)
+ (*table)->prevp = &child->next;
+ *table = child;
+ child->prevp = table;
+}
+
+static __inline__ void unhash_child(struct server_child_data *child)
+{
+ if (child->prevp) {
+ if (child->next)
+ child->next->prevp = child->prevp;
+ *(child->prevp) = child->next;
+ }
+}
+
+static __inline__ struct server_child_data
+*resolve_child(struct server_child_data **table, const pid_t pid)
+{
+ struct server_child_data *child;
+
+ for (child = table[HASH(pid)]; child; child = child->next) {
+ if (child->pid == pid)
+ break;
+ }
+
+ return child;
+}
+
+/* initialize server_child structure */
+server_child *server_child_alloc(const int connections, const int nforks)
+{
+ server_child *children;
+
+ children = (server_child *) calloc(1, sizeof(server_child));
+ if (!children)
+ return NULL;
+
+ children->nsessions = connections;
+ children->nforks = nforks;
+ children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
+
+ if (!children->fork) {
+ free(children);
+ return NULL;
+ }
+
+ return children;
+}
+
+/* add a child in. return 0 on success, -1 on serious error, and
+ * > 0 for a non-serious error. */
+int server_child_add(server_child *children, const int forkid,
+ const pid_t pid)
+{
+ server_child_fork *fork;
+ struct server_child_data *child;
+ sigset_t sig;
+
+ /* we need to prevent deletions from occuring before we get a
+ * chance to add the child in. */
+ sigemptyset(&sig);
+ sigaddset(&sig, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &sig, NULL);
+
+ /* it's possible that the child could have already died before the
+ * sigprocmask. we need to check for this. */
+ if (kill(pid, 0) < 0) {
+ sigprocmask(SIG_UNBLOCK, &sig, NULL);
+ return 1;
+ }
+
+ fork = (server_child_fork *) children->fork + forkid;
+
+ /* if we already have an entry. just return. */
+ if (resolve_child(fork->table, pid)) {
+ sigprocmask(SIG_UNBLOCK, &sig, NULL);
+ return 0;
+ }
+
+ if ((child = (struct server_child_data *)
+ calloc(1, sizeof(struct server_child_data))) == NULL) {
+ sigprocmask(SIG_UNBLOCK, &sig, NULL);
+ return -1;
+ }
+
+ child->pid = pid;
+ hash_child(fork->table, child);
+ children->count++;
+ sigprocmask(SIG_UNBLOCK, &sig, NULL);
+
+ return 0;
+}
+
+/* remove a child and free it */
+int server_child_remove(server_child *children, const int forkid,
+ const pid_t pid)
+{
+ server_child_fork *fork;
+ struct server_child_data *child;
+
+ fork = (server_child_fork *) children->fork + forkid;
+ if (!(child = resolve_child(fork->table, pid)))
+ return 0;
+
+ unhash_child(child);
+ free(child);
+ children->count--;
+ return 1;
+}
+
+/* free everything: by using a hash table, this increases the cost of
+ * this part over a linked list by the size of the hash table */
+void server_child_free(server_child *children)
+{
+ server_child_fork *fork;
+ struct server_child_data *child, *tmp;
+ int i, j;
+
+ for (i = 0; i < children->nforks; i++) {
+ fork = (server_child_fork *) children->fork + i;
+ for (j = 0; j < CHILD_HASHSIZE; j++) {
+ child = fork->table[j]; /* start at the beginning */
+ while (child) {
+ tmp = child->next;
+ free(child);
+ child = tmp;
+ }
+ }
+ }
+ free(children->fork);
+ free(children);
+}
+
+/* send kill to child processes: this also has an increased cost over
+ * a plain-old linked list */
+void server_child_kill(server_child *children, const int forkid,
+ const int sig)
+{
+ server_child_fork *fork;
+ struct server_child_data *child, *tmp;
+ int i;
+
+ fork = (server_child_fork *) children->fork + forkid;
+ for (i = 0; i < CHILD_HASHSIZE; i++) {
+ child = fork->table[i];
+ while (child) {
+ tmp = child->next;
+ kill(child->pid, sig);
+ child = tmp;
+ }
+ }
+}
+
+/* for extra cleanup if necessary */
+void server_child_setup(server_child *children, const int forkid,
+ void (*fcn)(const pid_t))
+{
+ server_child_fork *fork;
+
+ fork = (server_child_fork *) children->fork + forkid;
+ fork->cleanup = fcn;
+}
+
+
+/* keep track of children. */
+void server_child_handler(server_child *children)
+{
+ server_child_fork *fork;
+ int status, i;
+ pid_t pid;
+
+#ifndef WAIT_ANY
+#define WAIT_ANY (-1)
+#endif
+
+ while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
+ for (i = 0; i < children->nforks; i++) {
+ if (server_child_remove(children, i, pid)) {
+ fork = (server_child_fork *) children->fork + i;
+ if (fork->cleanup)
+ fork->cleanup(pid);
+ break;
+ }
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status)) {
+ syslog(LOG_INFO, "server_child[%d] %d exited %d", i, pid,
+ WEXITSTATUS(status));
+ } else {
+ syslog(LOG_INFO, "server_child[%d] %d done", i, pid);
+ }
+ } else {
+ if (WIFSIGNALED(status))
+ syslog(LOG_INFO, "server_child[%d] %d killed", i, pid);
+ else
+ syslog(LOG_INFO, "server_child[%d] %d died", i, pid);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ *
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include <atalk/compat.h>
+#include <atalk/util.h>
+
+/* this creates an open lock file which hangs around until the program
+ * dies. it returns the pid. due to problems w/ solaris, this has
+ * been changed to do the kill() thing. */
+pid_t server_lock(char *program, char *pidfile, int debug)
+{
+ char buf[10];
+ FILE *pf;
+ pid_t pid;
+ int mask;
+
+ mask = umask(022);
+ /* check for pid. this can get fooled by stale pid's. */
+ if ((pf = fopen(pidfile, "r"))) {
+ if (fgets(buf, sizeof(buf), pf) && !kill(pid = atol(buf), 0)) {
+ fprintf( stderr, "%s is already running (pid = %d), or the lock file is stale.\n",
+ program, pid);
+ fclose(pf);
+ return -1;
+ }
+ fclose(pf);
+ }
+
+ if ((pf = fopen(pidfile, "w")) == NULL) {
+ fprintf(stderr, "%s: can't open lock file, \"%s\"\n", program,
+ pidfile);
+ return -1;
+ }
+ umask(mask);
+
+ /*
+ * Disassociate from controlling tty.
+ */
+ if ( !debug ) {
+ int i;
+
+ switch (pid = fork()) {
+ case 0 :
+ fclose(stdin);
+ fclose(stdout);
+ fclose(stderr);
+
+ if (( i = open( "/dev/tty", O_RDWR )) >= 0 ) {
+ (void)ioctl( i, TIOCNOTTY, 0 );
+ setpgid( 0, getpid());
+ (void) close(i);
+ }
+ break;
+ case -1 : /* error */
+ perror( "fork" );
+ default : /* server */
+ fclose(pf);
+ return pid;
+ }
+ }
+
+ fprintf(pf, "%d\n", getpid());
+ fclose(pf);
+ return 0;
+}
--- /dev/null
+#include <atalk/util.h>
+
+unsigned const char _diacasemap[] = {
+ /* map value name */
+ 0 /* 0 NUL*/,
+ 1 /* 1 SOH*/,
+ 2 /* 2 STX*/,
+ 3 /* 3 ETX*/,
+ 4 /* 4 EOT*/,
+ 5 /* 5 ENQ*/,
+ 6 /* 6 ACK*/,
+ 7 /* 7 BEL*/,
+ 8 /* 8 BS*/,
+ 9 /* 9 HT*/,
+ 10 /* 10 NL*/,
+ 11 /* 11 VT*/,
+ 12 /* 12 NP*/,
+ 13 /* 13 CR*/,
+ 14 /* 14 SO*/,
+ 15 /* 15 SI*/,
+ 16 /* 16 DLE*/,
+ 17 /* 17 DC1*/,
+ 18 /* 18 DC2*/,
+ 19 /* 19 DC3*/,
+ 20 /* 20 DC4*/,
+ 21 /* 21 NAK*/,
+ 22 /* 22 SYN*/,
+ 23 /* 23 ETB*/,
+ 24 /* 24 CAN*/,
+ 25 /* 25 EM*/,
+ 26 /* 26 SUB*/,
+ 27 /* 27 ESC*/,
+ 28 /* 28 FS*/,
+ 29 /* 29 GS*/,
+ 30 /* 30 RS*/,
+ 31 /* 31 US*/,
+ 32 /* 32 SP*/,
+ 33 /* 33 ! */,
+ 34 /* 34 " */,
+ 35 /* 35 # */,
+ 36 /* 36 $ */,
+ 37 /* 37 % */,
+ 38 /* 38 & */,
+ 39 /* 39 ' */,
+ 40 /* 40 ( */,
+ 41 /* 41 ) */,
+ 42 /* 42 * */,
+ 43 /* 43 + */,
+ 44 /* 44 , */,
+ 45 /* 45 - */,
+ 46 /* 46 . */,
+ 47 /* 47 / */,
+ 48 /* 48 0 */,
+ 49 /* 49 1 */,
+ 50 /* 50 2 */,
+ 51 /* 51 3 */,
+ 52 /* 52 4 */,
+ 53 /* 53 5 */,
+ 54 /* 54 6 */,
+ 55 /* 55 7 */,
+ 56 /* 56 8 */,
+ 57 /* 57 9 */,
+ 58 /* 58 : */,
+ 59 /* 59 ; */,
+ 60 /* 60 < */,
+ 61 /* 61 = */,
+ 62 /* 62 > */,
+ 63 /* 63 ? */,
+ 64 /* 64 @ */,
+ 65 /* 65 A */,
+ 66 /* 66 B */,
+ 67 /* 67 C */,
+ 68 /* 68 D */,
+ 69 /* 69 E */,
+ 70 /* 70 F */,
+ 71 /* 71 G */,
+ 72 /* 72 H */,
+ 73 /* 73 I */,
+ 74 /* 74 J */,
+ 75 /* 75 K */,
+ 76 /* 76 L */,
+ 77 /* 77 M */,
+ 78 /* 78 N */,
+ 79 /* 79 O */,
+ 80 /* 80 P */,
+ 81 /* 81 Q */,
+ 82 /* 82 R */,
+ 83 /* 83 S */,
+ 84 /* 84 T */,
+ 85 /* 85 U */,
+ 86 /* 86 V */,
+ 87 /* 87 W */,
+ 88 /* 88 X */,
+ 89 /* 89 Y */,
+ 90 /* 90 Z */,
+ 91 /* 91 [ */,
+ 92 /* 92 \ */,
+ 93 /* 93 ] */,
+ 94 /* 94 ^ */,
+ 95 /* 95 _ */,
+ 96 /* 96 ` */,
+ 65 /* 97 a */,
+ 66 /* 98 b */,
+ 67 /* 99 c */,
+ 68 /* 100 d */,
+ 69 /* 101 e */,
+ 70 /* 102 f */,
+ 71 /* 103 g */,
+ 72 /* 104 h */,
+ 73 /* 105 i */,
+ 74 /* 106 j */,
+ 75 /* 107 k */,
+ 76 /* 108 l */,
+ 77 /* 109 m */,
+ 78 /* 110 n */,
+ 79 /* 111 o */,
+ 80 /* 112 p */,
+ 81 /* 113 q */,
+ 82 /* 114 r */,
+ 83 /* 115 s */,
+ 84 /* 116 t */,
+ 85 /* 117 u */,
+ 86 /* 118 v */,
+ 87 /* 119 w */,
+ 88 /* 120 x */,
+ 89 /* 121 y */,
+ 90 /* 122 z */,
+ 123 /* 123 { */,
+ 124 /* 124 | */,
+ 125 /* 125 } */,
+ 126 /* 126 ~ */,
+ 127 /* 127 DEL*/,
+ 128 /* 128 Adieresis*/,
+ 129 /* 129 Aring*/,
+ 130 /* 130 Ccedilla*/,
+ 131 /* 131 Eacute*/,
+ 132 /* 132 Ntilda*/,
+ 133 /* 133 Odieresis*/,
+ 134 /* 134 Udieresis*/,
+ 231 /* 135 aacute*/,
+ 203 /* 136 agrave*/,
+ 229 /* 137 acircumflex*/,
+ 128 /* 138 adieresis*/,
+ 204 /* 139 atilda*/,
+ 129 /* 140 aring*/,
+ 130 /* 141 ccedilla*/,
+ 131 /* 142 eacute*/,
+ 233 /* 143 egrave*/,
+ 230 /* 144 ecircumflex*/,
+ 232 /* 145 edieresis*/,
+ 234 /* 146 iacute*/,
+ 237 /* 147 igrave*/,
+ 235 /* 148 icircumflex*/,
+ 236 /* 149 idieresis*/,
+ 132 /* 150 ntilda*/,
+ 238 /* 151 oacute*/,
+ 241 /* 152 ograve*/,
+ 239 /* 153 ocircumflex*/,
+ 133 /* 154 odieresis*/,
+ 205 /* 155 otilda*/,
+ 242 /* 156 uacute*/,
+ 244 /* 157 ugrave*/,
+ 243 /* 158 ucircumflex*/,
+ 134 /* 159 udieresis*/,
+ 160 /* 160 daggar*/,
+ 161 /* 161 ring*/,
+ 162 /* 162 cent*/,
+ 163 /* 163 sterling*/,
+ 164 /* 164 section*/,
+ 165 /* 165 bullet*/,
+ 166 /* 166 paragraph*/,
+ 167 /* 167 germandbls*/,
+ 168 /* 168 registered*/,
+ 169 /* 169 copyright*/,
+ 170 /* 170 trademark*/,
+ 171 /* 171 acute*/,
+ 172 /* 172 dieresis*/,
+ 173 /* 173 notequal*/,
+ 174 /* 174 AE*/,
+ 175 /* 175 Oslash*/,
+ 176 /* 176 infinity*/,
+ 177 /* 177 plusminus*/,
+ 178 /* 178 lessequal*/,
+ 179 /* 179 greaterequal*/,
+ 180 /* 180 yen*/,
+ 181 /* 181 mu*/,
+ 198 /* 182 delta*/,
+ 183 /* 183 Sigma*/,
+ 184 /* 184 Pi*/,
+ 184 /* 185 pi*/,
+ 186 /* 186 intergral*/,
+ 187 /* 187 ordfeminine*/,
+ 188 /* 188 ordmasculine*/,
+ 189 /* 189 Omega*/,
+ 174 /* 190 ae*/,
+ 175 /* 191 oslash*/,
+ 192 /* 192 questiondown*/,
+ 193 /* 193 exclamdown*/,
+ 194 /* 194 not*/,
+ 195 /* 195 radical*/,
+ 196 /* 196 florin*/,
+ 197 /* 197 aprox*/,
+ 198 /* 198 Delta*/,
+ 199 /* 199 guillemotleft*/,
+ 200 /* 200 guillemotright*/,
+ 201 /* 201 ellipsis*/,
+ 202 /* 202 */,
+ 203 /* 203 Agrave*/,
+ 204 /* 204 Atilda*/,
+ 205 /* 205 Otilda*/,
+ 206 /* 206 OE*/,
+ 206 /* 207 oe*/,
+ 208 /* 208 endash*/,
+ 209 /* 209 emdash*/,
+ 210 /* 210 quotedblleft*/,
+ 211 /* 211 quotedblright*/,
+ 212 /* 212 quoteleft*/,
+ 213 /* 213 quoteright*/,
+ 214 /* 214 divide*/,
+ 215 /* 215 diamond*/,
+ 217 /* 216 ydieresis*/,
+ 217 /* 217 Ydieresis*/,
+ 218 /* 218 fraction*/,
+ 219 /* 219 currency*/,
+ 220 /* 220 guilsinglleft*/,
+ 221 /* 221 guilsinglright*/,
+ 222 /* 222 fi*/,
+ 223 /* 223 fl*/,
+ 224 /* 224 daggardbl*/,
+ 225 /* 225 periodcentered*/,
+ 226 /* 226 quotesinglbase*/,
+ 227 /* 227 quotedblbase*/,
+ 228 /* 228 perthousand*/,
+ 229 /* 229 Acircumflex*/,
+ 230 /* 230 Ecircumflex*/,
+ 231 /* 231 Aaccute*/,
+ 232 /* 232 Edieresis*/,
+ 233 /* 233 Egrave*/,
+ 234 /* 234 Iaccute*/,
+ 235 /* 235 Icircumflex*/,
+ 236 /* 236 Idieresis*/,
+ 237 /* 237 Igrave*/,
+ 238 /* 238 Oaccute*/,
+ 239 /* 239 Ocircumflex*/,
+ 240 /* 240 apple*/,
+ 241 /* 241 Ograve*/,
+ 242 /* 242 Uaccute*/,
+ 243 /* 243 Ucircumflex*/,
+ 244 /* 244 Ugrave*/,
+ 245 /* 245 dotlessi*/,
+ 246 /* 246 circumflex*/,
+ 247 /* 247 tilda*/,
+ 248 /* 248 macron*/,
+ 249 /* 249 breve*/,
+ 250 /* 250 dotaccent*/,
+ 251 /* 251 ring*/,
+ 252 /* 252 cedilla*/,
+ 253 /* 253 hungarumlaut*/,
+ 254 /* 254 ogonek*/,
+ 255 /* 255 caron*/,
+};
+
+unsigned const char _dialowermap[] = {
+ /* map value name */
+ 0 /* 0 NUL*/,
+ 1 /* 1 SOH*/,
+ 2 /* 2 STX*/,
+ 3 /* 3 ETX*/,
+ 4 /* 4 EOT*/,
+ 5 /* 5 ENQ*/,
+ 6 /* 6 ACK*/,
+ 7 /* 7 BEL*/,
+ 8 /* 8 BS*/,
+ 9 /* 9 HT*/,
+ 10 /* 10 NL*/,
+ 11 /* 11 VT*/,
+ 12 /* 12 NP*/,
+ 13 /* 13 CR*/,
+ 14 /* 14 SO*/,
+ 15 /* 15 SI*/,
+ 16 /* 16 DLE*/,
+ 17 /* 17 DC1*/,
+ 18 /* 18 DC2*/,
+ 19 /* 19 DC3*/,
+ 20 /* 20 DC4*/,
+ 21 /* 21 NAK*/,
+ 22 /* 22 SYN*/,
+ 23 /* 23 ETB*/,
+ 24 /* 24 CAN*/,
+ 25 /* 25 EM*/,
+ 26 /* 26 SUB*/,
+ 27 /* 27 ESC*/,
+ 28 /* 28 FS*/,
+ 29 /* 29 GS*/,
+ 30 /* 30 RS*/,
+ 31 /* 31 US*/,
+ 32 /* 32 SP*/,
+ 33 /* 33 ! */,
+ 34 /* 34 " */,
+ 35 /* 35 # */,
+ 36 /* 36 $ */,
+ 37 /* 37 % */,
+ 38 /* 38 & */,
+ 39 /* 39 ' */,
+ 40 /* 40 ( */,
+ 41 /* 41 ) */,
+ 42 /* 42 * */,
+ 43 /* 43 + */,
+ 44 /* 44 , */,
+ 45 /* 45 - */,
+ 46 /* 46 . */,
+ 47 /* 47 / */,
+ 48 /* 48 0 */,
+ 49 /* 49 1 */,
+ 50 /* 50 2 */,
+ 51 /* 51 3 */,
+ 52 /* 52 4 */,
+ 53 /* 53 5 */,
+ 54 /* 54 6 */,
+ 55 /* 55 7 */,
+ 56 /* 56 8 */,
+ 57 /* 57 9 */,
+ 58 /* 58 : */,
+ 59 /* 59 ; */,
+ 60 /* 60 < */,
+ 61 /* 61 = */,
+ 62 /* 62 > */,
+ 63 /* 63 ? */,
+ 64 /* 64 @ */,
+ 97 /* 65 A */,
+ 98 /* 66 B */,
+ 99 /* 67 C */,
+ 100 /* 68 D */,
+ 101 /* 69 E */,
+ 102 /* 70 F */,
+ 103 /* 71 G */,
+ 104 /* 72 H */,
+ 105 /* 73 I */,
+ 106 /* 74 J */,
+ 107 /* 75 K */,
+ 108 /* 76 L */,
+ 109 /* 77 M */,
+ 110 /* 78 N */,
+ 111 /* 79 O */,
+ 112 /* 80 P */,
+ 113 /* 81 Q */,
+ 114 /* 82 R */,
+ 115 /* 83 S */,
+ 116 /* 84 T */,
+ 117 /* 85 U */,
+ 118 /* 86 V */,
+ 119 /* 87 W */,
+ 120 /* 88 X */,
+ 121 /* 89 Y */,
+ 122 /* 90 Z */,
+ 91 /* 91 [ */,
+ 92 /* 92 \ */,
+ 93 /* 93 ] */,
+ 94 /* 94 ^ */,
+ 95 /* 95 _ */,
+ 96 /* 96 ` */,
+ 97 /* 97 a */,
+ 98 /* 98 b */,
+ 99 /* 99 c */,
+ 100 /* 100 d */,
+ 101 /* 101 e */,
+ 102 /* 102 f */,
+ 103 /* 103 g */,
+ 104 /* 104 h */,
+ 105 /* 105 i */,
+ 106 /* 106 j */,
+ 107 /* 107 k */,
+ 108 /* 108 l */,
+ 109 /* 109 m */,
+ 110 /* 110 n */,
+ 111 /* 111 o */,
+ 112 /* 112 p */,
+ 113 /* 113 q */,
+ 114 /* 114 r */,
+ 115 /* 115 s */,
+ 116 /* 116 t */,
+ 117 /* 117 u */,
+ 118 /* 118 v */,
+ 119 /* 119 w */,
+ 120 /* 120 x */,
+ 121 /* 121 y */,
+ 122 /* 122 z */,
+ 123 /* 123 { */,
+ 124 /* 124 | */,
+ 125 /* 125 } */,
+ 126 /* 126 ~ */,
+ 127 /* 127 DEL*/,
+ 138 /* 128 Adieresis*/,
+ 140 /* 129 Aring*/,
+ 141 /* 130 Ccedilla*/,
+ 142 /* 131 Eacute*/,
+ 150 /* 132 Ntilda*/,
+ 154 /* 133 Odieresis*/,
+ 159 /* 134 Udieresis*/,
+ 135 /* 135 aacute*/,
+ 136 /* 136 agrave*/,
+ 137 /* 137 acircumflex*/,
+ 138 /* 138 adieresis*/,
+ 139 /* 139 atilda*/,
+ 140 /* 140 aring*/,
+ 141 /* 141 ccedilla*/,
+ 142 /* 142 eacute*/,
+ 143 /* 143 egrave*/,
+ 144 /* 144 ecircumflex*/,
+ 145 /* 145 edieresis*/,
+ 146 /* 146 iacute*/,
+ 147 /* 147 igrave*/,
+ 148 /* 148 icircumflex*/,
+ 149 /* 149 idieresis*/,
+ 132 /* 150 ntilda*/,
+ 151 /* 151 oacute*/,
+ 152 /* 152 ograve*/,
+ 153 /* 153 ocircumflex*/,
+ 154 /* 154 odieresis*/,
+ 155 /* 155 otilda*/,
+ 156 /* 156 uacute*/,
+ 157 /* 157 ugrave*/,
+ 158 /* 158 ucircumflex*/,
+ 159 /* 159 udieresis*/,
+ 160 /* 160 daggar*/,
+ 161 /* 161 ring*/,
+ 162 /* 162 cent*/,
+ 163 /* 163 sterling*/,
+ 164 /* 164 section*/,
+ 165 /* 165 bullet*/,
+ 166 /* 166 paragraph*/,
+ 167 /* 167 germandbls*/,
+ 168 /* 168 registered*/,
+ 169 /* 169 copyright*/,
+ 170 /* 170 trademark*/,
+ 171 /* 171 acute*/,
+ 172 /* 172 dieresis*/,
+ 173 /* 173 notequal*/,
+ 190 /* 174 AE*/,
+ 191 /* 175 Oslash*/,
+ 176 /* 176 infinity*/,
+ 177 /* 177 plusminus*/,
+ 178 /* 178 lessequal*/,
+ 179 /* 179 greaterequal*/,
+ 180 /* 180 yen*/,
+ 181 /* 181 mu*/,
+ 198 /* 182 delta*/,
+ 183 /* 183 Sigma*/,
+ 185 /* 184 Pi*/,
+ 185 /* 185 pi*/,
+ 186 /* 186 intergral*/,
+ 187 /* 187 ordfeminine*/,
+ 188 /* 188 ordmasculine*/,
+ 189 /* 189 Omega*/,
+ 190 /* 190 ae*/,
+ 191 /* 191 oslash*/,
+ 192 /* 192 questiondown*/,
+ 193 /* 193 exclamdown*/,
+ 194 /* 194 not*/,
+ 195 /* 195 radical*/,
+ 196 /* 196 florin*/,
+ 197 /* 197 aprox*/,
+ 198 /* 198 Delta*/,
+ 199 /* 199 guillemotleft*/,
+ 200 /* 200 guillemotright*/,
+ 201 /* 201 ellipsis*/,
+ 202 /* 202 */,
+ 136 /* 203 Agrave*/,
+ 139 /* 204 Atilda*/,
+ 155 /* 205 Otilda*/,
+ 207 /* 206 OE*/,
+ 207 /* 207 oe*/,
+ 208 /* 208 endash*/,
+ 209 /* 209 emdash*/,
+ 210 /* 210 quotedblleft*/,
+ 211 /* 211 quotedblright*/,
+ 212 /* 212 quoteleft*/,
+ 213 /* 213 quoteright*/,
+ 214 /* 214 divide*/,
+ 215 /* 215 diamond*/,
+ 217 /* 216 ydieresis*/,
+ 217 /* 217 Ydieresis*/,
+ 218 /* 218 fraction*/,
+ 219 /* 219 currency*/,
+ 220 /* 220 guilsinglleft*/,
+ 221 /* 221 guilsinglright*/,
+ 222 /* 222 fi*/,
+ 223 /* 223 fl*/,
+ 224 /* 224 daggardbl*/,
+ 225 /* 225 periodcentered*/,
+ 226 /* 226 quotesinglbase*/,
+ 227 /* 227 quotedblbase*/,
+ 228 /* 228 perthousand*/,
+ 137 /* 229 Acircumflex*/,
+ 144 /* 230 Ecircumflex*/,
+ 135 /* 231 Aacute*/,
+ 145 /* 232 Edieresis*/,
+ 143 /* 233 Egrave*/,
+ 146 /* 234 Iaccute*/,
+ 148 /* 235 Icircumflex*/,
+ 149 /* 236 Idieresis*/,
+ 147 /* 237 Igrave*/,
+ 151 /* 238 Oacute*/,
+ 153 /* 239 Ocircumflex*/,
+ 240 /* 240 apple*/,
+ 152 /* 241 Ograve*/,
+ 156 /* 242 Uacute*/,
+ 158 /* 243 Ucircumflex*/,
+ 157 /* 244 Ugrave*/,
+ 245 /* 245 dotlessi*/,
+ 246 /* 246 circumflex*/,
+ 247 /* 247 tilda*/,
+ 248 /* 248 macron*/,
+ 249 /* 249 breve*/,
+ 250 /* 250 dotaccent*/,
+ 251 /* 251 ring*/,
+ 252 /* 252 cedilla*/,
+ 253 /* 253 hungarumlaut*/,
+ 254 /* 254 ogonek*/,
+ 255 /* 255 caron*/,
+};
+
+int strdiacasecmp( s1, s2 )
+ const unsigned char *s1, *s2;
+{
+ while ( _diacasemap[ *s1 ] == _diacasemap[ *s2++ ] ) {
+ if ( *s1++ == '\0' ) {
+ return( 0 );
+ }
+ }
+ return( _diacasemap[ *s1 ] - _diacasemap[ *--s2 ] );
+}
+
+int strndiacasecmp( s1, s2, n )
+ const unsigned char *s1, *s2;
+ int n;
+{
+ while ( --n >= 0 && _diacasemap[ *s1 ] == _diacasemap[ *s2++ ] ) {
+ if ( *s1++ == '\0' ) {
+ return( 0 );
+ }
+ }
+ return( n < 0 ? 0 : _diacasemap[ *s1 ] - _diacasemap[ *--s2 ] );
+}
--- /dev/null
+#!/bin/sh
+# pap script for lp systems
+
+chdir "/etc/lp/printers/`basename $0`"
+
+if [ -r "$6" ]; then
+ :BINDIR:/pap -E "$6"
+ exit $?
+fi
+
+exit 2
--- /dev/null
+ALL= man1 man3 man4 man8
+TAGSFILE=tags
+CC=cc
+INSTALL= install
+
+all: ${ALL}
+
+${ALL}: FRC
+ cd $@; ${MAKE} ${MFLAGS} CC="${CC}" DEFS="${DEFS}" \
+ OPTOPTS="${OPTOPTS}" DESTDIR="${DESTDIR}" MANDIR="${MANDIR}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}"
+
+
+FRC:
+
+tags:
+ for i in ${ALL}; do \
+ (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" \
+ TAGSFILE=../${TAGSFILE} tags); \
+ done
+
+install:
+ -mkdir ${MANDIR}
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} \
+ DESTDIR="${DESTDIR}" MANDIR="${MANDIR}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ INSTALL="${INSTALL}" install); \
+ done
+
+clean:
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+ done
+
+depend:
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} DEFS=${DEFS} depend); \
+ done
--- /dev/null
+SRC= aecho.1 getzones.1 nbp.1 nbplkup.1 nbprgstr.1 \
+ pap.1 papstatus.1 psorder.1 megatron.1 unhex.1 unbin.1 \
+ unsingle.1 macbinary.1 hqx2bin.1 single2bin.1
+SRCTMP= aecho.1.tmp getzones.1.tmp nbp.1.tmp nbplkup.1.tmp nbprgstr.1.tmp \
+ pap.1.tmp papstatus.1.tmp psorder.1.tmp megatron.1.tmp \
+ unhex.1.tmp unbin.1.tmp unsingle.1.tmp macbinary.1.tmp \
+ hqx2bin.1.tmp single2bin.1.tmp
+
+INCPATH=
+CFLAGS=
+TAGSFILE=
+CC=
+INSTALL= install
+
+LINKS=
+
+
+all: ${SRCTMP}
+
+${SRCTMP}: ../../Makefile
+ for i in ${SRC} ; do \
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < $$i > $$i.tmp; \
+ done
+
+install : ${SRCTMP}
+ -mkdir ${MANDIR}/man1
+ for i in ${SRC} ; do \
+ rm -f ${MANDIR}/man1/$$i; \
+ ${INSTALL} -m644 $$i.tmp ${MANDIR}/man1/$$i; \
+ done
+
+clean :
+ for i in ${SRC}; do \
+ rm -f $$i.tmp; \
+ done
+
+tags : ${SRC}
+
+depend :
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+.TH AECHO 1 "17 Dec 1991" "netatalk 1.2"
+.SH NAME
+aecho \- send AppleTalk Echo Protocol packets to network hosts
+.SH SYNOPSIS
+.B aecho
+[
+.B \-c\fI count
+]
+(
+.I address
+|
+.I nbpname
+)
+.SH DESCRIPTION
+.B aecho
+repeatedly sends an Apple Echo Protocol (AEP) packet to the host
+specified by the given AppleTalk
+.I address
+or
+.I nbpname
+and reports whether a reply was received. Requests are sent at the
+rate of one per second.
+.LP
+.I address
+is parsed by
+.BR atalk_aton (3).
+.I nbpname
+is parsed by
+.BR nbp_name (3).
+The nbp type defaults to
+.RB ` Workstation '.
+.LP
+When
+.B aecho
+is terminated, it reports the number of packets sent, the number of
+responses received, and the percentage of packets lost. If any
+responses were received, the minimum, average, and maximum round trip
+times are reported.
+.SH EXAMPLE
+Check to see if a particular host is up and responding to AEP packets:
+.sp
+.RS
+.nf
+ example% aecho bloodsport
+ 11 bytes from 8195.13: aep_seq=0. time=10. ms
+ 11 bytes from 8195.13: aep_seq=1. time=10. ms
+ 11 bytes from 8195.13: aep_seq=2. time=10. ms
+ 11 bytes from 8195.13: aep_seq=3. time=10. ms
+ 11 bytes from 8195.13: aep_seq=4. time=10. ms
+ 11 bytes from 8195.13: aep_seq=5. time=9. ms
+ ^C
+ ----8195.13 AEP Statistics----
+ 6 packets sent, 6 packets received, 0% packet loss
+ round-trip (ms) min/avg/max = 9/9/10
+.fi
+.RE
+.SH OPTIONS
+.TP
+.B \-c\fI count
+Stop after
+.I count
+packets.
+.SH SEE ALSO
+.BR ping (1),
+.BR atalk_aton (3),
+.BR nbp_name (3),
+.\" .BR aep (4),
+.BR atalkd (8).
--- /dev/null
+.TH GETZONES 1 "17 Dec 1991" "netatalk 1.2"
+.SH NAME
+getzones \- list AppleTalk zone names
+.SH SYNOPSIS
+.B getzones
+[
+.B -m
+|
+.B -l
+] [
+.I address
+]
+.SH DESCRIPTION
+.B Getzones
+is used to obtain a list of AppleTalk zone names using the Zone
+Information Protocol (ZIP). It sends a GetZoneList request to an
+AppleTalk router. By default, it sends the request to the locally
+running
+.BR atalkd (8).
+.SH OPTIONS
+.TP
+.B -m
+List the name of the local zone only; this is accomplished by sending a
+ZIP GetMyZone request.
+.TP
+.B -l
+List the local zones; this is accomplished by sending a GetLocalZones
+request.
+.TP
+.I address
+Contact the AppleTalk router at
+.I address.
+.I address
+is parsed by
+.BR atalk_aton (3).
+.SH SEE ALSO
+.BR atalk_aton (3),
+.\" .BR zip (4),
+.BR atalkd (8).
--- /dev/null
+.so man1/megatron.1
--- /dev/null
+.so man1/megatron.1
--- /dev/null
+.TH MEGATRON 1 "8 Jan 1992" "netatalk 1.2"
+.SH NAME
+megatron, unhex, unbin, unsingle, hqx2bin, single2bin, macbinary \- Macintosh file format transformer
+.SH SYNOPSIS
+.B megatron
+[
+.I sourcefile...
+]
+.LP
+.B unbin
+[
+.I sourcefile...
+]
+.LP
+.B unhex
+[
+.I sourcefile...
+]
+.LP
+.B unsingle
+[
+.I sourcefile...
+]
+.LP
+.B hqx2bin
+[
+.I sourcefile...
+]
+.LP
+.B single2bin
+[
+.I sourcefile...
+]
+.LP
+.B macbinary
+[
+.I sourcefile...
+]
+.SH DESCRIPTION
+.B megatron
+is used to transform files from BinHex, MacBinary, AppleSingle, or
+.B netatalk
+style AppleDouble formats into MacBinary or
+.B netatalk
+style AppleDouble formats. The
+.B netatalk
+style AppleDouble format is the file format used by
+.B afpd,
+the
+.B netatalk
+Apple Filing Protocol (AppleShare) server. BinHex, MacBinary, and
+AppleSingle are commonly used formats for transferring Macintosh files
+between machines via email or file transfer protocols.
+.B megatron
+uses its name to determine what type of tranformation is being asked of
+it.
+.LP
+If
+.B megatron
+is called as
+.B unhex, unbin,
+or
+.B unsingle,
+it tries to convert file(s) from BinHex, MacBinary, or AppleSingle into
+AppleDouble format. BinHex is the format most often used to send
+Macintosh files by e-mail. Usually these files have an extension of
+".hqx". MacBinary is the format most often used by terminal emulators
+"on the fly" when transferring Macintosh files in binary mode.
+MacBinary files often have an extension of ".bin". Some Macintosh
+LAN-based email packages use uuencoded AppleSingle format to "attach"
+or "enclose" files in email. AppleSingle files don't have a standard
+filename extension.
+.LP
+If
+.B megatron
+is called as
+.B hqx2bin, single2bin,
+or
+.B macbinary,
+it will try to convert the file(s) from BinHex, AppleSingle, or
+AppleDouble into MacBinary. This last translation may be useful in
+moving Macintosh files from your
+.B afpd
+server to some other machine when you can't copy them from the server
+using a Macintosh for some reason.
+.LP
+If
+.B megatron
+is called with any other name, it uses the default translation, namely
+.B unhex.
+.LP
+If no source file is given, or if
+.I sourcefile
+is
+.RB ` - ',
+and if the conversion is from a BinHex or MacBinary file,
+.B megatron
+will read from standard input.
+.LP
+The filename used to store any output file is the filename that is
+encoded in the source file. MacBinary files are created with a ".bin"
+extension. In the case of conflicts, the old file is overwritten!
+.SH SEE ALSO
+.BR afpd (8)
--- /dev/null
+.TH NBP 1 "17 Dec 1991" "netatalk 1.2"
+.SH NAME
+nbplkup, nbprgstr, nbpunrgstr \- access NBP database
+.SH SYNOPSIS
+.B nbplkup
+[
+.B -r
+.I nresp
+]
+[
+.I nbpname
+]
+.sp
+.B nbprgstr
+[
+.B -p
+.I port
+]
+.I nbpname
+.sp
+.B nbpunrgstr
+.I nbpname
+.SH DESCRIPTION
+.B nbprgstr
+registers
+.I nbpname
+with
+.BR atalkd (8),
+at the given
+.IR port .
+.B nbpunrgstr
+informs
+.B atalkd
+that
+.I nbpname
+is no longer to be advertised.
+.LP
+.B nbplkup
+displays up to
+.I nresp
+(default 1000) entities registered on the AppleTalk internet.
+.I nbpname
+is parsed by
+.BR nbp_name (3).
+An
+.RB ` = '
+for the
+.I object
+or
+.I type
+matches anything, and an
+.RB ` * '
+for
+.I zone
+means the local zone. The default values are taken from the
+.B NBPLKUP
+environment variable, parsed as an
+.IR nbpname .
+.SH EXAMPLE
+Find all devices of type
+.B LaserWriter
+in the local zone.
+.sp
+.RS
+.nf
+example% nbplkup :LaserWriter
+ Petoskey:LaserWriter 7942.129:218
+ Gloucester:LaserWriter 8200.188:186
+ Rahway:LaserWriter 7942.2:138
+ 517 Center:LaserWriter 7942.2:132
+ ionia:LaserWriter 7942.2:136
+ Evil DEC from Hell:LaserWriter 7942.2:130
+ Hamtramck:LaserWriter 7942.2:134
+ Iron Mountain :LaserWriter 7942.128:250
+example%
+.fi
+.RE
+.SH SEE ALSO
+.BR nbp_name (3),
+.\" .BR nbp (4),
+.\" .BR zip (4),
+.BR atalkd (8).
--- /dev/null
+.so man1/nbp.1
--- /dev/null
+.so man1/nbp.1
--- /dev/null
+.so man1/nbp.1
--- /dev/null
+.TH PAP 1 "3 Jun 1994" "netatalk 1.3"
+.SH NAME
+pap, papstatus \- client interface to remote printers using Printer Access Protocol
+.SH SYNOPSIS
+.B pap
+[
+.B -c
+] [
+.B -e
+] [
+.B -p
+.I nbpname
+] [
+.B -s
+.I statusfile
+] [
+.I files
+]
+.sp
+.B papstatus
+[
+.B -p
+.I nbpname
+]
+.SH DESCRIPTION
+.B pap
+is used to connect and send files to an AppleTalk connected printer using
+the Apple Printer Access Protocol (PAP).
+When
+.B pap
+starts execution, it
+tries to open a session with the printer using PAP, and
+then downloads the
+.I files
+to the printer.
+.LP
+If no
+.I files
+are given on the command line,
+.B pap
+begins reading from standard input.
+.LP
+If no printer is specified on the command line,
+.B pap
+looks for a file called
+.B .paprc
+in the current working directory and reads it to obtain the
+.I nbpname
+of a
+printer.
+Blank lines and lines that begin with a
+.RB ` # '
+are ignored.
+.I type
+and
+.I zone
+default to
+.B LaserWriter
+and the zone of the local host, respectively.
+.LP
+Note that
+.B pap
+is designed to be useful as a communication filter for sending
+.BR lpd (8)
+spooled print jobs to AppleTalk connected printers. See
+.BR psf (8)
+for hints on how to use it this way.
+.SH OPTIONS
+.TP
+.B -c
+Take cuts. Normally
+.B pap
+tells the printer how long it has been waiting. When
+.B -c
+is specified,
+.B pap
+claims to have been waiting forever.
+.TP
+.B -e
+Send any message from the printer to stderr instead of stdout.
+.BR psf (8)
+invokes
+.B pap
+with this option.
+.HP
+.B -p
+.I nbpname
+.br
+Connect to the printer named
+.I nbpname
+and do not consult the
+.B .paprc
+file to find a printer name. See
+.BR nbp_name (3)
+for the syntax of
+.IR nbpname .
+.HP
+.B -s
+.I statusfile
+.br
+Update the file called
+.I statusfile
+to contain the most recent status message from the printer.
+.B pap
+gets the status from the printer when it is waiting for the printer to
+process input. The
+.I statusfile
+will contain a single line terminated with a newline. This is useful
+when
+.B pap
+is invoked by
+.BR psf (8)
+within
+.BR lpd 's
+spool directory.
+.SH FILES
+.TP 20
+.B .paprc
+file that contains printer name
+.SH SEE ALSO
+.BR nbp_name (3),
+.BR lpd (8),
+.BR psf (8).
--- /dev/null
+.so man1/pap.1
--- /dev/null
+.TH PSORDER 1 "17 Dec 1991" "netatalk 1.2"
+.SH NAME
+psorder \- PostScript pageorder filter
+.SH SYNOPSIS
+.B psorder
+[
+.B -duf
+]
+.I sourcefile
+.SH DESCRIPTION
+.B psorder
+is a filter that re-orders the pages of a PostScript document.
+The result is written to the standard output. By default,
+documents are processed into ascending order (the lowest numbered page
+is printed first). Some PostScript documents specify that the order of
+their pages should never be changed; the default action of
+.B psorder
+is to follow this specification.
+.LP
+If no source file is given, or if
+.I sourcefile
+is
+.RB ` - ',
+.B psorder
+reads from the standard input file.
+.br
+.SH OPTIONS
+.TP
+.B \-d
+Re-order the pages of the document in downward or descending
+order. This is typically used to change the order of a document to
+be printed by a printer that stacks pages face-up,
+such as an Apple LaserWriter or LaserWriter Plus.
+.TP
+.B \-u
+Specifies forward order, and is the default. It is used to
+try and ensure the correct ordering when a document will be printed
+by a printer that stacks the pages face-down.
+.TP
+.B \-f
+Force
+.B psorder
+to re-order the pages, even if the document claims that its page
+order is not to be trifled with. This option should only be used
+experimentally, as it may cause documents to print incorrectly.
+.SH SEE ALSO
+.BR psf (8),
+.BR lpr (1).
--- /dev/null
+.so man1/megatron.1
--- /dev/null
+.so man1/megatron.1
--- /dev/null
+.so man1/megatron.1
--- /dev/null
+.so man1/megatron.1
--- /dev/null
+SRC= atalk_aton.3 nbp_name.3
+SRCTMP= atalk_aton.3.tmp nbp_name.3.tmp
+
+INCPATH=
+CFLAGS=
+TAGSFILE=
+CC=
+INSTALL= install
+
+LINKS=
+
+all : ${SRCTMP}
+
+${SRCTMP}: ../../Makefile
+ for i in ${SRC} ; do \
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < $$i > $$i.tmp; \
+ done
+
+install : ${SRCTMP}
+ -mkdir ${MANDIR}/man3
+ for i in ${SRC} ; do \
+ rm -f ${MANDIR}/man3/$$i; \
+ ${INSTALL} -m644 $$i.tmp ${MANDIR}/man3/$$i; \
+ done
+
+clean :
+ for i in ${SRC}; do \
+ rm -f $$i.tmp; \
+ done
+
+tags : ${SRC}
+
+depend :
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+.TH ATALK_ATON 3 "12 Jan 1994" "netatalk 1.3"
+.SH NAME
+atalk_aton \- AppleTalk address parsing
+.SH SYNOPSIS
+.nf
+#include <sys/types.h>
+#include <netatalk/at.h>
+.LP
+atalk_aton( cp, ata )
+char *cp;
+struct at_addr *ata;
+.fi
+.SH DESCRIPTION
+The
+.B atalk_aton()
+routine converts an ascii representation of an AppleTalk address to a
+format appropriate for system calls. Acceptable ascii representations
+include both hex and base 10, in triples or doubles. For instance, the
+address `0x1f6b.77' has a network part of `8043' and a node part of
+`119'. This same address could be written `8043.119', `31.107.119', or
+`0x1f.6b.77'. If the address is in hex and the first digit is one of
+`A-F', a leading `0x' is redundant.
+.SH SEE ALSO
+.BR atalk (4).
--- /dev/null
+.TH NBP_NAME 3 "12 Jan 1994" "netatalk 1.3"
+.SH NAME
+nbp_name \- NBP name parsing
+.SH SYNOPSIS
+.nf
+nbp_name( name, obj, type, zone )
+char *name, **obj, **type, **zone;
+.fi
+.SH DESCRIPTION
+.B nbp_name()
+parses user supplied names into their component object, type, and
+zone.
+.BR obj ,
+.BR type ,
+and
+.B zone
+should be passed by reference, and should point to the caller's default
+values.
+.B nbp_name()
+will change the pointers to the parsed-out values.
+.B name
+is of the form
+.IB object : \c
+.IB type @ \c
+.IR zone ,
+where each of
+.IR object ,
+.BI : type ,
+and
+.BI @ zone
+replace
+.BR obj ,
+.BR type ,
+and
+.BR zone,
+respectively.
+.I type
+must be proceeded by
+.RB ` : ',
+and
+.I zone
+must be preceded by
+.RB ` @ '.
+.SH EXAMPLE
+The argument of
+.BR afpd (8)'s
+.B -n
+option is parsed with
+.BR nbp_name() .
+The default value of
+.B obj
+is the first component of the machine's hostname (as returned by
+.BR gethostbyname (3)).
+The default value of
+.B type
+is ``AFPServer'', and of
+.B zone
+is ``*'', the default zone. To cause
+.B afpd
+to register itself in some zone other than the default, one would
+invoke it as
+.sp
+.RS
+.nf
+afpd -n @some-other-zone
+.fi
+.RE
+.sp
+.B obj
+and
+.B type
+would retain their default values.
+.SH BUGS
+.BR obj ,
+.BR type ,
+and
+.B zone
+return pointers into static area which may be over-written on each
+call.
--- /dev/null
+SRC= atalk.4
+SRCTMP= atalk.4.tmp
+INCPATH=
+CFLAGS=
+TAGSFILE=
+CC=
+INSTALL= install
+
+LINKS=
+
+all : ${SRCTMP}
+
+${SRCTMP}: ../../Makefile
+ for i in ${SRC} ; do \
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < $$i > $$i.tmp; \
+ done
+
+install : ${SRCTMP}
+ -mkdir ${MANDIR}/man4
+ for i in ${SRC} ; do \
+ rm -f ${MANDIR}/man4/$$i; \
+ ${INSTALL} -m644 $$i.tmp ${MANDIR}/man4/$$i; \
+ done
+
+clean :
+ for i in ${SRC}; do \
+ rm -f $$i.tmp; \
+ done
+
+tags : ${SRC}
+
+depend :
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+.TH ATALK 4F "17 Dec 1991" "netatalk 1.2"
+.SH NAME
+atalk \- AppleTalk protocol family
+.SH SYNOPSIS
+.B #include <sys/types.h>
+.br
+.B #include <netatalk/at.h>
+.SH DESCRIPTION
+The AppleTalk protocol family is a collection of protocols layered
+above the Datagram Delivery Protocol (DDP), and using AppleTalk address
+format. The AppleTalk family may provide SOCK_STREAM (ADSP), SOCK_DGRAM
+(DDP), SOCK_RDM (ATP), and SOCK_SEQPACKET (ASP). Currently, only DDP is
+implemented in the kernel; ATP and ASP are implemented in user level
+libraries; and ADSP is planned.
+.SH ADDRESSING
+AppleTalk addresses are three byte quantities, stored in network
+byte order. The include file
+.RB < netatalk/at.h >
+defines the AppleTalk address format.
+.LP
+Sockets in the AppleTalk protocol family use the following address
+structure:
+.sp 1
+.RS
+.nf
+struct sockaddr_at {
+ short sat_family;
+ u_char sat_port;
+ struct at_addr sat_addr;
+ char sat_zero[ 8 ];
+};
+.fi
+.RE
+.sp 1
+The port of a socket may be set with
+.BR bind (2).
+The node for
+.B bind
+must always be
+.BR ATADDR_ANYNODE :
+``this node.'' The net may be
+.B ATADDR_ANYNET
+or
+.BR ATADDR_LATENET .
+.B ATADDR_ANYNET
+coresponds to the machine's ``primary'' address (the first
+configured).
+.B ATADDR_LATENET
+causes the address in outgoing packets to be determined when a packet
+is sent, i.e. determined late.
+.B ATADDR_LATENET
+is equivalent to opening one socket for each network interface. The
+port of a socket and either the primary address or
+.B ATADDR_LATENET
+are returned with
+.BR getsockname (2).
+.SH SEE ALSO
+.\" .BR ddp (4P),
+.\" .BR atp (3N),
+.\" .BR asp (3N),
+.BR bind (2),
+.BR getsockname (2),
+.BR atalkd (8).
--- /dev/null
+SRC= afpd.8 atalkd.8 papd.8 psf.8
+SRCTMP= afpd.8.tmp atalkd.8.tmp papd.8.tmp psf.8.tmp
+
+INCPATH=
+CFLAGS=
+TAGSFILE=
+CC=
+INSTALL= install
+
+LINKS=
+
+all : ${SRCTMP}
+
+${SRCTMP}: ../../Makefile
+ for i in ${SRC} ; do \
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < $$i > $$i.tmp; \
+ done
+
+install : ${SRCTMP}
+ -mkdir ${MANDIR}/man8
+ for i in ${SRC} ; do \
+ rm -f ${MANDIR}/man8/$$i; \
+ ${INSTALL} -m644 $$i.tmp ${MANDIR}/man8/$$i; \
+ done
+
+clean :
+ for i in ${SRC}; do \
+ rm -f $$i.tmp; \
+ done
+
+tags : ${SRC}
+
+depend :
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+.TH AFPD 8 "23 Feb 1999" "netatalk 1.4b2/asun 2.1.3"
+
+.SH NAME
+afpd \- AppleTalk Filing Protocol daemon
+.SH SYNOPSIS
+.B :SBINDIR:/afpd
+[
+.B -d
+]
+[
+.B -f
+.I defaultvolumes
+]
+[
+.B -s
+.I systemvolumes
+]
+[
+.B -u
+]
+[
+.B -n
+.I nbpname
+]
+[
+.B -c
+.I maxconnections
+]
+[
+.B -g
+.I guest
+]
+[
+.B -G
+]
+[
+.B -K
+]
+[
+.B -C
+]
+[
+.B -A
+]
+.SH DESCRIPTION
+.B afpd
+provides an AppleTalk Filing Protocol (AFP)
+interface to the Unix file system. It is normally started at boot time
+from
+.BR /etc/rc .
+The list of volumes offered to the user is generated from
+.B :ETCDIR:/AppleVolumes.system
+and one of
+.BR :ETCDIR:/AppleVolumes.default ,
+.BR $HOME/AppleVolumes ,
+or
+.BR $HOME/.AppleVolumes .
+.LP
+The
+.B AppleVolumes
+files is used to specify volumes to mount and file name extension mappings.
+It is formatted as follows, one specification per line:
+.RS
+.sp
+.I pathname
+[
+.I volumename
+]
+.br
+.RI . extension
+[
+.I type
+[
+.I creator
+]
+]
+.sp
+.RE
+If
+.I volumename
+is unspecified, the last component of
+.I pathname
+is used. No two volumes may have the same name. If
+.I type
+is unspecified
+.RB ' ???? '
+is used. If
+.I creator
+is unspecified
+.RB ' UNIX '
+is used. The extension
+.RB ' . '
+sets the default creator and type for otherwise untyped Unix files.
+Blank lines and lines beginning with `#' are ignored.
+.SH OPTIONS
+.TP
+.B \-d
+Specifies that the daemon not fork, and that a trace of all AFP
+commands be written to stdout.
+.TP
+.BI \-f " defaultvolumes"
+Specifies that
+.I defaultvolumes
+should be read for a list of default volumes to offer, instead of
+.BR :ETCDIR:/AppleVolumes.default .
+.TP
+.BI \-s " systemvolumes"
+Specifies that
+.I systemvolumes
+should be read for a list of volume that all users will be offered,
+instead of
+.BR :ETCDIR:/AppleVolumes.system .
+.TP
+.B \-u
+Read the user's
+.B AppleVolumes
+file first. This option causes volume names in the user's
+.B AppleVolumes
+file to override volume names in the system's
+.B AppleVolumes
+file. The default is to read the system
+.B AppleVolumes
+file first. Note that this option doesn't effect the precendence of
+filename extension mappings: the user's AppleVolumes file always has
+precedence.
+.TP
+.BI \-n " nbpname"
+Specifies that
+.I nbpname
+should be used for NBP registration, instead of the first component of
+the hostname in the local zone.
+.TP
+.BI \-c " maxconnections"
+Specifies the maximum number of connections to allow for this
+.BR afpd .
+The default is 5.
+.TP
+.BI \-g " guest"
+Specifies the name of the guest account. The default is ``nobody''.
+.TP
+.B \-G
+.TP
+.B \-K
+.TP
+.B \-C
+.TP
+.B \-A
+Causes the server to not offer
+.BR NoUserAuthent ,
+.BR "Kerberos IV" ,
+.BR "Cleartxt Passwrd" ,
+and
+.B AFS Kerberos
+logins, respectively. The default is to enable all available login methods.
+.SH AUTHENTICATION
+.B afpd
+currently understands three User Authentication Methods (UAMs):
+.BR NoUserAuthent ,
+or guest,
+.B Cleartxt
+.BR passwrd ,
+and
+.B Kerberos
+.BR IV .
+If a user uses
+.BR NoUserAuthent ,
+s/he will only be offered default volumes to mount, and will only be able
+to read and write files that are permitted to the guest user. The
+.B -G
+option disables
+.BR NoUserAuthent .
+With
+.B Cleartxt passwd
+and
+.B Kerberos
+.BR IV ,
+.B afpd
+offers the user all volumes listed in
+.BR $HOME/AppleVolumes .
+The user may also read and write all files that s/he normally could.
+.B Cleartxt passwd
+is not recommended for AFS use.
+.B Kerberos IV
+is recommended for AFS use.
+A forth, depricated UAM is also included in the distribution,
+.B AFS
+.BR Kerberos .
+.SH CAVEATS
+.BR afpd 's
+Directory IDs are only fixed for the duration of a session. This means
+that Mac aliases won't work correctly in all cases.
+.LP
+If a user renames a folder that has an application as its progeny, the
+.B APPL
+mapping for the application will not longer be available. This implies
+that double-clicking on one of the application's documents will no
+longer launch the application. The
+.B APPL
+mapping will be rebuilt by the mac, the next time the Finder see the
+application.
+.LP
+If
+.B afpd
+is configured to downcase Macintosh filenames, Unix filenames with
+mixed case will be unavailable.
+.LP
+If carriage return/line feed translation is enabled, it is not
+safe to copy Unix binaries to a Macintosh.
+.LP
+It is not possible to move directories between devices.
+.LP
+When mounting the parent of an existing volume, the desktop database of
+the existing volume will not be available to the parent volume. The
+.B APPL
+mappings and icons of applications with the
+.B BNDL
+bit set will be generated in the parent volume as the applications are
+seen by the Finder.
+.LP
+If a user edits his
+.B $HOME/AppleVolumes
+so that his home directory is no longer offered, he will no longer be able
+to edit his
+.B $HOME/AppleVolumes
+from the Macintosh.
+.LP
+Unix files beginning with `.' are not accessible from the mac.
+.LP
+If the
+.I pathname
+in an
+.B $HOME/AppleVolumes
+file does not exist, the volume will not be offered in the Chooser.
+.LP
+Microsoft Word
+.B TEXT
+documents do not get carriage return/line feed translation. This is
+because MS Word uses a type other than
+.B TEXT
+while writing the document, then changes the type to
+.BR TEXT .
+To allow users to edit their
+.BR $HOME/AppleVolumes ,
+.B afpd
+parses the files with either end of line character.
+.LP
+Unix filenames that are longer than 31 characters are inaccessible from
+the Macintosh.
+.SH FILES
+.TP 20
+.B :ETCDIR:/AppleVolumes.default
+list of default volumes to mount
+.TP 20
+.B :ETCDIR:/AppleVolumes.system
+list of volumes to offer all users
+.TP 20
+.B $HOME/AppleVolumes
+user's list of volumes to mount
+.SH BUGS
+A few calls from the AFP specification are not implemented, because the
+Macintosh does not use them.
--- /dev/null
+.TH ATALKD 8 "17 Nov 1995" "netatalk 1.3"
+.SH NAME
+atalkd \- AppleTalk RTMP, NBP, ZIP, and AEP manager
+.SH SYNOPSIS
+.B :SBINDIR:/atalkd
+[
+.B -f
+.I configfile
+] [
+.B -1
+|
+.B -2
+]
+.SH DESCRIPTION
+.B atalkd
+is responsible for all user level AppleTalk network management. This
+includes routing, name registration and lookup, zone lookup, and the
+AppleTalk Echo Protocol (similar to
+.BR ping (8)).
+.B atalkd
+is typically started at boot time, out of
+.B /etc/rc.
+It first reads from its configuration file,
+.BR :ETCDIR:/atalkd.conf .
+If there is no configuration file,
+.B atalkd
+will attempt to configure all available interfaces and will create a
+configuration file. The file consists of a series of interfaces, one
+per line. Lines with
+.RB ` # '
+in the first column are ignored, as are blank lines. The syntax is
+.RS
+.sp
+.I interface
+[
+.B -seed
+] [
+.B -phase
+.I number
+] [
+.B -net
+.I net-range
+] [
+.B -addr
+.I address
+] [
+.B -zone
+.I zonename
+] ...
+.sp
+.RE
+Note that all field except the
+.I interface
+are optional. The loopback interface is configured automatically. If
+.B -seed
+is specified, all other fields must be present. Also,
+.B atalkd
+will exit during bootstrapping, if a router disagrees with its seed
+information. If
+.B -seed
+is not given, all other information may be overriden during
+auto-configuration. If no
+.B -phase
+option is given, the default phase as given on the command line is used
+(the default is 2). If
+.B -addr
+is given and
+.B -net
+is not, a
+.I net-range
+of one is assumed.
+.LP
+The first
+.B -zone
+directive for each interface is the ``default'' zone. Under Phase 1, there
+is only one zone. Under Phase 2, all routers on the network are
+configured with the default zone and must agree.
+.B atalkd
+maps ``*'' to the default zone of the first interface. Note: The
+default zone for a machine is determined by the configuration of the
+local routers; to appear in a non-default zone, each service, e.g.
+.BR afpd ,
+must individually specify the desired zone. See also
+.BR nbp_name (3).
+.SH ROUTING
+If you are connecting a netatalk router to an existing AppleTalk
+internet, you should first contact your local network administrators to
+obtain appropriate network addresses.
+.LP
+.B atalkd
+can provide routing between interfaces by configuring multiple
+interfaces. Each interface must be assigned a unique
+.I net-range
+between 1 and 65279 (0 and 65535 are illegal, and addresses between
+65280 and 65534 are reserved for startup). It is best to choose the
+smallest useful
+.IR net-range ,
+i.e. if you have three machines on an Ethernet, don't chose a
+.I net-range
+of 1000-2000. Each
+.I net-range
+may have an arbitrary list of zones associated with it.
+.SH EXAMPLE
+Below is an example configuration file for a sun4/40. The machine has
+two interfaces, ``le0'' and ``le1''. The ``le0'' interface is
+configured automatically from other routers on the network. The
+machine is the only router for the ``le1'' interface.
+.sp
+.RS
+.nf
+le0
+le1 -seed -net 9461-9471 -zone netatalk -zone Argus
+.fi
+.RE
+.sp
+.B atalkd
+automatically acts as a router if there is more than one interface.
+.SH FILES
+.TP 30
+.B :ETCDIR:/atalkd.conf
+configuration file
+.SH BUGS
+On some systems, atalkd can not be restarted.
--- /dev/null
+.TH PAP 8 "13 Dec 1991" "netatalk 1.2"
+.SH NAME
+.B pap
+\- download files to or communicate interactively with an
+AppleTalk network connected printer
+.SH SYNOPSIS
+.B :SBINDIR:/pap
+[
+.BI -d
+] [
+.B -p
+.I printer
+] [
+.B -s
+.I statusfile
+] [
+.I files
+]
+.SH DESCRIPTION
+.B pap
+is used to connect and send files to an AppleTalk connected printer using
+the Apple Printer Access Protocol (PAP).
+.B pap
+can also be used to conduct an interactive session with a PostScript
+printer. When
+.B pap
+starts execution, it first tries obtain the status of the printer. It
+then tries to open a session with the printer using PAP, and
+then downloads the
+.I files
+to the printer.
+.LP
+If no
+.I files
+are given on the command line,
+.B pap
+begins reading from standard input.
+.LP
+If no printer is specified on the command line,
+.B pap
+looks for a file called
+.B .paprc
+in the current directory and reads it to obtain the name of a printer. The
+.B .paprc
+file should contain a single line of the form
+.IB object : \c
+.IB type @ \c
+.I zone
+where each of
+.IR object ,
+.BI : type ,
+and
+.BI @ zone
+are optional.
+.I type
+and
+.I zone
+must be proceeded by
+.RB ` : '
+and
+.RB ` @ '
+respectively. Lines the begin with a
+.RB ` # '
+are ignored.
+.I type
+and
+.I zone
+default to
+.B LaserWriter
+and the zone of the local host, respectively.
+.LP
+Note that
+.B pap
+is designed to be useful as a communication filter for sending
+.BR lpd (8)
+spooled print jobs to AppleTalk connected printers. See
+.BR psf (8)
+for hints on how to use it this way.
+.SH OPTIONS
+.HP
+.B -p
+.I printer
+.br
+Connect to the printer named
+.I printer
+(do not consult the
+.B .paprc
+file to find a printer name). The syntax for
+.I printer
+is the same as discussed above for the
+.B .paprc
+file.
+.HP
+.B -s
+.I statusfile
+.br
+Update the file called
+.I statusfile
+to contain the most recent status message from the printer.
+.B pap
+gets the status from the printer when it is waiting for the printer to
+process input. The
+.I statusfile
+will contain a single line terminated with a newline. This is useful
+when
+.B pap
+is invoked by
+.BR lpd (8)
+within
+.BR lpd 's
+spool directory.
+.HP
+.B -c
+.br
+Take cuts. The PAP protocol specified a simple queuing procedure, such
+that the clients tell the printer how long they've been waiting to
+print. This option causes
+.B pap
+to lie about how long it's been waiting.
+.HP
+.B -e
+.br
+Send stdout to stderr. This causes information that the printer
+returns to be recorded as error output for lpd.
+.HP
+.B -E
+.br
+Don't wait for EOF from the printer. This option is useful for
+printers which don't implement PAP correctly. In a correct
+implementation, the client side should wait for the printer to return
+EOF before closing the connection. Some clients don't wait, and hence
+some printers have related bugs in their implementation.
+.HP
+.B -w
+.br
+Wait for the printer's status to contain the word "waiting" before
+sending the job. This is to defeat printer-side spool available on HP
+IV and V printers.
+.SH FILES
+.TP 20
+.B .paprc
+file that contains printer name
+.TP 20
+.B $HOME/.paprc
+secondary file to look in for printer name
+.SH SEE ALSO
+.BR nbp (1),
+.BR pap (4),
+.BR lpd (8),
+.BR papstatus (8),
+.BR psf (8).
+.SH BUGS
+.B pap
+will send a
+.B quit
+command to exit interactive mode when it gets an end-of-file
+on a tty. If the user has already typed
+.B quit
+themselves, the
+.B quit
+that
+.B pap
+sends is spurious and will cause a PostScript error. The fix would be for
+.B pap
+to watch what the user types and look for quit, but this is impractical.
--- /dev/null
+'\" t
+.TH PAPD 8 "17 Apr 1995" "netatalk 1.3.3"
+.SH NAME
+papd \- AppleTalk print server daemon
+.SH SYNOPSIS
+.B :SBINDIR:/papd
+[
+.B -d
+] [
+.B -f
+.I configfile
+] [
+.B -p
+.I printcap
+]
+.SH DESCRIPTION
+.B papd
+is the AppleTalk printer daemon. This daemon accepts print jobs from
+AppleTalk clients (typically Macintosh computers) using the Printer
+Access Protocol (PAP).
+.B papd
+spools jobs directly into an
+.BR lpd (8)
+spool directory and wakes up
+.B lpd
+after accepting a job from the network to have it re-examine the
+appropriate spool directory. The actual printing and spooling is
+handled entirely by
+.B lpd.
+.LP
+.B papd
+is typically started at boot time, out of
+.B /etc/rc.
+It first reads from its configuration file,
+.BR :ETCDIR:/papd.conf .
+The file is in the same format as
+.BR /etc/printcap .
+See
+.BR printcap (5)
+for details. The name of the entry is registered with
+.BR NBP .
+There are currently three supported capabilities.
+.LP
+.TS
+c c l l
+cfB l l l .
+Name Type Default Descripton
+.sp .5
+pd str ``.ppd'' Pathname to PPD file
+pr str ``lp'' LPD printer name (or pipe to print command)
+op str ``operator'' Operator name for LPD spooling
+.TE
+.LP
+If no configuration file is given, the hostname of the machine is used
+as the NBP name and all options take their default value.
+.SH OPTIONS
+.TP
+.B -d
+Do not fork or disassociate from the terminal. Write some
+debugging information to stderr.
+.HP
+.B -f
+.I configfile
+.br
+Consult
+.I configfile
+instead of
+.B :ETCDIR:/papd.conf
+for the configuration information.
+.HP
+.B -p
+.I printcap
+.br
+Consult
+.I printcap
+instead of
+.B /etc/printcap
+for LPD configuration information.
+.SH EXAMPLE
+The following papd configuration file sets up two print spoolers for the
+.B lpd
+printers named
+.B ps
+and
+.BR lp .
+The first spooler is known by the NBP name
+.B Mac Printer Spooler,
+and uses a PPD file located in
+.B /usr/share/lib/ppd.
+In addition, the user
+.B mcs
+will be the owner of all jobs that are spooled.
+The second spooler is known as
+.B HP Printer
+and all options are the default. The third spooler is known as
+.B Other Printer
+which prints with lpr.
+.sp
+.RS
+.nf
+Mac Printer Spooler:\\
+ :pr=ps:pd=/usr/share/lib/ppd/HPLJ_4M.PPD:op=mcs:
+
+HP Printer:\\
+ :
+
+Other Printer:\\
+ :pr=|/usr/bin/lpr -P Otherprinter -h:
+.fi
+.RE
+.SH FILES
+.TP 16
+.B :ETCDIR:/papd.conf
+Default configuration file.
+.TP 16
+.B /etc/printcap
+Printer capabilities database.
+.TP 16
+.B .ppd
+PostScript Printer Description file.
+.B papd
+answers configuration and font queries from printing clients by
+consulting the configured PPD file. Such files are available from
+Adobe, Inc, via anonymous ftp from ftp.adobe.com in /pub/adobe/PPDfiles
+(ftp://ftp.adobe.com/pub/adobe/PPDfiles), or from the printer's
+manufacturer. If no PPD file is configured,
+.B papd
+will return the default answer, possibly causing the client to send
+excessively large jobs.
+.SH SEE ALSO
+.BR lpr (1),
+.BR lprm (1),
+\." .BR pap (4),
+.BR printcap (5).
+.BR lpc (8),
+.BR lpd (8).
+.SH CAVEATS
+.I papd
+accepts characters with the high bit set (a full 8-bits) from the clients,
+but some PostScript printers (including Apple Computer's LaserWriter family)
+only accept 7-bit characters on their serial interface by default. You will
+need to configure your printer to accept a full 8 bits.
--- /dev/null
+.TH PAPSTATUS 8 "17 Dec 1991"
+.SH NAME
+papstatus \- get the status of an AppleTalk-connected printer
+.SH SYNOPSIS
+.B :SBINDIR:/papstatus
+[
+.BI -d
+] [
+.B -p
+.I printer
+] [
+.I retrytime
+]
+.SH DESCRIPTION
+.B papstatus
+is used to obtain the current status message from an AppleTalk connected
+printer. It uses the Printer Access Protocol (PAP) to obtain the
+status information.
+.LP
+If no printer is specified on the command line,
+.B papstatus
+looks for a file called
+.B .paprc
+in the current directory and reads it to obtain the name of a printer. The
+.B .paprc
+file should contain a single line of the form
+.IB object : \c
+.IB type @ \c
+.I zone
+where each of
+.IR object ,
+.BI : type ,
+and
+.BI @ zone
+are optional.
+.I type
+and
+.I zone
+must be proceeded by
+.RB ` : '
+and
+.RB ` @ '
+respectively. Blank lines and lines the begin with a
+.RB ` # '
+are ignored.
+.I type
+and
+.I zone
+default to
+.B LaserWriter
+and the zone of the local host, respectively.
+.SH OPTIONS
+.TP
+.B -d
+Turns on a debugging mode that prints some extra information to standard error.
+.HP
+.B -p
+.I printer
+.br
+Get status from
+.I printer
+(do not consult any
+.B .paprc
+files to find a printer name). The syntax for
+.I printer
+is the same as discussed above for the
+.B .paprc
+file.
+.TP
+.I retrytime
+Normally,
+.B papstatus
+only gets the status from the printer once. If
+.I retrytime
+is specified, the status is obtained repeatedly, with a sleep of
+.I retrytime
+seconds between inquiring the printer.
+.SH FILES
+.TP 20
+.B .paprc
+file that contains printer name
+.SH SEE ALSO
+.BR nbp (1),
+.BR pap (8)
--- /dev/null
+.TH PSF 8 "17 Dec 1991" "netatalk 1.2"
+.SH NAME
+psf \- PostScript filter
+.SH SYNOPSIS
+.B psf
+[
+.B -n
+.I name
+] [
+.B -h
+.I host
+] [
+.B -w
+.I width
+] [
+.B -l
+.I length
+] [
+.B -i
+.I indent
+] [
+.B -c
+]
+.SH DESCRIPTION
+.B psf
+is an
+.B lpd
+filter for PostScript printing.
+.B psf
+interprets the name it was called with to determine what filters to
+invoke. First, if the string ``pap'' appears anywhere in the name,
+.B psf
+invokes
+.B pap
+to talk to a printer via AppleTalk. Next, if the string ``rev'' appears,
+.B psf
+invokes
+.B psorder
+to reverse the pages of the job. Finally, if
+.B psf
+was called with a filter's name as the leading string, it invokes that
+filter. If there is no filter to run,
+.B psf
+examines the magic number of the input, and if the input is not
+PostScript, converts it to PostScript.
+.SH KLUDGE
+In the default configuration,
+.B psf
+supports two kludges. The first causes
+.B psf
+to check its name for the letter `m'. If this letter is found and accounting
+is turned on,
+.B psf
+calls
+.B pap
+twice, once to get an initial page count and to print the job, and
+another time to get a final page count. This is a work-around for bugs
+in a variety of PAP implementions that cause printers to never properly
+close the PAP output file. A notable example is any printer by
+Hewlett-Packard.
+.LP
+The second kludge causes
+.B psf
+to examine its name for the letter `w'. If this letter is found and
+accounting is turned on,
+.B psf
+calls
+.B pap
+with the
+.B \-w
+flag. This flag causes
+.B pap
+to wait until the printer's status contains the string `idle'. Once
+this string is found, the job is printed as normal. This kludge is a
+work-around for printers, notably Hewlett-Packard's LaserJet IV, which
+will report a page count while a previous jobs is still printing.
+.SH EXAMPLE
+The sample
+.B printcap
+entry below invokes
+.B psf
+to print text files, PostScript files,
+.BR troff 's
+C/A/T output, and
+.BR TeX 's
+DVI output, to an AppleTalk connected LaserWriter Plus. Since the
+LaserWriter Plus stacks pages in descending order, we reverse the pages
+and print the burst page last.
+.sp
+.RS
+.nf
+laser|lp|LaserWriter Plus on AppleTalk:\\
+ :sd=/usr/spool/lpd/laser:\\
+ :lp=/usr/spool/lpd/laser/null:\\
+ :lf=/var/adm/lpd-errs:pw#80:hl:\\
+ :of=:LIBDIR:/filters/ofpap:\\
+ :if=:LIBDIR:/filters/ifpaprev:\\
+ :tf=:LIBDIR:/filters/tfpaprev:\\
+ :df=:LIBDIR:/filters/dfpaprev:
+.fi
+.RE
+.sp
+Note that if the host in question spools to more than one AppleTalk
+printer,
+.B /dev/null
+should not be used for the
+.B lp
+capability. Instead, a null device should be created with
+.B mknod
+for each printer, as has been done above.
+.LP
+Finally, there is a file in the spool directory,
+.BR /var/spool/lpd/laser ,
+called
+.BR .paprc ,
+which
+.B pap
+reads for the AppleTalk name of the printer.
+.SH SEE ALSO
+.BR psorder (1),
+.BR printcap (5),
+.BR lpd (8),
+.BR mknod (8),
+.BR pap (8).
--- /dev/null
+rtmp 1/ddp # Routing Table Maintenance Protocol
+nbp 2/ddp # Name Binding Protocol
+echo 4/ddp # AppleTalk Echo Protocol
+zip 6/ddp # Zone Information Protocol
+
+afpovertcp 548/tcp # AFP over TCP
+afpovertcp 548/udp
--- /dev/null
+# FreeBSD specific defines, passed to subdirectories.
+DEFS= -DBSD4_4 $$OSDEFS
+OPTOPTS= -O2
+CC= gcc
+CSHAREDFLAGS=-fPIC
+LDSHARED=ld
+LDSHAREDFLAGS=-shared
+LDFLAGS_EXPORT=-export-dynamic
+#LIBSHARED=-ldl
+INSTALL= install
+AFPLIBS=
+ADDLIBS=
+
+ALL= ../../libatalk ../../include ../../bin ../../etc ../../man
+
+oops:
+ @echo "Read README again. Don't type 'make' here."
+ @exit 1
+
+all: ${ALL}
+
+../../bin ../../etc: ../../libatalk
+
+${ALL}: FRC
+ @case ${OSVERSION} in \
+ 1.*) ;; \
+ 2.*) ;; \
+ *) OSDEFS=-DSENDFILE_FLAVOR_BSD ;; \
+ esac; \
+ cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+ AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+ LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+ LIBSHARED="${LIBSHARED}" \
+ all
+
+FRC:
+
+install :
+ -mkdir ${DESTDIR} ${SBINDIR} ${BINDIR} ${ETCDIR} ${LIBDIR}
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" AFPLIBS="${AFPLIBS}" \
+ LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+ CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+ INSTALL="${INSTALL}" $@); \
+ done
+ rm -f ${ETCDIR}/rc.atalk
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../distrib/initscripts/rc.atalk.bsd > ${ETCDIR}/rc.atalk
+ if [ -f ${ETCDIR}/afpd.conf ]; then \
+ echo "Retaining old afpd.conf file."; \
+ else \
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../config/afpd.conf > ${ETCDIR}/afpd.conf; \
+ fi
+ @echo
+ @echo "Install is done. Don't forget to add lines from"
+ @echo "services.atalk to /etc/services and to call rc.atalk"
+ @echo "in /etc/rc. See README and README.FREEBSD for more"
+ @echo "information."
+
+clean :
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+ done
+
+depend :
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" depend); \
+ done
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+# generic system. here are some useful defines.
+# system lookalikes: -DBSD4_4 -D__svr4__ -DMACOSX_SERVER
+# statfs/quota support: -DNO_QUOTA_SUPPORT -DUSE_QUOTA_H -DUSE_UFS_QUOTA_H
+# -DHAVE_2ARG_DBTOB -DHAVE_DQB_BTIMELIMIT
+# -DUSE_OLD_RQUOTA (really old rquota support)
+# -DUSE_VFS_H -DUSE_STATFS_H -DUSE_STATVFS_H
+# -DUSE_MNTTAB_H -DUSE_MNTENT_H -DUSE_MOUNT_H
+# integer sizes: -D_ISOC9X_SOURCE -DHAVE_64BIT_LONGS -DHAVE_32BIT_LONGS
+# other 64-bit issues: -DTRY_64BITOFF_T (for byte locks)
+# afp/ddp: -DNO_DDP
+# to get sys/cdefs.h: -I../../sys/generic
+# misc: -DUSE_GETHOSTID -DNEED_GETUSERSHELL -DHAVE_BROKEN_CPP
+# -DNEED_RQUOTA -DNO_STRUCT_TM_GMTOFF
+# -DHAVE_IFNAMEINDEX
+#
+# plug-in uam support needs to access the run-time library
+# loader. here are the relevant options for that:
+# -DDLSYM_PREPEND_UNDERSCORE
+# -DNO_DLFCN_H (you'll need to make libatalk/util/module.c do the right thing)
+#
+# here's some flags appropriate for gnu gcc.
+# CSHAREDFLAGS=-fPIC
+# LDSHARED=gcc
+# LDSHAREDFLAGS=-shared
+# LDFLAGS_EXPORT=-rdynamic
+# LIBSHARED=-ldl
+#
+# the following combinations are not guaranteed to do anything, but
+# you might have luck with them. make sure that you comment out
+# TCPWRAPDIR, PAMDIR, or DESDIR/CRYPTODIR if you don't have the
+# respective parts installed.
+#
+# digital unix:
+# -DHAVE_64BIT_LONGS -DNO_DDP -DUSE_OLD_RQUOTA -DUSE_MOUNT_H \
+# -DUSE_UFS_QUOTA_H -I../../sys/generic
+# LIBSHARED=-ldl
+# LDSHAREDFLAGS=-shared -expect_unresolved '*' -s
+# uncomment the two touch lines in libatalk/Makefile to get around a
+# problem with ar. use gcc.
+#
+# i think this will work for machten, but i'm not sure:
+# -DBSD4_4 -DNO_QUOTA_SUPPORT -DNO_DDP
+#
+# sgi irix:
+# -DUSE_MNTENT_H -DHAVE_DQB_BTIMELIMIT -DUSE_QUOTA_H -DNO_DDP \
+# -DNEED_GETUSERSHELL -DUSE_STATVFS_H -DUSE_OLD_RQUOTA -DNO_STRUCT_TM_GMTOFF
+# change RANLIB in libatalk/Makefile from 'ranlib' to 'echo'
+#
+# aix:
+# -DHAVE_32BIT_LONGS -DHAVE_IFNAMEINDEX -DNO_DDP -DUSE_OLD_RQUOTA \
+# -I../../sys/generic
+# ADDLIBS=-lbsd
+# use cc.
+#
+DEFS=-DHAVE_64BIT_LONGS -DNO_DDP -DUSE_OLD_RQUOTA -DUSE_MOUNT_H \
+ -DUSE_UFS_QUOTA_H -I../../sys/generic
+OPTOPTS= -O2 -fomit-frame-pointer
+#CC = cc
+CC= gcc
+CSHAREDFLAGS=-fPIC
+LDSHARED=gcc
+LDSHAREDFLAGS=-Wl,-E
+INSTALL= install
+AFPLIBS=
+#ADDLIBS=-lbsd
+LIBSHARED=-ldl
+
+# some oses have -lcrypt but shouldn't use it. uncomment the following
+# line if that's the case.
+#USE_CRYPTLIB=no
+
+ALL= ../../libatalk ../../include ../../bin ../../etc ../../man
+
+oops:
+ @echo "Read README again. Don't type 'make' here."
+ @exit 1
+
+all: ${ALL}
+
+../../bin ../../etc: ../../libatalk
+
+${ALL}: FRC
+ cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+ AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+ LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+ LIBSHARED="${LIBSHARED}" USE_CRYPTLIB="${USE_CRYPTLIB}" \
+ all
+
+FRC:
+
+install :
+ -mkdir ${DESTDIR} ${SBINDIR} ${BINDIR} ${ETCDIR} ${LIBDIR}
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" AFPLIBS="${AFPLIBS}" \
+ LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+ CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+ INSTALL="${INSTALL}" $@); \
+ done
+ rm -f ${ETCDIR}/rc.atalk
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../distrib/scripts/rc.atalk.bsd > ${ETCDIR}/rc.atalk
+ chmod 744 ${ETCDIR}/rc.atalk
+ if [ -f ${ETCDIR}/afpd.conf ]; then \
+ echo "Retaining old afpd.conf file."; \
+ else \
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../config/afpd.conf > ${ETCDIR}/afpd.conf; \
+ fi
+ @echo
+ @echo "Install is done. Don't forget to add lines from"
+ @echo "services.atalk to /etc/services and to call rc.atalk"
+ @echo "in /etc/rc. See README and README.GENERIC for more"
+ @echo "information."
+
+clean :
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+ done
+
+depend :
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" depend); \
+ done
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+#ifndef _SYS_CDEFS_H
+#define _SYS_CDEFS_H 1
+
+#ifdef __STDC__
+#define __P(args) args
+#else
+#define __P(args) ()
+#endif
+
+#endif /* sys/cdefs.h */
--- /dev/null
+# Linux specific defines, passed to subdirectories.
+#DEFS= -DTRY_64BITOFF_T -DNO_STRUCT_TM_GMTOFF -DHAVE_IFNAMEINDEX
+DEFS=$$OSDEFS $$MACHINEDEFS $$QUOTADEF
+OPTOPTS=-O2 -fomit-frame-pointer -fsigned-char -Wunused -Wuninitialized
+#OPTOPTS= -g -fsigned-char
+CC= gcc
+CSHAREDFLAGS=-fPIC
+LDSHARED=ld
+LDSHAREDFLAGS=-shared
+LDFLAGS_EXPORT=-rdynamic
+LIBSHARED=-ldl
+
+AFPLIBS=
+ADDLIBS=
+
+INSTALL= install
+
+ALL= ../../libatalk ../../include ../../bin ../../etc ../../man
+
+oops:
+ @echo "Read README again. Don't type 'make' here."
+ @exit 1
+
+all: ${ALL}
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../distrib/initscripts/rc.atalk.redhat > atalk
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../config/afpd.conf > afpd.conf
+
+../../bin ../../etc: ../../libatalk
+
+${ALL}: FRC
+ @case ${OSVERSION} in \
+ 1.0*) ;; \
+ 2.0*) ;; \
+ *) OSDEFS=-DSENDFILE_FLAVOR_LINUX ;; \
+ esac; \
+ if [ x"${MACHINETYPE}" = x"alpha" ]; then \
+ MACHINEDEFS=-DHAVE_GCC_MEMCPY_BUG; \
+ fi; \
+ if [ ! -f /usr/include/sys/quota.h ]; then \
+ QUOTADEF=-DNEED_QUOTACTL_WRAPPER; \
+ fi; \
+ cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+ AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+ LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+ LIBSHARED="${LIBSHARED}" \
+ all
+
+FRC:
+
+${ETCDIR}:
+ -mkdir -p ${ETCDIR}
+
+install-sysv:
+ if [ -d ${INSTALL_PREFIX}/etc/rc.d/init.d ]; then \
+ ${INSTALL} -m744 atalk ${INSTALL_PREFIX}/etc/rc.d/init.d/atalk; \
+ rm -f ${INSTALL_PREFIX}/etc/rc.d/init.d/atalk.init \
+ ${INSTALL_PREFIX}/etc/rc.d/rc?.d/[SK]??atalk; \
+ fi; \
+ if [ x"${INSTALL_PREFIX}" = x ]; then \
+ /sbin/chkconfig --add atalk; \
+ fi; \
+ if [ -f ${ETCDIR}/netatalk.conf ]; then \
+ echo "Retaining old netatalk.conf file."; \
+ else \
+ ${INSTALL} -m644 ../../config/netatalk.conf ${ETCDIR}; \
+ fi
+
+install-bsd:
+ -if [ -f ${ETCDIR}/rc.atalk ]; then \
+ echo "Retaining old rc.atalk file."; \
+ else if [ ! -d /etc/rc.d/init.d ]; then \
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../distrib/initscripts/rc.atalk.bsd > ${ETCDIR}/rc.atalk; \
+ fi \
+ fi
+
+install : ${ETCDIR} install-sysv install-bsd
+ -mkdir ${DESTDIR} ${SBINDIR} ${BINDIR} ${LIBDIR}
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" AFPLIBS="${AFPLIBS}" \
+ LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+ CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+ INSTALL="${INSTALL}" $@); \
+ done
+ if [ -d ${INSTALL_PREFIX}/etc/pam.d -a \
+ ! -f ${INSTALL_PREFIX}/etc/pam.d/netatalk ]; then \
+ ${INSTALL} -m644 ../../config/netatalk.pamd \
+ ${INSTALL_PREFIX}/etc/pam.d/netatalk; \
+ echo "PAM netatalk file installed."; \
+ fi
+ if [ -f ${ETCDIR}/afpd.conf ]; then \
+ echo "Retaining old afpd.conf file."; \
+ else \
+ ${INSTALL} -m644 afpd.conf ${ETCDIR}/afpd.conf; \
+ fi
+ @echo
+ @echo "Install is done. Don't forget to add lines from"
+ @echo "services.atalk to /etc/services."
+ if [ ! -d /etc/rc.d/init.d ]; then \
+ echo "Don't forget to call rc.atalk in /etc/rc."; \
+ fi
+ @echo "See README and README.LINUX for more information."
+
+
+clean :
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+ done
+ -rm -f atalk afpd.conf
+
+depend :
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" depend); \
+ done
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/time.h>
+#ifndef _IBMR2
+#include <sys/kernel.h>
+#endif _IBMR2
+#include <net/if.h>
+#include <net/route.h>
+#include <net/af.h>
+#include <netinet/in.h>
+#undef s_net
+#include <netinet/if_ether.h>
+#ifdef _IBMR2
+#include <netinet/in_netarp.h>
+#include <net/spl.h>
+#include <sys/errno.h>
+#include <sys/err_rec.h>
+#endif _IBMR2
+
+#include "at.h"
+#include "at_var.h"
+#include "aarp.h"
+#include "ddp_var.h"
+#include "endian.h"
+#include "phase2.h"
+
+#ifdef GATEWAY
+#define AARPTAB_BSIZ 16
+#define AARPTAB_NB 37
+#else
+#define AARPTAB_BSIZ 9
+#define AARPTAB_NB 19
+#endif GATEWAY
+#define AARPTAB_SIZE (AARPTAB_BSIZ * AARPTAB_NB)
+struct aarptab aarptab[AARPTAB_SIZE];
+int aarptab_size = AARPTAB_SIZE;
+
+#define AARPTAB_HASH(a) \
+ ((((a).s_net << 8 ) + (a).s_node ) % AARPTAB_NB )
+
+#define AARPTAB_LOOK(aat,addr) { \
+ int n; \
+ aat = &aarptab[ AARPTAB_HASH(addr) * AARPTAB_BSIZ ]; \
+ for ( n = 0; n < AARPTAB_BSIZ; n++, aat++ ) \
+ if ( aat->aat_ataddr.s_net == (addr).s_net && \
+ aat->aat_ataddr.s_node == (addr).s_node ) \
+ break; \
+ if ( n >= AARPTAB_BSIZ ) \
+ aat = 0; \
+}
+
+#define AARPT_AGE (60 * 1)
+#define AARPT_KILLC 20
+#define AARPT_KILLI 3
+
+#ifdef sun
+extern struct ether_addr etherbroadcastaddr;
+#else sun
+extern u_char etherbroadcastaddr[6];
+#endif sun
+
+u_char atmulticastaddr[ 6 ] = {
+ 0x09, 0x00, 0x07, 0xff, 0xff, 0xff,
+};
+
+u_char at_org_code[ 3 ] = {
+ 0x08, 0x00, 0x07,
+};
+u_char aarp_org_code[ 3 ] = {
+ 0x00, 0x00, 0x00,
+};
+
+aarptimer()
+{
+ struct aarptab *aat;
+ int i, s;
+
+ timeout( aarptimer, (caddr_t)0, AARPT_AGE * hz );
+ aat = aarptab;
+ for ( i = 0; i < AARPTAB_SIZE; i++, aat++ ) {
+ if ( aat->aat_flags == 0 || ( aat->aat_flags & ATF_PERM ))
+ continue;
+ if ( ++aat->aat_timer < (( aat->aat_flags & ATF_COM ) ?
+ AARPT_KILLC : AARPT_KILLI ))
+ continue;
+ s = splimp();
+ aarptfree( aat );
+ splx( s );
+ }
+}
+
+struct ifaddr *
+at_ifawithnet( sat, ifa )
+ struct sockaddr_at *sat;
+ struct ifaddr *ifa;
+{
+ struct at_ifaddr *aa;
+
+ for (; ifa; ifa = ifa->ifa_next ) {
+#ifdef BSD4_4
+ if ( ifa->ifa_addr->sa_family != AF_APPLETALK ) {
+ continue;
+ }
+ if ( satosat( ifa->ifa_addr )->sat_addr.s_net ==
+ sat->sat_addr.s_net ) {
+ break;
+ }
+#else BSD4_4
+ if ( ifa->ifa_addr.sa_family != AF_APPLETALK ) {
+ continue;
+ }
+ aa = (struct at_ifaddr *)ifa;
+ if ( ntohs( sat->sat_addr.s_net ) >= ntohs( aa->aa_firstnet ) &&
+ ntohs( sat->sat_addr.s_net ) <= ntohs( aa->aa_lastnet )) {
+ break;
+ }
+#endif BSD4_4
+ }
+ return( ifa );
+}
+
+aarpwhohas( ac, sat )
+ struct arpcom *ac;
+ struct sockaddr_at *sat;
+{
+ struct mbuf *m;
+ struct ether_header *eh;
+ struct ether_aarp *ea;
+ struct at_ifaddr *aa;
+ struct llc *llc;
+ struct sockaddr sa;
+
+#ifdef BSD4_4
+ if (( m = m_gethdr( M_DONTWAIT, MT_DATA )) == NULL ) {
+ return;
+ }
+ m->m_len = sizeof( *ea );
+ m->m_pkthdr.len = sizeof( *ea );
+ MH_ALIGN( m, sizeof( *ea ));
+#else BSD4_4
+ if (( m = m_get( M_DONTWAIT, MT_DATA )) == NULL ) {
+ return;
+ }
+ m->m_len = sizeof( *ea );
+ m->m_off = MMAXOFF - sizeof( *ea );
+#endif BSD4_4
+
+ ea = mtod( m, struct ether_aarp *);
+ bzero((caddr_t)ea, sizeof( *ea ));
+
+ ea->aarp_hrd = htons( AARPHRD_ETHER );
+ ea->aarp_pro = htons( ETHERTYPE_AT );
+ ea->aarp_hln = sizeof( ea->aarp_sha );
+ ea->aarp_pln = sizeof( ea->aarp_spu );
+ ea->aarp_op = htons( AARPOP_REQUEST );
+#ifdef sun
+ bcopy((caddr_t)&ac->ac_enaddr, (caddr_t)ea->aarp_sha,
+ sizeof( ea->aarp_sha ));
+#else sun
+ bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->aarp_sha,
+ sizeof( ea->aarp_sha ));
+#endif sun
+
+ /*
+ * We need to check whether the output ethernet type should
+ * be phase 1 or 2. We have the interface that we'll be sending
+ * the aarp out. We need to find an AppleTalk network on that
+ * interface with the same address as we're looking for. If the
+ * net is phase 2, generate an 802.2 and SNAP header.
+ */
+ if (( aa = (struct at_ifaddr *)at_ifawithnet( sat, ac->ac_if.if_addrlist ))
+ == NULL ) {
+ m_freem( m );
+ return;
+ }
+
+ eh = (struct ether_header *)sa.sa_data;
+
+ if ( aa->aa_flags & AFA_PHASE2 ) {
+#ifdef sun
+ bcopy((caddr_t)atmulticastaddr, (caddr_t)&eh->ether_dhost,
+ sizeof( eh->ether_dhost ));
+#else sun
+ bcopy((caddr_t)atmulticastaddr, (caddr_t)eh->ether_dhost,
+ sizeof( eh->ether_dhost ));
+#endif sun
+#if defined( sun ) && defined( i386 )
+ eh->ether_type = htons( sizeof( struct llc ) +
+ sizeof( struct ether_aarp ));
+#else sun i386
+ eh->ether_type = sizeof( struct llc ) + sizeof( struct ether_aarp );
+#endif sun i386
+#ifdef BSD4_4
+ M_PREPEND( m, sizeof( struct llc ), M_WAIT );
+#else BSD4_4
+ m->m_len += sizeof( struct llc );
+ m->m_off -= sizeof( struct llc );
+#endif BSD4_4
+ llc = mtod( m, struct llc *);
+ llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+ llc->llc_control = LLC_UI;
+ bcopy( aarp_org_code, llc->llc_org_code, sizeof( aarp_org_code ));
+ llc->llc_ether_type = htons( ETHERTYPE_AARP );
+
+
+ bcopy( &AA_SAT( aa )->sat_addr.s_net, ea->aarp_spnet,
+ sizeof( ea->aarp_spnet ));
+ ea->aarp_spnode = AA_SAT( aa )->sat_addr.s_node;
+ bcopy( &sat->sat_addr.s_net, ea->aarp_tpnet,
+ sizeof( ea->aarp_tpnet ));
+ ea->aarp_tpnode = sat->sat_addr.s_node;
+ } else {
+#ifdef sun
+ bcopy((caddr_t)ðerbroadcastaddr, (caddr_t)&eh->ether_dhost,
+ sizeof( eh->ether_dhost ));
+#else sun
+ bcopy((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost,
+ sizeof( eh->ether_dhost ));
+#endif sun
+#if defined( sun ) && defined( i386 )
+ eh->ether_type = htons( ETHERTYPE_AARP );
+#else sun i386
+ eh->ether_type = ETHERTYPE_AARP;
+#endif sun i386
+
+ ea->aarp_spa = AA_SAT( aa )->sat_addr.s_node;
+ ea->aarp_tpa = sat->sat_addr.s_node;
+ }
+
+#ifdef BSD4_4
+ sa.sa_len = sizeof( struct sockaddr );
+#endif BSD4_4
+ sa.sa_family = AF_UNSPEC;
+ (*ac->ac_if.if_output)(&ac->ac_if, m, &sa );
+}
+
+aarpresolve( ac, m, destsat, desten )
+ struct arpcom *ac;
+ struct mbuf *m;
+ struct sockaddr_at *destsat;
+#ifdef sun
+ struct ether_addr *desten;
+#else sun
+ u_char *desten;
+#endif sun
+{
+ struct at_ifaddr *aa;
+ struct ifaddr ifa;
+ struct aarptab *aat;
+ int s;
+
+ if ( at_broadcast( destsat )) {
+ if (( aa = (struct at_ifaddr *)at_ifawithnet( destsat,
+ ((struct ifnet *)ac)->if_addrlist )) == NULL ) {
+ m_freem( m );
+ return( 0 );
+ }
+ if ( aa->aa_flags & AFA_PHASE2 ) {
+ bcopy( (caddr_t)atmulticastaddr, (caddr_t)desten,
+ sizeof( atmulticastaddr ));
+ } else {
+#ifdef sun
+ bcopy( (caddr_t)ðerbroadcastaddr, (caddr_t)desten,
+ sizeof( etherbroadcastaddr ));
+#else sun
+ bcopy( (caddr_t)etherbroadcastaddr, (caddr_t)desten,
+ sizeof( etherbroadcastaddr ));
+#endif sun
+ }
+ return( 1 );
+ }
+
+ s = splimp();
+ AARPTAB_LOOK( aat, destsat->sat_addr );
+ if ( aat == 0 ) { /* No entry */
+ aat = aarptnew( &destsat->sat_addr );
+ if ( aat == 0 ) {
+ panic( "aarpresolve: no free entry" );
+ }
+ aat->aat_hold = m;
+ aarpwhohas( ac, destsat );
+ splx( s );
+ return( 0 );
+ }
+ /* found an entry */
+ aat->aat_timer = 0;
+ if ( aat->aat_flags & ATF_COM ) { /* entry is COMplete */
+ bcopy( (caddr_t)aat->aat_enaddr, (caddr_t)desten,
+ sizeof( aat->aat_enaddr ));
+ splx( s );
+ return( 1 );
+ }
+ /* entry has not completed */
+ if ( aat->aat_hold ) {
+ m_freem( aat->aat_hold );
+ }
+ aat->aat_hold = m;
+ aarpwhohas( ac, destsat );
+ splx( s );
+ return( 0 );
+}
+
+aarpinput( ac, m )
+ struct arpcom *ac;
+ struct mbuf *m;
+{
+ struct arphdr *ar;
+
+ if ( ac->ac_if.if_flags & IFF_NOARP )
+ goto out;
+
+#ifndef BSD4_4
+ IF_ADJ( m );
+#endif BSD4_4
+
+ if ( m->m_len < sizeof( struct arphdr )) {
+ goto out;
+ }
+
+ ar = mtod( m, struct arphdr *);
+ if ( ntohs( ar->ar_hrd ) != AARPHRD_ETHER ) {
+ goto out;
+ }
+
+ if ( m->m_len < sizeof( struct arphdr ) + 2 * ar->ar_hln +
+ 2 * ar->ar_pln ) {
+ goto out;
+ }
+
+ switch( ntohs( ar->ar_pro )) {
+ case ETHERTYPE_AT :
+ at_aarpinput( ac, m );
+ return;
+
+ default:
+ break;
+ }
+
+out:
+ m_freem( m );
+}
+
+
+at_aarpinput( ac, m )
+ struct arpcom *ac;
+ struct mbuf *m;
+{
+ struct mbuf *m0;
+ struct ether_aarp *ea;
+ struct at_ifaddr *aa;
+ struct aarptab *aat;
+ struct ether_header *eh;
+ struct llc *llc;
+ struct sockaddr_at sat;
+ struct sockaddr sa;
+ struct at_addr spa, tpa, ma;
+ int op, s;
+ u_short net;
+
+ ea = mtod( m, struct ether_aarp *);
+
+ /* Check to see if from my hardware address */
+#ifdef sun
+ if ( !bcmp(( caddr_t )ea->aarp_sha, ( caddr_t )&ac->ac_enaddr,
+ sizeof( ac->ac_enaddr ))) {
+ m_freem( m );
+ return;
+ }
+#else sun
+ if ( !bcmp(( caddr_t )ea->aarp_sha, ( caddr_t )ac->ac_enaddr,
+ sizeof( ac->ac_enaddr ))) {
+ m_freem( m );
+ return;
+ }
+#endif sun
+
+ /*
+ * Check if from broadcast address. This could be a more robust
+ * check, since we could look for multicasts.
+ */
+#ifdef sun
+ if ( !bcmp(( caddr_t )ea->aarp_sha, ( caddr_t )ðerbroadcastaddr,
+ sizeof( etherbroadcastaddr ))) {
+ log( LOG_ERR, "aarp: source is broadcast!\n" );
+ m_freem( m );
+ return;
+ }
+#else sun
+ if ( !bcmp(( caddr_t )ea->aarp_sha, ( caddr_t )etherbroadcastaddr,
+ sizeof( etherbroadcastaddr ))) {
+#ifndef _IBMR2
+#ifdef ultrix
+ mprintf( LOG_ERR,
+#else ultrix
+ log( LOG_ERR,
+#endif ultrix
+ "aarp: source is broadcast!\n" );
+#endif _IBMR2
+ m_freem( m );
+ return;
+ }
+#endif sun
+
+ op = ntohs( ea->aarp_op );
+ bcopy( ea->aarp_tpnet, &net, sizeof( net ));
+
+ if ( net != 0 ) {
+ sat.sat_family = AF_APPLETALK;
+ sat.sat_addr.s_net = net;
+ if (( aa = (struct at_ifaddr *)at_ifawithnet( &sat,
+ ac->ac_if.if_addrlist )) == NULL ) {
+ m_freem( m );
+ return;
+ }
+ bcopy( ea->aarp_spnet, &spa.s_net, sizeof( spa.s_net ));
+ bcopy( ea->aarp_tpnet, &tpa.s_net, sizeof( tpa.s_net ));
+ } else {
+ /*
+ * Since we don't know the net, we just look for the first
+ * phase 1 address on the interface.
+ */
+ for ( aa = (struct at_ifaddr *)ac->ac_if.if_addrlist; aa;
+ aa = (struct at_ifaddr *)aa->aa_ifa.ifa_next ) {
+ if ( AA_SAT( aa )->sat_family == AF_APPLETALK &&
+ ( aa->aa_flags & AFA_PHASE2 ) == 0 ) {
+ break;
+ }
+ }
+ if ( aa == NULL ) {
+ m_freem( m );
+ return;
+ }
+ tpa.s_net = spa.s_net = AA_SAT( aa )->sat_addr.s_net;
+ }
+
+ spa.s_node = ea->aarp_spnode;
+ tpa.s_node = ea->aarp_tpnode;
+ ma.s_net = AA_SAT( aa )->sat_addr.s_net;
+ ma.s_node = AA_SAT( aa )->sat_addr.s_node;
+
+ /*
+ * This looks like it's from us.
+ */
+ if ( spa.s_net == ma.s_net && spa.s_node == ma.s_node ) {
+ if ( aa->aa_flags & AFA_PROBING ) {
+ /*
+ * We're probing, someone either responded to our probe, or
+ * probed for the same address we'd like to use. Change the
+ * address we're probing for.
+ */
+ untimeout( aarpprobe, ac );
+ wakeup( aa );
+ m_freem( m );
+ return;
+ } else if ( op != AARPOP_PROBE ) {
+ /*
+ * This is not a probe, and we're not probing. This means
+ * that someone's saying they have the same source address
+ * as the one we're using. Get upset...
+ */
+#ifndef _IBMR2
+#ifdef ultrix
+ mprintf( LOG_ERR,
+#else ultrix
+ log( LOG_ERR,
+#endif ultrix
+ "aarp: duplicate AT address!! %x:%x:%x:%x:%x:%x\n",
+ ea->aarp_sha[ 0 ], ea->aarp_sha[ 1 ], ea->aarp_sha[ 2 ],
+ ea->aarp_sha[ 3 ], ea->aarp_sha[ 4 ], ea->aarp_sha[ 5 ]);
+#endif _IBMR2
+ m_freem( m );
+ return;
+ }
+ }
+
+ AARPTAB_LOOK( aat, spa );
+ if ( aat ) {
+ if ( op == AARPOP_PROBE ) {
+ /*
+ * Someone's probing for spa, dealocate the one we've got,
+ * so that if the prober keeps the address, we'll be able
+ * to arp for him.
+ */
+ aarptfree( aat );
+ m_freem( m );
+ return;
+ }
+
+ bcopy(( caddr_t )ea->aarp_sha, ( caddr_t )aat->aat_enaddr,
+ sizeof( ea->aarp_sha ));
+ aat->aat_flags |= ATF_COM;
+ if ( aat->aat_hold ) {
+#ifdef _IBMR2
+ /*
+ * Like in ddp_output(), we can't rely on the if_output
+ * routine to resolve AF_APPLETALK addresses, on the rs6k.
+ * So, we fill the destination ethernet address here.
+ *
+ * This should really be replaced with something like
+ * rsif_output(). XXX Will have to be for phase 2.
+ */
+ /* XXX maybe fill in the rest of the frame header */
+ sat.sat_family = AF_UNSPEC;
+ bcopy( aat->aat_enaddr, (*(struct sockaddr *)&sat).sa_data,
+ sizeof( aat->aat_enaddr ));
+#else _IBMR2
+ sat.sat_family = AF_APPLETALK;
+ sat.sat_addr = spa;
+#endif _IBMR2
+ (*ac->ac_if.if_output)( &ac->ac_if, aat->aat_hold,
+ (struct sockaddr *)&sat );
+ aat->aat_hold = 0;
+ }
+ }
+
+ if ( aat == 0 && tpa.s_net == ma.s_net && tpa.s_node == ma.s_node
+ && op != AARPOP_PROBE ) {
+ if ( aat = aarptnew( &spa )) {
+ bcopy(( caddr_t )ea->aarp_sha, ( caddr_t )aat->aat_enaddr,
+ sizeof( ea->aarp_sha ));
+ aat->aat_flags |= ATF_COM;
+ }
+ }
+
+ /*
+ * Don't respond to responses, and never respond if we're
+ * still probing.
+ */
+ if ( tpa.s_net != ma.s_net || tpa.s_node != ma.s_node ||
+ op == AARPOP_RESPONSE || ( aa->aa_flags & AFA_PROBING )) {
+ m_freem( m );
+ return;
+ }
+
+ bcopy(( caddr_t )ea->aarp_sha, ( caddr_t )ea->aarp_tha,
+ sizeof( ea->aarp_sha ));
+#ifdef sun
+ bcopy(( caddr_t )&ac->ac_enaddr, ( caddr_t )ea->aarp_sha,
+ sizeof( ea->aarp_sha ));
+#else sun
+ bcopy(( caddr_t )ac->ac_enaddr, ( caddr_t )ea->aarp_sha,
+ sizeof( ea->aarp_sha ));
+#endif sun
+
+ eh = (struct ether_header *)sa.sa_data;
+#ifdef sun
+ bcopy(( caddr_t )ea->aarp_tha, ( caddr_t )&eh->ether_dhost,
+ sizeof( eh->ether_dhost ));
+#else sun
+ bcopy(( caddr_t )ea->aarp_tha, ( caddr_t )eh->ether_dhost,
+ sizeof( eh->ether_dhost ));
+#endif sun
+
+ if ( aa->aa_flags & AFA_PHASE2 ) {
+#if defined( sun ) && defined( i386 )
+ eh->ether_type = htons( sizeof( struct llc ) +
+ sizeof( struct ether_aarp ));
+#else sun i386
+ eh->ether_type = sizeof( struct llc ) + sizeof( struct ether_aarp );
+#endif sun i386
+#ifdef BSD4_4
+ M_PREPEND( m, sizeof( struct llc ), M_DONTWAIT );
+ if ( m == NULL ) {
+ m_freem( m );
+ return;
+ }
+#else BSD4_4
+ MGET( m0, M_DONTWAIT, MT_HEADER );
+ if ( m0 == NULL ) {
+ m_freem( m );
+ return;
+ }
+ m0->m_next = m;
+ m = m0;
+ m->m_off = MMAXOFF - sizeof( struct llc );
+ m->m_len = sizeof ( struct llc );
+#endif BSD4_4
+ llc = mtod( m, struct llc *);
+ llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+ llc->llc_control = LLC_UI;
+ bcopy( aarp_org_code, llc->llc_org_code, sizeof( aarp_org_code ));
+ llc->llc_ether_type = htons( ETHERTYPE_AARP );
+
+ bcopy( ea->aarp_spnet, ea->aarp_tpnet, sizeof( ea->aarp_tpnet ));
+ bcopy( &ma.s_net, ea->aarp_spnet, sizeof( ea->aarp_spnet ));
+ } else {
+#if defined( sun ) && defined( i386 )
+ eh->ether_type = htons( ETHERTYPE_AARP );
+#else sun i386
+ eh->ether_type = ETHERTYPE_AARP;
+#endif sun i386
+ }
+
+ ea->aarp_tpnode = ea->aarp_spnode;
+ ea->aarp_spnode = ma.s_node;
+ ea->aarp_op = htons( AARPOP_RESPONSE );
+
+#ifdef BSD4_4
+ sa.sa_len = sizeof( struct sockaddr );
+#endif BSD4_4
+ sa.sa_family = AF_UNSPEC;
+ (*ac->ac_if.if_output)( &ac->ac_if, m, &sa );
+ return;
+}
+
+aarptfree( aat )
+ struct aarptab *aat;
+{
+
+ if ( aat->aat_hold )
+ m_freem( aat->aat_hold );
+ aat->aat_hold = 0;
+ aat->aat_timer = aat->aat_flags = 0;
+ aat->aat_ataddr.s_net = 0;
+ aat->aat_ataddr.s_node = 0;
+}
+
+ struct aarptab *
+aarptnew( addr )
+ struct at_addr *addr;
+{
+ int n;
+ int oldest = -1;
+ struct aarptab *aat, *aato = NULL;
+ static int first = 1;
+
+ if ( first ) {
+ first = 0;
+ timeout( aarptimer, (caddr_t)0, hz );
+ }
+ aat = &aarptab[ AARPTAB_HASH( *addr ) * AARPTAB_BSIZ ];
+ for ( n = 0; n < AARPTAB_BSIZ; n++, aat++ ) {
+ if ( aat->aat_flags == 0 )
+ goto out;
+ if ( aat->aat_flags & ATF_PERM )
+ continue;
+ if ((int) aat->aat_timer > oldest ) {
+ oldest = aat->aat_timer;
+ aato = aat;
+ }
+ }
+ if ( aato == NULL )
+ return( NULL );
+ aat = aato;
+ aarptfree( aat );
+out:
+ aat->aat_ataddr = *addr;
+ aat->aat_flags = ATF_INUSE;
+ return( aat );
+}
+
+aarpprobe( ac )
+ struct arpcom *ac;
+{
+ struct mbuf *m;
+ struct ether_header *eh;
+ struct ether_aarp *ea;
+ struct at_ifaddr *aa;
+ struct llc *llc;
+ struct sockaddr sa;
+
+ /*
+ * We need to check whether the output ethernet type should
+ * be phase 1 or 2. We have the interface that we'll be sending
+ * the aarp out. We need to find an AppleTalk network on that
+ * interface with the same address as we're looking for. If the
+ * net is phase 2, generate an 802.2 and SNAP header.
+ */
+ for ( aa = (struct at_ifaddr *)ac->ac_if.if_addrlist; aa;
+ aa = (struct at_ifaddr *)aa->aa_ifa.ifa_next ) {
+ if ( AA_SAT( aa )->sat_family == AF_APPLETALK &&
+ ( aa->aa_flags & AFA_PROBING )) {
+ break;
+ }
+ }
+ if ( aa == NULL ) { /* serious error XXX */
+ printf( "aarpprobe why did this happen?!\n" );
+ return;
+ }
+
+ if ( aa->aa_probcnt <= 0 ) {
+ aa->aa_flags &= ~AFA_PROBING;
+ wakeup( aa );
+ return;
+ } else {
+ timeout( aarpprobe, (caddr_t)ac, hz / 5 );
+ }
+
+#ifdef BSD4_4
+ if (( m = m_gethdr( M_DONTWAIT, MT_DATA )) == NULL ) {
+ return;
+ }
+ m->m_len = sizeof( *ea );
+ m->m_pkthdr.len = sizeof( *ea );
+ MH_ALIGN( m, sizeof( *ea ));
+#else BSD4_4
+ if (( m = m_get( M_DONTWAIT, MT_DATA )) == NULL ) {
+ return;
+ }
+ m->m_len = sizeof( *ea );
+ m->m_off = MMAXOFF - sizeof( *ea );
+#endif BSD4_4
+
+ ea = mtod( m, struct ether_aarp *);
+ bzero((caddr_t)ea, sizeof( *ea ));
+
+ ea->aarp_hrd = htons( AARPHRD_ETHER );
+ ea->aarp_pro = htons( ETHERTYPE_AT );
+ ea->aarp_hln = sizeof( ea->aarp_sha );
+ ea->aarp_pln = sizeof( ea->aarp_spu );
+ ea->aarp_op = htons( AARPOP_PROBE );
+#ifdef sun
+ bcopy((caddr_t)&ac->ac_enaddr, (caddr_t)ea->aarp_sha,
+ sizeof( ea->aarp_sha ));
+#else sun
+ bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->aarp_sha,
+ sizeof( ea->aarp_sha ));
+#endif sun
+
+ eh = (struct ether_header *)sa.sa_data;
+
+ if ( aa->aa_flags & AFA_PHASE2 ) {
+#ifdef sun
+ bcopy((caddr_t)atmulticastaddr, (caddr_t)&eh->ether_dhost,
+ sizeof( eh->ether_dhost ));
+#else sun
+ bcopy((caddr_t)atmulticastaddr, (caddr_t)eh->ether_dhost,
+ sizeof( eh->ether_dhost ));
+#endif sun
+#if defined( sun ) && defined( i386 )
+ eh->ether_type = htons( sizeof( struct llc ) +
+ sizeof( struct ether_aarp ));
+#else sun i386
+ eh->ether_type = sizeof( struct llc ) + sizeof( struct ether_aarp );
+#endif sun i386
+#ifdef BSD4_4
+ M_PREPEND( m, sizeof( struct llc ), M_WAIT );
+#else BSD4_4
+ m->m_len += sizeof( struct llc );
+ m->m_off -= sizeof( struct llc );
+#endif BSD4_4
+ llc = mtod( m, struct llc *);
+ llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+ llc->llc_control = LLC_UI;
+ bcopy( aarp_org_code, llc->llc_org_code, sizeof( aarp_org_code ));
+ llc->llc_ether_type = htons( ETHERTYPE_AARP );
+
+ bcopy( &AA_SAT( aa )->sat_addr.s_net, ea->aarp_spnet,
+ sizeof( ea->aarp_spnet ));
+ bcopy( &AA_SAT( aa )->sat_addr.s_net, ea->aarp_tpnet,
+ sizeof( ea->aarp_tpnet ));
+ ea->aarp_spnode = ea->aarp_tpnode = AA_SAT( aa )->sat_addr.s_node;
+ } else {
+#ifdef sun
+ bcopy((caddr_t)ðerbroadcastaddr, (caddr_t)&eh->ether_dhost,
+ sizeof( eh->ether_dhost ));
+#else sun
+ bcopy((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost,
+ sizeof( eh->ether_dhost ));
+#endif sun
+#if defined( sun ) && defined( i386 )
+ eh->ether_type = htons( ETHERTYPE_AARP );
+#else sun i386
+ eh->ether_type = ETHERTYPE_AARP;
+#endif sun i386
+ ea->aarp_spa = ea->aarp_tpa = AA_SAT( aa )->sat_addr.s_node;
+ }
+
+#ifdef BSD4_4
+ sa.sa_len = sizeof( struct sockaddr );
+#endif BSD4_4
+ sa.sa_family = AF_UNSPEC;
+ (*ac->ac_if.if_output)(&ac->ac_if, m, &sa );
+ aa->aa_probcnt--;
+}
+
+aarp_clean()
+{
+ struct aarptab *aat;
+ int i;
+
+ untimeout( aarptimer, 0 );
+ for ( i = 0, aat = aarptab; i < AARPTAB_SIZE; i++, aat++ ) {
+ if ( aat->aat_hold ) {
+ m_freem( aat->aat_hold );
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ */
+
+/*
+ * This structure is used for both phase 1 and 2. Under phase 1
+ * the net is not filled in. It is in phase 2. In both cases, the
+ * hardware address length is (for some unknown reason) 4. If
+ * anyone at Apple could program their way out of paper bag, it
+ * would be 1 and 3 respectively for phase 1 and 2.
+ */
+union aapa {
+ u_char ap_pa[4];
+ struct ap_node {
+ u_char an_zero;
+ u_char an_net[2];
+ u_char an_node;
+ } ap_node;
+};
+
+struct ether_aarp {
+ struct arphdr eaa_hdr;
+ u_char aarp_sha[6];
+ union aapa aarp_spu;
+ u_char aarp_tha[6];
+ union aapa aarp_tpu;
+};
+#define aarp_hrd eaa_hdr.ar_hrd
+#define aarp_pro eaa_hdr.ar_pro
+#define aarp_hln eaa_hdr.ar_hln
+#define aarp_pln eaa_hdr.ar_pln
+#define aarp_op eaa_hdr.ar_op
+#define aarp_spa aarp_spu.ap_node.an_node
+#define aarp_tpa aarp_tpu.ap_node.an_node
+#define aarp_spnet aarp_spu.ap_node.an_net
+#define aarp_tpnet aarp_tpu.ap_node.an_net
+#define aarp_spnode aarp_spu.ap_node.an_node
+#define aarp_tpnode aarp_tpu.ap_node.an_node
+
+struct aarptab {
+ struct at_addr aat_ataddr;
+ u_char aat_enaddr[ 6 ];
+ u_char aat_timer;
+ u_char aat_flags;
+ struct mbuf *aat_hold;
+};
+
+#define AARPHRD_ETHER 0x0001
+
+#define AARPOP_REQUEST 0x01
+#define AARPOP_RESPONSE 0x02
+#define AARPOP_PROBE 0x03
+
+#ifdef KERNEL
+struct aarptab *aarptnew();
+int aarpprobe();
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ *
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef __AT_HEADER__
+#define __AT_HEADER__
+
+#ifdef linux /* pull in the linux header */
+#include <sys/socket.h>
+#include <asm/types.h>
+#include <linux/atalk.h>
+#else
+
+#include <sys/types.h>
+#include <netinet/in.h> /* so that we can deal with sun's s_net #define */
+
+/*
+ * Supported protocols
+ */
+#ifdef ATPROTO_DDP
+#undef ATPROTO_DDP
+#endif
+#define ATPROTO_DDP 0
+#define ATPROTO_AARP 254
+
+/*
+ * Ethernet types, for DIX.
+ * These should really be in some global header file, but we can't
+ * count on them being there, and it's annoying to patch system files.
+ */
+#define ETHERTYPE_AT 0x809B /* AppleTalk protocol */
+#define ETHERTYPE_AARP 0x80F3 /* AppleTalk ARP */
+
+#define DDP_MAXSZ 587
+
+/*
+ * If ATPORT_FIRST <= Port < ATPORT_RESERVED,
+ * Port was created by a privileged process.
+ * If ATPORT_RESERVED <= Port < ATPORT_LAST,
+ * Port was not necessarily created by a
+ * privileged process.
+ */
+#define ATPORT_FIRST 1
+#define ATPORT_RESERVED 128
+#define ATPORT_LAST 254 /* 254 is reserved on ether/tokentalk networks */
+
+/*
+ * AppleTalk address.
+ */
+struct at_addr {
+#ifdef s_net
+#undef s_net
+#endif
+ u_short s_net;
+ u_char s_node;
+};
+
+#define ATADDR_ANYNET (u_short)0x0000
+#define ATADDR_ANYNODE (u_char)0x00
+#define ATADDR_ANYPORT (u_char)0x00
+#define ATADDR_BCAST (u_char)0xff /* There is no BCAST for NET */
+
+/*
+ * Socket address, AppleTalk style. We keep magic information in the
+ * zero bytes. There are three types, NONE, CONFIG which has the phase
+ * and a net range, and IFACE which has the network address of an
+ * interface. IFACE may be filled in by the client, and is filled in
+ * by the kernel.
+ */
+struct sockaddr_at {
+#ifdef BSD4_4
+ u_char sat_len;
+ u_char sat_family;
+#else BSD4_4
+ short sat_family;
+#endif BSD4_4
+ u_char sat_port;
+ struct at_addr sat_addr;
+#ifdef notdef
+ struct {
+ u_char sh_type;
+# define SATHINT_NONE 0
+# define SATHINT_CONFIG 1
+# define SATHINT_IFACE 2
+ union {
+ char su_zero[ 7 ]; /* XXX check size */
+ struct {
+ u_char sr_phase;
+ u_short sr_firstnet, sr_lastnet;
+ } su_range;
+ u_short su_interface;
+ } sh_un;
+ } sat_hints;
+#else notdef
+ char sat_zero[ 8 ];
+#endif notdef
+};
+
+struct netrange {
+ u_char nr_phase;
+ u_short nr_firstnet;
+ u_short nr_lastnet;
+};
+
+#ifdef KERNEL
+extern struct domain atalkdomain;
+extern struct protosw atalksw[];
+#endif
+
+#endif /* linux */
+#endif __AT_HEADER__
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#ifdef ibm032
+#include <sys/dir.h>
+#endif ibm032
+#include <sys/user.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/mbuf.h>
+#ifndef _IBMR2
+#include <sys/kernel.h>
+#endif _IBMR2
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <net/if.h>
+#include <net/af.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#undef s_net
+#include <netinet/if_ether.h>
+#ifdef _IBMR2
+#include <net/spl.h>
+#endif _IBMR2
+
+#include "at.h"
+#include "at_var.h"
+#include "aarp.h"
+#include "phase2.h"
+
+#ifdef BSD4_4
+# define sateqaddr(a,b) ((a)->sat_len == (b)->sat_len && \
+ (a)->sat_family == (b)->sat_family && \
+ (a)->sat_addr.s_net == (b)->sat_addr.s_net && \
+ (a)->sat_addr.s_node == (b)->sat_addr.s_node )
+#else BSD4_4
+atalk_hash( sat, hp )
+ struct sockaddr_at *sat;
+ struct afhash *hp;
+{
+ hp->afh_nethash = sat->sat_addr.s_net;
+ hp->afh_hosthash = ( sat->sat_addr.s_net << 8 ) +
+ sat->sat_addr.s_node;
+}
+
+/*
+ * Note the magic to get ifa_ifwithnet() to work without adding an
+ * ifaddr entry for each net in our local range.
+ */
+atalk_netmatch( sat1, sat2 )
+ struct sockaddr_at *sat1, *sat2;
+{
+ struct at_ifaddr *aa;
+
+ for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+ if ( AA_SAT( aa ) == sat1 ) {
+ break;
+ }
+ }
+ if ( aa ) {
+ return( ntohs( aa->aa_firstnet ) <= ntohs( sat2->sat_addr.s_net ) &&
+ ntohs( aa->aa_lastnet ) >= ntohs( sat2->sat_addr.s_net ));
+ }
+ return( sat1->sat_addr.s_net == sat2->sat_addr.s_net );
+}
+#endif BSD4_4
+
+at_control( cmd, data, ifp )
+ int cmd;
+ caddr_t data;
+ struct ifnet *ifp;
+{
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct sockaddr_at *sat;
+ struct netrange *nr;
+#ifdef BSD4_4
+ struct at_aliasreq *ifra = (struct at_aliasreq *)data;
+ struct at_ifaddr *aa0;
+#endif BSD4_4
+ struct at_ifaddr *aa = 0;
+ struct mbuf *m;
+ struct ifaddr *ifa;
+
+ if ( ifp ) {
+ for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+ if ( aa->aa_ifp == ifp ) break;
+ }
+ }
+
+ switch ( cmd ) {
+#ifdef BSD4_4
+ case SIOCAIFADDR:
+ case SIOCDIFADDR:
+ if ( ifra->ifra_addr.sat_family == AF_APPLETALK ) {
+ for ( ; aa; aa = aa->aa_next ) {
+ if ( aa->aa_ifp == ifp &&
+ sateqaddr( &aa->aa_addr, &ifra->ifra_addr )) {
+ break;
+ }
+ }
+ }
+ if ( cmd == SIOCDIFADDR && aa == 0 ) {
+ return( EADDRNOTAVAIL );
+ }
+ /*FALLTHROUGH*/
+#endif BSD4_4
+
+ case SIOCSIFADDR:
+#ifdef BSD4_4
+ /*
+ * What a great idea this is: Let's reverse the meaning of
+ * the return...
+ */
+ if ( suser( u.u_cred, &u.u_acflag )) {
+ return( EPERM );
+ }
+#else BSD4_4
+ if ( !suser()) {
+ return( EPERM );
+ }
+#endif BSD4_4
+
+ sat = satosat( &ifr->ifr_addr );
+ nr = (struct netrange *)sat->sat_zero;
+ if ( nr->nr_phase == 1 ) {
+ for ( ; aa; aa = aa->aa_next ) {
+ if ( aa->aa_ifp == ifp &&
+ ( aa->aa_flags & AFA_PHASE2 ) == 0 ) {
+ break;
+ }
+ }
+ } else { /* default to phase 2 */
+ for ( ; aa; aa = aa->aa_next ) {
+ if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 )) {
+ break;
+ }
+ }
+ }
+
+ if ( ifp == 0 )
+ panic( "at_control" );
+
+ if ( aa == (struct at_ifaddr *) 0 ) {
+ m = m_getclr( M_WAIT, MT_IFADDR );
+ if ( m == (struct mbuf *)NULL ) {
+ return( ENOBUFS );
+ }
+
+ if (( aa = at_ifaddr ) != NULL ) {
+ /*
+ * Don't let the loopback be first, since the first
+ * address is the machine's default address for
+ * binding.
+ */
+ if ( at_ifaddr->aa_ifp->if_flags & IFF_LOOPBACK ) {
+ aa = mtod( m, struct at_ifaddr *);
+ aa->aa_next = at_ifaddr;
+ at_ifaddr = aa;
+ } else {
+ for ( ; aa->aa_next; aa = aa->aa_next )
+ ;
+ aa->aa_next = mtod( m, struct at_ifaddr *);
+ }
+ } else {
+ at_ifaddr = mtod( m, struct at_ifaddr *);
+ }
+
+ aa = mtod( m, struct at_ifaddr *);
+
+ if (( ifa = ifp->if_addrlist ) != NULL ) {
+ for ( ; ifa->ifa_next; ifa = ifa->ifa_next )
+ ;
+ ifa->ifa_next = (struct ifaddr *)aa;
+ } else {
+ ifp->if_addrlist = (struct ifaddr *)aa;
+ }
+
+#ifdef BSD4_4
+ aa->aa_ifa.ifa_addr = (struct sockaddr *)&aa->aa_addr;
+ aa->aa_ifa.ifa_dstaddr = (struct sockaddr *)&aa->aa_addr;
+ aa->aa_ifa.ifa_netmask = (struct sockaddr *)&aa->aa_netmask;
+#endif BSD4_4
+
+ /*
+ * Set/clear the phase 2 bit.
+ */
+ if ( nr->nr_phase == 1 ) {
+ aa->aa_flags &= ~AFA_PHASE2;
+ } else {
+ aa->aa_flags |= AFA_PHASE2;
+ }
+ aa->aa_ifp = ifp;
+ } else {
+ at_scrub( ifp, aa );
+ }
+ break;
+
+ case SIOCGIFADDR :
+ sat = satosat( &ifr->ifr_addr );
+ nr = (struct netrange *)sat->sat_zero;
+ if ( nr->nr_phase == 1 ) {
+ for ( ; aa; aa = aa->aa_next ) {
+ if ( aa->aa_ifp == ifp &&
+ ( aa->aa_flags & AFA_PHASE2 ) == 0 ) {
+ break;
+ }
+ }
+ } else { /* default to phase 2 */
+ for ( ; aa; aa = aa->aa_next ) {
+ if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 )) {
+ break;
+ }
+ }
+ }
+
+ if ( aa == (struct at_ifaddr *) 0 )
+ return( EADDRNOTAVAIL );
+ break;
+ }
+
+ switch ( cmd ) {
+ case SIOCGIFADDR:
+#ifdef BSD4_4
+ *(struct sockaddr_at *)&ifr->ifr_addr = aa->aa_addr;
+#else BSD4_4
+ ifr->ifr_addr = aa->aa_addr;
+#endif BSD4_4
+ break;
+
+ case SIOCSIFADDR:
+ return( at_ifinit( ifp, aa, (struct sockaddr_at *)&ifr->ifr_addr ));
+
+#ifdef BSD4_4
+ case SIOCAIFADDR:
+ if ( sateqaddr( &ifra->ifra_addr, &aa->aa_addr )) {
+ return( 0 );
+ }
+ return( at_ifinit( ifp, aa, (struct sockaddr_at *)&ifr->ifr_addr ));
+
+ case SIOCDIFADDR:
+ at_scrub( ifp, aa );
+ if (( ifa = ifp->if_addrlist ) == (struct ifaddr *)aa ) {
+ ifp->if_addrlist = ifa->ifa_next;
+ } else {
+ while ( ifa->ifa_next && ( ifa->ifa_next != (struct ifaddr *)aa )) {
+ ifa = ifa->ifa_next;
+ }
+ if ( ifa->ifa_next ) {
+ ifa->ifa_next = ((struct ifaddr *)aa)->ifa_next;
+ } else {
+ panic( "at_control" );
+ }
+ }
+
+ aa0 = aa;
+ if ( aa0 == ( aa = at_ifaddr )) {
+ at_ifaddr = aa->aa_next;
+ } else {
+ while ( aa->aa_next && ( aa->aa_next != aa0 )) {
+ aa = aa->aa_next;
+ }
+ if ( aa->aa_next ) {
+ aa->aa_next = aa0->aa_next;
+ } else {
+ panic( "at_control" );
+ }
+ }
+ m_free( dtom( aa0 ));
+ break;
+#endif BSD4_4
+
+ default:
+ if ( ifp == 0 || ifp->if_ioctl == 0 )
+ return( EOPNOTSUPP );
+ return( (*ifp->if_ioctl)( ifp, cmd, data ));
+ }
+ return( 0 );
+}
+
+at_scrub( ifp, aa )
+ struct ifnet *ifp;
+ struct at_ifaddr *aa;
+{
+#ifndef BSD4_4
+ struct sockaddr_at netsat;
+ int error;
+ u_short net;
+#endif BSD4_4
+
+ if ( aa->aa_flags & AFA_ROUTE ) {
+#ifdef BSD4_4
+ if (( error = rtinit( &(aa->aa_ifa), RTM_DELETE,
+ ( ifp->if_flags & IFF_LOOPBACK ) ? RTF_HOST : 0 )) != 0 ) {
+ return( error );
+ }
+ aa->aa_ifa.ifa_flags &= ~IFA_ROUTE;
+#else BSD4_4
+ if ( ifp->if_flags & IFF_LOOPBACK ) {
+ rtinit( &aa->aa_addr, &aa->aa_addr, SIOCDELRT, RTF_HOST );
+ } else {
+ bzero( &netsat, sizeof( struct sockaddr_at ));
+ netsat.sat_family = AF_APPLETALK;
+ netsat.sat_addr.s_node = ATADDR_ANYNODE;
+
+ /*
+ * If the range is the full 0-fffe range, just use
+ * the default route.
+ */
+ if ( aa->aa_firstnet == htons( 0x0000 ) &&
+ aa->aa_lastnet == htons( 0xfffe )) {
+ netsat.sat_addr.s_net = 0;
+ rtinit((struct sockaddr *)&netsat, &aa->aa_addr,
+ (int)SIOCDELRT, 0 );
+ } else {
+ for ( net = ntohs( aa->aa_firstnet );
+ net <= ntohs( aa->aa_lastnet ); net++ ) {
+ netsat.sat_addr.s_net = htons( net );
+ rtinit((struct sockaddr *)&netsat, &aa->aa_addr,
+ (int)SIOCDELRT, 0 );
+ }
+ }
+ }
+#endif BSD4_4
+ aa->aa_flags &= ~AFA_ROUTE;
+ }
+ return( 0 );
+}
+
+extern struct timeval time;
+
+at_ifinit( ifp, aa, sat )
+ struct ifnet *ifp;
+ struct at_ifaddr *aa;
+ struct sockaddr_at *sat;
+{
+ struct netrange nr, onr;
+#ifdef BSD4_4
+ struct sockaddr_at oldaddr;
+#else BSD4_4
+ struct sockaddr oldaddr;
+#endif BSD4_4
+ struct sockaddr_at netaddr;
+ int s = splimp(), error = 0, i, j, netinc, nodeinc, nnets;
+ u_short net;
+
+ oldaddr = aa->aa_addr;
+ bzero( AA_SAT( aa ), sizeof( struct sockaddr_at ));
+ bcopy( sat->sat_zero, &nr, sizeof( struct netrange ));
+ nnets = ntohs( nr.nr_lastnet ) - ntohs( nr.nr_firstnet ) + 1;
+
+ onr.nr_firstnet = aa->aa_firstnet;
+ onr.nr_lastnet = aa->aa_lastnet;
+ aa->aa_firstnet = nr.nr_firstnet;
+ aa->aa_lastnet = nr.nr_lastnet;
+
+ /*
+ * We could eliminate the need for a second phase 1 probe (post
+ * autoconf) if we check whether we're resetting the node. Note
+ * that phase 1 probes use only nodes, not net.node pairs. Under
+ * phase 2, both the net and node must be the same.
+ */
+ if ( ifp->if_flags & IFF_LOOPBACK ) {
+#ifdef BSD4_4
+ AA_SAT( aa )->sat_len = sat->sat_len;
+#endif BSD4_4
+ AA_SAT( aa )->sat_family = AF_APPLETALK;
+ AA_SAT( aa )->sat_addr.s_net = sat->sat_addr.s_net;
+ AA_SAT( aa )->sat_addr.s_node = sat->sat_addr.s_node;
+ } else {
+ aa->aa_flags |= AFA_PROBING;
+ AA_SAT( aa )->sat_family = AF_APPLETALK;
+ if ( aa->aa_flags & AFA_PHASE2 ) {
+ if ( sat->sat_addr.s_net == ATADDR_ANYNET ) {
+ if ( nnets != 1 ) {
+ net = ntohs( nr.nr_firstnet ) + time.tv_sec % ( nnets - 1 );
+ } else {
+ net = ntohs( nr.nr_firstnet );
+ }
+ } else {
+ if ( ntohs( sat->sat_addr.s_net ) < ntohs( nr.nr_firstnet ) ||
+ ntohs( sat->sat_addr.s_net ) > ntohs( nr.nr_lastnet )) {
+ aa->aa_addr = oldaddr;
+ aa->aa_firstnet = onr.nr_firstnet;
+ aa->aa_lastnet = onr.nr_lastnet;
+ return( EINVAL );
+ }
+ net = ntohs( sat->sat_addr.s_net );
+ }
+ } else {
+ net = ntohs( sat->sat_addr.s_net );
+ }
+
+ if ( sat->sat_addr.s_node == ATADDR_ANYNODE ) {
+ AA_SAT( aa )->sat_addr.s_node = time.tv_sec;
+ } else {
+ AA_SAT( aa )->sat_addr.s_node = sat->sat_addr.s_node;
+ }
+
+ for ( i = nnets, netinc = 1; i > 0; net = ntohs( nr.nr_firstnet ) +
+ (( net - ntohs( nr.nr_firstnet ) + netinc ) % nnets ), i-- ) {
+ AA_SAT( aa )->sat_addr.s_net = htons( net );
+
+ for ( j = 0, nodeinc = time.tv_sec | 1; j < 256;
+ j++, AA_SAT( aa )->sat_addr.s_node += nodeinc ) {
+ if ( AA_SAT( aa )->sat_addr.s_node > 253 ||
+ AA_SAT( aa )->sat_addr.s_node < 1 ) {
+ continue;
+ }
+ aa->aa_probcnt = 10;
+ timeout( aarpprobe, (caddr_t)ifp, hz / 5 );
+ splx( s );
+ if ( sleep( aa, PSLEP|PCATCH )) {
+ printf( "at_ifinit why did this happen?!\n" );
+ aa->aa_addr = oldaddr;
+ aa->aa_firstnet = onr.nr_firstnet;
+ aa->aa_lastnet = onr.nr_lastnet;
+ return( EINTR );
+ }
+ s = splimp();
+ if (( aa->aa_flags & AFA_PROBING ) == 0 ) {
+ break;
+ }
+ }
+ if (( aa->aa_flags & AFA_PROBING ) == 0 ) {
+ break;
+ }
+ /* reset node for next network */
+ AA_SAT( aa )->sat_addr.s_node = time.tv_sec;
+ }
+
+ if ( aa->aa_flags & AFA_PROBING ) {
+ aa->aa_addr = oldaddr;
+ aa->aa_firstnet = onr.nr_firstnet;
+ aa->aa_lastnet = onr.nr_lastnet;
+ splx( s );
+ return( EADDRINUSE );
+ }
+ }
+
+ if ( ifp->if_ioctl &&
+ ( error = (*ifp->if_ioctl)( ifp, SIOCSIFADDR, aa ))) {
+ splx( s );
+ aa->aa_addr = oldaddr;
+ aa->aa_firstnet = onr.nr_firstnet;
+ aa->aa_lastnet = onr.nr_lastnet;
+ return( error );
+ }
+
+#ifdef BSD4_4
+ aa->aa_netmask.sat_len = 6;
+ aa->aa_netmask.sat_family = AF_APPLETALK;
+ aa->aa_netmask.sat_addr.s_net = 0xffff;
+ aa->aa_netmask.sat_addr.s_node = 0;
+#endif BSD4_4
+
+ if ( ifp->if_flags & IFF_LOOPBACK ) {
+#ifndef BSD4_4
+ rtinit( &aa->aa_addr, &aa->aa_addr, (int)SIOCADDRT,
+ RTF_HOST|RTF_UP );
+#else BSD4_4
+ error = rtinit( &(aa->aa_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP );
+#endif BSD4_4
+ } else {
+#ifndef BSD4_4
+ /*
+ * rtrequest looks for point-to-point links first. The
+ * broadaddr is in the same spot as the destaddr. So, if
+ * ATADDR_ANYNET is 0, and we don't fill in the broadaddr, we
+ * get 0.0 routed out the ether interface. So, initialize the
+ * broadaddr, even tho we don't use it.
+ *
+ * We *could* use the broadaddr field to reduce some of the
+ * sockaddr_at overloading that we've done. E.g. Just send
+ * to INTERFACE-NET.255, and have the kernel reroute that
+ * to broadaddr, which would be 0.255 for phase 2 interfaces,
+ * and IFACE-NET.255 for phase 1 interfaces.
+ */
+ ((struct sockaddr_at *)&aa->aa_broadaddr)->sat_addr.s_net =
+ sat->sat_addr.s_net;
+ ((struct sockaddr_at *)&aa->aa_broadaddr)->sat_addr.s_node =
+ ATADDR_BCAST;
+
+ bzero( &netaddr, sizeof( struct sockaddr_at ));
+ netaddr.sat_family = AF_APPLETALK;
+ netaddr.sat_addr.s_node = ATADDR_ANYNODE;
+ if (( aa->aa_flags & AFA_PHASE2 ) == 0 ) {
+ netaddr.sat_addr.s_net = AA_SAT( aa )->sat_addr.s_net;
+ rtinit((struct sockaddr *)&netaddr, &aa->aa_addr,
+ (int)SIOCADDRT, RTF_UP );
+ } else {
+ /*
+ * If the range is the full 0-fffe range, just use
+ * the default route.
+ */
+ if ( aa->aa_firstnet == htons( 0x0000 ) &&
+ aa->aa_lastnet == htons( 0xfffe )) {
+ netaddr.sat_addr.s_net = 0;
+ rtinit((struct sockaddr *)&netaddr, &aa->aa_addr,
+ (int)SIOCADDRT, RTF_UP );
+ } else {
+ for ( net = ntohs( aa->aa_firstnet );
+ net <= ntohs( aa->aa_lastnet ); net++ ) {
+ netaddr.sat_addr.s_net = htons( net );
+ rtinit((struct sockaddr *)&netaddr, &aa->aa_addr,
+ (int)SIOCADDRT, RTF_UP );
+ }
+ }
+ }
+#else BSD4_4
+ error = rtinit( &(aa->aa_ifa), (int)RTM_ADD, RTF_UP );
+#endif BSD4_4
+ }
+ if ( error ) {
+ aa->aa_addr = oldaddr;
+ aa->aa_firstnet = onr.nr_firstnet;
+ aa->aa_lastnet = onr.nr_lastnet;
+ splx( s );
+ return( error );
+ }
+
+#ifdef BSD4_4
+ aa->aa_ifa.ifa_flags |= IFA_ROUTE;
+#endif BSD4_4
+ aa->aa_flags |= AFA_ROUTE;
+ splx( s );
+ return( 0 );
+}
+
+at_broadcast( sat )
+ struct sockaddr_at *sat;
+{
+ struct at_ifaddr *aa;
+
+ if ( sat->sat_addr.s_node != ATADDR_BCAST ) {
+ return( 0 );
+ }
+ if ( sat->sat_addr.s_net == 0 ) {
+ return( 1 );
+ } else {
+ for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+ if (( aa->aa_ifp->if_flags & IFF_BROADCAST ) &&
+ ( ntohs( sat->sat_addr.s_net ) >= ntohs( aa->aa_firstnet ) &&
+ ntohs( sat->sat_addr.s_net ) <= ntohs( aa->aa_lastnet ))) {
+ return( 1 );
+ }
+ }
+ }
+ return( 0 );
+}
+
+aa_clean()
+{
+ struct at_ifaddr *aa;
+ struct ifaddr *ifa;
+ struct ifnet *ifp;
+
+ while ( aa = at_ifaddr ) {
+ ifp = aa->aa_ifp;
+ at_scrub( ifp, aa );
+ at_ifaddr = aa->aa_next;
+ if (( ifa = ifp->if_addrlist ) == (struct ifaddr *)aa ) {
+ ifp->if_addrlist = ifa->ifa_next;
+ } else {
+ while ( ifa->ifa_next &&
+ ( ifa->ifa_next != (struct ifaddr *)aa )) {
+ ifa = ifa->ifa_next;
+ }
+ if ( ifa->ifa_next ) {
+ ifa->ifa_next = ((struct ifaddr *)aa)->ifa_next;
+ } else {
+ panic( "at_entry" );
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ *
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "at.h"
+
+extern int ddp_usrreq();
+extern int ddp_output();
+extern int ddp_init();
+
+#ifdef ultrix
+extern int ddp_ifoutput();
+extern int ddp_ifinput();
+extern int ddp_ifioctl();
+#endif ultrix
+
+struct protosw atalksw[] = {
+ {
+ /* Identifiers */
+ SOCK_DGRAM, &atalkdomain, ATPROTO_DDP, PR_ATOMIC|PR_ADDR,
+ /*
+ * protocol-protocol interface.
+ * fields are pr_input, pr_output, pr_ctlinput, and pr_ctloutput.
+ * pr_input can be called from the udp protocol stack for iptalk
+ * packets bound for a local socket.
+ * pr_output can be used by higher level appletalk protocols, should
+ * they be included in the kernel.
+ */
+ 0, ddp_output, 0, 0,
+ /* socket-protocol interface. */
+ ddp_usrreq,
+ /* utility routines. */
+ ddp_init, 0, 0, 0,
+#ifdef ultrix
+ /* interface hooks */
+ ddp_ifoutput, ddp_ifinput, ddp_ifioctl, 0,
+#endif ultrix
+ },
+};
+
+struct domain atalkdomain = {
+ AF_APPLETALK, "appletalk", 0, 0, 0, atalksw,
+ &atalksw[ sizeof( atalksw ) / sizeof( atalksw[ 0 ] ) ]
+};
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ *
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+/*
+ * For phase2, we need to keep not only our address on an interface,
+ * but also the legal networks on the interface.
+ */
+struct at_ifaddr {
+ struct ifaddr aa_ifa;
+# define aa_ifp aa_ifa.ifa_ifp
+#ifdef BSD4_4
+ struct sockaddr_at aa_addr;
+ struct sockaddr_at aa_broadaddr;
+ struct sockaddr_at aa_netmask;
+#else BSD4_4
+# define aa_addr aa_ifa.ifa_addr
+# define aa_broadaddr aa_ifa.ifa_broadaddr
+# define aa_dstaddr aa_ifa.ifa_dstaddr
+#endif BSD4_4
+ int aa_flags;
+ u_short aa_firstnet, aa_lastnet;
+ int aa_probcnt;
+ struct at_ifaddr *aa_next;
+};
+
+#ifdef BSD4_4
+struct at_aliasreq {
+ char ifra_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ struct sockaddr_at ifra_addr;
+ struct sockaddr_at ifra_broadaddr;
+#define ifra_dstaddr ifra_broadaddr
+ struct sockaddr_at ifra_mask;
+};
+#endif BSD4_4
+
+#define AA_SAT(aa) \
+ ((struct sockaddr_at *)&((struct at_ifaddr *)(aa))->aa_addr)
+#define satosat(sa) ((struct sockaddr_at *)(sa))
+
+#define AFA_ROUTE 0x0001
+#define AFA_PROBING 0x0002
+#define AFA_PHASE2 0x0004
+
+#ifdef KERNEL
+struct at_ifaddr *at_ifaddr;
+struct ifqueue atintrq1, atintrq2;
+int atdebug;
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ */
+
+/*
+ * <-1byte(8bits) ->
+ * +---------------+
+ * | 0 | hopc |len|
+ * +---------------+
+ * | len (cont) |
+ * +---------------+
+ * | |
+ * +- DDP csum -+
+ * | |
+ * +---------------+
+ * | |
+ * +- Dest NET -+
+ * | |
+ * +---------------+
+ * | |
+ * +- Src NET -+
+ * | |
+ * +---------------+
+ * | Dest NODE |
+ * +---------------+
+ * | Src NODE |
+ * +---------------+
+ * | Dest PORT |
+ * +---------------+
+ * | Src PORT |
+ * +---------------+
+ *
+ * On Apples, there is also a ddp_type field, after src_port. However,
+ * under this unix implementation, user level processes need to be able
+ * to set the ddp_type. In later revisions, the ddp_type may only be
+ * available in a raw_appletalk interface.
+ */
+
+#ifndef _NETATALK_DDP_H
+#define _NETATALK_DDP_H 1
+
+#include <netatalk/endian.h>
+
+struct elaphdr {
+ u_char el_dnode;
+ u_char el_snode;
+ u_char el_type;
+};
+
+#define SZ_ELAPHDR 3
+
+#define ELAP_DDPSHORT 0x01
+#define ELAP_DDPEXTEND 0x02
+
+/*
+ * Extended DDP header. Includes sickness for dealing with arbitrary
+ * bitfields on a little-endian arch.
+ */
+struct ddpehdr {
+ union {
+ struct {
+#if BYTE_ORDER == BIG_ENDIAN
+ unsigned dub_pad:2;
+ unsigned dub_hops:4;
+ unsigned dub_len:10;
+ unsigned dub_sum:16;
+#else
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned dub_sum:16;
+ unsigned dub_len:10;
+ unsigned dub_hops:4;
+ unsigned dub_pad:2;
+#else
+ OOPS!
+#endif
+#endif
+ } du_bits;
+ unsigned du_bytes;
+ } deh_u;
+#define deh_pad deh_u.du_bits.dub_pad
+#define deh_hops deh_u.du_bits.dub_hops
+#define deh_len deh_u.du_bits.dub_len
+#define deh_sum deh_u.du_bits.dub_sum
+#define deh_bytes deh_u.du_bytes
+ u_short deh_dnet;
+ u_short deh_snet;
+ u_char deh_dnode;
+ u_char deh_snode;
+ u_char deh_dport;
+ u_char deh_sport;
+};
+
+#define SZ_DDPEHDR 12
+
+#define DDP_MAXHOPS 15
+
+struct ddpshdr {
+ union {
+ struct {
+#if BYTE_ORDER == BIG_ENDIAN
+ unsigned dub_pad:6;
+ unsigned dub_len:10;
+ unsigned dub_dport:8;
+ unsigned dub_sport:8;
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned dub_sport:8;
+ unsigned dub_dport:8;
+ unsigned dub_len:10;
+ unsigned dub_pad:6;
+#endif
+ } du_bits;
+ unsigned du_bytes;
+ } dsh_u;
+#define dsh_pad dsh_u.du_bits.dub_pad
+#define dsh_len dsh_u.du_bits.dub_len
+#define dsh_dport dsh_u.du_bits.dub_dport
+#define dsh_sport dsh_u.du_bits.dub_sport
+#define dsh_bytes dsh_u.du_bytes
+};
+#define SZ_DDPSHDR 4
+
+#endif /* netatalk/ddp.h */
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/syslog.h>
+#include <net/if.h>
+#include <net/route.h>
+#ifdef _IBMR2
+#include <net/spl.h>
+#endif _IBMR2
+
+#include "at.h"
+#include "at_var.h"
+#include "endian.h"
+#include "ddp.h"
+#include "ddp_var.h"
+
+int ddp_forward = 1;
+int ddp_firewall = 0;
+extern int ddp_cksum;
+extern u_short at_cksum();
+
+/*
+ * Could probably merge these two code segments a little better...
+ */
+atintr()
+{
+ struct elaphdr *elhp, elh;
+ struct ifnet *ifp;
+ struct mbuf *m;
+ struct at_ifaddr *aa;
+ int s;
+
+ for (;;) {
+#ifndef _IBMR2
+ s = splimp();
+#endif _IBMR2
+
+#ifdef BSD4_4
+ IF_DEQUEUE( &atintrq2, m );
+#else BSD4_4
+ IF_DEQUEUEIF( &atintrq2, m, ifp );
+#endif BSD4_4
+
+#ifndef _IBMR2
+ splx( s );
+#endif _IBMR2
+
+ if ( m == 0 ) { /* no more queued packets */
+ break;
+ }
+
+#ifdef BSD4_4
+ ifp = m->m_pkthdr.rcvif;
+#endif BSD4_4
+ for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+ if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 )) {
+ break;
+ }
+ }
+ if ( aa == NULL ) { /* ifp not an appletalk interface */
+ m_freem( m );
+ continue;
+ }
+
+ ddp_input( m, ifp, (struct elaphdr *)NULL, 2 );
+ }
+
+ for (;;) {
+#ifndef _IBMR2
+ s = splimp();
+#endif _IBMR2
+
+#ifdef BSD4_4
+ IF_DEQUEUE( &atintrq1, m );
+#else BSD4_4
+ IF_DEQUEUEIF( &atintrq1, m, ifp );
+#endif BSD4_4
+
+#ifndef _IBMR2
+ splx( s );
+#endif _IBMR2
+
+ if ( m == 0 ) { /* no more queued packets */
+ break;
+ }
+
+#ifdef BSD4_4
+ ifp = m->m_pkthdr.rcvif;
+#endif BSD4_4
+ for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+ if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 ) == 0 ) {
+ break;
+ }
+ }
+ if ( aa == NULL ) { /* ifp not an appletalk interface */
+ m_freem( m );
+ continue;
+ }
+
+ if ( m->m_len < SZ_ELAPHDR &&
+ (( m = m_pullup( m, SZ_ELAPHDR )) == 0 )) {
+ ddpstat.ddps_tooshort++;
+ continue;
+ }
+
+ elhp = mtod( m, struct elaphdr *);
+ m_adj( m, SZ_ELAPHDR );
+
+ if ( elhp->el_type == ELAP_DDPEXTEND ) {
+ ddp_input( m, ifp, (struct elaphdr *)NULL, 1 );
+ } else {
+ bcopy((caddr_t)elhp, (caddr_t)&elh, SZ_ELAPHDR );
+ ddp_input( m, ifp, &elh, 1 );
+ }
+ }
+ return;
+}
+
+struct route forwro;
+
+ddp_input( m, ifp, elh, phase )
+ struct mbuf *m;
+ struct ifnet *ifp;
+ struct elaphdr *elh;
+ int phase;
+{
+ struct sockaddr_at from, to;
+ struct ddpshdr *dsh, ddps;
+ struct at_ifaddr *aa;
+ struct ddpehdr *deh, ddpe;
+#ifndef BSD4_4
+ struct mbuf *mp;
+#endif BSD4_4
+ struct ddpcb *ddp;
+ int dlen, mlen;
+ u_short cksum;
+
+ bzero( (caddr_t)&from, sizeof( struct sockaddr_at ));
+ if ( elh ) {
+ ddpstat.ddps_short++;
+
+ if ( m->m_len < sizeof( struct ddpshdr ) &&
+ (( m = m_pullup( m, sizeof( struct ddpshdr ))) == 0 )) {
+ ddpstat.ddps_tooshort++;
+ return;
+ }
+
+ dsh = mtod( m, struct ddpshdr *);
+ bcopy( (caddr_t)dsh, (caddr_t)&ddps, sizeof( struct ddpshdr ));
+ ddps.dsh_bytes = ntohl( ddps.dsh_bytes );
+ dlen = ddps.dsh_len;
+
+ to.sat_addr.s_net = 0;
+ to.sat_addr.s_node = elh->el_dnode;
+ to.sat_port = ddps.dsh_dport;
+ from.sat_addr.s_net = 0;
+ from.sat_addr.s_node = elh->el_snode;
+ from.sat_port = ddps.dsh_sport;
+
+ for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+ if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 ) == 0 &&
+ ( AA_SAT( aa )->sat_addr.s_node == to.sat_addr.s_node ||
+ to.sat_addr.s_node == ATADDR_BCAST )) {
+ break;
+ }
+ }
+ if ( aa == NULL ) {
+ m_freem( m );
+ return;
+ }
+ } else {
+ ddpstat.ddps_long++;
+
+ if ( m->m_len < sizeof( struct ddpehdr ) &&
+ (( m = m_pullup( m, sizeof( struct ddpehdr ))) == 0 )) {
+ ddpstat.ddps_tooshort++;
+ return;
+ }
+
+ deh = mtod( m, struct ddpehdr *);
+ bcopy( (caddr_t)deh, (caddr_t)&ddpe, sizeof( struct ddpehdr ));
+ ddpe.deh_bytes = ntohl( ddpe.deh_bytes );
+ dlen = ddpe.deh_len;
+
+ if (( cksum = ddpe.deh_sum ) == 0 ) {
+ ddpstat.ddps_nosum++;
+ }
+
+ from.sat_addr.s_net = ddpe.deh_snet;
+ from.sat_addr.s_node = ddpe.deh_snode;
+ from.sat_port = ddpe.deh_sport;
+ to.sat_addr.s_net = ddpe.deh_dnet;
+ to.sat_addr.s_node = ddpe.deh_dnode;
+ to.sat_port = ddpe.deh_dport;
+
+ if ( to.sat_addr.s_net == 0 ) {
+ for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+ if ( phase == 1 && ( aa->aa_flags & AFA_PHASE2 )) {
+ continue;
+ }
+ if ( phase == 2 && ( aa->aa_flags & AFA_PHASE2 ) == 0 ) {
+ continue;
+ }
+ if ( aa->aa_ifp == ifp &&
+ ( AA_SAT( aa )->sat_addr.s_node == to.sat_addr.s_node ||
+ to.sat_addr.s_node == ATADDR_BCAST ||
+ ( ifp->if_flags & IFF_LOOPBACK ))) {
+ break;
+ }
+ }
+ } else {
+ for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+ if ( to.sat_addr.s_net == aa->aa_firstnet &&
+ to.sat_addr.s_node == 0 ) {
+ break;
+ }
+ if (( ntohs( to.sat_addr.s_net ) < ntohs( aa->aa_firstnet ) ||
+ ntohs( to.sat_addr.s_net ) > ntohs( aa->aa_lastnet )) &&
+ ( ntohs( to.sat_addr.s_net ) < 0xff00 ||
+ ntohs( to.sat_addr.s_net ) > 0xfffe)) {
+ continue;
+ }
+ if ( to.sat_addr.s_node != AA_SAT( aa )->sat_addr.s_node &&
+ to.sat_addr.s_node != ATADDR_BCAST ) {
+ continue;
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * Adjust the length, removing any padding that may have been added
+ * at a link layer. We do this before we attempt to forward a packet,
+ * possibly on a different media.
+ */
+#ifdef BSD4_4
+ mlen = m->m_pkthdr.len;
+#else BSD4_4
+ for ( mlen = 0, mp = m; mp; mp = mp->m_next ) {
+ mlen += mp->m_len;
+ }
+#endif BSD4_4
+ if ( mlen < dlen ) {
+ ddpstat.ddps_toosmall++;
+ m_freem( m );
+ return;
+ }
+ if ( mlen > dlen ) {
+ m_adj( m, dlen - mlen );
+ }
+
+ /*
+ * XXX Should we deliver broadcasts locally, also, or rely on the
+ * link layer to give us a copy? For the moment, the latter.
+ */
+ if ( aa == NULL || ( to.sat_addr.s_node == ATADDR_BCAST &&
+ aa->aa_ifp != ifp && ( ifp->if_flags & IFF_LOOPBACK ) == 0 )) {
+ if ( ddp_forward == 0 ) {
+ m_freem( m );
+ return;
+ }
+ if ( forwro.ro_rt && ( satosat( &forwro.ro_dst )->sat_addr.s_net !=
+ to.sat_addr.s_net ||
+ satosat( &forwro.ro_dst )->sat_addr.s_node !=
+ to.sat_addr.s_node )) {
+#ifdef ultrix
+ rtfree( forwro.ro_rt );
+#else ultrix
+ RTFREE( forwro.ro_rt );
+#endif ultrix
+ forwro.ro_rt = (struct rtentry *)0;
+ }
+ if ( forwro.ro_rt == (struct rtentry *)0 ||
+ forwro.ro_rt->rt_ifp == (struct ifnet *)0 ) {
+#ifdef BSD4_4
+ forwro.ro_dst.sa_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ forwro.ro_dst.sa_family = AF_APPLETALK;
+ satosat( &forwro.ro_dst )->sat_addr.s_net = to.sat_addr.s_net;
+ satosat( &forwro.ro_dst )->sat_addr.s_node = to.sat_addr.s_node;
+ rtalloc( &forwro );
+ }
+
+ if ( to.sat_addr.s_net != satosat( &forwro.ro_dst )->sat_addr.s_net &&
+ ddpe.deh_hops == DDP_MAXHOPS ) {
+ m_freem( m );
+ return;
+ }
+
+ if ( ddp_firewall &&
+ ( forwro.ro_rt == NULL || ( forwro.ro_rt->rt_ifp != ifp &&
+ forwro.ro_rt->rt_ifp != at_ifaddr->aa_ifp ))) {
+ m_freem( m );
+ return;
+ }
+
+ ddpe.deh_hops++;
+ ddpe.deh_bytes = htonl( ddpe.deh_bytes );
+ bcopy( (caddr_t)&ddpe, (caddr_t)deh, sizeof( u_short ));
+ if ( ddp_route( m, &forwro )) {
+ ddpstat.ddps_cantforward++;
+ } else {
+ ddpstat.ddps_forward++;
+ }
+ return;
+ }
+
+#ifdef BSD4_4
+ from.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ from.sat_family = AF_APPLETALK;
+
+ if ( elh ) {
+ m_adj( m, sizeof( struct ddpshdr ));
+ } else {
+ if ( ddp_cksum && cksum && cksum != at_cksum( m, sizeof( int ))) {
+ ddpstat.ddps_badsum++;
+ m_freem( m );
+ return;
+ }
+ m_adj( m, sizeof( struct ddpehdr ));
+ }
+
+ if (( ddp = ddp_search( &from, &to, aa )) == NULL ) {
+ m_freem( m );
+ return;
+ }
+
+ if ( sbappendaddr( &ddp->ddp_socket->so_rcv, (struct sockaddr *)&from,
+ m, (struct mbuf *)0 ) == 0 ) {
+ ddpstat.ddps_nosockspace++;
+ m_freem( m );
+ return;
+ }
+ sorwakeup( ddp->ddp_socket );
+}
+
+m_printm( m )
+ struct mbuf *m;
+{
+ for (; m; m = m->m_next ) {
+ bprint( mtod( m, char * ), m->m_len );
+ }
+}
+
+#define BPXLEN 48
+#define BPALEN 16
+#include <ctype.h>
+char hexdig[] = "0123456789ABCDEF";
+
+bprint( data, len )
+ char *data;
+ int len;
+{
+ char xout[ BPXLEN ], aout[ BPALEN ];
+ int i = 0;
+
+ bzero( xout, BPXLEN );
+ bzero( aout, BPALEN );
+
+ for ( ;; ) {
+ if ( len < 1 ) {
+ if ( i != 0 ) {
+ printf( "%s\t%s\n", xout, aout );
+ }
+ printf( "%s\n", "(end)" );
+ break;
+ }
+
+ xout[ (i*3) ] = hexdig[ ( *data & 0xf0 ) >> 4 ];
+ xout[ (i*3) + 1 ] = hexdig[ *data & 0x0f ];
+
+ if ( (u_char)*data < 0x7f && (u_char)*data > 0x20 ) {
+ aout[ i ] = *data;
+ } else {
+ aout[ i ] = '.';
+ }
+
+ xout[ (i*3) + 2 ] = ' ';
+
+ i++;
+ len--;
+ data++;
+
+ if ( i > BPALEN - 2 ) {
+ printf( "%s\t%s\n", xout, aout );
+ bzero( xout, BPXLEN );
+ bzero( aout, BPALEN );
+ i = 0;
+ continue;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#undef s_net
+#include <netinet/if_ether.h>
+
+#include "at.h"
+#include "at_var.h"
+#include "endian.h"
+#include "ddp.h"
+#include "ddp_var.h"
+
+u_short at_cksum();
+int ddp_cksum = 1;
+
+ddp_output( ddp, m )
+ struct ddpcb *ddp;
+ struct mbuf *m;
+{
+#ifndef BSD4_4
+ struct mbuf *m0;
+ int len;
+#endif BSD4_4
+ struct ifnet *ifp;
+ struct at_ifaddr *aa = NULL;
+ struct ddpehdr *deh;
+ u_short net;
+
+#ifdef BSD4_4
+ M_PREPEND( m, sizeof( struct ddpehdr ), M_WAIT );
+#else BSD4_4
+ for ( len = 0, m0 = m; m; m = m->m_next ) {
+ len += m->m_len;
+ }
+ MGET( m, M_WAIT, MT_HEADER );
+ if ( m == 0 ) {
+ m_freem( m0 );
+ return( ENOBUFS );
+ }
+ m->m_next = m0;
+#endif BSD4_4
+
+#ifndef BSD4_4
+# define align(a) (((a)+3)&0xfc)
+ m->m_off = MMINOFF + align( SZ_ELAPHDR );
+ m->m_len = sizeof( struct ddpehdr );
+#endif BSD4_4
+
+ deh = mtod( m, struct ddpehdr *);
+ deh->deh_pad = 0;
+ deh->deh_hops = 0;
+
+#ifdef BSD4_4
+ deh->deh_len = m->m_pkthdr.len;
+#else BSD4_4
+ deh->deh_len = len + sizeof( struct ddpehdr );
+#endif BSD4_4
+
+ deh->deh_dnet = ddp->ddp_fsat.sat_addr.s_net;
+ deh->deh_dnode = ddp->ddp_fsat.sat_addr.s_node;
+ deh->deh_dport = ddp->ddp_fsat.sat_port;
+ deh->deh_snet = ddp->ddp_lsat.sat_addr.s_net;
+ deh->deh_snode = ddp->ddp_lsat.sat_addr.s_node;
+ deh->deh_sport = ddp->ddp_lsat.sat_port;
+
+ /*
+ * The checksum calculation is done after all of the other bytes have
+ * been filled in.
+ */
+ if ( ddp_cksum ) {
+ deh->deh_sum = at_cksum( m, sizeof( int ));
+ } else {
+ deh->deh_sum = 0;
+ }
+ deh->deh_bytes = htonl( deh->deh_bytes );
+
+ return( ddp_route( m, &ddp->ddp_route ));
+}
+
+ u_short
+at_cksum( m, skip )
+ struct mbuf *m;
+ int skip;
+{
+ u_char *data, *end;
+ u_int32_t cksum = 0;
+
+ for (; m; m = m->m_next ) {
+ for ( data = mtod( m, u_char * ), end = data + m->m_len; data < end;
+ data++ ) {
+ if ( skip ) {
+ skip--;
+ continue;
+ }
+ cksum = ( cksum + *data ) << 1;
+ if ( cksum & 0x00010000 ) {
+ cksum++;
+ }
+ cksum &= 0x0000ffff;
+ }
+ }
+
+ if ( cksum == 0 ) {
+ cksum = 0x0000ffff;
+ }
+ return( (u_short)cksum );
+}
+
+ddp_route( m, ro )
+ struct mbuf *m;
+ struct route *ro;
+{
+ struct sockaddr_at gate;
+ struct elaphdr *elh;
+ struct mbuf *m0;
+ struct at_ifaddr *aa = NULL;
+ struct ifnet *ifp;
+ int mlen;
+ u_short net;
+
+ if ( ro->ro_rt && ( ifp = ro->ro_rt->rt_ifp )) {
+#ifdef BSD4_4
+ net = satosat( ro->ro_rt->rt_gateway )->sat_addr.s_net;
+#else BSD4_4
+ net = satosat( &ro->ro_rt->rt_gateway )->sat_addr.s_net;
+#endif BSD4_4
+ for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+ if ( aa->aa_ifp == ifp &&
+ ntohs( net ) >= ntohs( aa->aa_firstnet ) &&
+ ntohs( net ) <= ntohs( aa->aa_lastnet )) {
+ break;
+ }
+ }
+ }
+ if ( aa == NULL ) {
+ m_freem( m );
+ return( EINVAL );
+ }
+
+ /*
+ * There are several places in the kernel where data is added to
+ * an mbuf without ensuring that the mbuf pointer is aligned.
+ * This is bad for transition routing, since phase 1 and phase 2
+ * packets end up poorly aligned due to the three byte elap header.
+ */
+ if ( aa->aa_flags & AFA_PHASE2 ) {
+ for ( mlen = 0, m0 = m; m0; m0 = m0->m_next ) {
+ mlen += m0->m_len;
+ }
+ if (( m = m_pullup( m, MIN( MLEN, mlen ))) == 0 ) {
+ return( ENOBUFS );
+ }
+ } else {
+# ifdef notdef
+#ifdef BSD4_4
+ M_PREPEND( m, SZ_ELAPHDR, M_DONTWAIT );
+ if ( m == NULL ) {
+ return( ENOBUFS );
+ }
+#else BSD4_4
+ m->m_off -= SZ_ELAPHDR;
+ m->m_len += SZ_ELAPHDR;
+#endif BSD4_4
+# endif notdef
+
+ MGET( m0, M_WAIT, MT_HEADER );
+ if ( m0 == 0 ) {
+ m_freem( m );
+ return( ENOBUFS );
+ }
+ m0->m_next = m;
+ m0->m_off = MMINOFF + align( sizeof( struct ether_header ));
+ m0->m_len = SZ_ELAPHDR;
+ m = m0;
+
+ elh = mtod( m, struct elaphdr *);
+ elh->el_snode = satosat( &aa->aa_addr )->sat_addr.s_node;
+ elh->el_type = ELAP_DDPEXTEND;
+ if ( ntohs( satosat( &ro->ro_dst )->sat_addr.s_net ) >=
+ ntohs( aa->aa_firstnet ) &&
+ ntohs( satosat( &ro->ro_dst )->sat_addr.s_net ) <=
+ ntohs( aa->aa_lastnet )) {
+ elh->el_dnode = satosat( &ro->ro_dst )->sat_addr.s_node;
+ } else {
+#ifdef BSD4_4
+ elh->el_dnode = satosat( ro->ro_rt->rt_gateway )->sat_addr.s_node;
+#else BSD4_4
+ elh->el_dnode = satosat( &ro->ro_rt->rt_gateway )->sat_addr.s_node;
+#endif BSD4_4
+ }
+ }
+
+ if ( ntohs( satosat( &ro->ro_dst )->sat_addr.s_net ) >=
+ ntohs( aa->aa_firstnet ) &&
+ ntohs( satosat( &ro->ro_dst )->sat_addr.s_net ) <=
+ ntohs( aa->aa_lastnet )) {
+ gate = *satosat( &ro->ro_dst );
+ } else {
+#ifdef BSD4_4
+ gate = *satosat( ro->ro_rt->rt_gateway );
+#else BSD4_4
+ gate = *satosat( &ro->ro_rt->rt_gateway );
+#endif BSD4_4
+ }
+ ro->ro_rt->rt_use++;
+
+#ifdef ultrix
+ /*
+ * SAIEW: We can't make changes to net/if_loop.c, so we don't route
+ * further than this: if it's going to go through the lookback,
+ * short-circuit to ddp_input(). Who needs queuing?
+ *
+ * Note: Passing NULL for the elaphdr is cool, since we'll only ever
+ * try to send long form ddp throught the loopback.
+ */
+ if ( ifp->if_flags & IFF_LOOPBACK ) {
+#ifdef notdef
+ m->m_off += SZ_ELAPHDR;
+ m->m_len -= SZ_ELAPHDR;
+#endif notdef
+ ddp_input( m, ifp, (struct elaphdr *)NULL, 2 );
+ return( 0 );
+ }
+#endif ultrix
+
+#ifdef _IBMR2
+ /*
+ * We can't make changes to the interface routines on RS6ks, and
+ * they don't provide hooks for if_output, so we just resolve
+ * our address here, and pass the packet as a raw ethernet packet.
+ * This doesn't work particularly well, if we aren't *on* ethernet,
+ * but it's ok for the moment.
+ */
+ if ( ! ( ifp->if_flags & IFF_LOOPBACK )) {
+ struct ether_header eh;
+
+ if ( !aarpresolve(( struct arpcom *)ifp, m,
+ &gate, eh.ether_dhost )) {
+ return( 0 );
+ }
+ eh.ether_type = htons( ETHERTYPE_AT );
+ gate.sat_family = AF_UNSPEC;
+ bcopy( &eh, (*(struct sockaddr *)&gate).sa_data,
+ sizeof( (*(struct sockaddr *)&gate).sa_data ));
+ }
+#endif _IBMR2
+ return((*ifp->if_output)( ifp, m, &gate ));
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#ifdef ibm032
+#include <sys/dir.h>
+#endif ibm032
+#include <sys/user.h>
+#include <sys/mbuf.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <net/if.h>
+#include <net/route.h>
+#ifdef _IBMR2
+#include <net/spl.h>
+#endif _IBMR2
+
+#include "at.h"
+#include "at_var.h"
+#include "ddp_var.h"
+#include "endian.h"
+
+struct ddpcb *ddpcb = NULL;
+u_int32_t ddp_sendspace = DDP_MAXSZ; /* Max ddp size + 1 (ddp_type) */
+u_int32_t ddp_recvspace = 10 * ( 587 + sizeof( struct sockaddr_at ));
+
+/*ARGSUSED*/
+ddp_usrreq( so, req, m, addr, rights )
+ struct socket *so;
+ int req;
+ struct mbuf *m, *addr, *rights;
+{
+ struct ddpcb *ddp;
+ int error = 0;
+
+ ddp = sotoddpcb( so );
+
+ if ( req == PRU_CONTROL ) {
+ return( at_control( (int) m, (caddr_t) addr,
+ (struct ifnet *) rights ));
+ }
+
+ if ( rights && rights->m_len ) {
+ error = EINVAL;
+ goto release;
+ }
+
+ if ( ddp == NULL && req != PRU_ATTACH ) {
+ error = EINVAL;
+ goto release;
+ }
+
+ switch ( req ) {
+ case PRU_ATTACH :
+ if ( ddp != NULL ) {
+ error = EINVAL;
+ break;
+ }
+ if (( error = at_pcballoc( so )) != 0 ) {
+ break;
+ }
+ error = soreserve( so, ddp_sendspace, ddp_recvspace );
+ break;
+
+ case PRU_DETACH :
+ at_pcbdetach( so, ddp );
+ break;
+
+ case PRU_BIND :
+ error = at_pcbsetaddr( ddp, addr );
+ break;
+
+ case PRU_SOCKADDR :
+ at_sockaddr( ddp, addr );
+ break;
+
+ case PRU_CONNECT:
+ if ( ddp->ddp_fsat.sat_port != ATADDR_ANYPORT ) {
+ error = EISCONN;
+ break;
+ }
+
+ error = at_pcbconnect( ddp, addr );
+ if ( error == 0 )
+ soisconnected( so );
+ break;
+
+ case PRU_DISCONNECT:
+ if ( ddp->ddp_fsat.sat_addr.s_node == ATADDR_ANYNODE ) {
+ error = ENOTCONN;
+ break;
+ }
+ at_pcbdisconnect( ddp );
+ soisdisconnected( so );
+ break;
+
+ case PRU_SHUTDOWN:
+ socantsendmore( so );
+ break;
+
+ case PRU_SEND: {
+ int s;
+
+ if ( addr ) {
+ if ( ddp->ddp_fsat.sat_port != ATADDR_ANYPORT ) {
+ error = EISCONN;
+ break;
+ }
+
+ s = splnet();
+ error = at_pcbconnect( ddp, addr );
+ if ( error ) {
+ splx( s );
+ break;
+ }
+ } else {
+ if ( ddp->ddp_fsat.sat_port == ATADDR_ANYPORT ) {
+ error = ENOTCONN;
+ break;
+ }
+ }
+
+ error = ddp_output( ddp, m );
+ m = NULL;
+ if ( addr ) {
+ at_pcbdisconnect( ddp );
+ splx( s );
+ }
+ }
+ break;
+
+ case PRU_ABORT:
+ soisdisconnected( so );
+ at_pcbdetach( so, ddp );
+ break;
+
+ case PRU_LISTEN:
+ case PRU_CONNECT2:
+ case PRU_ACCEPT:
+ case PRU_SENDOOB:
+ case PRU_FASTTIMO:
+ case PRU_SLOWTIMO:
+ case PRU_PROTORCV:
+ case PRU_PROTOSEND:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_RCVD:
+ case PRU_RCVOOB:
+ /*
+ * Don't mfree. Good architecture...
+ */
+ return( EOPNOTSUPP );
+
+ case PRU_SENSE:
+ /*
+ * 1. Don't return block size.
+ * 2. Don't mfree.
+ */
+ return( 0 );
+
+ default:
+ error = EOPNOTSUPP;
+ }
+
+release:
+ if ( m != NULL ) {
+ m_freem( m );
+ }
+ return( error );
+}
+
+at_sockaddr( ddp, addr )
+ struct ddpcb *ddp;
+ struct mbuf *addr;
+{
+ struct sockaddr_at *sat;
+
+ addr->m_len = sizeof( struct sockaddr_at );
+ sat = mtod( addr, struct sockaddr_at *);
+ *sat = ddp->ddp_lsat;
+}
+
+at_pcbsetaddr( ddp, addr )
+ struct ddpcb *ddp;
+ struct mbuf *addr;
+{
+ struct sockaddr_at lsat, *sat;
+ struct at_ifaddr *aa;
+ struct ddpcb *ddpp;
+
+ if ( ddp->ddp_lsat.sat_port != ATADDR_ANYPORT ) { /* shouldn't be bound */
+ return( EINVAL );
+ }
+
+ if ( addr != 0 ) { /* validate passed address */
+ sat = mtod( addr, struct sockaddr_at *);
+ if ( addr->m_len != sizeof( *sat )) {
+ return( EINVAL );
+ }
+ if ( sat->sat_family != AF_APPLETALK ) {
+ return( EAFNOSUPPORT );
+ }
+
+ if ( sat->sat_addr.s_node != ATADDR_ANYNODE ||
+ sat->sat_addr.s_net != ATADDR_ANYNET ) {
+ for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+ if (( sat->sat_addr.s_net == AA_SAT( aa )->sat_addr.s_net ) &&
+ ( sat->sat_addr.s_node == AA_SAT( aa )->sat_addr.s_node )) {
+ break;
+ }
+ }
+ if ( !aa ) {
+ return( EADDRNOTAVAIL );
+ }
+ }
+
+ if ( sat->sat_port != ATADDR_ANYPORT ) {
+ if ( sat->sat_port < ATPORT_FIRST ||
+ sat->sat_port >= ATPORT_LAST ) {
+ return( EINVAL );
+ }
+#ifdef BSD4_4
+ if ( sat->sat_port < ATPORT_RESERVED &&
+ suser( u.u_cred, &u.u_acflag )) {
+ return( EACCES );
+ }
+#else BSD4_4
+ if ( sat->sat_port < ATPORT_RESERVED && ( !suser())) {
+ return( EACCES );
+ }
+#endif BSD4_4
+ }
+ } else {
+ bzero( (caddr_t)&lsat, sizeof( struct sockaddr_at ));
+ lsat.sat_family = AF_APPLETALK;
+ sat = &lsat;
+ }
+
+ if ( sat->sat_addr.s_node == ATADDR_ANYNODE &&
+ sat->sat_addr.s_net == ATADDR_ANYNET ) {
+ if ( at_ifaddr == NULL ) {
+ return( EADDRNOTAVAIL );
+ }
+ sat->sat_addr = AA_SAT( at_ifaddr )->sat_addr;
+ }
+ ddp->ddp_lsat = *sat;
+
+ /*
+ * Choose port.
+ */
+ if ( sat->sat_port == ATADDR_ANYPORT ) {
+ for ( sat->sat_port = ATPORT_RESERVED;
+ sat->sat_port < ATPORT_LAST; sat->sat_port++ ) {
+ if ( ddp_ports[ sat->sat_port - 1 ] == 0 ) {
+ break;
+ }
+ }
+ if ( sat->sat_port == ATPORT_LAST ) {
+ return( EADDRNOTAVAIL );
+ }
+ ddp->ddp_lsat.sat_port = sat->sat_port;
+ ddp_ports[ sat->sat_port - 1 ] = ddp;
+ } else {
+ for ( ddpp = ddp_ports[ sat->sat_port - 1 ]; ddpp;
+ ddpp = ddpp->ddp_pnext ) {
+ if ( ddpp->ddp_lsat.sat_addr.s_net == sat->sat_addr.s_net &&
+ ddpp->ddp_lsat.sat_addr.s_node == sat->sat_addr.s_node ) {
+ break;
+ }
+ }
+ if ( ddpp != NULL ) {
+ return( EADDRINUSE );
+ }
+ ddp->ddp_pnext = ddp_ports[ sat->sat_port - 1 ];
+ ddp_ports[ sat->sat_port - 1 ] = ddp;
+ if ( ddp->ddp_pnext ) {
+ ddp->ddp_pnext->ddp_pprev = ddp;
+ }
+ }
+
+ return( 0 );
+}
+
+at_pcbconnect( ddp, addr )
+ struct ddpcb *ddp;
+ struct mbuf *addr;
+{
+ struct sockaddr_at *sat = mtod( addr, struct sockaddr_at *);
+ struct route *ro;
+ struct at_ifaddr *aa = 0;
+ struct ifnet *ifp;
+ u_short hintnet = 0, net;
+
+ if ( addr->m_len != sizeof( *sat ))
+ return( EINVAL );
+ if ( sat->sat_family != AF_APPLETALK ) {
+ return( EAFNOSUPPORT );
+ }
+
+ /*
+ * Under phase 2, network 0 means "the network". We take "the
+ * network" to mean the network the control block is bound to.
+ * If the control block is not bound, there is an error.
+ */
+ if ( sat->sat_addr.s_net == 0 && sat->sat_addr.s_node != ATADDR_ANYNODE ) {
+ if ( ddp->ddp_lsat.sat_port == ATADDR_ANYPORT ) {
+ return( EADDRNOTAVAIL );
+ }
+ hintnet = ddp->ddp_lsat.sat_addr.s_net;
+ }
+
+ ro = &ddp->ddp_route;
+ /*
+ * If we've got an old route for this pcb, check that it is valid.
+ * If we've changed our address, we may have an old "good looking"
+ * route here. Attempt to detect it.
+ */
+ if ( ro->ro_rt ) {
+ if ( hintnet ) {
+ net = hintnet;
+ } else {
+ net = sat->sat_addr.s_net;
+ }
+ aa = 0;
+ if ( ifp = ro->ro_rt->rt_ifp ) {
+ for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+ if ( aa->aa_ifp == ifp &&
+ ntohs( net ) >= ntohs( aa->aa_firstnet ) &&
+ ntohs( net ) <= ntohs( aa->aa_lastnet )) {
+ break;
+ }
+ }
+ }
+ if ( aa == NULL || ( satosat( &ro->ro_dst )->sat_addr.s_net !=
+ ( hintnet ? hintnet : sat->sat_addr.s_net ) ||
+ satosat( &ro->ro_dst )->sat_addr.s_node !=
+ sat->sat_addr.s_node )) {
+#ifdef ultrix
+ rtfree( ro->ro_rt );
+#else ultrix
+ RTFREE( ro->ro_rt );
+#endif ultrix
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ }
+
+ /*
+ * If we've got no route for this interface, try to find one.
+ */
+ if ( ro->ro_rt == (struct rtentry *)0 ||
+ ro->ro_rt->rt_ifp == (struct ifnet *)0 ) {
+#ifdef BSD4_4
+ ro->ro_dst.sa_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+ ro->ro_dst.sa_family = AF_APPLETALK;
+ if ( hintnet != 0 ) {
+ satosat( &ro->ro_dst )->sat_addr.s_net = hintnet;
+ } else {
+ satosat( &ro->ro_dst )->sat_addr.s_net = sat->sat_addr.s_net;
+ }
+ satosat( &ro->ro_dst )->sat_addr.s_node = sat->sat_addr.s_node;
+ rtalloc( ro );
+ }
+
+ /*
+ * Make sure any route that we have has a valid interface.
+ */
+ aa = 0;
+ if ( ro->ro_rt && ( ifp = ro->ro_rt->rt_ifp )) {
+ for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+ if ( aa->aa_ifp == ifp ) {
+ break;
+ }
+ }
+ }
+ if ( aa == 0 ) {
+ return( ENETUNREACH );
+ }
+
+ ddp->ddp_fsat = *sat;
+ if ( ddp->ddp_lsat.sat_port == ATADDR_ANYPORT ) {
+ return( at_pcbsetaddr( ddp, (struct mbuf *)0 ));
+ }
+ return( 0 );
+}
+
+at_pcbdisconnect( ddp )
+ struct ddpcb *ddp;
+{
+ ddp->ddp_fsat.sat_addr.s_net = ATADDR_ANYNET;
+ ddp->ddp_fsat.sat_addr.s_node = ATADDR_ANYNODE;
+ ddp->ddp_fsat.sat_port = ATADDR_ANYPORT;
+}
+
+at_pcballoc( so )
+ struct socket *so;
+{
+ struct ddpcb *ddp;
+ struct mbuf *m;
+
+ m = m_getclr( M_WAIT, MT_PCB );
+ ddp = mtod( m, struct ddpcb * );
+ ddp->ddp_lsat.sat_port = ATADDR_ANYPORT;
+
+ ddp->ddp_next = ddpcb;
+ ddp->ddp_prev = NULL;
+ ddp->ddp_pprev = NULL;
+ ddp->ddp_pnext = NULL;
+ if ( ddpcb ) {
+ ddpcb->ddp_prev = ddp;
+ }
+ ddpcb = ddp;
+
+ ddp->ddp_socket = so;
+ so->so_pcb = (caddr_t)ddp;
+ return( 0 );
+}
+
+at_pcbdetach( so, ddp )
+ struct socket *so;
+ struct ddpcb *ddp;
+{
+ soisdisconnected( so );
+ so->so_pcb = 0;
+ sofree( so );
+
+ /* remove ddp from ddp_ports list */
+ if ( ddp->ddp_lsat.sat_port != ATADDR_ANYPORT &&
+ ddp_ports[ ddp->ddp_lsat.sat_port - 1 ] != NULL ) {
+ if ( ddp->ddp_pprev != NULL ) {
+ ddp->ddp_pprev->ddp_pnext = ddp->ddp_pnext;
+ } else {
+ ddp_ports[ ddp->ddp_lsat.sat_port - 1 ] = ddp->ddp_pnext;
+ }
+ if ( ddp->ddp_pnext != NULL ) {
+ ddp->ddp_pnext->ddp_pprev = ddp->ddp_pprev;
+ }
+ }
+
+ if ( ddp->ddp_route.ro_rt ) {
+ rtfree( ddp->ddp_route.ro_rt );
+ }
+
+ if ( ddp->ddp_prev ) {
+ ddp->ddp_prev->ddp_next = ddp->ddp_next;
+ } else {
+ ddpcb = ddp->ddp_next;
+ }
+ if ( ddp->ddp_next ) {
+ ddp->ddp_next->ddp_prev = ddp->ddp_prev;
+ }
+
+ (void) m_free( dtom( ddp ));
+}
+
+/*
+ * For the moment, this just find the pcb with the correct local address.
+ * In the future, this will actually do some real searching, so we can use
+ * the sender's address to do de-multiplexing on a single port to many
+ * sockets (pcbs).
+ */
+ struct ddpcb *
+ddp_search( from, to, aa )
+ struct sockaddr_at *from, *to;
+ struct at_ifaddr *aa;
+{
+ struct ddpcb *ddp;
+
+ /*
+ * Check for bad ports.
+ */
+ if ( to->sat_port < ATPORT_FIRST || to->sat_port >= ATPORT_LAST ) {
+ return( NULL );
+ }
+
+ /*
+ * Make sure the local address matches the sent address. What about
+ * the interface?
+ */
+ for ( ddp = ddp_ports[ to->sat_port - 1 ]; ddp; ddp = ddp->ddp_pnext ) {
+ /* XXX should we handle 0.YY? */
+
+ /* XXXX.YY to socket on destination interface */
+ if ( to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net &&
+ to->sat_addr.s_node == ddp->ddp_lsat.sat_addr.s_node ) {
+ break;
+ }
+
+ /* 0.255 to socket on receiving interface */
+ if ( to->sat_addr.s_node == ATADDR_BCAST && ( to->sat_addr.s_net == 0 ||
+ to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net ) &&
+ ddp->ddp_lsat.sat_addr.s_net == AA_SAT( aa )->sat_addr.s_net ) {
+ break;
+ }
+
+ /* XXXX.0 to socket on destination interface */
+ if ( to->sat_addr.s_net == aa->aa_firstnet &&
+ to->sat_addr.s_node == 0 &&
+ ntohs( ddp->ddp_lsat.sat_addr.s_net ) >=
+ ntohs( aa->aa_firstnet ) &&
+ ntohs( ddp->ddp_lsat.sat_addr.s_net ) <=
+ ntohs( aa->aa_lastnet )) {
+ break;
+ }
+ }
+ return( ddp );
+}
+
+ddp_init()
+{
+ atintrq1.ifq_maxlen = IFQ_MAXLEN;
+ atintrq2.ifq_maxlen = IFQ_MAXLEN;
+}
+
+ddp_clean()
+{
+ struct ddpcb *ddp;
+
+ for ( ddp = ddpcb; ddp; ddp = ddp->ddp_next ) {
+ at_pcbdetach( ddp->ddp_socket, ddp );
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef _NETATALK_DDP_VAR_H
+#define _NETATALK_DDP_VAR_H 1
+
+#include <netatalk/endian.h>
+
+struct ddpcb {
+ struct sockaddr_at ddp_fsat, ddp_lsat;
+ struct route ddp_route;
+ struct socket *ddp_socket;
+ struct ddpcb *ddp_prev, *ddp_next;
+ struct ddpcb *ddp_pprev, *ddp_pnext;
+};
+
+#define sotoddpcb(so) ((struct ddpcb *)(so)->so_pcb)
+
+struct ddpstat {
+ u_int32_t ddps_short; /* short header packets received */
+ u_int32_t ddps_long; /* long header packets received */
+ u_int32_t ddps_nosum; /* no checksum */
+ u_int32_t ddps_badsum; /* bad checksum */
+ u_int32_t ddps_tooshort; /* packet too short */
+ u_int32_t ddps_toosmall; /* not enough data */
+ u_int32_t ddps_forward; /* packets forwarded */
+ u_int32_t ddps_encap; /* packets encapsulated */
+ u_int32_t ddps_cantforward; /* packets rcvd for unreachable dest */
+ u_int32_t ddps_nosockspace; /* no space in sockbuf for packet */
+};
+
+#ifdef KERNEL
+struct ddpcb *ddp_ports[ ATPORT_LAST ];
+struct ddpcb *ddpcb;
+struct ddpstat ddpstat;
+struct ddpcb *ddp_search();
+#endif
+
+#endif /* netatalk/ddp_var.h */
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * This file handles both byte ordering and integer sizes.
+ */
+
+# ifndef _ATALK_ENDIAN_H_
+#define _ATALK_ENDIAN_H_
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#ifdef _IBMR2
+#include <sys/machine.h>
+#endif /*_IBMR2*/
+
+#ifdef _ISOC9X_SOURCE
+#include <inttypes.h>
+typedef uint8_t u_int8_t;
+typedef uint16_t u_int16_t;
+typedef uint32_t u_int32_t;
+typedef uint64_t u_int64_t;
+#else
+
+/* handle sunos and solaris */
+#ifdef sun
+#ifdef BSD4_3
+#include <sys/bitypes.h>
+#else
+/* solaris and sunos don't consistently define u_int*_t. */
+typedef unsigned char u_int8_t;
+typedef unsigned short u_int16_t;
+typedef unsigned int u_int32_t;
+typedef int int32_t;
+#endif
+
+typedef unsigned long long u_int64_t;
+
+#ifndef _SSIZE_T
+#define _SSIZE_T
+typedef int ssize_t;
+#endif /* ssize_t */
+
+#else /* sun */
+
+/* luckily ultrix is dead. as a result, we know what the sizes of
+ * various types are forever. this makes some assumptions about integer
+ * sizes. */
+#if defined (ultrix) || defined(HAVE_32BIT_LONGS) || defined(HAVE_64BIT_LONGS)
+typedef unsigned char u_int8_t;
+typedef unsigned short u_int16_t;
+typedef unsigned int u_int32_t;
+typedef int int32_t;
+#endif
+
+#ifdef ultrix
+typedef int ssize_t;
+#endif
+
+#ifdef HAVE_64BIT_LONGS
+typedef unsigned long u_int64_t;
+#else
+/* check for long long support. currently, i assume that if 64-bit
+ * ints exist that their made available via long long */
+#ifdef linux
+#include <endian.h> /* i think this is here for libc4 */
+#else
+#if defined(HAVE_32BIT_LONGS) && !(defined(BSD4_4) || \
+ defined(NO_LARGE_VOL_SUPPORT))
+typedef unsigned long long u_int64_t;
+#endif
+#endif /* linux */
+#endif /* HAVE_64BIT_LONGS */
+#endif /* sun */
+#endif /* ISOC9X */
+
+# ifndef BYTE_ORDER
+#define LITTLE_ENDIAN 1234
+#define BIG_ENDIAN 4321
+#define PDP_ENDIAN 3412
+
+#ifdef sun
+#if defined(i386) || defined(_LITTLE_ENDIAN)
+#define BYTE_ORDER LITTLE_ENDIAN
+#else /*i386*/
+#define BYTE_ORDER BIG_ENDIAN
+#endif /*i386*/
+#else
+#ifdef MIPSEB
+#define BYTE_ORDER BIG_ENDIAN
+#else
+#ifdef MIPSEL
+#define BYTE_ORDER LITTLE_ENDIAN
+#else
+#error Like, what is your byte order, man?
+#endif /*MIPSEL*/
+#endif /*MIPSEB*/
+#endif /*sun*/
+# endif /*BYTE_ORDER*/
+
+# ifndef ntohl
+# if defined( sun ) || defined( ultrix ) || defined( _IBMR2 )
+#if BYTE_ORDER == BIG_ENDIAN
+#define ntohl(x) (x)
+#define ntohs(x) (x)
+#define htonl(x) (x)
+#define htons(x) (x)
+
+#else
+#if defined( mips ) && defined( KERNEL )
+#define ntohl(x) nuxi_l(x)
+#define ntohs(x) nuxi_s(x)
+#define htonl(x) nuxi_l(x)
+#define htons(x) nuxi_s(x)
+
+#else /*mips KERNEL*/
+
+#if !( defined( sun ) && defined( i386 ))
+unsigned short ntohs(), htons();
+unsigned int ntohl(), htonl();
+#endif
+
+#endif /*mips KERNEL*/
+#endif /*BYTE_ORDER*/
+# endif /*sun ultrix _IBMR2*/
+# endif /*ntohl*/
+
+# endif /*_ATALK_ENDIAN_H_*/
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ */
+
+# if defined( ultrix ) || defined( BSD4_4 )
+#include <net/if_llc.h>
+# else ultrix BSD4_4
+
+#if defined( sun ) && !defined( __svr4__ )
+#include <net/if_ieee802.h>
+#endif sun __svr4__
+
+/*
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * @(#)if_llc.h 7.2 (Berkeley) 6/28/90
+ */
+
+/*
+ * IEEE 802.2 Link Level Control headers, for use in conjunction with
+ * 802.{3,4,5} media access control methods.
+ *
+ * Headers here do not use bit fields due to shortcommings in many
+ * compilers.
+ */
+
+struct llc {
+ u_char llc_dsap;
+ u_char llc_ssap;
+ union {
+ struct {
+ u_char control;
+ u_char format_id;
+ u_char class;
+ u_char window_x2;
+ } type_u;
+ struct {
+ u_char num_snd_x2;
+ u_char num_rcv_x2;
+ } type_i;
+ struct {
+ u_char control;
+ u_char num_rcv_x2;
+ } type_s;
+ struct {
+ u_char control;
+ u_char org_code[3];
+ u_short ether_type;
+ } type_snap;
+ } llc_un;
+};
+#define llc_control llc_un.type_u.control
+#define llc_fid llc_un.type_u.format_id
+#define llc_class llc_un.type_u.class
+#define llc_window llc_un.type_u.window_x2
+#define llc_org_code llc_un.type_snap.org_code
+#define llc_ether_type llc_un.type_snap.ether_type
+
+#define LLC_UI 0x3
+#define LLC_UI_P 0x13
+#define LLC_XID 0xaf
+#define LLC_XID_P 0xbf
+#define LLC_TEST 0xe3
+#define LLC_TEST_P 0xf3
+
+#define LLC_ISO_LSAP 0xfe
+#define LLC_SNAP_LSAP 0xaa
+
+# endif ultrix BSD4_4
+
+#if defined( sun ) || defined( ibm032 )
+#define SIOCPHASE1 _IOW(i, 100, struct ifreq) /* AppleTalk phase 1 */
+#define SIOCPHASE2 _IOW(i, 101, struct ifreq) /* AppleTalk phase 2 */
+#endif sun ibm032
+
+#if defined( ultrix ) || defined( BSD4_4 ) || defined( _IBMR2 )
+#define SIOCPHASE1 _IOW('i', 100, struct ifreq) /* AppleTalk phase 1 */
+#define SIOCPHASE2 _IOW('i', 101, struct ifreq) /* AppleTalk phase 2 */
+#endif ultrix BSD4_4 _IBMR2
--- /dev/null
+# NetBSD specific defines, passed to subdirectories.
+DEFS= -DBSD4_4 -DDLSYM_PREPEND_UNDERSCORE
+OPTOPTS= -O2
+CC= gcc -I../../sys/netbsd/ -I/usr/include/kerberosIV
+CSHAREDFLAGS=-fPIC
+LDSHARED=ld
+LDSHAREDFLAGS=$$MACHLDSHAREDFLAGS
+#LDFLAGS_EXPORT=-Wl,-E
+#LIBSHARED=-ldl
+INSTALL= install
+AFPLIBS =
+ADDLIBS =
+
+ALL= ../../libatalk ../../include ../../bin ../../etc ../../man
+
+oops:
+ @echo "Read README again. Don't type 'make' here."
+ @exit 1
+
+all: ${ALL}
+
+../../bin ../../etc: ../../libatalk
+
+${ALL}: FRC
+ @case ${MACHINETYPE} in \
+ alpha|mips|powerpc) MACHLDSHAREDFLAGS=-shared ;; \
+ *) MACHLDSHAREDFLAGS=-Bshareable ;; \
+ esac; \
+ cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+ AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+ LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+ LIBSHARED="${LIBSHARED}" \
+ all
+
+FRC:
+
+install :
+ -mkdir ${DESTDIR} ${SBINDIR} ${BINDIR} ${ETCDIR} ${LIBDIR}
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" AFPLIBS="${AFPLIBS}" \
+ LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+ CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+ INSTALL="${INSTALL}" $@); \
+ done
+ rm -f ${ETCDIR}/rc.atalk
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../distrib/initscripts/rc.atalk.bsd > ${ETCDIR}/rc.atalk
+ if [ -f ${ETCDIR}/afpd.conf ]; then \
+ echo "Retaining old afpd.conf file."; \
+ else \
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../config/afpd.conf > ${ETCDIR}/afpd.conf; \
+ fi
+ @echo
+ @echo "Install is done. You might need to add services from"
+ @echo "services.atalk to /etc/services. The ddp services need to be"
+ @echo "added for NetBSD versions earlier than 1.3, and the AFP over"
+ @echo "TCP services need to be added for NetBSD 1.3.X, and for"
+ @echo "1.3-current dated June 27, 1998 and earlier."
+ @echo
+ @echo "Don't forget to call rc.atalk in /etc/rc.local. See README"
+ @echo "and README.NETBSD for more information."
+
+clean :
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+ done
+
+depend :
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" depend); \
+ done
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+
+/*
+ * netatalk/endian.h
+ *
+ * This file is a dummy replacement for endian.h. NetBSD includes
+ * machine/endian.h in sys/types.h, and it was decided that a
+ * second include file was not needed. This file is included to
+ * make all the source code compile correctly.
+ */
--- /dev/null
+# OpenBSD specific defines, passed to subdirectories.
+DEFS= -DBSD4_4
+OPTOPTS= -O2
+CC= gcc
+CSHAREDFLAGS=-fPIC
+LDSHARED=ld
+LDSHAREDFLAGS=-Bforcearchive -shared
+#LDFLAGS_EXPORT=-Wl,-E
+#LIBSHARED=-ldl
+INSTALL= install
+AFPLIBS=
+ADDLIBS=
+
+ALL= ../../libatalk ../../include ../../bin ../../etc ../../man
+
+oops:
+ @echo "Read README again. Don't type 'make' here."
+ @exit 1
+
+all: ${ALL}
+
+../../bin ../../etc: ../../libatalk
+
+${ALL}: FRC
+ cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+ AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+ LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+ LIBSHARED="${LIBSHARED}" \
+ all
+
+FRC:
+
+install :
+ -mkdir ${DESTDIR} ${SBINDIR} ${BINDIR} ${ETCDIR} ${LIBDIR}
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" AFPLIBS="${AFPLIBS}" \
+ LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+ CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+ INSTALL="${INSTALL}" $@); \
+ done
+ rm -f ${ETCDIR}/rc.atalk
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../distrib/initscripts/rc.atalk.bsd > ${ETCDIR}/rc.atalk
+ if [ -f ${ETCDIR}/afpd.conf ]; then \
+ echo "Retaining old afpd.conf file."; \
+ else \
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../config/afpd.conf > ${ETCDIR}/afpd.conf; \
+ fi
+ @echo
+ @echo "Install is done. Don't forget to add lines from"
+ @echo "services.atalk to /etc/services and to call rc.atalk"
+ @echo "in /etc/rc. See README and README.OPENBSD for more"
+ @echo "information."
+
+clean :
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+ done
+
+depend :
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" depend); \
+ done
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+# stuff for os x server. This needs some include files from the darwin
+# source to work properly. namely, it needs /usr/include/at,
+# /usr/include/h, and
+# /System/Library/Frameworks/LibcAT/Headers/at_proto.h. The first two are
+# found in the kernel sources while the last one is found in LibcAT.
+#
+# notes: apple's dynamic library loader is pretty braindead.
+# you cannot do a kill -HUP afpd as a result. in addition, you
+# can't load both uams_randnum.so and uams_dhx.so at the same time
+# as they use the same libraries.
+# don't do tail /var/log/system.log a lot. it will cause os x
+# server to crash. it's probably a race somewhere.
+#
+DEFS=-DBSD4_4 -DHAVE_BROKEN_CPP -DHAVE_2ARG_DBTOB -DNO_DLFCN_H \
+ -DMACOSX_SERVER
+OPTOPTS= -O2 -fomit-frame-pointer
+CC = cc
+#CC= gcc
+CSHAREDFLAGS=-fPIC -fno-common
+LDSHARED=cc
+LDSHAREDFLAGS=-bundle -undefined suppress
+INSTALL= install
+ADDLIBS=-framework LibcAT
+
+ALL= ../../libatalk ../../include ../../bin ../../etc ../../man
+
+oops:
+ @echo "Read README again. Don't type 'make' here."
+ @exit 1
+
+all: ${ALL}
+
+../../bin ../../etc: ../../libatalk
+
+${ALL}: FRC
+ cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+ AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+ LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+ LIBSHARED="${LIBSHARED}" \
+ all
+
+FRC:
+
+install :
+ -mkdir ${DESTDIR} ${SBINDIR} ${BINDIR} ${ETCDIR} ${LIBDIR}
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" AFPLIBS="${AFPLIBS}" \
+ LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+ CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+ INSTALL="${INSTALL}" $@); \
+ done
+ rm -f ${ETCDIR}/rc.atalk
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../distrib/initscripts/rc.atalk.bsd > ${ETCDIR}/rc.atalk
+ chmod 744 ${ETCDIR}/rc.atalk
+ if [ -f ${ETCDIR}/afpd.conf ]; then \
+ echo "Retaining old afpd.conf file."; \
+ else \
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../config/afpd.conf > ${ETCDIR}/afpd.conf; \
+ fi
+ @echo
+ @echo "Install is done. Don't forget to add lines from"
+ @echo "services.atalk to /etc/services and to call rc.atalk"
+ @echo "in /etc/rc. See README and README.GENERIC for more"
+ @echo "information. Also, you do not need to start up atalkd."
+
+clean :
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+ done
+
+depend :
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" depend); \
+ done
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+# Solaris specific defines, passed to subdirectories.
+# To use Sun CC, uncomment the CC and KFLAGS variables.
+
+# uncomment for 64-bit sparc kernel
+#SPARC64=true
+
+# solaris has -lcrypt, but we shouldn't use it. this may only be true
+# for solaris 2.5.1
+USE_CRYPTLIB=no
+
+# use gcc (gcc-2.95 on 64-bit sparc)
+CC= gcc
+KCFLAGS= -D_KERNEL -Wall -Wstrict-prototypes ${KGCCFLAGS} # -mcpu=ultrasparc
+OPTOPTS= -O
+CSHAREDFLAGS= -fPIC
+LDSHARED= gcc
+LDSHAREDFLAGS= -shared
+
+# use Sun CC (for a 64-bit kernel, uncomment " -xarch=v9 -xregs=no%appl ")
+#CC= cc
+#KCFLAGS= -D_KERNEL # -xarch=v9 -xregs=no%appl
+#OPTOPTS= -fast -xO5 -xstrconst
+#CSHAREDFLAGS= -KPIC
+#LDSHARED= cc
+#LDSHAREDFLAGS= -G
+#LDFLAGS_EXPORT=
+
+LIBSHARED= -ldl
+
+# -D_ISOC9X_SOURCE is handled by OSVERSION. basically, it's not needed
+# with 2.5.1.
+# add -DHAVE_IFNAMEINDEX if you're using solaris 8.
+DEFS= -DNO_STRUCT_TM_GMTOFF -D__svr4__ -DSOLARIS -I../../sys/generic \
+ ${OSDEFS} ${MACHINEDEFS}
+INSTALL= /usr/ucb/install
+AFPLIBS=
+ADDLIBS= -lsocket -lnsl
+
+# Local build stuff.
+
+SRC= linkage.c tpi.c dlpi.c ioc.c if.c aarp.c ddp.c sock.c rt.c
+OBJ= linkage.o tpi.o dlpi.o ioc.o if.o aarp.o ddp.o sock.o rt.o
+
+INCPATH= -I../../include -I../netatalk
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH} ${KCFLAGS}
+
+ALL= ../../libatalk ../../include ../../bin ../../etc ../../man
+
+oops:
+ @echo "Read README again. Don't type 'make' here."
+ @exit 1
+
+all : kernel ${ALL}
+
+kernel : FRC
+ @case `uname -p` in \
+ i386) \
+ PROCESSOR=i386; \
+ KGCCFLAGS=""; \
+ ;; \
+ sparc) \
+ PROCESSOR=sparc; \
+ KGCCFLAGS="-mno-app-regs -munaligned-doubles \
+ -fpcc-struct-return" \
+ ;; \
+ *) echo "Unknown processor type..."; exit 1 \
+ ;; \
+ esac; \
+ if [ x"${OSVERSION}" != x"5.5.1" ]; then \
+ OSDEFS=-D_ISOC9X_SOURCE; \
+ fi; \
+ echo "Making $@ for $$PROCESSOR..."; \
+ ${MAKE} ${MFLAGS} \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}"\
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ MANDIR="${MANDIR}" DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" \
+ KRBDIR="${KRBDIR}" OSDEFS="$${OSDEFS}" \
+ MACHINEDEFS="$${MACHINEDEFS}" \
+ AFPLIBS="${AFPLIBS}" KGCCFLAGS="$${KGCCFLAGS}" ddp
+
+FRC:
+
+ddp : ${OBJ}
+ ld -r -o ddp ${OBJ}
+
+linkage.o : linkage.c
+ ${CC} ${CFLAGS} -DVERSION=\"`cat ../../VERSION`\" -c linkage.c
+
+../../bin ../../etc: ../../libatalk
+
+${ALL}: FRC
+ if [ x"${OSVERSION}" != x"5.5.1" ]; then \
+ OSDEFS=-D_ISOC9X_SOURCE; \
+ fi; \
+ cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS} $${OSDEFS}" \
+ OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+ AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+ LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+ LIBSHARED="${LIBSHARED}" USE_CRYPTLIB="${USE_CRYPTLIB}" \
+ OSDEFS="$${OSDEFS}" MACHINEDEFS="$${MACHINEDEFS}" \
+ all
+
+FRC:
+
+kuninstall : FRC
+ rm -f /etc/rc2.d/S79atalk /etc/rc0.d/K79atalk
+ if [ x"${SPARC64}" != x ] ; then \
+ ${RM} /usr/kernel/drv/sparcv9/ddp; \
+ ${RM} /usr/kernel/strmod/sparcv9/ddp; \
+ else \
+ ${RM} /usr/kernel/drv/ddp; \
+ ${RM} /usr/kernel/strmod/ddp; \
+ fi
+ ${RM} /usr/kernel/drv/ddp.conf
+ -rem_drv ddp
+ sync;sync;sync
+
+kinstall : kernel kuninstall
+ if [ x"${SPARC64}" != x ]; then \
+ ${INSTALL} -c ddp /usr/kernel/drv/sparcv9/ddp; \
+ ln /usr/kernel/drv/sparcv9/ddp /usr/kernel/strmod/sparcv9/ddp; \
+ else \
+ ${INSTALL} -c ddp /usr/kernel/drv/ddp; \
+ ln /usr/kernel/drv/ddp /usr/kernel/strmod/ddp; \
+ fi
+ ${INSTALL} -c ddp.conf /usr/kernel/drv/ddp.conf
+ add_drv -m '* 0666 root sys' ddp
+ sync;sync;sync
+ if [ -f /etc/init.d/atalk ]; then \
+ echo "Preserving existing /etc/init.d/atalk settings."; \
+ else \
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../distrib/initscripts/rc.atalk.sysv > /etc/init.d/atalk; \
+ fi
+ chmod 744 /etc/init.d/atalk
+ -ln -s ../init.d/atalk /etc/rc2.d/S79atalk
+ -ln -s ../init.d/atalk /etc/rc0.d/K79atalk
+
+install :
+ -mkdir ${DESTDIR} ${SBINDIR} ${BINDIR} ${ETCDIR} ${LIBDIR}
+ sed -e s@:BINDIR:@${BINDIR}@ < ../../lp2pap.sh > ${RESDIR}/lp2pap.sh
+ chmod 744 ${RESDIR}/lp2pap.sh
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+ AFPLIBS="${AFPLIBS}" \
+ LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+ CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+ OSDEFS="$${OSDEFS}" MACHINEDEFS="$${MACHINEDEFS}" \
+ INSTALL="${INSTALL}" $@); \
+ done
+ if [ -f ${ETCDIR}/afpd.conf ]; then \
+ echo "Retaining old afpd.conf file."; \
+ else \
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../config/afpd.conf > ${ETCDIR}/afpd.conf; \
+ fi
+ @echo
+ @echo "Install is done. Don't forget to add lines from"
+ @echo "services.atalk to /etc/services and to call rc.atalk"
+ @echo "in /etc/rc. See README and README.SOLARIS for more"
+ @echo "information."
+
+clean : sysclean
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+ done
+
+klean sysclean :
+ rm -f a.out core* *.o *.bak *[Ee]rrs tags
+ rm -f ddp
+
+depend :
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" depend); \
+ done
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/byteorder.h>
+#include <sys/errno.h>
+#include <sys/stream.h>
+#include <sys/ethernet.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#include <netinet/arp.h>
+#include <net/if.h>
+
+#include <string.h>
+
+#include <netatalk/at.h>
+#include <netatalk/aarp.h>
+#include <netatalk/phase2.h>
+
+#include "if.h"
+
+struct aarplist {
+ struct aarplist *aal_next, *aal_prev;
+ struct at_addr aal_addr;
+ u_char aal_hwaddr[ ETHERADDRL ];
+ u_char aal_age;
+ u_char aal_flags;
+ mblk_t *aal_m;
+};
+
+ struct aarplist *
+aarp_find( struct atif_data *aid, ushort net, unchar node )
+{
+ struct aarplist *aal;
+
+ for ( aal = aid->aid_aarplist; aal != NULL; aal = aal->aal_next ) {
+ if ( aal->aal_addr.s_net == net && aal->aal_addr.s_node == node ) {
+ break;
+ }
+ }
+ return( aal );
+}
+
+ struct aarplist *
+aarp_alloc( struct atif_data *aid, ushort net, unchar node )
+{
+ struct aarplist *aal;
+
+ for ( aal = aid->aid_aarplist; aal != NULL; aal = aal->aal_next ) {
+ if ( aal->aal_addr.s_net == net && aal->aal_addr.s_node == node ) {
+ return( aal );
+ }
+ }
+
+ if ( aid->aid_aarpflist == NULL ) {
+ if (( aal = (struct aarplist *)kmem_alloc( sizeof( struct aarplist ),
+ KM_NOSLEEP )) == NULL ) {
+ return( NULL );
+ }
+ } else {
+ aal = aid->aid_aarpflist;
+ aid->aid_aarpflist = aal->aal_next;
+ if ( aid->aid_aarpflist != NULL ) {
+ aid->aid_aarpflist->aal_prev = NULL;
+ }
+ }
+
+ aal->aal_addr.s_net = net;
+ aal->aal_addr.s_node = node;
+ bzero( aal->aal_hwaddr, sizeof( aal->aal_hwaddr ));
+ aal->aal_age = 0;
+ aal->aal_flags = 0;
+ aal->aal_m = NULL;
+
+ aal->aal_next = aid->aid_aarplist;
+ aal->aal_prev = NULL;
+ if ( aid->aid_aarplist != NULL ) {
+ aid->aid_aarplist->aal_prev = aal;
+ }
+ aid->aid_aarplist = aal;
+
+ return( aal );
+}
+
+/*
+ * Move entry to free list.
+ */
+ void
+aarp_free( struct atif_data *aid, struct aarplist *aal )
+{
+ if ( aal->aal_next != NULL ) {
+ aal->aal_next->aal_prev = aal->aal_prev;
+ }
+ if ( aal->aal_prev != NULL ) {
+ aal->aal_prev->aal_next = aal->aal_next;
+ }
+ if ( aid->aid_aarplist == aal ) {
+ aid->aid_aarplist = aal->aal_next;
+ }
+
+ if ( aal->aal_m != NULL ) {
+ freemsg( aal->aal_m );
+ aal->aal_m = NULL;
+ }
+
+ aal->aal_prev = NULL;
+ aal->aal_next = aid->aid_aarpflist;
+ if ( aid->aid_aarpflist != NULL ) {
+ aid->aid_aarpflist->aal_prev = aal;
+ }
+ aid->aid_aarpflist = aal;
+ return;
+}
+
+ void
+aarp_timeout( struct atif_data *aid )
+{
+ struct aarplist *aal, *p;
+
+ aid->aid_aarptimeo = qtimeout( aid->aid_q, aarp_timeout,
+ (caddr_t)aid, 60 * hz );
+ for ( aal = aid->aid_aarplist; aal != NULL; aal = p ) {
+ p = aal->aal_next;
+ if ( ++aal->aal_age < (( aal->aal_flags ) ? 5 : 3 )) {
+ continue;
+ }
+ aarp_free( aid, aal );
+ }
+ return;
+}
+
+ void
+aarp_init( struct atif_data *aid )
+{
+ aid->aid_aarptimeo = qtimeout( aid->aid_q, aarp_timeout,
+ (caddr_t)aid, 60 * hz );
+ return;
+}
+
+ void
+aarp_clean( struct atif_data *aid )
+{
+ struct aarplist *aal, *p;
+
+ if ( aid->aid_aarptimeo != 0 ) {
+ quntimeout( aid->aid_q, aid->aid_aarptimeo );
+ aid->aid_aarptimeo = 0;
+ }
+
+ for ( aal = aid->aid_aarplist; aal != NULL; aal = p ) {
+ p = aal->aal_next;
+ if ( aal->aal_m != NULL ) {
+ freemsg( aal->aal_m );
+ aal->aal_m = NULL;
+ }
+ kmem_free( aal, sizeof( struct aarplist ));
+ }
+ aid->aid_aarplist = NULL;
+
+ for ( aal = aid->aid_aarpflist; aal != NULL; aal = p ) {
+ p = aal->aal_next;
+ if ( aal->aal_m != NULL ) {
+ freemsg( aal->aal_m );
+ aal->aal_m = NULL;
+ }
+ kmem_free( aal, sizeof( struct aarplist ));
+ }
+ aid->aid_aarpflist = NULL;
+
+ return;
+}
+
+ int
+aarp_rput( queue_t *q, mblk_t *m )
+{
+ struct atif_data *aid = (struct atif_data *)q->q_ptr;
+ struct ether_aarp *ea;
+ struct aarplist *aal;
+ ushort tpnet, spnet, op;
+
+ if ( m->b_wptr - m->b_rptr < sizeof( struct ether_aarp )) {
+ cmn_err( CE_NOTE, "aarp_rput short packet\n" );
+ goto done;
+ }
+
+ ea = (struct ether_aarp *)m->b_rptr;
+
+ if ( ea->aarp_hrd != htons( AARPHRD_ETHER ) ||
+ ea->aarp_pro != htons( ETHERTYPE_AT ) ||
+ ea->aarp_hln != sizeof( ea->aarp_sha ) ||
+ ea->aarp_pln != sizeof( ea->aarp_spu )) {
+ cmn_err( CE_NOTE, "aarp_rput bad constants\n" );
+ goto done;
+ }
+
+ if ( bcmp( ea->aarp_sha, aid->aid_hwaddr, sizeof( ea->aarp_sha )) == 0 ) {
+ goto done;
+ }
+
+ op = ntohs( ea->aarp_op );
+ bcopy( ea->aarp_tpnet, &tpnet, sizeof( tpnet ));
+ bcopy( ea->aarp_spnet, &spnet, sizeof( spnet ));
+
+ if ( aid->aid_flags & AIDF_PROBING ) {
+ if ( tpnet == aid->aid_sat.sat_addr.s_net &&
+ ea->aarp_tpnode == aid->aid_sat.sat_addr.s_node ) {
+ aid->aid_flags &= ~AIDF_PROBING;
+ aid->aid_flags |= AIDF_PROBEFAILED;
+ cmn_err( CE_NOTE, "aarp_rput probe collision %s\n", aid->aid_name );
+ }
+ } else {
+ if ( tpnet == aid->aid_sat.sat_addr.s_net &&
+ ea->aarp_tpnode == aid->aid_sat.sat_addr.s_node ) {
+ switch ( op ) {
+ case AARPOP_REQUEST :
+ aal = aarp_alloc( aid, spnet, ea->aarp_spnode );
+ bcopy( ea->aarp_sha, aal->aal_hwaddr, sizeof( ea->aarp_sha ));
+ aal->aal_age = 0;
+ aal->aal_flags = 1; /* complete */
+ case AARPOP_PROBE :
+ aarp_send( aid, AARPOP_RESPONSE, ea->aarp_sha,
+ spnet, ea->aarp_spnode );
+ break;
+
+ case AARPOP_RESPONSE :
+ if (( aal =
+ aarp_find( aid, spnet, ea->aarp_spnode )) == NULL ) {
+ break;
+ }
+ bcopy( ea->aarp_sha, aal->aal_hwaddr, sizeof( ea->aarp_sha ));
+ aal->aal_age = 0;
+ aal->aal_flags = 1; /* complete */
+ if ( aal->aal_m != NULL ) {
+ dl_unitdata_req( WR( q ), aal->aal_m, ETHERTYPE_AT,
+ aal->aal_hwaddr );
+ aal->aal_m = NULL;
+ }
+ break;
+
+ default :
+ cmn_err( CE_NOTE, "aarp_rput bad op %X\n", op );
+ break;
+ }
+ } else {
+ switch ( op ) {
+ case AARPOP_REQUEST :
+ break;
+ case AARPOP_PROBE :
+ if (( aal =
+ aarp_find( aid, spnet, ea->aarp_spnode )) != NULL ) {
+ aarp_free( aid, aal );
+ }
+ break;
+
+ case AARPOP_RESPONSE :
+ cmn_err( CE_NOTE, "aarp_rput someone using our address\n" );
+ break;
+
+ default :
+ cmn_err( CE_NOTE, "aarp_rput bad op %X\n", op );
+ break;
+ }
+ }
+ }
+
+done :
+ freemsg( m );
+ return( 0 );
+}
+
+ void
+aarp_send( struct atif_data *aid, int op, caddr_t hwaddr,
+ ushort net, unchar node )
+{
+ mblk_t *m;
+ struct ether_aarp *ea;
+
+ if (( m = allocb( sizeof( struct ether_aarp ), BPRI_HI )) == NULL ) {
+ return;
+ }
+ m->b_wptr = m->b_rptr + sizeof( struct ether_aarp );
+ ea = (struct ether_aarp *)m->b_rptr;
+ bzero( (caddr_t)ea, sizeof( struct ether_aarp ));
+
+ ea->aarp_hrd = htons( AARPHRD_ETHER );
+ ea->aarp_pro = htons( ETHERTYPE_AT );
+ ea->aarp_hln = sizeof( ea->aarp_sha );
+ ea->aarp_pln = sizeof( ea->aarp_spu );
+ ea->aarp_op = htons( op );
+ bcopy( aid->aid_hwaddr, ea->aarp_sha, sizeof( ea->aarp_sha ));
+
+ if ( hwaddr == NULL ) {
+ bzero( ea->aarp_tha, sizeof( ea->aarp_tha ));
+ } else {
+ bcopy( hwaddr, ea->aarp_tha, sizeof( ea->aarp_tha ));
+ }
+
+ ea->aarp_tpnode = node;
+ bcopy( &aid->aid_sat.sat_addr.s_net, ea->aarp_spnet,
+ sizeof( ea->aarp_spnet ));
+ bcopy( &net, ea->aarp_tpnet, sizeof( ea->aarp_tpnet ));
+ ea->aarp_spnode = aid->aid_sat.sat_addr.s_node;
+ ea->aarp_tpnode = node;
+
+ if ( hwaddr == NULL ) {
+ dl_unitdata_req( WR( aid->aid_q ), m, ETHERTYPE_AARP,
+ at_multicastaddr );
+ } else {
+ dl_unitdata_req( WR( aid->aid_q ), m, ETHERTYPE_AARP, hwaddr );
+ }
+ return;
+}
+
+ int
+aarp_resolve( struct atif_data *aid, mblk_t *m, struct sockaddr_at *sat )
+{
+ struct aarplist *aal;
+
+ if ( sat->sat_addr.s_node == ATADDR_BCAST ) {
+ dl_unitdata_req( WR( aid->aid_q ), m, ETHERTYPE_AT, at_multicastaddr );
+ return( 0 );
+ }
+
+ if (( aal = aarp_alloc( aid, sat->sat_addr.s_net, sat->sat_addr.s_node )) ==
+ NULL ) {
+ freemsg( m );
+ return( ENOMEM );
+ }
+ aal->aal_age = 0;
+
+ if ( aal->aal_flags ) { /* complete */
+ dl_unitdata_req( WR( aid->aid_q ), m, ETHERTYPE_AT, aal->aal_hwaddr );
+ } else {
+ /* send aarp request */
+ if ( aal->aal_m != NULL ) {
+ freemsg( aal->aal_m );
+ }
+ /* either freed above, in timeout, or sent in aarp_rput() */
+ aal->aal_m = m;
+ aarp_send( aid, AARPOP_REQUEST, NULL,
+ sat->sat_addr.s_net, sat->sat_addr.s_node );
+ }
+ return( 0 );
+}
--- /dev/null
+#include <sys/types.h>
+#include <sys/stream.h>
+#include <sys/cmn_err.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/tihdr.h>
+#include <sys/ddi.h>
+#include <sys/ethernet.h>
+#include <net/if.h>
+
+#include <string.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/ddp.h>
+#include <netatalk/at.h>
+
+#include "if.h"
+#include "sock.h"
+
+ int
+ddp_rput( struct atif_data *aid, mblk_t *m )
+{
+ struct atif_data *daid;
+ struct ddpehdr *deh;
+ struct sockaddr_at sat;
+ struct sock_data *sd;
+
+ if ( m->b_wptr - m->b_rptr < sizeof( struct ddpehdr )) {
+ cmn_err( CE_NOTE, "ddp_rput short packet\n" );
+ freemsg( m );
+ return( EINVAL );
+ }
+
+ deh = (struct ddpehdr *)m->b_rptr;
+
+ sat.sat_addr.s_net = deh->deh_dnet;
+ sat.sat_addr.s_node = deh->deh_dnode;
+ sat.sat_port = deh->deh_dport;
+
+ if (( daid = if_dest( aid, &sat )) != NULL ) {
+ if (( sd = sock_dest( daid, &sat )) != NULL ) {
+ if ( sd->sd_state != TS_IDLE ) {
+ freemsg( m );
+ return( EHOSTDOWN );
+ }
+ bzero( (caddr_t)&sat, sizeof( struct sockaddr_at ));
+ sat.sat_family = AF_APPLETALK;
+ sat.sat_addr.s_net = deh->deh_snet;
+ sat.sat_addr.s_node = deh->deh_snode;
+ sat.sat_port = deh->deh_sport;
+ adjmsg( m, sizeof( struct ddpehdr ));
+ t_unitdata_ind( WR( sd->sd_q ), m, &sat );
+ } else {
+ /* toss it */
+ freemsg( m );
+ return( EHOSTDOWN );
+ }
+ } else {
+ /* route it */
+ freemsg( m );
+ return( ENETUNREACH );
+ }
+ return( 0 );
+}
--- /dev/null
+#
+# Netatalk driver's configuration file
+#
+
+name="ddp" parent="pseudo" instance=0;
--- /dev/null
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/stream.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/dlpi.h>
+#include <sys/ethernet.h>
+#include <sys/byteorder.h>
+#include <sys/sunddi.h>
+#include <net/if.h>
+#include <errno.h>
+
+#include <netatalk/phase2.h>
+#include <netatalk/at.h>
+
+#include "ioc.h"
+#include "if.h"
+
+u_char at_multicastaddr[ ETHERADDRL ] = {
+ 0x09, 0x00, 0x07, 0xff, 0xff, 0xff,
+};
+u_char at_org_code[ 3 ] = {
+ 0x08, 0x00, 0x07,
+};
+u_char aarp_org_code[ 3 ] = {
+ 0x00, 0x00, 0x00,
+};
+
+ static int
+dlpi_open( queue_t *q, dev_t *dev, int oflag, int sflag, cred_t *cred )
+{
+ struct atif_data *aid;
+ int err = 0;
+
+ if (( err = drv_priv( cred )) != 0 ) {
+ return( err );
+ }
+ if (( aid = if_alloc( q )) == NULL ) {
+ return( ENOMEM );
+ }
+ q->q_ptr = (void *)aid;
+
+ qprocson( q );
+ return( err );
+}
+
+ static int
+dlpi_close( queue_t *q, int oflag, cred_t *cred )
+{
+ struct atif_data *aid = (struct atif_data *)q->q_ptr;
+
+ qprocsoff( q );
+ if_free( aid );
+ return( 0 );
+}
+
+ static int
+dl_bind_req( queue_t *q, ulong sap )
+{
+ union DL_primitives *dl;
+ mblk_t *m;
+
+ if (( m = allocb( DL_BIND_REQ_SIZE, BPRI_HI )) == NULL ) {
+ return( ENOMEM );
+ }
+ m->b_wptr = m->b_rptr + DL_BIND_REQ_SIZE;
+ m->b_datap->db_type = M_PROTO;
+
+ dl = (union DL_primitives *)m->b_rptr;
+ dl->dl_primitive = DL_BIND_REQ;
+ dl->bind_req.dl_sap = sap;
+ dl->bind_req.dl_max_conind = 0;
+ dl->bind_req.dl_service_mode = DL_CLDLS;
+ dl->bind_req.dl_conn_mgmt = 0;
+ dl->bind_req.dl_xidtest_flg = 0; /* XXX */
+ putnext( q, m );
+ return( 0 );
+}
+
+ static int
+dl_attach_req( queue_t *q, ulong ppa )
+{
+ union DL_primitives *dl;
+ mblk_t *m;
+
+ if (( m = allocb( DL_ATTACH_REQ_SIZE, BPRI_HI )) == NULL ) {
+ return( ENOMEM );
+ }
+ m->b_wptr = m->b_rptr + DL_ATTACH_REQ_SIZE;
+ m->b_datap->db_type = M_PROTO;
+
+ dl = (union DL_primitives *)m->b_rptr;
+ dl->dl_primitive = DL_ATTACH_REQ;
+ dl->attach_req.dl_ppa = ppa;
+ putnext( q, m );
+ return( 0 );
+}
+
+ int
+dl_enabmulti_req( queue_t *q, caddr_t addr )
+{
+ union DL_primitives *dl;
+ mblk_t *m;
+
+ if (( m = allocb( DL_ENABMULTI_REQ_SIZE + ETHERADDRL, BPRI_HI )) == NULL ) {
+ return( ENOMEM );
+ }
+ m->b_wptr = m->b_rptr + DL_ENABMULTI_REQ_SIZE;
+ m->b_datap->db_type = M_PROTO;
+
+ dl = (union DL_primitives *)m->b_rptr;
+ dl->dl_primitive = DL_ENABMULTI_REQ;
+ dl->enabmulti_req.dl_addr_length = ETHERADDRL;
+ dl->enabmulti_req.dl_addr_offset = m->b_wptr - m->b_rptr;
+ bcopy( addr, m->b_wptr, ETHERADDRL );
+ m->b_wptr += ETHERADDRL;
+ putnext( q, m );
+ return( 0 );
+}
+
+ int
+dl_unitdata_req( queue_t *q, mblk_t *m0, ushort type, caddr_t addr )
+{
+ union DL_primitives *dl;
+ struct llc *llc;
+ mblk_t *m1, *m;
+ ushort len;
+
+ /* len = msgdsize( m0 ) + sizeof( struct llc ); */
+
+ if (( m1 = allocb( sizeof( struct llc ), BPRI_HI )) == NULL ) {
+ cmn_err( CE_NOTE, "dl_unitdate_req NOMEM 1\n" );
+ return( ENOMEM );
+ }
+ m1->b_wptr = m1->b_rptr + sizeof( struct llc );
+ m1->b_datap->db_type = M_DATA;
+ llc = (struct llc *)m1->b_rptr;
+
+ llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+ llc->llc_control = LLC_UI;
+ if ( type == ETHERTYPE_AARP ) {
+ bcopy( aarp_org_code, llc->llc_org_code, sizeof( aarp_org_code ));
+ } else if ( type == ETHERTYPE_AT ) {
+ bcopy( at_org_code, llc->llc_org_code, sizeof( aarp_org_code ));
+ } else {
+ cmn_err( CE_NOTE, "dl_unitdate_req type %X\n", type );
+ return( EINVAL );
+ }
+ llc->llc_ether_type = htons( type );
+ linkb( m1, m0 );
+
+ if (( m = allocb( DL_UNITDATA_REQ_SIZE + ETHERADDRL + sizeof( ushort ),
+ BPRI_HI )) == NULL ) {
+ cmn_err( CE_NOTE, "dl_unitdate_req NOMEM 2\n" );
+ return( ENOMEM );
+ }
+ m->b_wptr = m->b_rptr + DL_UNITDATA_REQ_SIZE;
+ m->b_datap->db_type = M_PROTO;
+ linkb( m, m1 );
+
+ dl = (union DL_primitives *)m->b_rptr;
+ dl->dl_primitive = DL_UNITDATA_REQ;
+ dl->unitdata_req.dl_dest_addr_length = ETHERADDRL + sizeof ( ushort );
+ dl->unitdata_req.dl_dest_addr_offset = m->b_wptr - m->b_rptr;
+
+ bcopy(addr, m->b_wptr, ETHERADDRL );
+ m->b_wptr += ETHERADDRL;
+ len = 0;
+ bcopy( &len, m->b_wptr, sizeof( ushort ));
+ m->b_wptr += sizeof( ushort );
+ putnext( q, m );
+ return( 0 );
+}
+
+ static int
+dlpi_rput( queue_t *q, mblk_t *m )
+{
+ struct atif_data *aid = (struct atif_data *)q->q_ptr;
+ union DL_primitives *dl;
+ mblk_t *m0;
+ struct llc *llc;
+
+ switch ( m->b_datap->db_type ) {
+ case M_IOCNAK :
+ putnext( q, m );
+ return( 0 );
+
+ case M_PCPROTO :
+ case M_PROTO :
+ if ( m->b_wptr - m->b_rptr < sizeof( dl->dl_primitive )) {
+ break;
+ }
+ dl = (union DL_primitives *)m->b_rptr;
+ switch ( dl->dl_primitive ) {
+ case DL_UNITDATA_IND :
+ if ( m->b_wptr - m->b_rptr < sizeof( DL_UNITDATA_IND_SIZE )) {
+ break;
+ }
+ if (( m0 = unlinkb( m )) == NULL ) {
+ break;
+ }
+ if ( m0->b_wptr - m0->b_rptr < sizeof( struct llc )) {
+ freemsg( m0 );
+ break;
+ }
+ llc = (struct llc *)m0->b_rptr;
+ if ( llc->llc_dsap != LLC_SNAP_LSAP ||
+ llc->llc_ssap != LLC_SNAP_LSAP ||
+ llc->llc_control != LLC_UI ) {
+ freemsg( m0 );
+ break;
+ }
+
+ if ( bcmp( llc->llc_org_code, at_org_code,
+ sizeof( at_org_code )) == 0 &&
+ ntohs( llc->llc_ether_type ) == ETHERTYPE_AT ) {
+ adjmsg( m0, sizeof( struct llc ));
+ ddp_rput( aid, m0 );
+ } else if ( bcmp( llc->llc_org_code, aarp_org_code,
+ sizeof( aarp_org_code )) == 0 &&
+ ntohs( llc->llc_ether_type ) == ETHERTYPE_AARP ) {
+ adjmsg( m0, sizeof( struct llc ));
+ aarp_rput( q, m0 );
+ } else {
+ freemsg( m0 );
+ }
+ break;
+
+ case DL_OK_ACK :
+ if ( m->b_wptr - m->b_rptr < sizeof( DL_OK_ACK_SIZE )) {
+ break;
+ }
+ switch ( dl->ok_ack.dl_correct_primitive ) {
+ case DL_ATTACH_REQ :
+ if ( aid->aid_state != DL_ATTACH_PENDING ) {
+ cmn_err( CE_NOTE, "dlpi_rput DL_OK_ACK attach state %d\n",
+ aid->aid_state );
+ break;
+ }
+ if ( aid->aid_c.c_type != IF_UNITSEL ) {
+ cmn_err( CE_NOTE, "dlpi_rput DL_OK_ACK attach context %x\n",
+ aid->aid_c.c_type );
+ break;
+ }
+
+ if ( WR(q)->q_next == NULL || WR(q)->q_next->q_qinfo == NULL ||
+ WR(q)->q_next->q_qinfo->qi_minfo == NULL ||
+ WR(q)->q_next->q_qinfo->qi_minfo->mi_idname == NULL ) {
+ cmn_err( CE_NOTE, "dlpi_rput can't get interface name\n" );
+ break;
+ }
+
+ if_name( aid, WR(q)->q_next->q_qinfo->qi_minfo->mi_idname,
+ aid->aid_c.c_u.u_unit.uu_ppa );
+
+ aid->aid_state = DL_BIND_PENDING;
+
+#ifdef i386
+ /*
+ * As of Solaris 7 (nice name), the i386 arch needs to be
+ * bound as 0 to receive 802 frames. However, in the same
+ * OS, Sparcs must be bound as ETHERMTU (or at least not 0)
+ * to receive the same frames. A bug? In the Solaris 7
+ * (nice name) kernel?
+ */
+ dl_bind_req( WR( q ), 0 );
+#else /* i386 */
+ dl_bind_req( WR( q ), ETHERMTU );
+#endif /* i386 */
+ break;
+
+ case DL_ENABMULTI_REQ :
+ if ( aid->aid_c.c_type != SIOCADDMULTI ) {
+ cmn_err( CE_NOTE,
+ "dlpi_rput DL_OK_ACK enabmulti context %x\n",
+ aid->aid_c.c_type );
+ break;
+ }
+
+ ioc_ok_ack( aid->aid_c.c_u.u_multi.um_q,
+ aid->aid_c.c_u.u_multi.um_m, 0 );
+ aid->aid_c.c_type = 0;
+ aid->aid_c.c_u.u_multi.um_q = NULL;
+ aid->aid_c.c_u.u_multi.um_m = 0;
+ break;
+
+ default :
+ cmn_err( CE_CONT, "!dlpi_rput DL_OK_ACK unhandled %d\n",
+ dl->ok_ack.dl_correct_primitive );
+ break;
+ }
+ break;
+
+ case DL_BIND_ACK :
+ if ( m->b_wptr - m->b_rptr < sizeof( DL_BIND_ACK_SIZE )) {
+ break;
+ }
+ if ( aid->aid_state != DL_BIND_PENDING ) {
+ break;
+ }
+ if ( aid->aid_c.c_type != IF_UNITSEL ) {
+ break;
+ }
+ bcopy( m->b_rptr + dl->bind_ack.dl_addr_offset, aid->aid_hwaddr,
+ dl->bind_ack.dl_addr_length );
+ aid->aid_state = DL_IDLE;
+ ioc_ok_ack( WR(q), aid->aid_c.c_u.u_unit.uu_m, 0 );
+ aid->aid_c.c_type = 0;
+ aid->aid_c.c_u.u_unit.uu_m = NULL;
+ aid->aid_c.c_u.u_unit.uu_ppa = 0;
+ break;
+
+ case DL_ERROR_ACK :
+ if ( m->b_wptr - m->b_rptr < sizeof( DL_ERROR_ACK_SIZE )) {
+ break;
+ }
+
+ switch ( aid->aid_c.c_type ) {
+ case IF_UNITSEL :
+ if ( dl->error_ack.dl_errno == DL_SYSERR ) {
+ ioc_error_ack( WR(q), aid->aid_c.c_u.u_unit.uu_m,
+ dl->error_ack.dl_unix_errno );
+ } else {
+ cmn_err( CE_CONT, "dlpi_rput DL_ERROR_ACK 0x%x\n",
+ dl->error_ack.dl_errno );
+ ioc_error_ack( WR(q), aid->aid_c.c_u.u_unit.uu_m, EINVAL );
+ }
+ aid->aid_c.c_type = 0;
+ aid->aid_c.c_u.u_unit.uu_m = NULL;
+ aid->aid_c.c_u.u_unit.uu_ppa = 0;
+ break;
+
+ default :
+ cmn_err( CE_NOTE, "dlpi_rput DL_ERROR_ACK unhandled %d %d %d\n",
+ dl->error_ack.dl_error_primitive,
+ dl->error_ack.dl_errno, dl->error_ack.dl_unix_errno );
+ break;
+ }
+ break;
+
+ default :
+ cmn_err( CE_NOTE, "dlpi_rput M_PCPROTO 0x%x\n", dl->dl_primitive );
+ break;
+ }
+ break;
+
+ default :
+ cmn_err( CE_NOTE, "dlpi_rput 0x%X\n", m->b_datap->db_type );
+ break;
+ }
+
+ freemsg( m );
+ return( 0 );
+}
+
+ static int
+dlpi_wput( queue_t *q, mblk_t *m )
+{
+ struct atif_data *aid = (struct atif_data *)RD(q)->q_ptr;
+ struct iocblk *ioc;
+ int rc;
+
+ switch ( m->b_datap->db_type ) {
+ case M_IOCTL :
+ if ( m->b_wptr - m->b_rptr < sizeof( struct iocblk )) {
+ freemsg( m );
+ break;
+ }
+ ioc = (struct iocblk *)m->b_rptr;
+ switch ( ioc->ioc_cmd ) {
+ case IF_UNITSEL :
+ if ( ioc->ioc_count != TRANSPARENT ) {
+ cmn_err( CE_NOTE, "dlpi_wput IF_UNITSEL non-TRANSPARENT\n" );
+ ioc_error_ack( q, m, EINVAL );
+ break;
+ }
+ if ( m->b_cont == NULL ) {
+ cmn_err( CE_NOTE, "dlpi_wput IF_UNITSEL no arg\n" );
+ ioc_error_ack( q, m, EINVAL );
+ break;
+ }
+ if ( aid->aid_state != DL_UNATTACHED ) {
+ cmn_err( CE_NOTE, "dlpi_wput IF_UNITSEL already attached\n" );
+ ioc_error_ack( q, m, EINVAL );
+ break;
+ }
+ if ( aid->aid_c.c_type != 0 ) {
+ cmn_err( CE_NOTE, "dlpi_wput IF_UNITSEL context %x\n",
+ aid->aid_c.c_type );
+ ioc_error_ack( q, m, EINVAL );
+ break;
+ }
+
+ aid->aid_state = DL_ATTACH_PENDING;
+ aid->aid_c.c_type = IF_UNITSEL;
+ aid->aid_c.c_u.u_unit.uu_m = m;
+ aid->aid_c.c_u.u_unit.uu_ppa = *(ulong *)m->b_cont->b_rptr;
+ if (( rc = dl_attach_req( q, aid->aid_c.c_u.u_unit.uu_ppa )) < 0 ) {
+ ioc_error_ack( q, m, rc );
+ break;
+ }
+ break;
+
+ default :
+ cmn_err( CE_NOTE, "dlpi_wput M_IOCTL 0x%X\n", ioc->ioc_cmd );
+ putnext( q, m );
+ break;
+ }
+ break;
+
+ default :
+ cmn_err( CE_NOTE, "dlpi_wput 0x%X\n", m->b_datap->db_type );
+ freemsg( m );
+ break;
+ }
+
+ return( 0 );
+}
+
+static struct module_info dlpi_info = {
+ 0,
+ "ddp",
+ 0,
+ 1500,
+ 3000,
+ 64
+};
+
+static struct qinit dlpi_rinit = {
+ dlpi_rput, /* qi_putp */
+ NULL, /* qi_srvp */
+ dlpi_open, /* qi_qopen */
+ dlpi_close, /* qi_qclose */
+ NULL,
+ &dlpi_info, /* qi_minfo */
+ NULL,
+};
+
+static struct qinit dlpi_winit = {
+ dlpi_wput, /* qi_putp */
+ NULL, /* qi_srvp */
+ NULL, /* qi_qopen */
+ NULL, /* qi_qclose */
+ NULL,
+ &dlpi_info, /* qi_minfo */
+ NULL,
+};
+
+static struct streamtab dlpi_stream = {
+ &dlpi_rinit,
+ &dlpi_winit,
+ NULL,
+ NULL
+};
+
+static struct fmodsw dlpi_fmodsw = {
+ "ddp",
+ &dlpi_stream,
+ D_NEW | D_MP | D_MTPERMOD
+};
+
+/*
+ * DDP Streams module. This module is pushed on DLPI drivers by atalkd.
+ */
+struct modlstrmod dlpi_lstrmod = {
+ &mod_strmodops,
+ "DDP Streams module",
+ &dlpi_fmodsw,
+};
--- /dev/null
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/stream.h>
+#include <sys/kmem.h>
+#include <sys/dlpi.h>
+#include <sys/cmn_err.h>
+#include <sys/errno.h>
+#include <sys/byteorder.h>
+#include <sys/ethernet.h>
+#include <sys/ddi.h>
+#include <net/if.h>
+#include <netinet/arp.h>
+#include <string.h>
+
+#include <netatalk/at.h>
+#include <netatalk/aarp.h>
+
+#include "if.h"
+#include "rt.h"
+#include "ioc.h"
+
+static struct atif_data *interfaces = NULL;
+
+ struct atif_data *
+if_primary()
+{
+ return( interfaces );
+}
+
+ struct atif_data *
+if_alloc( queue_t *q )
+{
+ struct atif_data *aid;
+
+ if (( aid = kmem_zalloc( sizeof( struct atif_data ), KM_SLEEP )) == NULL ) {
+ return( NULL );
+ }
+ aid->aid_q = q;
+ aid->aid_state = DL_UNATTACHED;
+
+ return( aid );
+}
+
+/*
+ * Name an interface, insert it in our list of interfaces. If this is the
+ * first interface, create the loopback interface. If it's not the first
+ * interfaces, keep the first interface the same, i.e. the first configured
+ * interface should be the primary interface.
+ */
+ int
+if_name( struct atif_data *aid, char *name, ulong ppa )
+{
+ sprintf( aid->aid_name, "%s%ld", name, ppa );
+
+ if ( interfaces == NULL ) { /* create fake loopback */
+ if (( interfaces = if_alloc( NULL )) == NULL ) {
+ return( ENOMEM );
+ }
+ strcpy( interfaces->aid_name, "lo0" );
+ interfaces->aid_state = DL_IDLE;
+ bzero( interfaces->aid_hwaddr, sizeof( interfaces->aid_hwaddr ));
+ interfaces->aid_flags = AIDF_LOOPBACK;
+ interfaces->aid_c.c_type = 0;
+
+ aid->aid_next = interfaces;
+ aid->aid_next->aid_prev = aid;
+ interfaces = aid;
+ } else {
+ aid->aid_next = interfaces->aid_next;
+ aid->aid_prev = interfaces;
+ aid->aid_next->aid_prev = aid;
+ interfaces->aid_next = aid;
+ }
+
+ aarp_init( aid );
+ return( 0 );
+}
+
+ void
+if_free( struct atif_data *aid )
+{
+ if ( aid->aid_c.c_type != 0 ) {
+ cmn_err( CE_NOTE, "if_free context %x\n", aid->aid_c.c_type );
+ return;
+ }
+
+ aarp_clean( aid );
+
+ if ( aid->aid_next != NULL ) {
+ aid->aid_next->aid_prev = aid->aid_prev;
+ }
+ if ( aid->aid_prev != NULL ) {
+ aid->aid_prev->aid_next = aid->aid_next;
+ }
+ if ( aid == interfaces ) {
+ interfaces = aid->aid_next;
+ }
+ kmem_free( aid, sizeof( struct atif_data ));
+
+ if ( interfaces != NULL && interfaces->aid_next == NULL ) {
+ kmem_free( interfaces, sizeof( struct atif_data ));
+ interfaces = NULL;
+ }
+ return;
+}
+
+ int
+if_getaddr( char *name, struct sockaddr_at *sat )
+{
+ struct atif_data *aid;
+
+ for ( aid = interfaces; aid != NULL; aid = aid->aid_next ) {
+ if ( strcmp( name, aid->aid_name ) == 0 ) {
+ break;
+ }
+ }
+ if ( aid == NULL ) {
+ return( EADDRNOTAVAIL );
+ }
+
+ bcopy( &aid->aid_sat, sat, sizeof( struct sockaddr_at ));
+ return( 0 );
+}
+
+ int
+if_addmulti( queue_t *q, mblk_t *m, char *name, struct sockaddr *sa )
+{
+ struct atif_data *aid;
+
+ for ( aid = interfaces; aid != NULL; aid = aid->aid_next ) {
+ if ( strcmp( name, aid->aid_name ) == 0 ) {
+ break;
+ }
+ }
+ if ( aid == NULL ) {
+ return( EADDRNOTAVAIL );
+ }
+
+ if ( aid->aid_c.c_type != 0 ) {
+ cmn_err( CE_NOTE, "if_addmulti context %x\n", aid->aid_c.c_type );
+ return( EINVAL );
+ }
+
+ aid->aid_c.c_type = SIOCADDMULTI;
+ aid->aid_c.c_u.u_multi.um_q = q;
+ aid->aid_c.c_u.u_multi.um_m = m;
+ dl_enabmulti_req( WR( aid->aid_q ), sa->sa_data );
+
+ return( 0 );
+}
+
+ void
+if_pickaddr( struct atif_data *aid )
+{
+ if ( aid->aid_c.c_type != SIOCSIFADDR ) {
+ cmn_err( CE_NOTE, "if_pickaddr context %x\n", aid->aid_c.c_type );
+ return;
+ }
+
+ if ( aid->aid_flags & AIDF_PROBEFAILED ) {
+ aid->aid_flags &= ~AIDF_PROBEFAILED;
+ /* choose new address */
+ for (;;) {
+ if ( aid->aid_c.c_u.u_addr.ua_nodecnt == 0 ) {
+ /* if we've exausted all addresses, fail */
+ if ( aid->aid_c.c_u.u_addr.ua_netcnt == 0 ) {
+ ioc_error_ack( aid->aid_c.c_u.u_addr.ua_q,
+ aid->aid_c.c_u.u_addr.ua_m, EADDRINUSE );
+ aid->aid_c.c_type = 0;
+ aid->aid_c.c_u.u_addr.ua_q = NULL;
+ aid->aid_c.c_u.u_addr.ua_m = NULL;
+ aid->aid_c.c_u.u_addr.ua_probecnt = 0;
+ aid->aid_c.c_u.u_addr.ua_netcnt = 0;
+ aid->aid_c.c_u.u_addr.ua_nodecnt = 0;
+ } else {
+ aid->aid_c.c_u.u_addr.ua_nodecnt = 256;
+ aid->aid_c.c_u.u_addr.ua_netcnt--;
+ if ( ntohs(aid->aid_sat.sat_addr.s_net) >
+ ntohs(aid->aid_nr.nr_lastnet) ) {
+ aid->aid_sat.sat_addr.s_net = aid->aid_nr.nr_firstnet;
+ } else
+ aid->aid_sat.sat_addr.s_net =
+ htons(ntohs(aid->aid_sat.sat_addr.s_net) + 1);
+ }
+ } else {
+ aid->aid_sat.sat_addr.s_node++;
+ aid->aid_c.c_u.u_addr.ua_nodecnt--;
+ if ( aid->aid_sat.sat_addr.s_node == 0 ||
+ aid->aid_sat.sat_addr.s_node == 255 ||
+ aid->aid_sat.sat_addr.s_node == 254 ) {
+ continue;
+ }
+ break;
+ }
+ }
+ }
+ if ( aid->aid_c.c_u.u_addr.ua_probecnt-- <= 0 ) {
+ aid->aid_flags &= ~AIDF_PROBING;
+ /* worked, send ioctl reponse */
+ ioc_ok_ack( aid->aid_c.c_u.u_addr.ua_q, aid->aid_c.c_u.u_addr.ua_m, 0 );
+ aid->aid_c.c_type = 0;
+ aid->aid_c.c_u.u_addr.ua_q = NULL;
+ aid->aid_c.c_u.u_addr.ua_m = NULL;
+ aid->aid_c.c_u.u_addr.ua_probecnt = 0;
+ aid->aid_c.c_u.u_addr.ua_netcnt = 0;
+ aid->aid_c.c_u.u_addr.ua_nodecnt = 0;
+ return;
+ }
+
+ aarp_send( aid, AARPOP_PROBE, NULL,
+ aid->aid_sat.sat_addr.s_net, aid->aid_sat.sat_addr.s_node );
+ qtimeout( aid->aid_q, if_pickaddr, (caddr_t)aid, hz / 5 );
+}
+
+ int
+if_setaddr( queue_t *q, mblk_t *m, char *name, struct sockaddr_at *sat )
+{
+ struct atif_data *aid;
+ struct netrange nr;
+ ulong time;
+
+ for ( aid = interfaces; aid != NULL; aid = aid->aid_next ) {
+ if ( strcmp( name, aid->aid_name ) == 0 ) {
+ break;
+ }
+ }
+ if ( aid == NULL ) {
+ return( EADDRNOTAVAIL );
+ }
+
+ if ( aid->aid_c.c_type != 0 ) {
+ cmn_err( CE_NOTE, "if_setaddr context %x\n", aid->aid_c.c_type );
+ return( EINVAL );
+ }
+
+ bcopy( sat->sat_zero, &nr, sizeof( struct netrange ));
+
+ if ( aid->aid_flags & AIDF_LOOPBACK ) {
+ aid->aid_sat = *sat;
+ aid->aid_nr = nr;
+
+ /* routes? */
+
+ ioc_ok_ack( q, m, 0 );
+ return( 0 );
+ }
+
+ drv_getparm( TIME, &time );
+ if ( sat->sat_addr.s_net == ATADDR_ANYNET ) {
+ if ( nr.nr_lastnet == nr.nr_firstnet ) {
+ sat->sat_addr.s_net = nr.nr_firstnet;
+ } else {
+ sat->sat_addr.s_net = htons(ntohs(nr.nr_firstnet) + time %
+ (ntohs(nr.nr_lastnet) - ntohs(nr.nr_firstnet)));
+ }
+ } else {
+ if ( ntohs( sat->sat_addr.s_net ) < ntohs( nr.nr_firstnet ) ||
+ ntohs( sat->sat_addr.s_net ) > ntohs( nr.nr_lastnet )) {
+ return( EINVAL );
+ }
+ }
+
+ if ( sat->sat_addr.s_node == ATADDR_ANYNODE ) {
+ sat->sat_addr.s_node = time;
+ }
+
+ aid->aid_flags |= AIDF_PROBING;
+ aid->aid_sat = *sat;
+ aid->aid_nr = nr;
+
+ aid->aid_c.c_type = SIOCSIFADDR;
+ aid->aid_c.c_u.u_addr.ua_q = q;
+ aid->aid_c.c_u.u_addr.ua_m = m;
+ aid->aid_c.c_u.u_addr.ua_probecnt = 10;
+ aid->aid_c.c_u.u_addr.ua_netcnt = ntohs(nr.nr_lastnet) -
+ ntohs(nr.nr_firstnet);
+ aid->aid_c.c_u.u_addr.ua_nodecnt = 256;
+ qtimeout( aid->aid_q, if_pickaddr, (caddr_t)aid, 0 );
+ return( 0 );
+}
+
+/*
+ * These three routines are all a big mess...
+ */
+ struct atif_data *
+if_dest( struct atif_data *aid, struct sockaddr_at *sat )
+{
+ struct atif_data *dest;
+
+ for ( dest = interfaces; dest != NULL; dest = dest->aid_next ) {
+ if ((( sat->sat_addr.s_net == 0 && aid == dest ) ||
+ ( ntohs(sat->sat_addr.s_net) >=
+ ntohs(dest->aid_nr.nr_firstnet) &&
+ ntohs(sat->sat_addr.s_net) <=
+ ntohs(dest->aid_nr.nr_lastnet) )) &&
+ ( sat->sat_addr.s_node == dest->aid_sat.sat_addr.s_node ||
+ sat->sat_addr.s_node == ATADDR_BCAST )) {
+ break;
+ }
+ }
+ if ( dest == NULL ) {
+ return( NULL );
+ }
+ return( dest );
+}
+
+ struct atif_data *
+if_withaddr( struct sockaddr_at *sat )
+{
+ struct atif_data *dest;
+
+ for ( dest = interfaces; dest != NULL; dest = dest->aid_next ) {
+ if ( sat->sat_addr.s_net == dest->aid_sat.sat_addr.s_net &&
+ sat->sat_addr.s_node == dest->aid_sat.sat_addr.s_node ) {
+ break;
+ }
+ }
+ return( dest );
+}
+
+ struct atif_data *
+if_withnet( struct sockaddr_at *sat )
+{
+ struct atif_data *dest;
+
+ for ( dest = interfaces; dest != NULL; dest = dest->aid_next ) {
+ if ( ntohs(sat->sat_addr.s_net) >= ntohs(dest->aid_nr.nr_firstnet) &&
+ ntohs(sat->sat_addr.s_net) <= ntohs(dest->aid_nr.nr_lastnet)) {
+ break;
+ }
+ }
+ return( dest );
+}
+
+ int
+if_route( struct atif_data *aid, mblk_t *m, struct sockaddr_at *sat )
+{
+ struct sockaddr_at gate;
+
+ if ( sat->sat_addr.s_net == 0 ) {
+ if ( sat->sat_addr.s_node == 0 ) {
+ aid = if_withaddr( sat );
+ }
+ if ( aid == NULL ) {
+ freemsg( m );
+ return( ENETUNREACH );
+ }
+ gate = *sat;
+ } else {
+ if ( rt_gate( sat, &gate ) < 0 ) { /* no route */
+ gate = *sat;
+ }
+ if (( aid = if_withaddr( &gate )) == NULL ) {
+ if (( aid = if_withnet( &gate )) == NULL ) {
+ freemsg( m );
+ return( ENETUNREACH );
+ }
+ }
+ }
+
+ if ( aid->aid_flags & AIDF_LOOPBACK ) {
+ return( ddp_rput( aid, m ));
+ } else {
+ /* the aarp layer is expected to send broadcast packets appropriately */
+ return( aarp_resolve( aid, m, &gate ));
+ }
+}
--- /dev/null
+struct atif_data {
+ struct atif_data *aid_next, *aid_prev;
+ char aid_name[ IFNAMSIZ ];
+ unchar aid_hwaddr[ ETHERADDRL ];
+ queue_t *aid_q; /* RD() side */
+ int aid_state;
+ int aid_flags;
+ struct sockaddr_at aid_sat;
+ struct netrange aid_nr;
+ struct aarplist *aid_aarplist, *aid_aarpflist;
+ /* solaris 7 wants timeout_id_t, but solaris 2.6 doesn't have that.
+ * so, we compromise with an unsigned long as we know that's big
+ * enough to hold a pointer. */
+ unsigned long aid_aarptimeo;
+ /*
+ * A little bit of cleverness, to overcome the inability of
+ * streams to sleep. The type of context must be checked before
+ * the data is accessed. The atif_data can't be freed if the
+ * type is non-zero.
+ */
+ struct {
+ int c_type; /* ioctl command */
+ union {
+ struct { /* unit select */
+ mblk_t *uu_m;
+ ulong uu_ppa;
+ } u_unit;
+ struct { /* set addr */
+ mblk_t *ua_m;
+ queue_t *ua_q;
+ int ua_probecnt;
+ int ua_netcnt;
+ int ua_nodecnt;
+ } u_addr;
+ struct { /* add multi */
+ mblk_t *um_m;
+ queue_t *um_q;
+ } u_multi;
+ } c_u;
+ } aid_c;
+};
+
+#define AIDF_LOOPBACK (1<<0)
+#define AIDF_PROBING (1<<1)
+#define AIDF_PROBEFAILED (1<<2)
+
+extern u_char at_multicastaddr[ ETHERADDRL ];
+extern u_char at_org_code[ 3 ];
+extern u_char aarp_org_code[ 3 ];
+
+int if_setaddr( queue_t *, mblk_t *, char *,
+ struct sockaddr_at * );
+int if_getaddr( char *, struct sockaddr_at * );
+int if_addmulti( queue_t *, mblk_t *, char *,
+ struct sockaddr * );
+
+struct atif_data *if_alloc( queue_t * );
+void if_free( struct atif_data * );
+int if_name( struct atif_data *, char *, ulong );
+int if_attach( struct atif_data *, char * );
+struct atif_data *if_primary( void );
+struct atif_data *if_dest( struct atif_data *, struct sockaddr_at * );
+struct atif_data *if_withaddr( struct sockaddr_at * );
+struct atif_data *if_withnet( struct sockaddr_at * );
+int if_route( struct atif_data *, mblk_t *,
+ struct sockaddr_at * );
+
+int dl_unitdata_req( queue_t *, mblk_t *, ushort, caddr_t );
+int dl_enabmulti_req( queue_t *, caddr_t );
+void aarp_send( struct atif_data *, int, caddr_t,
+ ushort, unchar );
+int aarp_rput( queue_t *, mblk_t * );
+int aarp_resolve( struct atif_data *, mblk_t *,
+ struct sockaddr_at *);
+void aarp_init( struct atif_data * );
+void aarp_clean( struct atif_data * );
+int ddp_rput( struct atif_data *, mblk_t * );
--- /dev/null
+#include <sys/types.h>
+#include <sys/stream.h>
+#include <sys/cmn_err.h>
+
+#include <string.h>
+
+#include "ioc.h"
+
+ void
+ioc_ok_ack( queue_t *q, mblk_t *m, int rval )
+{
+ struct iocblk *ioc;
+ mblk_t *m0;
+
+ if (( m0 = unlinkb( m )) != NULL ) {
+ freemsg( m0 );
+ }
+
+ if ( m->b_wptr - m->b_rptr < sizeof( struct iocblk )) {
+ cmn_err( CE_CONT, "ioc_ok_ack too small\n" );
+ freemsg( m );
+ return;
+ }
+ m->b_datap->db_type = M_IOCACK;
+ m->b_wptr = m->b_rptr + sizeof( struct iocblk );
+ ioc = (struct iocblk *)m->b_rptr;
+ ioc->ioc_error = 0;
+ ioc->ioc_count = 0;
+ ioc->ioc_rval = rval;
+ qreply( q, m );
+ return;
+}
+
+ void
+ioc_error_ack( queue_t *q, mblk_t *m, int errno )
+{
+ struct iocblk *ioc;
+ mblk_t *m0;
+
+ if (( m0 = unlinkb( m )) != NULL ) {
+ freemsg( m0 );
+ }
+
+ if ( m->b_wptr - m->b_rptr < sizeof( struct iocblk )) {
+ cmn_err( CE_CONT, "ioc_error_ack too small\n" );
+ freemsg( m );
+ return;
+ }
+ m->b_datap->db_type = M_IOCNAK;
+ m->b_wptr = m->b_rptr + sizeof( struct iocblk );
+ ioc = (struct iocblk *)m->b_rptr;
+ ioc->ioc_error = errno;
+ ioc->ioc_count = 0;
+ ioc->ioc_rval = -1;
+ qreply( q, m );
+ return;
+}
+
+ void
+ioc_copyin( queue_t *q, mblk_t *m, mblk_t *private, caddr_t addr, uint size )
+{
+ struct copyreq *cq;
+ mblk_t *m0;
+
+ if (( m0 = unlinkb( m )) != NULL ) {
+ freemsg( m0 );
+ }
+
+#ifdef notdef
+ /* supposedly this will fit anyway */
+ if ( m->b_wptr - m->b_rptr < sizeof( struct copyreq )) {
+ cmn_err( CE_CONT, "ioc_copyin too small\n" );
+ freemsg( m );
+ return;
+ }
+#endif notdef
+ m->b_datap->db_type = M_COPYIN;
+ m->b_wptr = m->b_rptr + sizeof( struct copyreq );
+ cq = (struct copyreq *)m->b_rptr;
+ cq->cq_addr = addr;
+ cq->cq_size = size;
+ cq->cq_flag = 0;
+ cq->cq_private = private;
+ qreply( q, m );
+ return;
+}
+
+ void
+ioc_copyout( queue_t *q, mblk_t *m, mblk_t *private, caddr_t data,
+ caddr_t addr, uint size )
+{
+ struct copyreq *cq;
+ mblk_t *m0;
+
+ if (( m0 = unlinkb( m )) != NULL ) {
+ freemsg( m0 );
+ }
+
+#ifdef notdef
+ /* supposedly this will fit anyway */
+ if ( m->b_wptr - m->b_rptr < sizeof( struct copyreq )) {
+ cmn_err( CE_CONT, "ioc_copyout too small\n" );
+ freemsg( m );
+ return;
+ }
+#endif notdef
+ if (( m0 = allocb( size, BPRI_MED )) == NULL ) {
+ cmn_err( CE_CONT, "ioc_copyout nomem\n" );
+ freemsg( m );
+ return;
+ }
+ m0->b_wptr = m0->b_rptr + size;
+ bcopy( data, m0->b_rptr, size );
+ linkb( m, m0 );
+
+ m->b_datap->db_type = M_COPYOUT;
+ m->b_wptr = m->b_rptr + sizeof( struct copyreq );
+ cq = (struct copyreq *)m->b_rptr;
+ cq->cq_addr = addr;
+ cq->cq_size = size;
+ cq->cq_flag = 0;
+ cq->cq_private = private;
+
+ qreply( q, m );
+ return;
+}
--- /dev/null
+void ioc_ok_ack( queue_t *, mblk_t *, int );
+void ioc_error_ack( queue_t *, mblk_t *, int );
+void ioc_copyin( queue_t *, mblk_t *, mblk_t *, caddr_t, uint );
+void ioc_copyout( queue_t *, mblk_t *, mblk_t *, caddr_t, caddr_t, uint );
--- /dev/null
+/*
+ * Linkage information. Mostly this is Solaris specific, but not all.
+ * Code to do real work is in other files, this file just has the crap
+ * to get the real code loaded and called.
+ */
+
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/cmn_err.h>
+
+char *netatalk_version = VERSION;
+
+extern struct modldrv tpi_ldrv;
+extern struct modldrv dlpi_lstrmod;
+
+static struct modlinkage ddp_linkage = {
+ MODREV_1,
+ {
+ (void *)&tpi_ldrv,
+ (void *)&dlpi_lstrmod,
+ NULL,
+ }
+};
+
+/*
+ * While these are code, they're mostly related to linkage, so
+ * we leave them here.
+ */
+ int
+_init( void )
+{
+ cmn_err( CE_CONT, "?netatalk %s\n", netatalk_version );
+ return( mod_install( &ddp_linkage ));
+}
+
+ int
+ _info( struct modinfo *modinfop )
+{
+ return( mod_info( &ddp_linkage, modinfop ));
+}
+
+ int
+_fini( void )
+{
+ return( mod_remove( &ddp_linkage ));
+}
--- /dev/null
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/cmn_err.h>
+#include <sys/kmem.h>
+#include <net/route.h>
+#include <netatalk/at.h>
+#include <errno.h>
+
+#include "rt.h"
+
+struct rtab {
+ struct rtab *r_next, *r_prev;
+ struct sockaddr_at r_dst;
+ struct sockaddr_at r_gate;
+};
+
+static struct rtab *rt_net = NULL;
+static struct rtab *rt_host = NULL;
+
+ int
+rt_add( struct sockaddr_at *dst, struct sockaddr_at *gate, int flags )
+{
+ struct rtab *r;
+ struct rtab *rtab;
+
+ if ( flags & RTF_HOST ) {
+ rtab = rt_host;
+ } else {
+ rtab = rt_net;
+ }
+ for ( r = rtab; r != NULL; r = r->r_next ) {
+ if (( r->r_dst.sat_addr.s_net == dst->sat_addr.s_net ) &&
+ (( flags & RTF_HOST ) ?
+ r->r_dst.sat_addr.s_node == dst->sat_addr.s_node : 1 )) {
+ return( EEXIST );
+ }
+ }
+
+ if (( r = kmem_alloc( sizeof( struct rtab ), KM_NOSLEEP )) == NULL ) {
+ return( ENOMEM );
+ }
+ r->r_dst = *dst;
+ r->r_gate = *gate;
+
+ r->r_prev = NULL;
+ r->r_next = rtab;
+ if ( rtab != NULL ) {
+ rtab->r_prev = r;
+ }
+ if ( flags & RTF_HOST ) {
+ rt_host = r;
+ } else {
+ rt_net = r;
+ }
+ return( 0 );
+}
+
+ int
+rt_del( struct sockaddr_at *dst, struct sockaddr_at *gate, int flags )
+{
+ struct rtab *r;
+ struct rtab *rtab;
+
+ if ( flags & RTF_HOST ) {
+ rtab = rt_host;
+ } else {
+ rtab = rt_net;
+ }
+ for ( r = rtab; r != NULL; r = r->r_next ) {
+ if (( r->r_dst.sat_addr.s_net == dst->sat_addr.s_net ) &&
+ (( flags & RTF_HOST ) ?
+ r->r_dst.sat_addr.s_node == dst->sat_addr.s_node : 1 )) {
+ break;
+ }
+ }
+ if ( r == NULL ) {
+ return( ESRCH );
+ }
+
+ if ( r == rtab ) {
+ if ( flags & RTF_HOST ) {
+ rt_host = r->r_next;
+ } else {
+ rt_net = r->r_next;
+ }
+ }
+ if ( r->r_next != NULL ) {
+ r->r_next->r_prev = r->r_prev;
+ }
+ if ( r->r_prev != NULL ) {
+ r->r_prev->r_next = r->r_next;
+ }
+ kmem_free( r, sizeof( struct rtab ));
+ return( 0 );
+}
+
+ int
+rt_gate( struct sockaddr_at *dst, struct sockaddr_at *gate )
+{
+ struct rtab *r;
+
+ for ( r = rt_host; r != NULL; r = r->r_next ) {
+ if ( r->r_dst.sat_addr.s_net == dst->sat_addr.s_net &&
+ r->r_dst.sat_addr.s_node == dst->sat_addr.s_node ) {
+ break;
+ }
+ }
+ if ( r != NULL ) {
+ *gate = r->r_gate;
+ return( 0 );
+ }
+
+ for ( r = rt_net; r != NULL; r = r->r_next ) {
+ if ( r->r_dst.sat_addr.s_net == dst->sat_addr.s_net ) {
+ break;
+ }
+ }
+ if ( r == NULL ) {
+ return( -1 );
+ }
+
+ *gate = r->r_gate;
+ return( 0 );
+}
--- /dev/null
+int rt_add( struct sockaddr_at *, struct sockaddr_at *, int );
+int rt_del( struct sockaddr_at *, struct sockaddr_at *, int );
+int rt_gate( struct sockaddr_at *, struct sockaddr_at * );
--- /dev/null
+#include <sys/types.h>
+#include <sys/stream.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/tihdr.h>
+#include <sys/ethernet.h>
+#include <net/if.h>
+
+#include <string.h>
+
+#include <netatalk/at.h>
+
+#include "if.h"
+#include "sock.h"
+
+static struct sock_data *sockets = NULL;
+
+ struct sock_data *
+sock_alloc( queue_t *q )
+{
+ struct sock_data *sd;
+
+ if (( sd = kmem_alloc( sizeof( struct sock_data ), KM_SLEEP )) == NULL ) {
+ return( NULL );
+ }
+ sd->sd_state = TS_UNBND;
+ sd->sd_q = q;
+ sd->sd_next = sd->sd_prev = NULL;
+ bzero( (caddr_t)&sd->sd_sat, sizeof( struct sockaddr_at ));
+
+ sd->sd_next = sockets;
+ if ( sockets != NULL ) {
+ sockets->sd_prev = sd;
+ }
+ sockets = sd;
+
+ return( sd );
+}
+
+ void
+sock_free( struct sock_data *sd )
+{
+ if ( sd == sockets ) {
+ sockets = sd->sd_next;
+ }
+ if ( sd->sd_next != NULL ) {
+ sd->sd_next->sd_prev = sd->sd_prev;
+ }
+ if ( sd->sd_prev != NULL ) {
+ sd->sd_prev->sd_next = sd->sd_next;
+ }
+ kmem_free( sd, sizeof( struct sock_data ));
+ return;
+}
+
+ struct sock_data *
+sock_dest( struct atif_data *aid, struct sockaddr_at *sat )
+{
+ struct sock_data *sd;
+
+ for ( sd = sockets; sd != NULL; sd = sd->sd_next ) {
+ if ( sat->sat_port == sd->sd_sat.sat_port &&
+ /* huh? */
+ aid->aid_sat.sat_addr.s_net == sd->sd_sat.sat_addr.s_net &&
+ ( sat->sat_addr.s_node == sd->sd_sat.sat_addr.s_node ||
+ sat->sat_addr.s_node == ATADDR_BCAST )) {
+ break;
+ }
+ }
+ return( sd );
+}
+
+/*
+ * This is a change in semantics. The port must be ATADDR_ANYPORT for
+ * ATADDR_ANYNET/NODE to not mean the loopback.
+ */
+ int
+sock_bind( struct sock_data *sd, struct sockaddr_at *sat )
+{
+ struct atif_data *paid;
+ struct sock_data *psd;
+ struct sockaddr_at psat;
+ u_short port;
+
+ psat = *sat;
+ if ( psat.sat_family != AF_APPLETALK ) {
+ cmn_err( CE_CONT, "sock_bind non-AppleTalk\n" );
+ return( EPROTOTYPE );
+ }
+
+ if ( psat.sat_port == ATADDR_ANYPORT ) {
+ if ( psat.sat_addr.s_net == ATADDR_ANYNET &&
+ psat.sat_addr.s_node == ATADDR_ANYNODE ) {
+ /* chose primary interface */
+ if (( paid = if_primary()) == NULL ) {
+ return( EADDRNOTAVAIL );
+ }
+ psat.sat_addr.s_net = paid->aid_sat.sat_addr.s_net;
+ psat.sat_addr.s_node = paid->aid_sat.sat_addr.s_node;
+ }
+
+ /* pick unused port */
+ for ( port = ATPORT_RESERVED; port < ATPORT_LAST; port++ ) {
+ for ( psd = sockets; psd != NULL; psd = psd->sd_next ) {
+ if ( port == psd->sd_sat.sat_port &&
+ psat.sat_addr.s_net == psd->sd_sat.sat_addr.s_net &&
+ psat.sat_addr.s_node == psd->sd_sat.sat_addr.s_node ) {
+ break;
+ }
+ }
+ if ( psd == NULL ) {
+ break;
+ }
+ }
+ if ( psd != NULL ) {
+ return( EADDRINUSE );
+ }
+ psat.sat_port = port;
+ }
+
+ sd->sd_sat = psat;
+ sd->sd_state = TS_IDLE;
+ return( 0 );
+}
--- /dev/null
+struct sock_data {
+ struct sock_data *sd_next, *sd_prev;
+ int sd_state;
+ queue_t *sd_q;
+ struct sockaddr_at sd_sat;
+};
+
+struct sock_data *sock_alloc( queue_t * );
+void sock_free( struct sock_data * );
+struct sock_data *sock_dest( struct atif_data *, struct sockaddr_at * );
+int sock_bind( struct sock_data *, struct sockaddr_at * );
+void t_unitdata_ind( queue_t *, mblk_t *, struct sockaddr_at * );
--- /dev/null
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/conf.h>
+#include <sys/stream.h>
+#include <sys/devops.h>
+#include <sys/modctl.h>
+#include <sys/ddi.h>
+#include <sys/stat.h>
+#include <sys/sockio.h>
+#include <sys/socket.h>
+#include <sys/tihdr.h>
+#include <sys/tiuser.h>
+#include <sys/timod.h>
+#include <sys/sunddi.h>
+#include <sys/ethernet.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <errno.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <netatalk/ddp.h>
+
+#include "ioc.h"
+#include "if.h"
+#include "sock.h"
+#include "rt.h"
+
+ static int
+tpi_getinfo( dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp )
+{
+ *resultp = NULL;
+ return( DDI_FAILURE );
+}
+
+ static int
+tpi_identify( dev_info_t *dip )
+{
+ if ( strcmp( ddi_get_name( dip ), "ddp" ) == 0 ) {
+ return( DDI_IDENTIFIED );
+ } else {
+ return( DDI_NOT_IDENTIFIED );
+ }
+}
+
+ static int
+tpi_attach( dev_info_t *dip, ddi_attach_cmd_t cmd )
+{
+ int rc;
+
+ if ( cmd != DDI_ATTACH ) {
+ return( DDI_FAILURE );
+ }
+
+ if (( rc = ddi_create_minor_node( dip, "ddp", S_IFCHR, 0, DDI_PSEUDO,
+ CLONE_DEV )) != DDI_SUCCESS ) {
+ /* undo anything */
+ }
+ return( rc );
+}
+
+ static int
+tpi_detach( dev_info_t *dip, ddi_detach_cmd_t cmd )
+{
+ if ( cmd != DDI_DETACH ) {
+ return( DDI_FAILURE );
+ }
+
+ ddi_remove_minor_node( dip, "ddp" );
+
+ return( DDI_SUCCESS );
+}
+
+ static int
+tpi_open( queue_t *q, dev_t *dev, int oflag, int sflag, cred_t *cred )
+{
+ static minor_t minor = 1;
+
+ if ( sflag != CLONEOPEN ) {
+ return( EINVAL );
+ }
+ if (( q->q_ptr = (void *)sock_alloc( q )) == NULL ) {
+ return( ENOMEM );
+ }
+
+ *dev = makedevice( getmajor( *dev ), minor++ );
+ qprocson( q );
+ return( 0 );
+}
+
+ static int
+tpi_close( queue_t *q, int oflag, cred_t *cred )
+{
+ struct sock_data *sd = (struct sock_data *)q->q_ptr;
+
+ qprocsoff( q );
+ sock_free( sd );
+ return( 0 );
+}
+
+ static int
+tpi_rput( queue_t *q, mblk_t *m )
+{
+ cmn_err( CE_NOTE, "tpi_rput dp_type = 0x%X\n", m->b_datap->db_type );
+ freemsg( m );
+ return( 0 );
+}
+
+ void
+t_bind_ack( queue_t *q, struct sockaddr_at *sat )
+{
+ mblk_t *m;
+ struct T_bind_ack *t;
+
+ if (( m = allocb( sizeof( struct T_bind_ack ) +
+ sizeof( struct sockaddr_at ), BPRI_HI )) == NULL ) {
+ return;
+ }
+ m->b_wptr = m->b_rptr + sizeof( struct T_bind_ack );
+ m->b_datap->db_type = M_PCPROTO;
+
+ t = (struct T_bind_ack *)m->b_rptr;
+ t->PRIM_type = T_BIND_ACK;
+ t->ADDR_length = sizeof( struct sockaddr_at );
+ t->ADDR_offset = m->b_wptr - m->b_rptr;
+ t->CONIND_number = 0;
+
+ bcopy( (caddr_t)sat, m->b_wptr, sizeof( struct sockaddr_at ));
+ m->b_wptr += sizeof( struct sockaddr_at );
+
+ qreply( q, m );
+ return;
+}
+
+ void
+t_ok_ack( queue_t *q, long prim )
+{
+ mblk_t *m;
+ struct T_ok_ack *t;
+
+
+ if (( m = allocb( sizeof( struct T_ok_ack ), BPRI_HI )) == NULL ) {
+ return;
+ }
+ m->b_wptr = m->b_rptr + sizeof( struct T_ok_ack );
+ m->b_datap->db_type = M_PCPROTO;
+
+ t = (struct T_ok_ack *)m->b_rptr;
+ t->PRIM_type = T_OK_ACK;
+ t->CORRECT_prim = prim;
+ qreply( q, m );
+ return;
+}
+
+ void
+t_error_ack( queue_t *q, long prim, long terror, long uerror )
+{
+ mblk_t *m;
+ struct T_error_ack *t;
+
+
+ if (( m = allocb( sizeof( struct T_error_ack ), BPRI_HI )) == NULL ) {
+ return;
+ }
+ m->b_wptr = m->b_rptr + sizeof( struct T_error_ack );
+ m->b_datap->db_type = M_PCPROTO;
+
+ t = (struct T_error_ack *)m->b_rptr;
+ t->PRIM_type = T_ERROR_ACK;
+ t->ERROR_prim = prim;
+ t->TLI_error = terror;
+ t->UNIX_error = uerror;
+ qreply( q, m );
+ return;
+}
+
+ void
+t_info_ack( queue_t *q, long state )
+{
+ mblk_t *m;
+ struct T_info_ack *t;
+
+
+ if (( m = allocb( sizeof( struct T_info_ack ), BPRI_HI )) == NULL ) {
+ return;
+ }
+ m->b_wptr = m->b_rptr + sizeof( struct T_info_ack );
+ m->b_datap->db_type = M_PCPROTO;
+
+ t = (struct T_info_ack *)m->b_rptr;
+ t->PRIM_type = T_INFO_ACK;
+ t->TSDU_size = 586;
+ t->ETSDU_size = -2;
+ t->CDATA_size = -2;
+ t->DDATA_size = -2;
+ t->ADDR_size = sizeof( struct sockaddr_at );
+ t->OPT_size = 64;
+ t->TIDU_size = 1024;
+ t->SERV_type = T_CLTS;
+ t->CURRENT_state = state;
+ t->PROVIDER_flag = 0;
+ qreply( q, m );
+ return;
+}
+
+ void
+t_unitdata_ind( queue_t *q, mblk_t *m0, struct sockaddr_at *sat )
+{
+ mblk_t *m;
+ struct T_unitdata_ind *t;
+
+ if (( m = allocb( sizeof( struct T_unitdata_ind ) +
+ sizeof( struct sockaddr_at ), BPRI_HI )) == NULL ) {
+ return;
+ }
+ m->b_wptr = m->b_rptr + sizeof( struct T_unitdata_ind );
+ m->b_datap->db_type = M_PROTO;
+
+ t = (struct T_unitdata_ind *)m->b_rptr;
+ t->PRIM_type = T_UNITDATA_IND;
+ t->SRC_length = sizeof( struct sockaddr_at );
+ t->SRC_offset = m->b_wptr - m->b_rptr;
+ bcopy( (caddr_t)sat, m->b_wptr, sizeof( struct sockaddr_at ));
+ m->b_wptr += sizeof( struct sockaddr_at );
+ t->OPT_length = 0;
+ t->OPT_offset = 0;
+ linkb( m, m0 );
+
+ qreply( q, m );
+ return;
+}
+
+struct ioc_state {
+ int is_state;
+ int is_count;
+ caddr_t is_addr;
+};
+
+ static int
+tpi_wput( queue_t *q, mblk_t *m )
+{
+ struct sock_data *sd = (struct sock_data *)RD(q)->q_ptr;
+ union T_primitives *tl;
+ struct iocblk *ioc;
+ struct copyresp *cp;
+ struct ioc_state *is;
+ struct ddpehdr *deh;
+ mblk_t *m0;
+ struct sockaddr_at sat;
+ struct netbuf nb;
+ struct rtentry rt;
+ struct ifreq ifr;
+ int err;
+
+ switch ( m->b_datap->db_type ) {
+ case M_PCPROTO :
+ case M_PROTO :
+ if ( m->b_wptr - m->b_rptr < sizeof( tl->type )) {
+ freemsg( m );
+ break;
+ }
+ tl = (union T_primitives *)m->b_rptr;
+ switch ( tl->type ) {
+ case T_INFO_REQ :
+ t_info_ack( q, sd->sd_state );
+ freemsg( m );
+ break;
+
+ case T_UNBIND_REQ :
+ if ( m->b_wptr - m->b_rptr < sizeof( struct T_unbind_req )) {
+ freemsg( m );
+ break;
+ }
+ if ( sd->sd_state != TS_IDLE ) {
+ t_error_ack( q, T_BIND_REQ, TOUTSTATE, 0 );
+ freemsg( m );
+ break;
+ }
+ bzero( (caddr_t)&sd->sd_sat, sizeof( struct sockaddr_at ));
+ sd->sd_state = TS_UNBND;
+ t_ok_ack( q, T_UNBIND_REQ );
+ break;
+
+ case T_BIND_REQ :
+ if ( m->b_wptr - m->b_rptr < sizeof( struct T_bind_req )) {
+ freemsg( m );
+ break;
+ }
+ if ( sd->sd_state != TS_UNBND ) {
+ t_error_ack( q, T_BIND_REQ, TOUTSTATE, 0 );
+ freemsg( m );
+ break;
+ }
+
+ if ( tl->bind_req.ADDR_length == 0 ) {
+ bzero( (caddr_t)&sat, sizeof( struct sockaddr_at ));
+ sat.sat_family = AF_APPLETALK;
+ } else {
+ if ( tl->bind_req.ADDR_length != sizeof( struct sockaddr ) ||
+ m->b_wptr - m->b_rptr <
+ tl->bind_req.ADDR_offset + tl->bind_req.ADDR_length ) {
+ cmn_err( CE_CONT, "tpi_wput T_BIND_REQ wierd\n" );
+ freemsg( m );
+ break;
+ }
+ sat = *(struct sockaddr_at *)(m->b_rptr +
+ tl->bind_req.ADDR_offset );
+ }
+
+ if (( err = sock_bind( sd, &sat )) != 0 ) {
+ t_error_ack( q, T_BIND_REQ, TSYSERR, err );
+ } else {
+ /* seems like we must return the requested address */
+ t_bind_ack( q, &sat );
+ }
+ freemsg( m );
+ break;
+
+ case T_UNITDATA_REQ :
+ if ( m->b_wptr - m->b_rptr < sizeof( struct T_unitdata_req )) {
+ freemsg( m );
+ break;
+ }
+ if ( sd->sd_state != TS_IDLE ) {
+ cmn_err( CE_NOTE, "tpi_wput unitdata on unbound socket\n" );
+ t_error_ack( q, T_UNITDATA_REQ, TOUTSTATE, 0 );
+ freemsg( m );
+ break;
+ }
+ if ( tl->unitdata_req.DEST_length != sizeof( struct sockaddr )) {
+ cmn_err( CE_NOTE, "tpi_wput T_UNITDATA_REQ %d\n",
+ tl->unitdata_req.DEST_length );
+ freemsg( m );
+ break;
+ }
+
+#ifdef notdef
+ /*
+ * Sometimes, the socket layer gives us crap... Sound like a bug?
+ */
+ if ( m->b_rptr + tl->unitdata_req.DEST_offset +
+ tl->unitdata_req.DEST_length > m->b_wptr ) {
+cmn_err( CE_CONT, "tpi_wput T_UNITDATA_REQ mblk size %X %X\n", m->b_rptr + tl->unitdata_req.DEST_offset + tl->unitdata_req.DEST_length, m->b_wptr );
+ freemsg( m );
+ break;
+ }
+#endif notdef
+
+ sat = *(struct sockaddr_at *)(m->b_rptr +
+ tl->unitdata_req.DEST_offset );
+ if ( sat.sat_family != AF_APPLETALK ) {
+ cmn_err( CE_CONT, "tpi_wput non-AppleTalk\n" );
+ freemsg( m );
+ break;
+ }
+
+ if ( m->b_wptr - m->b_rptr < sizeof( struct ddpehdr )) {
+ cmn_err( CE_CONT, "tpi_wput m too short\n" );
+ freemsg( m );
+ break;
+ }
+ m->b_wptr = m->b_rptr + sizeof( struct ddpehdr );
+ m->b_datap->db_type = M_DATA;
+ deh = (struct ddpehdr *)m->b_rptr;
+ deh->deh_pad = 0;
+ deh->deh_hops = 0;
+ deh->deh_len = msgdsize( m );
+
+ deh->deh_dnet = sat.sat_addr.s_net;
+ deh->deh_dnode = sat.sat_addr.s_node;
+ deh->deh_dport = sat.sat_port;
+
+ deh->deh_snet = sd->sd_sat.sat_addr.s_net;
+ deh->deh_snode = sd->sd_sat.sat_addr.s_node;
+ deh->deh_sport = sd->sd_sat.sat_port;
+
+ deh->deh_sum = 0; /* XXX */
+ deh->deh_bytes = htonl( deh->deh_bytes );
+ return( if_route( if_withaddr( &sd->sd_sat ), m, &sat ));
+
+ default :
+ /* cmn_err( CE_NOTE, "tpi_wput M_PCPROTO 0x%X\n", tl->type ); */
+ t_error_ack( q, tl->type, TNOTSUPPORT, 0 );
+ freemsg( m );
+ break;
+ }
+ break;
+
+ case M_IOCTL :
+ if ( m->b_wptr - m->b_rptr < sizeof( struct iocblk )) {
+ freemsg( m );
+ break;
+ }
+ ioc = (struct iocblk *)m->b_rptr;
+ if ( ioc->ioc_count != TRANSPARENT ) {
+ cmn_err( CE_CONT, "tpi_wput non-TRANSPARENT %X\n", ioc->ioc_cmd );
+ ioc_error_ack( q, m, EINVAL );
+ break;
+ }
+ if ( m->b_cont == NULL ) {
+ cmn_err( CE_CONT, "tpi_wput M_IOCTL no arg\n" );
+ ioc_error_ack( q, m, EINVAL );
+ break;
+ }
+
+ /* de-allocated after M_IOCDATA processing */
+ if (( m0 = allocb( sizeof( struct ioc_state ), BPRI_HI )) == NULL ) {
+ cmn_err( CE_CONT, "tpi_wput m0 no mem\n" );
+ ioc_error_ack( q, m, EINVAL );
+ break;
+ }
+ m0->b_wptr = m->b_rptr + sizeof( struct ioc_state );
+ is = (struct ioc_state *)m0->b_rptr;
+
+ switch ( ioc->ioc_cmd ) {
+ case SIOCADDRT :
+ case SIOCDELRT :
+ if (( err = drv_priv( ioc->ioc_cr )) != 0 ) {
+ ioc_error_ack( q, m, err );
+ break;
+ }
+ is->is_state = M_COPYIN;
+ is->is_addr = *(caddr_t *)m->b_cont->b_rptr;
+ ioc_copyin( q, m, m0, is->is_addr, sizeof( struct rtentry ));
+ break;
+
+ case SIOCADDMULTI :
+ case SIOCSIFADDR :
+ if (( err = drv_priv( ioc->ioc_cr )) != 0 ) {
+ ioc_error_ack( q, m, err );
+ break;
+ }
+
+ case SIOCGIFADDR :
+ is->is_state = M_COPYIN;
+ is->is_addr = *(caddr_t *)m->b_cont->b_rptr;
+ ioc_copyin( q, m, m0, is->is_addr, sizeof( struct ifreq ));
+ break;
+
+ case TI_GETMYNAME :
+ is->is_state = M_COPYIN;
+ is->is_addr = *(caddr_t *)m->b_cont->b_rptr;
+ ioc_copyin( q, m, m0, is->is_addr, sizeof( struct netbuf ));
+ break;
+
+ default :
+ ioc_error_ack( q, m, EINVAL );
+ break;
+ }
+ break;
+
+ case M_IOCDATA :
+ if ( m->b_wptr - m->b_rptr < sizeof( struct copyresp )) {
+ freemsg( m );
+ break;
+ }
+ cp = (struct copyresp *)m->b_rptr;
+ if ( cp->cp_rval != 0 ) {
+ cmn_err( CE_CONT, "tpi_wput IOCDATA failed %d\n", cp->cp_rval );
+ freemsg( m );
+ break;
+ }
+
+ if (( m0 = cp->cp_private ) == NULL ) {
+ cmn_err( CE_CONT, "tpi_wput IOCDATA no state\n" );
+ ioc_error_ack( q, m, EINVAL );
+ break;
+ }
+ if ( m0->b_wptr - m0->b_rptr < sizeof( struct ioc_state )) {
+ cmn_err( CE_CONT, "tpi_wput IOCDATA private too short\n" );
+ ioc_error_ack( q, m, EINVAL );
+ break;
+ }
+ is = (struct ioc_state *)m0->b_rptr;
+
+ switch ( cp->cp_cmd ) {
+ case TI_GETMYNAME :
+ switch ( is->is_state ) {
+ case M_COPYIN :
+ if ( m->b_cont == NULL ) {
+ cmn_err( CE_CONT, "tpi_wput TI_GETMYNAME COPYIN no arg\n" );
+ ioc_error_ack( q, m, EINVAL );
+ break;
+ }
+ nb = *(struct netbuf *)m->b_cont->b_rptr;
+ nb.len = sizeof( struct sockaddr_at );
+ /* copy out netbuf */
+ is->is_state = M_COPYOUT;
+ is->is_count = 1;
+ ioc_copyout( q, m, m0, (caddr_t)&nb, is->is_addr,
+ sizeof( struct netbuf ));
+ is->is_addr = nb.buf;
+ return( 0 );
+
+ case M_COPYOUT :
+ switch ( is->is_count ) {
+ case 1 :
+ /* copy out address to nb.buf */
+ is->is_state = M_COPYOUT;
+ is->is_count = 2;
+ ioc_copyout( q, m, m0, (caddr_t)&sd->sd_sat, is->is_addr,
+ sizeof( struct sockaddr_at ));
+ return( 0 );
+
+ case 2 :
+ ioc_ok_ack( q, m, 0 );
+ break;
+
+ default :
+ cmn_err( CE_NOTE, "tpi_wput TI_GETMYNAME count %d\n",
+ is->is_count );
+ ioc_error_ack( q, m, EINVAL );
+ break;
+ }
+ break;
+
+ default :
+ cmn_err( CE_NOTE, "tpi_wput TI_GETMYNAME state %d\n",
+ is->is_state );
+ ioc_error_ack( q, m, EINVAL );
+ break;
+ }
+ break;
+
+ case SIOCADDRT : /* manipulate routing table */
+ case SIOCDELRT :
+ if (( err = drv_priv( cp->cp_cr )) != 0 ) {
+ ioc_error_ack( q, m, err );
+ break;
+ }
+ if ( is->is_state != M_COPYIN ) {
+ cmn_err( CE_CONT, "tpi_wput SIOC(ADD|DEL)RT bad state\n" );
+ freemsg( m );
+ break;
+ }
+
+ rt = *(struct rtentry *)m->b_cont->b_rptr;
+
+ if ( cp->cp_cmd == SIOCADDRT ) {
+ err = rt_add( (struct sockaddr_at *)&rt.rt_dst,
+ (struct sockaddr_at *)&rt.rt_gateway, rt.rt_flags );
+ } else if ( cp->cp_cmd == SIOCDELRT ) {
+ err = rt_del( (struct sockaddr_at *)&rt.rt_dst,
+ (struct sockaddr_at *)&rt.rt_gateway, rt.rt_flags );
+ } else {
+ cmn_err( CE_CONT, "tpi_wput SIOC(ADD|DEL)RT bad cmd\n" );
+ freemsg( m );
+ break;
+ }
+ if ( err != 0 ) {
+ ioc_error_ack( q, m, err );
+ } else {
+ ioc_ok_ack( q, m, 0 );
+ }
+ break;
+
+ /*
+ * These both require lower messages to be sent.
+ */
+ case SIOCADDMULTI :
+ case SIOCSIFADDR :
+ if (( err = drv_priv( cp->cp_cr )) != 0 ) {
+ ioc_error_ack( q, m, err );
+ break;
+ }
+ if ( is->is_state != M_COPYIN ) {
+ cmn_err( CE_CONT, "tpi_wput SIOCSIFADDR bad state\n" );
+ freemsg( m );
+ break;
+ }
+
+ ifr = *(struct ifreq *)m->b_cont->b_rptr;
+
+ /* initiate command, pass q and m (current context to be saved */
+ if ( cp->cp_cmd == SIOCSIFADDR ) {
+ err = if_setaddr( q, m, ifr.ifr_name,
+ (struct sockaddr_at *)&ifr.ifr_addr );
+ } else {
+ err = if_addmulti( q, m, ifr.ifr_name, &ifr.ifr_addr );
+ }
+ if ( err != 0 ) {
+ ioc_error_ack( q, m, err );
+ break;
+ }
+ break;
+
+ case SIOCGIFADDR : /* get interface address */
+ switch ( is->is_state ) {
+ case M_COPYOUT :
+ /* ack the original ioctl */
+ ioc_ok_ack( q, m, 0 );
+ break;
+
+ case M_COPYIN :
+ if ( m->b_cont == NULL ) {
+ cmn_err( CE_CONT, "tpi_wput SIOCGIFADDR COPYIN no arg\n" );
+ ioc_error_ack( q, m, EINVAL );
+ break;
+ }
+
+ /* size??? */
+ ifr = *(struct ifreq *)m->b_cont->b_rptr;
+ if (( err = if_getaddr( ifr.ifr_name,
+ (struct sockaddr_at *)&ifr.ifr_addr )) != 0 ) {
+ ioc_error_ack( q, m, err );
+ }
+ is->is_state = M_COPYOUT;
+ ioc_copyout( q, m, m0, (caddr_t)&ifr, is->is_addr,
+ sizeof( struct ifreq ));
+ return( 0 ); /* avoid freemsg( m0 ) below */
+
+ default :
+ cmn_err( CE_CONT, "tpi_wput SIOCGIFADDR bad state\n" );
+ freemsg( m );
+ break;
+ }
+ break;
+
+ default :
+ cmn_err( CE_NOTE, "tpi_wput M_IOCDATA 0x%X\n", cp->cp_cmd );
+ ioc_error_ack( q, m, EINVAL );
+ break;
+ }
+ freemsg( m0 );
+ break;
+
+ default :
+ cmn_err( CE_NOTE, "!tpi_wput dp_type = 0x%X\n", m->b_datap->db_type );
+ freemsg( m );
+ break;
+ }
+
+ return( 0 );
+}
+
+static struct module_info tpi_info = {
+ 0, /* XXX */
+ "ddp",
+ 0,
+ 1500,
+ 3000,
+ 64
+};
+
+static struct qinit tpi_rinit = {
+ tpi_rput, /* qi_putp */
+ NULL, /* qi_srvp */
+ tpi_open, /* qi_qopen */
+ tpi_close, /* qi_qclose */
+ NULL,
+ &tpi_info, /* qi_minfo */
+ NULL,
+};
+
+static struct qinit tpi_winit = {
+ tpi_wput, /* qi_putp */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &tpi_info,
+ NULL,
+};
+
+static struct streamtab tpi_stream = {
+ &tpi_rinit,
+ &tpi_winit,
+ NULL,
+ NULL
+};
+
+static struct cb_ops tpi_cbops = {
+ nulldev, /* cb_open */
+ nulldev, /* cb_close */
+ nodev,
+ nodev,
+ nodev,
+ nodev,
+ nodev,
+ nodev,
+ nodev,
+ nodev,
+ nodev,
+ nochpoll,
+ ddi_prop_op,
+ &tpi_stream,
+ D_NEW | D_MP | D_MTPERMOD, /* cb_flag */
+ CB_REV, /* cb_rev */
+ nodev, /* cb_aread */
+ nodev, /* cb_awrite */
+};
+
+static struct dev_ops tpi_devops = {
+ DEVO_REV,
+ 0,
+ tpi_getinfo,
+ tpi_identify,
+ nulldev,
+ tpi_attach,
+ tpi_detach,
+ nodev,
+ &tpi_cbops,
+ (struct bus_ops *)NULL,
+ NULL,
+};
+
+/*
+ * DDP Streams device. This device is opened by socket().
+ */
+struct modldrv tpi_ldrv = {
+ &mod_driverops,
+ "DDP Streams device",
+ &tpi_devops,
+};
--- /dev/null
+# Sun specific defines, passed to subdirectories.
+DEFS= -DBSD4_3 -DUSE_OLD_RQUOTA -DUSE_UFS_QUOTA_H -DUSE_MNTENT_H \
+ -DDLSYM_PREPEND_UNDERSCORE
+OPTOPTS= -O
+CC= gcc
+INSTALL= install
+# use -lbind instead of -lresolv if you're using BIND >= 8.x
+AFPLIBS=-lresolv
+ADDLIBS=
+
+#CSHAREDFLAGS= -pic
+CSHAREDFLAGS= -fPIC
+
+#LDFLAGS_EXPORT=
+LDSHARED= ld
+LDSHAREDFLAGS= -assert pure-text
+LIBSHARED= -ldl
+
+
+# source for kernel module
+SRC= at_sun.c aarp.c at_control.c at_proto.c ddp_input.c ddp_output.c \
+ ddp_usrreq.c
+OBJ= at_sun.o aarp.o at_control.o at_proto.o ddp_input.o ddp_output.o \
+ ddp_usrreq.o
+
+INCPATH = -I../../include -I../netatalk
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH}
+
+ALL= ../../libatalk ../../include ../../bin ../../etc ../../man
+
+all: kernel ${ALL}
+
+kernel : netatalk.o
+
+netatalk.o: ${OBJ}
+ ${LD} -r -o netatalk.o ${OBJ}
+
+at_sun.o: at_sun.c
+ ${CC} ${CFLAGS} -DKERNEL -D`arch -k` -c at_sun.c
+
+aarp.o: ../netatalk/aarp.c
+ ${CC} ${CFLAGS} -DKERNEL -D`arch -k` -c ../netatalk/aarp.c
+
+at_control.o: ../netatalk/at_control.c
+ ${CC} ${CFLAGS} -DKERNEL -D`arch -k` -c ../netatalk/at_control.c
+
+at_proto.o: ../netatalk/at_proto.c
+ ${CC} ${CFLAGS} -DKERNEL -D`arch -k` -c ../netatalk/at_proto.c
+
+ddp_input.o: ../netatalk/ddp_input.c
+ ${CC} ${CFLAGS} -DKERNEL -D`arch -k` -c ../netatalk/ddp_input.c
+
+ddp_output.o: ../netatalk/ddp_output.c
+ ${CC} ${CFLAGS} -DKERNEL -D`arch -k` -c ../netatalk/ddp_output.c
+
+ddp_usrreq.o: ../netatalk/ddp_usrreq.c
+ ${CC} ${CFLAGS} -DKERNEL -D`arch -k` -c ../netatalk/ddp_usrreq.c
+
+../../bin ../../etc: ../../libatalk
+
+${ALL}: FRC
+ cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+ LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+ LIBSHARED="${LIBSHARED}" \
+ all
+
+FRC:
+
+kinstall : kernel
+ -mkdir ${DESTDIR}
+ -mkdir ${ETCDIR}
+ ${INSTALL} -c netatalk.o ${ETCDIR}
+
+install : kinstall
+ -mkdir ${DESTDIR}
+ -mkdir ${SBINDIR}
+ -mkdir ${BINDIR}
+ -mkdir ${ETCDIR}
+ -mkdir ${LIBDIR}
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" MANDIR="${MANDIR}" AFPLIBS="${AFPLIBS}" \
+ AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+ LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+ CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+ INSTALL="${INSTALL}" $@); \
+ done
+ rm -f ${ETCDIR}/rc.atalk
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ -e 's@^##@@' \
+ < ../../distrib/initscripts/rc.atalk.bsd > ${ETCDIR}/rc.atalk
+ if [ -f ${ETCDIR}/afpd.conf ]; then \
+ echo "Retaining old afpd.conf file."; \
+ else \
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../config/afpd.conf > ${ETCDIR}/afpd.conf; \
+ fi
+ @echo
+ @echo "Install is done. Don't forget to add lines from"
+ @echo "services.atalk to /etc/services and to call rc.atalk"
+ @echo "in /etc/rc. See README and README.SUN for more"
+ @echo "information."
+
+clean : sysclean
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+ done
+
+sysclean :
+ rm -f a.out core* *.o *.bak *[Ee]rrs tags
+ rm -f netatalk.o
+
+depend :
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} DEFS=${DEFS} depend); \
+ done
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/errno.h>
+
+#include <net/if_arp.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <net/af.h>
+#include <net/netisr.h>
+
+#include <sun/vddrv.h>
+
+#include <netinet/in.h>
+#undef s_net
+#include <netinet/if_ether.h>
+
+#include <netatalk/at.h>
+#include <netatalk/at_var.h>
+#include <netatalk/ddp_var.h>
+#include <netatalk/phase2.h>
+
+struct vdlat {
+ int vda_magic;
+ char *vda_name;
+} atvdl = {
+ VDMAGIC_USER, "netatalk"
+};
+
+struct ifqueue *atef_input();
+int atef_output();
+extern int atintr();
+
+extern u_char aarp_org_code[ 3 ];
+extern u_char at_org_code[ 3 ];
+
+/*
+ * Entries in this table are inserted into the ether_families linked
+ * list at the beginnning. As such, they will be searched by the input
+ * and output routines opposite to the order here.
+ *
+ * In order to do both phase 1 and phase 2 during output, we have a
+ * special entry (the AF_APPLETALK entry) whose ethertype field is
+ * changed by the output function, to reflect the actual link layer
+ * to be used. This ether_family entry is never seen during Ethernet
+ * input, since the earlier entry captures all packets (it is seen
+ * during loopback input, in that the input function is called directly
+ * on loopback output).
+ */
+struct ether_family ether_atalk[] = {
+ {
+ AF_APPLETALK, 0, /* Changed by atef_output() */
+ atef_input, atef_output, atintr,
+ },
+ {
+ 0, ETHERTYPE_AARP,
+ atef_input, 0, 0,
+ },
+ {
+ 0, ETHERTYPE_AT,
+ atef_input, 0, atintr,
+ },
+ {
+ 0, EF_8023_TYPE,
+ atef_input, 0, atintr,
+ },
+};
+int ether_atalkN = sizeof( ether_atalk ) / sizeof( ether_atalk[ 0 ] );
+
+extern struct ether_family *ether_families;
+
+extern int atalk_hash(), atalk_netmatch();
+extern int null_hash(), null_netmatch();
+
+xxxinit( cmd, vdd, vdi, vds )
+ unsigned int cmd;
+ struct vddrv *vdd;
+ addr_t vdi;
+ struct vdstat *vds;
+{
+ struct ether_family *ef;
+ struct domain *dom;
+ struct protosw *pr;
+ int i;
+
+ switch( cmd ) {
+ case VDLOAD :
+ vdd->vdd_vdtab = (struct vdlinkage *)&atvdl;
+
+ /*
+ * Register with the network-interface layer (ethernet).
+ */
+ for ( i = 0; i < ether_atalkN; i++ ) {
+ ether_register( ðer_atalk[ i ] );
+ }
+
+ /*
+ * Register with the socket layer.
+ */
+ atalkdomain.dom_next = domains;
+ domains = &atalkdomain;
+ if ( atalkdomain.dom_init ) {
+ (*atalkdomain.dom_init)();
+ }
+ for ( pr = atalkdomain.dom_protosw;
+ pr < atalkdomain.dom_protoswNPROTOSW; pr++ ) {
+ if ( pr->pr_init ) {
+ (*pr->pr_init)();
+ }
+ }
+
+ /*
+ * Cobble ourselves into the routing table.
+ */
+ afswitch[ AF_APPLETALK ].af_hash = atalk_hash;
+ afswitch[ AF_APPLETALK ].af_netmatch = atalk_netmatch;
+ return( 0 );
+
+ case VDUNLOAD :
+ /*
+ * Make sure that there are no open appletalk sockets.
+ */
+ if ( ddpcb != NULL ) {
+ return( EMFILE );
+ }
+
+ /*
+ * There is no ether_unregister(), so we'll have to do it
+ * our selves...
+ */
+ for ( i = 0; i < ether_atalkN; i++ ) {
+ if ( ether_families == ðer_atalk[ i ] ) {
+ ether_families = ether_families->ef_next;
+ continue;
+ } else {
+ for ( ef = ether_families; ef->ef_next; ef = ef->ef_next ) {
+ if ( ef->ef_next == ðer_atalk[ i ] ) {
+ ef->ef_next = ef->ef_next->ef_next;
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * Remove aarp timers and held packets.
+ */
+ aarp_clean();
+
+ /*
+ * Remove AppleTalk interface addresses.
+ */
+ aa_clean();
+
+ /*
+ * Remove our routines from the routing table.
+ */
+ afswitch[ AF_APPLETALK ].af_hash = null_hash;
+ afswitch[ AF_APPLETALK ].af_netmatch = null_netmatch;
+
+ /*
+ * Remove atalkdomain from the domains list.
+ * Unlikely, but someone may have registered after us.
+ */
+ if ( domains == &atalkdomain ) {
+ domains = domains->dom_next;
+ } else {
+ for ( dom = domains; dom->dom_next; dom = dom->dom_next ) {
+ if ( dom->dom_next == &atalkdomain ) {
+ dom->dom_next = dom->dom_next->dom_next;
+ break;
+ }
+ }
+ }
+ return( 0 );
+
+ case VDSTAT :
+ return( 0 );
+ default :
+ return( EIO );
+ }
+}
+
+/*
+ * Input routine for netatalk on suns. There are five possible
+ * packets. First, packets received on the loopback interface
+ * are immediately sent to the phase 1 interrupt queue (this will
+ * have to change if we ever do a phase 2 only version). Second,
+ * IEEE802 packet are sent to either the aarpinput() routine or
+ * the phase 2 interrupt queue. Finally, DIX packets are sent
+ * to either aarpinput() or the phase 1 interrupt queue.
+ */
+ struct ifqueue *
+atef_input( ifp, m, header )
+ struct ifnet *ifp;
+ struct mbuf *m;
+ struct ether_header *header;
+{
+ struct llc llc;
+ struct mbuf *n = 0;
+
+ /*
+ * Check first for LOOPBACK flag, since loopback code passes NULL for
+ * the header.
+ */
+ if ( ifp->if_flags & IFF_LOOPBACK ) {
+ return( &atintrq2 );
+ }
+
+ /*
+ * Before SunOS 4.1, the ether_type was passed as is from the
+ * packet. After SunOS 4.1, the ether_type is swapped in
+ * do_protocol(), before the ether_family routines are called.
+ */
+#if defined( sun ) && defined( i386 )
+ header->ether_type = ntohs( header->ether_type );
+#endif sun i386
+
+ if ( header->ether_type <= ETHERMTU ) { /* IEEE802 */
+ /*
+ * We need to remove the interface pointer from the beginning of this
+ * packet. We can't always use IF_ADJ(), since it can (and will,
+ * very often) MFREE() the first mbuf in our chain. If IF_ADJ()
+ * would free the first mbuf, we just advance our pointer to the
+ * next mbuf. Since our calling routine passes m by value, we're
+ * not actually losing m. Later, we don't need to put the interface
+ * pointer back on, since the caller still has it in its copy of m.
+ */
+ if ( m->m_len == sizeof( struct ifnet * )) {
+ n = m;
+ m = m->m_next;
+ } else {
+ IF_ADJ( m );
+ }
+
+ /*
+ * We can't call m_pullup(), since we need to preserve
+ * the value of m.
+ */
+ if ( m->m_len < sizeof( struct llc )) {
+printf( "atef_input size llc\n" );
+ ( n ) ? m_freem( n ) : m_freem( m );
+ return( 0 );
+ }
+ bcopy( mtod( m, caddr_t ), &llc, sizeof( struct llc ));
+ if ( llc.llc_dsap != LLC_SNAP_LSAP || llc.llc_ssap != LLC_SNAP_LSAP ||
+ llc.llc_control != LLC_UI ) {
+ ( n ) ? m_freem( n ) : m_freem( m );
+ return( 0 );
+ }
+
+ /*
+ * See IF_ADJ() above. Here we prepend ifp to the mbuf chain. If we
+ * didn't remove it earlier, we don't replace it here.
+ */
+ if ( n ) {
+ m_adj( m, sizeof( struct llc ));
+ } else {
+ m_adj( m, sizeof( struct llc ) - sizeof( struct ifnet *));
+ if ( m->m_len < sizeof( struct ifnet * )) {
+printf( "atef_input too small!\n" );
+ m_freem( m );
+ return( 0 );
+ }
+ *mtod( m, struct ifnet ** ) = ifp;
+ }
+
+ if ( ntohs( llc.llc_ether_type ) == ETHERTYPE_AT &&
+ bcmp( llc.llc_org_code, at_org_code,
+ sizeof( at_org_code )) == 0 ) {
+ return( &atintrq2 );
+ }
+
+ /* do we really want to pass m, here? what happened to n? XXX */
+ if ( ntohs( llc.llc_ether_type ) == ETHERTYPE_AARP &&
+ bcmp( llc.llc_org_code, aarp_org_code,
+ sizeof( aarp_org_code )) == 0 ) {
+ aarpinput( ifp, n ? n : m );
+ return( 0 );
+ }
+
+ } else { /* DIX */
+ switch ( header->ether_type ) {
+ case ETHERTYPE_AT :
+ return( &atintrq1 );
+
+ case ETHERTYPE_AARP :
+ aarpinput( ifp, m );
+ return( 0 );
+ }
+ }
+
+ ( n ) ? m_freem( n ) : m_freem( m );
+ return( 0 );
+}
+
+/*
+ * If the destination is on a 802.3 wire, do phase 2 encapsulation,
+ * adding the 802.2 and SNAP headers. Always fill in the edst with the
+ * ethernet address of the destination.
+ */
+atef_output( dst, m, ifp, edst )
+ struct sockaddr_at *dst;
+ struct mbuf *m;
+ struct ifnet *ifp;
+ struct ether_addr *edst;
+{
+ struct at_ifaddr *aa;
+ struct mbuf *m0;
+ struct llc llc;
+ int s;
+
+ s = splimp();
+ if ( !aarpresolve( ifp, m, dst, edst )) {
+ (void) splx( s );
+ return( 1 );
+ }
+ (void) splx( s );
+
+ /*
+ * ifaddr is the first thing in at_ifaddr
+ */
+ if (( aa = (struct at_ifaddr *)at_ifawithnet( dst, ifp->if_addrlist ))
+ == 0 ) {
+ m_freem( m );
+ return( 1 );
+ }
+
+ /*
+ * In the phase 2 case, we need to prepend an mbuf for the llc header.
+ * Since we must preserve the value of m, which is passed to us by
+ * value, we m_copy() the first mbuf, and use it for our llc header.
+ *
+ * We could worry about leaving space for the ether header, but
+ * since we'll have to go through all sorts of hoops, including a
+ * possibly large copy, there's really no sense.
+ */
+ if ( aa->aa_flags & AFA_PHASE2 ) {
+ if ( M_HASCL( m ) || m->m_off - MMINOFF < sizeof( struct llc )) {
+ if (( m0 = m_copy( m, 0, m->m_len )) == 0 ) {
+ m_freem( m );
+ return( 1 );
+ }
+ if ( M_HASCL( m )) { /* m is a cluster */
+ int s = splimp();
+
+ mclput( m );
+ splx( s );
+ }
+
+ m0->m_next = m->m_next;
+ m->m_next = m0;
+ m->m_off = MMAXOFF - sizeof( struct llc );
+ m->m_len = sizeof( struct llc );
+ } else {
+ m->m_off -= sizeof( struct llc );
+ m->m_len += sizeof( struct llc );
+ }
+
+ llc.llc_dsap = llc.llc_ssap = LLC_SNAP_LSAP;
+ llc.llc_control = LLC_UI;
+ bcopy( at_org_code, llc.llc_org_code, sizeof( at_org_code ));
+ llc.llc_ether_type = htons( ETHERTYPE_AT );
+ bcopy( &llc, mtod( m, caddr_t ), sizeof( struct llc ));
+ ether_atalk[ 0 ].ef_ethertype = EF_8023_TYPE;
+ return( 0 );
+ } else {
+ ether_atalk[ 0 ].ef_ethertype = ETHERTYPE_AT;
+ return( 0 );
+ }
+}
--- /dev/null
+# Ultrix specific defines, passed to subdirectories.
+# i believe that the current setup will break with this.
+#DEFS=
+DEFS=-I../../sys/generic -DUSE_OLD_RQUOTA
+OPTOPTS=
+#CC= cc
+CC = gcc
+CSHAREDFLAGS= -fPIC
+LDSHARED= gcc
+LDSHAREDFLAGS= -shared
+LDFLAGS_EXPORT=-rdynamic
+
+INSTALL= install
+
+LIBSHARED= -ldl
+AFPLIBS=
+ADDLIBS=
+
+INCPATH = -I../../include -I../netatalk
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH}
+
+ALL= ../../libatalk ../../include ../../bin ../../etc ../../man
+
+oops:
+ @echo "Read README again. Don't type 'make' here."
+ @exit 1
+
+all: ${ALL}
+
+../../bin ../../etc: ../../libatalk
+
+${ALL}: FRC
+ cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+ AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+ LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+ LIBSHARED="${LIBSHARED}" \
+ all
+
+FRC: kpatch-4.3 kpatch-4.4
+
+kpatch-4.3:
+ -ln -s kpatch-4.2 kpatch-4.3
+kpatch-4.4:
+ -ln -s kpatch-4.2 kpatch-4.4
+
+install :
+ -mkdir ${DESTDIR}
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+ ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+ SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+ ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+ DESTDIR="${DESTDIR}" MANDIR="${MANDIR}" \
+ AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" AFPLIBS="${AFPLIBS}" \
+ LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+ LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+ CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+ INSTALL="${INSTALL}" $@); \
+ done
+ rm -f ${ETCDIR}/rc.atalk
+ sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+ -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+ -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+ -e s@:INCDIR:@${INCDIR}@ \
+ < ../../distrib/initscripts/rc.atalk.bsd > ${ETCDIR}/rc.atalk
+ @echo
+ @echo "Install is done. Don't forget to add lines from"
+ @echo "services.atalk to /etc/services and to call rc.atalk
+ @echo "in /etc/rc. Next, install the kernel patches."
+ @echo "See README.ULTRIX for more information."
+
+kpatch :
+ @echo "WARNING!!! This patches your kernel!!!"
+ @echo -n "(hit control-c with in 10 seconds, to stop)"
+ @sleep 10
+ @echo
+ @echo
+ @if grep -s -w atalk /sys/conf/files; then \
+ echo "You already have a version of netatalk installed."; \
+ echo "You will have to remove this old version. See"; \
+ echo "README.ULTRIX for specific instructions."; \
+ exit 1; \
+ else \
+ case `/bin/uname -r` in \
+ 4.1) echo -n "Applying 4.1 patches..."; \
+ patch -s -d /sys -p0 < kpatch-4.1; \
+ echo " done."; \
+ ;; \
+ 4.2|4.3|4.4) echo -n "Applying 4.2/4.3/4.4 patches..."; \
+ patch -s -d /sys -p0 < kpatch-4.2; \
+ echo " done."; \
+ ;; \
+ *) echo "Unknown release of Ultrix"; exit 1; \
+ ;; \
+ esac; \
+ fi
+ @echo
+ @echo "Next, install the netatalk kernel files. See README.ULTRIX"
+ @echo "for specific instructions."
+
+kinstall :
+ @echo "Copying netatalk to kernel building area..."
+ -mkdir /sys/net/netatalk
+ cp ../netatalk/*.[ch] *.[ch] /sys/net/netatalk
+ @echo "Done."
+ @echo
+ @echo "Next, make a new kernel. See README.ULTRIX"
+ @echo "for specific instructions."
+
+clean :
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+ done
+
+sysclean :
+
+depend :
+ for i in ${ALL}; \
+ do (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" depend); \
+ done
+ for i in ${SRC} ; do \
+ ${CC} -M ${DEFS} ${INCPATH} $$i | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' >> makedep; done
+ sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+ cat makedep >> Makefile.tmp
+ rm makedep
+ echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+ echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+ echo '# see make depend above' >> Makefile.tmp
+ rm -f Makefile.bak
+ cp Makefile Makefile.bak
+ mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
--- /dev/null
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#include <sys/types.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <net/if.h>
+#include <net/if_llc.h>
+#include <net/if_to_proto.h>
+#include <net/netisr.h>
+#include <netinet/in.h>
+#undef s_net
+#include <netinet/if_ether.h>
+
+#include "at.h"
+#include "at_var.h"
+
+extern u_char at_org_code[ 3 ];
+extern u_char aarp_org_code[ 3 ];
+
+/*
+ * This is the magic input routine, for all AppleTalk related packets.
+ * It will pick up *all* packets received, on all interfaces, apparently.
+ * If it turns out that receiving all packets in this fashion causes
+ * DLI to not receive packets what it should, we may need to call DLI
+ * directly from within the AppleTalk input routines. Ick.
+ */
+struct mbuf *
+ddp_ifinput( m, ifp, inq, eh )
+ struct mbuf *m;
+ struct ifnet *ifp;
+ struct ifqueue **inq;
+ struct ether_header *eh;
+{
+ struct llc llc;
+ struct if_family *ifam;
+
+ switch ( eh->ether_type ) {
+ case ETHERTYPE_AT :
+ *inq = &atintrq1;
+ smp_lock( &(*inq)->lk_ifqueue, LK_RETRY );
+ schednetisr( NETISR_AT );
+ return( m );
+
+ case ETHERTYPE_AARP :
+ aarpinput( ifp, m );
+ return( 0 );
+
+ default :
+ if ( eh->ether_type <= ETHERMTU ) { /* ieee802 */
+ if ( m->m_len < sizeof( struct llc )) {
+ break;
+ }
+
+ bcopy( mtod( m, caddr_t ), &llc, sizeof( struct llc ));
+ if ( llc.llc_dsap != LLC_SNAP_LSAP ||
+ llc.llc_ssap != LLC_SNAP_LSAP ||
+ llc.llc_control != LLC_UI ) {
+ break;
+ }
+
+ if ( bcmp( llc.llc_org_code, at_org_code,
+ sizeof( at_org_code )) == 0 &&
+ ntohs( llc.llc_ether_type ) == ETHERTYPE_AT ) {
+ m_adj( m, sizeof( struct llc ));
+ *inq = &atintrq2;
+ smp_lock( &(*inq)->lk_ifqueue, LK_RETRY );
+ schednetisr( NETISR_AT );
+ return( m );
+ }
+
+ if ( bcmp( llc.llc_org_code, aarp_org_code,
+ sizeof( aarp_org_code )) == 0 &&
+ ntohs( llc.llc_ether_type ) == ETHERTYPE_AARP ) {
+ m_adj( m, sizeof( struct llc ));
+ aarpinput( ifp, m );
+ return( 0 );
+ }
+ }
+ }
+
+ /*
+ * Check is anyone else wants this packet.
+ */
+ for ( ifam = if_family; ifam->domain != -1; ifam++ ) {
+ if (( eh->ether_type == ifam->if_type || ifam->if_type == -1 ) &&
+ ifam->prswitch &&
+ ifam->prswitch->pr_ifinput != (int (*)())ddp_ifinput ) {
+ break;
+ }
+ }
+ if ( ifam->domain != -1 && ifam->prswitch->pr_ifinput ) {
+ return( (struct mbuf *)(*ifam->prswitch->pr_ifinput)( m, ifp,
+ inq, eh ));
+ }
+
+ m_freem( m );
+ return( 0 );
+}
+
+/*
+ * Fill in type and odst. odst is the media output address, i.e.
+ * the MAC layer address. Type is the MAC type. Should be 0 to
+ * indicate IEEE addressing.
+ *
+ * Stupidly enough, there's no way to say "can't send this now."
+ * So, we just let the first packet go into the air. Not much
+ * else to be done, except maybe bitch at DEC. Note: we're not
+ * passing the mbuf to aarpresolve() -- that way it doesn't get
+ * mfree-ed twice.
+ */
+ddp_ifoutput( ifp, m, dst, type, odst )
+ struct ifnet *ifp;
+ struct mbuf *m;
+ struct sockaddr_at *dst;
+ short *type;
+ char *odst;
+{
+ struct at_ifaddr *aa;
+ struct llc *llc;
+ struct mbuf *m0;
+
+ if ( !aarpresolve( ifp, 0, dst, odst )) {
+ *type = 0xffff;
+ return( 0 );
+ }
+
+ if (( aa = (struct at_ifaddr *)at_ifawithnet( dst, ifp->if_addrlist ))
+ == 0 ) {
+ *type = 0xffff;
+ return( 0 );
+ }
+
+ if ( aa->aa_flags & AFA_PHASE2 ) {
+ /*
+ * This code needs to be modeled after the similar code in
+ * at_sun.c -- you can't just MGET() and bcopy(), since we might be
+ * dealing with mbufs which are really pages.
+ */
+ MGET( m0, M_WAIT, MT_HEADER );
+ if ( m0 == 0 ) {
+ *type = 0xffff;
+ return( 0 );
+ }
+ m0->m_next = m->m_next;
+ m0->m_off = m->m_off;
+ m0->m_len = m->m_len;
+ bcopy( mtod( m, caddr_t ), mtod( m0, caddr_t ), m->m_len );
+ m->m_next = m0;
+ m->m_off = MMINOFF;
+ m->m_len = sizeof( struct llc );
+
+ llc = mtod( m, struct llc *);
+ llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+ llc->llc_control = LLC_UI;
+ bcopy( at_org_code, llc->llc_org_code, sizeof( at_org_code ));
+ llc->llc_ether_type = htons( ETHERTYPE_AT );
+
+ /*
+ * Set the type to be the length of the packet, instead of 0.
+ * Ultrix used to put the length in the packet when we set type
+ * to 0, however, now we do it ourselves.
+ */
+ for ( *type = 0; m; m = m->m_next ) {
+ *type += m->m_len;
+ }
+ } else {
+ *type = ETHERTYPE_AT;
+ }
+
+ return( 1 );
+}
+
+ddp_ifioctl( ifp, cmd, data )
+ struct ifnet *ifp;
+ int cmd;
+ caddr_t data;
+{
+ switch( cmd ) {
+ case SIOCSIFADDR :
+ aarpwhohas((struct arpcom *)ifp,
+ &AA_SAT((struct ifaddr *)data)->sat_addr );
+ break;
+ default :
+ return( EINVAL );
+ }
+ return( 0 );
+}
--- /dev/null
+*** ../sys.old/conf/files Fri Jul 6 10:19:49 1990
+--- ./conf/files Thu Mar 12 17:33:58 1992
+***************
+*** 75,80 ****
+--- 75,87 ----
+ net/netinet/tcp_timer.c optional inet Binary
+ net/netinet/tcp_usrreq.c optional inet Binary
+ net/netinet/udp_usrreq.c optional inet Binary
++ net/netatalk/aarp.c optional atalk
++ net/netatalk/at_control.c optional atalk
++ net/netatalk/at_proto.c optional atalk
++ net/netatalk/at_ultrix.c optional atalk
++ net/netatalk/ddp_input.c optional atalk
++ net/netatalk/ddp_output.c optional atalk
++ net/netatalk/ddp_usrreq.c optional atalk
+ net/netbsc/bsc_pcb.c optional bsc Binary
+ net/netbsc/bsc_proto.c optional bsc
+ net/netbsc/bsc_states.c optional bsc Binary
+*** ../sys.old/data/af_data.c Fri Jul 6 09:40:50 1990
+--- ./data/af_data.c Thu Mar 12 17:34:03 1992
+***************
+*** 69,74 ****
+--- 69,82 ----
+ #define AFNS AFNULL
+ #endif NS
+
++ #ifdef ATALK
++ extern int atalk_hash(), atalk_netmatch();
++ #define AFATALK \
++ { atalk_hash, atalk_netmatch }
++ #else
++ #define AFATALK AFNULL
++ #endif
++
+ #ifdef BINARY
+
+ extern struct afswitch afswitch[];
+***************
+*** 78,83 ****
+ struct afswitch afswitch[AF_MAX] = {
+ AFNULL, AFNULL, AFINET, AFINET, AFPUP,
+ AFNULL, AFNS, AFNULL, AFNULL, AFNULL,
+! AFNULL
+ };
+ #endif
+--- 86,92 ----
+ struct afswitch afswitch[AF_MAX] = {
+ AFNULL, AFNULL, AFINET, AFINET, AFPUP,
+ AFNULL, AFNS, AFNULL, AFNULL, AFNULL,
+! AFNULL, AFNULL, AFNULL, AFNULL, AFNULL,
+! AFNULL, AFATALK, AFNULL, AFNULL, AFNULL,
+ };
+ #endif
+*** ../sys.old/data/if_to_proto_data.c Fri Jul 6 09:40:26 1990
+--- ./data/if_to_proto_data.c Thu Jun 3 11:53:44 1993
+***************
+*** 135,140 ****
+--- 135,156 ----
+ { ETHERTYPE_RC, AF_DLI, 0, 0 }
+ #endif
+
++ /*
++ * Hook for netatalk. We receive all packets. If we're not interested
++ * in the packet, we do a normal search on if_family for someone else
++ * who might want the packet.
++ * (Not yet. XXX)
++ */
++ #ifdef ATALK
++ #include "atalk.h"
++ #undef s_net
++ #include "../net/netatalk/at.h"
++ #define ETHER_ATALK \
++ { -1, AF_APPLETALK, ATPROTO_DDP, 0 }
++ #else ATALK
++ #define ETHER_ATALK IFNULL
++ #endif ATALK
++
+ #ifdef BINARY
+
+ extern struct if_family if_family[];
+***************
+*** 145,151 ****
+
+ struct if_family if_family[] = {
+! ETHER_DECNET, ETHER_LAT, ETHER_APPLE, ETHER_APPLEARP,
+ ETHER_NS, ETHER_DLI, IFEND
+ };
+
+ #endif
+--- 162,168 ----
+
+ struct if_family if_family[] = {
+! ETHER_ATALK, ETHER_DECNET, ETHER_LAT, ETHER_APPLE, ETHER_APPLEARP,
+ ETHER_NS, ETHER_DLI, IFEND
+ };
+
+ #endif
+*** ../sys.old/data/uipc_domain_data.c Fri Jul 6 09:40:44 1990
+--- ./data/uipc_domain_data.c Thu Mar 12 17:38:18 1992
+***************
+*** 107,112 ****
+--- 107,116 ----
+ ADDDOMAIN(ccitt);
+ #endif CCITT
+ #endif
++ #ifdef ATALK
++ #include "atalk.h"
++ ADDDOMAIN(atalk);
++ #endif ATALK
+
+ for (dp = domains; dp; dp = dp->dom_next)
+ for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
+*** ../sys.old/net/net/conf_net.c Fri Jul 6 10:03:25 1990
+--- ./net/net/conf_net.c Thu Mar 12 17:39:11 1992
+***************
+*** 236,241 ****
+--- 236,245 ----
+ extern int ddpintr();
+ #endif
+
++ #ifdef ATALK
++ extern int atintr();
++ #endif
++
+ #include "../net/net/netisr.h"
+ /*
+ * table of interrupt vectors - scanned in locore when sofware
+***************
+*** 280,285 ****
+--- 284,292 ----
+ #ifdef OSI
+ {NETISR_OSI,osiintr},
+ #endif OSI
++ #ifdef ATALK
++ {NETISR_AT,atintr},
++ #endif ATALK
+ {-1 ,0}
+ };
+
+*** ../sys.old/net/net/netisr.h Fri Jul 6 10:03:28 1990
+--- ./net/net/netisr.h Thu Mar 12 17:39:45 1992
+***************
+*** 66,71 ****
+--- 66,72 ----
+ #define NETISR_DLI 13 /* same as AF_DLI */
+ #define NETISR_LAT 14 /* same as AF_LAT */
+ #define NETISR_BSC 15 /* same as AF_BSC */
++ #define NETISR_AT 16 /* same as AF_APPLETALK */
+ #define NETISR_OSI 19 /* same as AF_OSI */
+
+ #define schednetisr(anisr) { set_bit_atomic(anisr,&netisr); setsoftnet(); }
--- /dev/null
+*** ../sys.old/conf/files Wed Jun 19 14:23:30 1991
+--- ./conf/files Fri Mar 6 18:06:45 1992
+***************
+*** 75,80 ****
+--- 75,87 ----
+ net/netinet/tcp_timer.c optional inet Binary
+ net/netinet/tcp_usrreq.c optional inet Binary
+ net/netinet/udp_usrreq.c optional inet Binary
++ net/netatalk/aarp.c optional atalk
++ net/netatalk/at_control.c optional atalk
++ net/netatalk/at_proto.c optional atalk
++ net/netatalk/at_ultrix.c optional atalk
++ net/netatalk/ddp_input.c optional atalk
++ net/netatalk/ddp_output.c optional atalk
++ net/netatalk/ddp_usrreq.c optional atalk
+ net/netbsc/bsc_pcb.c optional bsc Binary
+ net/netbsc/bsc_proto.c optional bsc
+ net/netbsc/bsc_states.c optional bsc Binary
+*** ../sys.old/data/af_data.c Mon Apr 29 15:45:53 1991
+--- ./data/af_data.c Sun Mar 8 22:25:15 1992
+***************
+*** 69,74 ****
+--- 69,82 ----
+ #define AFNS AFNULL
+ #endif NS
+
++ #ifdef ATALK
++ extern int atalk_hash(), atalk_netmatch();
++ #define AFATALK \
++ { atalk_hash, atalk_netmatch }
++ #else
++ #define AFATALK AFNULL
++ #endif
++
+ #ifdef BINARY
+
+ extern struct afswitch afswitch[];
+***************
+*** 78,83 ****
+ struct afswitch afswitch[AF_MAX] = {
+ AFNULL, AFNULL, AFINET, AFINET, AFPUP,
+ AFNULL, AFNS, AFNULL, AFNULL, AFNULL,
+! AFNULL
+ };
+ #endif
+--- 86,92 ----
+ struct afswitch afswitch[AF_MAX] = {
+ AFNULL, AFNULL, AFINET, AFINET, AFPUP,
+ AFNULL, AFNS, AFNULL, AFNULL, AFNULL,
+! AFNULL, AFNULL, AFNULL, AFNULL, AFNULL,
+! AFNULL, AFATALK, AFNULL, AFNULL, AFNULL,
+ };
+ #endif
+*** ../sys.old/data/if_to_proto_data.c Mon Apr 29 15:46:34 1991
+--- ./data/if_to_proto_data.c Thu Jun 3 11:53:44 1993
+***************
+*** 125,130 ****
+--- 125,143 ----
+ { ETHERTYPE_RC, AF_DLI, 0, 0 }
+ #endif
+
++ /*
++ * Hook for netatalk. We receive all packets. If we're not interested
++ * in the packet, we do a normal search on if_family for someone else
++ * who might want the packet.
++ */
++ #ifdef ATALK
++ #include "atalk.h"
++ #undef s_net
++ #include "../net/netatalk/at.h"
++ #define ETHER_ATALK \
++ { -1, AF_APPLETALK, ATPROTO_DDP, 0 }
++ #endif ATALK
++
+ #ifdef BINARY
+
+ extern struct if_family if_family[];
+***************
+*** 134,139 ****
+--- 148,156 ----
+ /* INET specific stuff is kept in drivers for now */
+
+ struct if_family if_family[] = {
++ #ifdef ETHER_ATALK
++ ETHER_ATALK,
++ #endif
+ #ifdef ETHER_DECNET
+ ETHER_DECNET,
+ #endif
+*** ../sys.old/data/uipc_domain_data.c Mon Apr 29 15:47:08 1991
+--- ./data/uipc_domain_data.c Mon Mar 2 15:24:28 1992
+***************
+*** 123,128 ****
+--- 123,132 ----
+ ADDDOMAIN(x25);
+ #endif XXXVNATV
+ #endif X25_DONE
++ #ifdef ATALK
++ #include "atalk.h"
++ ADDDOMAIN(atalk);
++ #endif ATALK
+
+ #endif
+
+*** ../sys.old/net/net/conf_net.c Mon Apr 29 16:16:23 1991
+--- ./net/net/conf_net.c Sun Mar 8 22:28:32 1992
+***************
+*** 233,238 ****
+--- 233,242 ----
+ extern int ddpintr();
+ #endif
+
++ #ifdef ATALK
++ extern int atintr();
++ #endif
++
+ #include "../net/net/netisr.h"
+ /*
+ * table of interrupt vectors - scanned in locore when sofware
+***************
+*** 272,277 ****
+--- 276,284 ----
+ #if NLAT == 1
+ {NETISR_LAT,latintr},
+ #endif /* NLAT */
++ #ifdef ATALK
++ {NETISR_AT,atintr},
++ #endif ATALK
+ #ifdef APPLETALK
+ {NETISR_DDP,ddpintr},
+ #endif APPLETALK
+*** ../sys.old/net/net/netisr.h Mon Apr 29 16:16:21 1991
+--- ./net/net/netisr.h Sun Mar 8 22:29:04 1992
+***************
+*** 72,77 ****
+--- 72,78 ----
+ #define NETISR_DLI 13 /* same as AF_DLI */
+ #define NETISR_LAT 14 /* same as AF_LAT */
+ #define NETISR_BSC 15 /* same as AF_BSC */
++ #define NETISR_AT 16 /* same as AF_APPLETALK */
+ #define NETISR_DLO 19 /* same as AF_OSI */
+
+ #define schednetisr(anisr) { set_bit_atomic(anisr,&netisr); setsoftnet(); }