From 31843674b7bd32eabcce3a1ad6159b4f94921f79 Mon Sep 17 00:00:00 2001 From: rufustfirefly Date: Tue, 25 Jul 2000 21:08:58 +0000 Subject: [PATCH] Initial revision --- BUGS | 26 + CHANGES | 247 +++ CONTRIBUTORS | 37 + COPYRIGHT | 46 + ChangeLog | 1823 ++++++++++++++++++++++ INSTALL/README.AFS | 51 + INSTALL/README.FREEBSD | 18 + INSTALL/README.GENERIC | 49 + INSTALL/README.LINUX | 37 + INSTALL/README.NETBSD | 18 + INSTALL/README.OPENBSD | 18 + INSTALL/README.SOLARIS | 68 + INSTALL/README.SUNOS | 18 + INSTALL/README.ULTRIX | 85 + Makefile | 124 ++ README | 116 ++ README.ASUN | 164 ++ TODO | 82 + VERSION | 1 + bin/Makefile | 40 + bin/adv1tov2/Makefile | 49 + bin/adv1tov2/adv1tov2.c | 135 ++ bin/aecho/Makefile | 48 + bin/aecho/aecho.c | 257 +++ bin/afppasswd/Makefile | 83 + bin/afppasswd/afppasswd.c | 329 ++++ bin/getzones/Makefile | 48 + bin/getzones/getzones.c | 138 ++ bin/megatron/Makefile | 55 + bin/megatron/asingle.c | 446 ++++++ bin/megatron/hqx.c | 692 ++++++++ bin/megatron/macbin.c | 560 +++++++ bin/megatron/megatron.c | 340 ++++ bin/megatron/megatron.h | 86 + bin/megatron/nad.c | 435 ++++++ bin/megatron/updcrc.c | 191 +++ bin/nbp/Makefile | 56 + bin/nbp/nbplkup.c | 153 ++ bin/nbp/nbprgstr.c | 89 ++ bin/nbp/nbpunrgstr.c | 87 ++ bin/pap/Makefile | 52 + bin/pap/pap.c | 844 ++++++++++ bin/pap/papstatus.c | 197 +++ bin/psorder/Makefile | 55 + bin/psorder/pa.c | 92 ++ bin/psorder/pa.h | 70 + bin/psorder/psorder.c | 561 +++++++ bin/psorder/psorder.h | 103 ++ config/AppleVolumes.default | 72 + config/AppleVolumes.system | 282 ++++ config/AppleVolumes.system.cobalt | 285 ++++ config/afpd.conf | 125 ++ config/atalkd.conf | 37 + config/atalkd.conf.cobalt | 38 + config/netatalk.conf | 25 + config/netatalk.conf.cobalt | 23 + config/netatalk.pamd | 6 + config/papd.conf | 20 + contrib/ICDumpSuffixMap | 1 + contrib/printing/add_netatalk_printer | 364 +++++ contrib/printing/netatalk.template | 610 ++++++++ contrib/printing/timeout.c | 234 +++ distrib/initscripts/rc.atalk.bsd | 39 + distrib/initscripts/rc.atalk.cobalt | 148 ++ distrib/initscripts/rc.atalk.redhat | 127 ++ distrib/initscripts/rc.atalk.sysv | 91 ++ distrib/rpm/buildrpm | 26 + distrib/rpm/netatalk-asun-cobalt.spec | 287 ++++ distrib/rpm/netatalk-asun.makefile.patch | 65 + distrib/rpm/netatalk-asun.spec | 297 ++++ etc/Makefile | 46 + etc/afpd/Makefile | 141 ++ etc/afpd/afp_asp.c | 223 +++ etc/afpd/afp_dsi.c | 280 ++++ etc/afpd/afp_options.c | 331 ++++ etc/afpd/afs.c | 338 ++++ etc/afpd/appl.c | 452 ++++++ etc/afpd/auth.c | 411 +++++ etc/afpd/auth.h | 40 + etc/afpd/codepage.c | 277 ++++ etc/afpd/codepage.h | 47 + etc/afpd/config.c | 353 +++++ etc/afpd/config.h | 23 + etc/afpd/desktop.c | 873 +++++++++++ etc/afpd/desktop.h | 67 + etc/afpd/directory.c | 1773 +++++++++++++++++++++ etc/afpd/directory.h | 175 +++ etc/afpd/enumerate.c | 428 +++++ etc/afpd/file.c | 1489 ++++++++++++++++++ etc/afpd/file.h | 98 ++ etc/afpd/filedir.c | 556 +++++++ etc/afpd/filedir.h | 19 + etc/afpd/fork.c | 1297 +++++++++++++++ etc/afpd/fork.h | 72 + etc/afpd/gettok.c | 162 ++ etc/afpd/globals.h | 82 + etc/afpd/icon.h | 268 ++++ etc/afpd/main.c | 218 +++ etc/afpd/messages.c | 63 + etc/afpd/misc.h | 26 + etc/afpd/nfsquota.c | 163 ++ etc/afpd/nls/Makefile | 25 + etc/afpd/nls/makecode.c | 136 ++ etc/afpd/nls/parsecode.c | 77 + etc/afpd/ofork.c | 260 +++ etc/afpd/passwd.c | 107 ++ etc/afpd/quota.c | 365 +++++ etc/afpd/status.c | 368 +++++ etc/afpd/status.h | 32 + etc/afpd/switch.c | 222 +++ etc/afpd/switch.h | 29 + etc/afpd/uam.c | 362 +++++ etc/afpd/uam_auth.h | 63 + etc/afpd/unix.c | 450 ++++++ etc/afpd/unix.h | 100 ++ etc/afpd/volume.c | 1293 +++++++++++++++ etc/afpd/volume.h | 146 ++ etc/atalkd/Makefile | 56 + etc/atalkd/aep.c | 38 + etc/atalkd/atserv.h | 21 + etc/atalkd/config.c | 753 +++++++++ etc/atalkd/gate.h | 13 + etc/atalkd/interface.h | 55 + etc/atalkd/list.h | 10 + etc/atalkd/main.c | 1493 ++++++++++++++++++ etc/atalkd/multicast.c | 395 +++++ etc/atalkd/multicast.h | 19 + etc/atalkd/nbp.c | 633 ++++++++ etc/atalkd/nbp.h | 12 + etc/atalkd/route.c | 77 + etc/atalkd/rtmp.c | 957 ++++++++++++ etc/atalkd/rtmp.h | 85 + etc/atalkd/zip.c | 1049 +++++++++++++ etc/atalkd/zip.h | 23 + etc/papd/Makefile | 88 ++ etc/papd/comment.c | 122 ++ etc/papd/comment.h | 48 + etc/papd/file.c | 116 ++ etc/papd/file.h | 31 + etc/papd/headers.c | 66 + etc/papd/lp.c | 719 +++++++++ etc/papd/magics.c | 185 +++ etc/papd/main.c | 727 +++++++++ etc/papd/ppd.c | 302 ++++ etc/papd/ppd.h | 17 + etc/papd/printcap.c | 513 ++++++ etc/papd/printer.h | 55 + etc/papd/queries.c | 592 +++++++ etc/papd/session.c | 302 ++++ etc/psf/Makefile | 72 + etc/psf/etc2ps.sh | 56 + etc/psf/pagecount.ps | 5 + etc/psf/psa.c | 125 ++ etc/psf/psf.c | 694 ++++++++ etc/uams/Makefile | 171 ++ etc/uams/uams_dhx_pam.c | 523 +++++++ etc/uams/uams_dhx_passwd.c | 269 ++++ etc/uams/uams_guest.c | 66 + etc/uams/uams_krb4/kuam.c | 211 +++ etc/uams/uams_krb4/lifetime.c | 145 ++ etc/uams/uams_krb4/send_to_kdc.c | 320 ++++ etc/uams/uams_krb4/uams_krb4.c | 590 +++++++ etc/uams/uams_pam.c | 289 ++++ etc/uams/uams_passwd.c | 153 ++ etc/uams/uams_pgp.c | 172 ++ etc/uams/uams_randnum.c | 522 +++++++ include/Makefile | 23 + include/atalk/adouble.h | 313 ++++ include/atalk/aep.h | 30 + include/atalk/afp.h | 125 ++ include/atalk/asp.h | 103 ++ include/atalk/atp.h | 191 +++ include/atalk/cnid.h | 44 + include/atalk/compat.h | 81 + include/atalk/ddp.h | 35 + include/atalk/dsi.h | 167 ++ include/atalk/nbp.h | 94 ++ include/atalk/netddp.h | 36 + include/atalk/pap.h | 40 + include/atalk/paths.h | 68 + include/atalk/rtmp.h | 45 + include/atalk/server_child.h | 36 + include/atalk/uam.h | 71 + include/atalk/util.h | 46 + include/atalk/zip.h | 58 + libatalk/Makefile | 77 + libatalk/adouble/Makefile | 58 + libatalk/adouble/ad_attr.c | 33 + libatalk/adouble/ad_date.c | 54 + libatalk/adouble/ad_flush.c | 162 ++ libatalk/adouble/ad_lock.c | 405 +++++ libatalk/adouble/ad_mmap.c | 104 ++ libatalk/adouble/ad_open.c | 792 ++++++++++ libatalk/adouble/ad_private.h | 35 + libatalk/adouble/ad_read.c | 103 ++ libatalk/adouble/ad_sendfile.c | 93 ++ libatalk/adouble/ad_size.c | 27 + libatalk/adouble/ad_write.c | 144 ++ libatalk/asp/Makefile | 60 + libatalk/asp/asp_attn.c | 53 + libatalk/asp/asp_child.h | 32 + libatalk/asp/asp_close.c | 56 + libatalk/asp/asp_cmdreply.c | 81 + libatalk/asp/asp_getreq.c | 61 + libatalk/asp/asp_getsess.c | 267 ++++ libatalk/asp/asp_init.c | 70 + libatalk/asp/asp_shutdown.c | 57 + libatalk/asp/asp_tickle.c | 23 + libatalk/asp/asp_write.c | 94 ++ libatalk/atp/Makefile | 58 + libatalk/atp/atp_bprint.c | 63 + libatalk/atp/atp_bufs.c | 144 ++ libatalk/atp/atp_close.c | 69 + libatalk/atp/atp_internals.h | 58 + libatalk/atp/atp_open.c | 80 + libatalk/atp/atp_packet.c | 329 ++++ libatalk/atp/atp_rreq.c | 106 ++ libatalk/atp/atp_rresp.c | 92 ++ libatalk/atp/atp_rsel.c | 355 +++++ libatalk/atp/atp_sreq.c | 131 ++ libatalk/atp/atp_sresp.c | 149 ++ libatalk/cnid/Makefile | 58 + libatalk/cnid/README | 37 + libatalk/cnid/cnid_add.c | 185 +++ libatalk/cnid/cnid_close.c | 57 + libatalk/cnid/cnid_delete.c | 109 ++ libatalk/cnid/cnid_get.c | 51 + libatalk/cnid/cnid_lookup.c | 105 ++ libatalk/cnid/cnid_meta.c | 7 + libatalk/cnid/cnid_meta.h | 47 + libatalk/cnid/cnid_nextid.c | 22 + libatalk/cnid/cnid_open.c | 369 +++++ libatalk/cnid/cnid_private.h | 120 ++ libatalk/cnid/cnid_resolve.c | 42 + libatalk/cnid/cnid_update.c | 132 ++ libatalk/compat/Makefile | 58 + libatalk/compat/flock.c | 52 + libatalk/compat/getusershell.c | 133 ++ libatalk/compat/inet_aton.c | 19 + libatalk/compat/mktemp.c | 116 ++ libatalk/compat/rquota_xdr.c | 115 ++ libatalk/compat/strcasecmp.c | 105 ++ libatalk/compat/strdup.c | 17 + libatalk/compat/strstr.c | 73 + libatalk/dsi/Makefile | 69 + libatalk/dsi/README | 45 + libatalk/dsi/dsi_attn.c | 49 + libatalk/dsi/dsi_close.c | 23 + libatalk/dsi/dsi_cmdreply.c | 21 + libatalk/dsi/dsi_getsess.c | 119 ++ libatalk/dsi/dsi_getstat.c | 22 + libatalk/dsi/dsi_init.c | 54 + libatalk/dsi/dsi_opensess.c | 44 + libatalk/dsi/dsi_private.h | 23 + libatalk/dsi/dsi_read.c | 62 + libatalk/dsi/dsi_stream.c | 191 +++ libatalk/dsi/dsi_tcp.c | 298 ++++ libatalk/dsi/dsi_tickle.c | 35 + libatalk/dsi/dsi_write.c | 89 ++ libatalk/nbp/Makefile | 56 + libatalk/nbp/nbp_conf.h | 39 + libatalk/nbp/nbp_lkup.c | 216 +++ libatalk/nbp/nbp_rgstr.c | 144 ++ libatalk/nbp/nbp_unrgstr.c | 138 ++ libatalk/nbp/nbp_util.c | 157 ++ libatalk/netddp/Makefile | 56 + libatalk/netddp/netddp_open.c | 77 + libatalk/netddp/netddp_recvfrom.c | 61 + libatalk/netddp/netddp_sendto.c | 64 + libatalk/pap/pap_child.h | 32 + libatalk/pap/pap_close.c | 44 + libatalk/pap/pap_init.c | 25 + libatalk/pap/pap_open.c | 119 ++ libatalk/pap/pap_read.c | 25 + libatalk/pap/pap_sendstatus.c | 21 + libatalk/pap/pap_slinit.c | 254 +++ libatalk/pap/pap_tickle.c | 19 + libatalk/util/Makefile | 58 + libatalk/util/atalk_addr.c | 112 ++ libatalk/util/bprint.c | 46 + libatalk/util/getiface.c | 152 ++ libatalk/util/module.c | 66 + libatalk/util/server_child.c | 249 +++ libatalk/util/server_lock.c | 80 + libatalk/util/strdicasecmp.c | 544 +++++++ lp2pap.sh | 11 + man/Makefile | 41 + man/man1/Makefile | 46 + man/man1/aecho.1 | 68 + man/man1/getzones.1 | 39 + man/man1/hqx2bin.1 | 1 + man/man1/macbinary.1 | 1 + man/man1/megatron.1 | 104 ++ man/man1/nbp.1 | 82 + man/man1/nbplkup.1 | 1 + man/man1/nbprgstr.1 | 1 + man/man1/nbpunrgstr.1 | 1 + man/man1/pap.1 | 123 ++ man/man1/papstatus.1 | 1 + man/man1/psorder.1 | 48 + man/man1/single2bin.1 | 1 + man/man1/unbin.1 | 1 + man/man1/unhex.1 | 1 + man/man1/unsingle.1 | 1 + man/man3/Makefile | 40 + man/man3/atalk_aton.3 | 24 + man/man3/nbp_name.3 | 79 + man/man4/Makefile | 39 + man/man4/atalk.4 | 63 + man/man8/Makefile | 40 + man/man8/afpd.8 | 261 ++++ man/man8/atalkd.8 | 123 ++ man/man8/pap.8 | 165 ++ man/man8/papd.8 | 142 ++ man/man8/papstatus.8 | 84 + man/man8/psf.8 | 124 ++ services.atalk | 7 + sys/freebsd/Makefile | 88 ++ sys/generic/Makefile | 142 ++ sys/generic/sys/cdefs.h | 10 + sys/linux/Makefile | 138 ++ sys/netatalk/aarp.c | 796 ++++++++++ sys/netatalk/aarp.h | 58 + sys/netatalk/at.h | 113 ++ sys/netatalk/at_control.c | 580 +++++++ sys/netatalk/at_proto.c | 51 + sys/netatalk/at_var.h | 51 + sys/netatalk/ddp.h | 125 ++ sys/netatalk/ddp_input.c | 401 +++++ sys/netatalk/ddp_output.c | 261 ++++ sys/netatalk/ddp_usrreq.c | 530 +++++++ sys/netatalk/ddp_var.h | 41 + sys/netatalk/endian.h | 129 ++ sys/netatalk/phase2.h | 81 + sys/netbsd/Makefile | 91 ++ sys/netbsd/netatalk/endian.h | 9 + sys/openbsd/Makefile | 83 + sys/osx/Makefile | 95 ++ sys/solaris/Makefile | 194 +++ sys/solaris/aarp.c | 343 ++++ sys/solaris/ddp.c | 64 + sys/solaris/ddp.conf | 5 + sys/solaris/dlpi.c | 473 ++++++ sys/solaris/if.c | 368 +++++ sys/solaris/if.h | 77 + sys/solaris/ioc.c | 126 ++ sys/solaris/ioc.h | 4 + sys/solaris/linkage.c | 46 + sys/solaris/rt.c | 125 ++ sys/solaris/rt.h | 3 + sys/solaris/sock.c | 126 ++ sys/solaris/sock.h | 12 + sys/solaris/tpi.c | 714 +++++++++ sys/sunos/Makefile | 148 ++ sys/sunos/at_sun.c | 374 +++++ sys/ultrix/Makefile | 143 ++ sys/ultrix/at_ultrix.c | 211 +++ sys/ultrix/kpatch-4.1 | 151 ++ sys/ultrix/kpatch-4.2 | 143 ++ 359 files changed, 62996 insertions(+) create mode 100644 BUGS create mode 100644 CHANGES create mode 100644 CONTRIBUTORS create mode 100644 COPYRIGHT create mode 100644 ChangeLog create mode 100644 INSTALL/README.AFS create mode 100644 INSTALL/README.FREEBSD create mode 100644 INSTALL/README.GENERIC create mode 100644 INSTALL/README.LINUX create mode 100644 INSTALL/README.NETBSD create mode 100644 INSTALL/README.OPENBSD create mode 100644 INSTALL/README.SOLARIS create mode 100644 INSTALL/README.SUNOS create mode 100644 INSTALL/README.ULTRIX create mode 100644 Makefile create mode 100644 README create mode 100644 README.ASUN create mode 100644 TODO create mode 100644 VERSION create mode 100644 bin/Makefile create mode 100644 bin/adv1tov2/Makefile create mode 100644 bin/adv1tov2/adv1tov2.c create mode 100644 bin/aecho/Makefile create mode 100644 bin/aecho/aecho.c create mode 100644 bin/afppasswd/Makefile create mode 100644 bin/afppasswd/afppasswd.c create mode 100644 bin/getzones/Makefile create mode 100644 bin/getzones/getzones.c create mode 100644 bin/megatron/Makefile create mode 100644 bin/megatron/asingle.c create mode 100644 bin/megatron/hqx.c create mode 100644 bin/megatron/macbin.c create mode 100644 bin/megatron/megatron.c create mode 100644 bin/megatron/megatron.h create mode 100644 bin/megatron/nad.c create mode 100644 bin/megatron/updcrc.c create mode 100644 bin/nbp/Makefile create mode 100644 bin/nbp/nbplkup.c create mode 100644 bin/nbp/nbprgstr.c create mode 100644 bin/nbp/nbpunrgstr.c create mode 100644 bin/pap/Makefile create mode 100644 bin/pap/pap.c create mode 100644 bin/pap/papstatus.c create mode 100644 bin/psorder/Makefile create mode 100644 bin/psorder/pa.c create mode 100644 bin/psorder/pa.h create mode 100644 bin/psorder/psorder.c create mode 100644 bin/psorder/psorder.h create mode 100644 config/AppleVolumes.default create mode 100644 config/AppleVolumes.system create mode 100644 config/AppleVolumes.system.cobalt create mode 100644 config/afpd.conf create mode 100644 config/atalkd.conf create mode 100644 config/atalkd.conf.cobalt create mode 100644 config/netatalk.conf create mode 100644 config/netatalk.conf.cobalt create mode 100644 config/netatalk.pamd create mode 100644 config/papd.conf create mode 100644 contrib/ICDumpSuffixMap create mode 100644 contrib/printing/add_netatalk_printer create mode 100644 contrib/printing/netatalk.template create mode 100644 contrib/printing/timeout.c create mode 100755 distrib/initscripts/rc.atalk.bsd create mode 100644 distrib/initscripts/rc.atalk.cobalt create mode 100755 distrib/initscripts/rc.atalk.redhat create mode 100755 distrib/initscripts/rc.atalk.sysv create mode 100755 distrib/rpm/buildrpm create mode 100644 distrib/rpm/netatalk-asun-cobalt.spec create mode 100644 distrib/rpm/netatalk-asun.makefile.patch create mode 100644 distrib/rpm/netatalk-asun.spec create mode 100644 etc/Makefile create mode 100644 etc/afpd/Makefile create mode 100644 etc/afpd/afp_asp.c create mode 100644 etc/afpd/afp_dsi.c create mode 100644 etc/afpd/afp_options.c create mode 100644 etc/afpd/afs.c create mode 100644 etc/afpd/appl.c create mode 100644 etc/afpd/auth.c create mode 100644 etc/afpd/auth.h create mode 100644 etc/afpd/codepage.c create mode 100644 etc/afpd/codepage.h create mode 100644 etc/afpd/config.c create mode 100644 etc/afpd/config.h create mode 100644 etc/afpd/desktop.c create mode 100644 etc/afpd/desktop.h create mode 100644 etc/afpd/directory.c create mode 100644 etc/afpd/directory.h create mode 100644 etc/afpd/enumerate.c create mode 100644 etc/afpd/file.c create mode 100644 etc/afpd/file.h create mode 100644 etc/afpd/filedir.c create mode 100644 etc/afpd/filedir.h create mode 100644 etc/afpd/fork.c create mode 100644 etc/afpd/fork.h create mode 100644 etc/afpd/gettok.c create mode 100644 etc/afpd/globals.h create mode 100644 etc/afpd/icon.h create mode 100644 etc/afpd/main.c create mode 100644 etc/afpd/messages.c create mode 100644 etc/afpd/misc.h create mode 100644 etc/afpd/nfsquota.c create mode 100644 etc/afpd/nls/Makefile create mode 100644 etc/afpd/nls/makecode.c create mode 100644 etc/afpd/nls/parsecode.c create mode 100644 etc/afpd/ofork.c create mode 100644 etc/afpd/passwd.c create mode 100644 etc/afpd/quota.c create mode 100644 etc/afpd/status.c create mode 100644 etc/afpd/status.h create mode 100644 etc/afpd/switch.c create mode 100644 etc/afpd/switch.h create mode 100644 etc/afpd/uam.c create mode 100644 etc/afpd/uam_auth.h create mode 100644 etc/afpd/unix.c create mode 100644 etc/afpd/unix.h create mode 100644 etc/afpd/volume.c create mode 100644 etc/afpd/volume.h create mode 100644 etc/atalkd/Makefile create mode 100644 etc/atalkd/aep.c create mode 100644 etc/atalkd/atserv.h create mode 100644 etc/atalkd/config.c create mode 100644 etc/atalkd/gate.h create mode 100644 etc/atalkd/interface.h create mode 100644 etc/atalkd/list.h create mode 100644 etc/atalkd/main.c create mode 100644 etc/atalkd/multicast.c create mode 100644 etc/atalkd/multicast.h create mode 100644 etc/atalkd/nbp.c create mode 100644 etc/atalkd/nbp.h create mode 100644 etc/atalkd/route.c create mode 100644 etc/atalkd/rtmp.c create mode 100644 etc/atalkd/rtmp.h create mode 100644 etc/atalkd/zip.c create mode 100644 etc/atalkd/zip.h create mode 100644 etc/papd/Makefile create mode 100644 etc/papd/comment.c create mode 100644 etc/papd/comment.h create mode 100644 etc/papd/file.c create mode 100644 etc/papd/file.h create mode 100644 etc/papd/headers.c create mode 100644 etc/papd/lp.c create mode 100644 etc/papd/magics.c create mode 100644 etc/papd/main.c create mode 100644 etc/papd/ppd.c create mode 100644 etc/papd/ppd.h create mode 100644 etc/papd/printcap.c create mode 100644 etc/papd/printer.h create mode 100644 etc/papd/queries.c create mode 100644 etc/papd/session.c create mode 100644 etc/psf/Makefile create mode 100644 etc/psf/etc2ps.sh create mode 100644 etc/psf/pagecount.ps create mode 100644 etc/psf/psa.c create mode 100644 etc/psf/psf.c create mode 100644 etc/uams/Makefile create mode 100644 etc/uams/uams_dhx_pam.c create mode 100644 etc/uams/uams_dhx_passwd.c create mode 100644 etc/uams/uams_guest.c create mode 100644 etc/uams/uams_krb4/kuam.c create mode 100644 etc/uams/uams_krb4/lifetime.c create mode 100644 etc/uams/uams_krb4/send_to_kdc.c create mode 100644 etc/uams/uams_krb4/uams_krb4.c create mode 100644 etc/uams/uams_pam.c create mode 100644 etc/uams/uams_passwd.c create mode 100644 etc/uams/uams_pgp.c create mode 100644 etc/uams/uams_randnum.c create mode 100644 include/Makefile create mode 100644 include/atalk/adouble.h create mode 100644 include/atalk/aep.h create mode 100644 include/atalk/afp.h create mode 100644 include/atalk/asp.h create mode 100644 include/atalk/atp.h create mode 100644 include/atalk/cnid.h create mode 100644 include/atalk/compat.h create mode 100644 include/atalk/ddp.h create mode 100644 include/atalk/dsi.h create mode 100644 include/atalk/nbp.h create mode 100644 include/atalk/netddp.h create mode 100644 include/atalk/pap.h create mode 100644 include/atalk/paths.h create mode 100644 include/atalk/rtmp.h create mode 100644 include/atalk/server_child.h create mode 100644 include/atalk/uam.h create mode 100644 include/atalk/util.h create mode 100644 include/atalk/zip.h create mode 100644 libatalk/Makefile create mode 100644 libatalk/adouble/Makefile create mode 100644 libatalk/adouble/ad_attr.c create mode 100644 libatalk/adouble/ad_date.c create mode 100644 libatalk/adouble/ad_flush.c create mode 100644 libatalk/adouble/ad_lock.c create mode 100644 libatalk/adouble/ad_mmap.c create mode 100644 libatalk/adouble/ad_open.c create mode 100644 libatalk/adouble/ad_private.h create mode 100644 libatalk/adouble/ad_read.c create mode 100644 libatalk/adouble/ad_sendfile.c create mode 100644 libatalk/adouble/ad_size.c create mode 100644 libatalk/adouble/ad_write.c create mode 100644 libatalk/asp/Makefile create mode 100644 libatalk/asp/asp_attn.c create mode 100644 libatalk/asp/asp_child.h create mode 100644 libatalk/asp/asp_close.c create mode 100644 libatalk/asp/asp_cmdreply.c create mode 100644 libatalk/asp/asp_getreq.c create mode 100644 libatalk/asp/asp_getsess.c create mode 100644 libatalk/asp/asp_init.c create mode 100644 libatalk/asp/asp_shutdown.c create mode 100644 libatalk/asp/asp_tickle.c create mode 100644 libatalk/asp/asp_write.c create mode 100644 libatalk/atp/Makefile create mode 100644 libatalk/atp/atp_bprint.c create mode 100644 libatalk/atp/atp_bufs.c create mode 100644 libatalk/atp/atp_close.c create mode 100644 libatalk/atp/atp_internals.h create mode 100644 libatalk/atp/atp_open.c create mode 100644 libatalk/atp/atp_packet.c create mode 100644 libatalk/atp/atp_rreq.c create mode 100644 libatalk/atp/atp_rresp.c create mode 100644 libatalk/atp/atp_rsel.c create mode 100644 libatalk/atp/atp_sreq.c create mode 100644 libatalk/atp/atp_sresp.c create mode 100644 libatalk/cnid/Makefile create mode 100644 libatalk/cnid/README create mode 100644 libatalk/cnid/cnid_add.c create mode 100644 libatalk/cnid/cnid_close.c create mode 100644 libatalk/cnid/cnid_delete.c create mode 100644 libatalk/cnid/cnid_get.c create mode 100644 libatalk/cnid/cnid_lookup.c create mode 100644 libatalk/cnid/cnid_meta.c create mode 100644 libatalk/cnid/cnid_meta.h create mode 100644 libatalk/cnid/cnid_nextid.c create mode 100644 libatalk/cnid/cnid_open.c create mode 100644 libatalk/cnid/cnid_private.h create mode 100644 libatalk/cnid/cnid_resolve.c create mode 100644 libatalk/cnid/cnid_update.c create mode 100644 libatalk/compat/Makefile create mode 100644 libatalk/compat/flock.c create mode 100644 libatalk/compat/getusershell.c create mode 100644 libatalk/compat/inet_aton.c create mode 100644 libatalk/compat/mktemp.c create mode 100644 libatalk/compat/rquota_xdr.c create mode 100644 libatalk/compat/strcasecmp.c create mode 100644 libatalk/compat/strdup.c create mode 100644 libatalk/compat/strstr.c create mode 100644 libatalk/dsi/Makefile create mode 100644 libatalk/dsi/README create mode 100644 libatalk/dsi/dsi_attn.c create mode 100644 libatalk/dsi/dsi_close.c create mode 100644 libatalk/dsi/dsi_cmdreply.c create mode 100644 libatalk/dsi/dsi_getsess.c create mode 100644 libatalk/dsi/dsi_getstat.c create mode 100644 libatalk/dsi/dsi_init.c create mode 100644 libatalk/dsi/dsi_opensess.c create mode 100644 libatalk/dsi/dsi_private.h create mode 100644 libatalk/dsi/dsi_read.c create mode 100644 libatalk/dsi/dsi_stream.c create mode 100644 libatalk/dsi/dsi_tcp.c create mode 100644 libatalk/dsi/dsi_tickle.c create mode 100644 libatalk/dsi/dsi_write.c create mode 100644 libatalk/nbp/Makefile create mode 100644 libatalk/nbp/nbp_conf.h create mode 100644 libatalk/nbp/nbp_lkup.c create mode 100644 libatalk/nbp/nbp_rgstr.c create mode 100644 libatalk/nbp/nbp_unrgstr.c create mode 100644 libatalk/nbp/nbp_util.c create mode 100644 libatalk/netddp/Makefile create mode 100644 libatalk/netddp/netddp_open.c create mode 100644 libatalk/netddp/netddp_recvfrom.c create mode 100644 libatalk/netddp/netddp_sendto.c create mode 100644 libatalk/pap/pap_child.h create mode 100644 libatalk/pap/pap_close.c create mode 100644 libatalk/pap/pap_init.c create mode 100644 libatalk/pap/pap_open.c create mode 100644 libatalk/pap/pap_read.c create mode 100644 libatalk/pap/pap_sendstatus.c create mode 100644 libatalk/pap/pap_slinit.c create mode 100644 libatalk/pap/pap_tickle.c create mode 100644 libatalk/util/Makefile create mode 100644 libatalk/util/atalk_addr.c create mode 100644 libatalk/util/bprint.c create mode 100644 libatalk/util/getiface.c create mode 100644 libatalk/util/module.c create mode 100644 libatalk/util/server_child.c create mode 100644 libatalk/util/server_lock.c create mode 100644 libatalk/util/strdicasecmp.c create mode 100755 lp2pap.sh create mode 100644 man/Makefile create mode 100644 man/man1/Makefile create mode 100644 man/man1/aecho.1 create mode 100644 man/man1/getzones.1 create mode 100644 man/man1/hqx2bin.1 create mode 100644 man/man1/macbinary.1 create mode 100644 man/man1/megatron.1 create mode 100644 man/man1/nbp.1 create mode 100644 man/man1/nbplkup.1 create mode 100644 man/man1/nbprgstr.1 create mode 100644 man/man1/nbpunrgstr.1 create mode 100644 man/man1/pap.1 create mode 100644 man/man1/papstatus.1 create mode 100644 man/man1/psorder.1 create mode 100644 man/man1/single2bin.1 create mode 100644 man/man1/unbin.1 create mode 100644 man/man1/unhex.1 create mode 100644 man/man1/unsingle.1 create mode 100644 man/man3/Makefile create mode 100644 man/man3/atalk_aton.3 create mode 100644 man/man3/nbp_name.3 create mode 100644 man/man4/Makefile create mode 100644 man/man4/atalk.4 create mode 100644 man/man8/Makefile create mode 100644 man/man8/afpd.8 create mode 100644 man/man8/atalkd.8 create mode 100644 man/man8/pap.8 create mode 100644 man/man8/papd.8 create mode 100644 man/man8/papstatus.8 create mode 100644 man/man8/psf.8 create mode 100644 services.atalk create mode 100644 sys/freebsd/Makefile create mode 100644 sys/generic/Makefile create mode 100644 sys/generic/sys/cdefs.h create mode 100644 sys/linux/Makefile create mode 100644 sys/netatalk/aarp.c create mode 100644 sys/netatalk/aarp.h create mode 100644 sys/netatalk/at.h create mode 100644 sys/netatalk/at_control.c create mode 100644 sys/netatalk/at_proto.c create mode 100644 sys/netatalk/at_var.h create mode 100644 sys/netatalk/ddp.h create mode 100644 sys/netatalk/ddp_input.c create mode 100644 sys/netatalk/ddp_output.c create mode 100644 sys/netatalk/ddp_usrreq.c create mode 100644 sys/netatalk/ddp_var.h create mode 100644 sys/netatalk/endian.h create mode 100644 sys/netatalk/phase2.h create mode 100644 sys/netbsd/Makefile create mode 100644 sys/netbsd/netatalk/endian.h create mode 100644 sys/openbsd/Makefile create mode 100644 sys/osx/Makefile create mode 100644 sys/solaris/Makefile create mode 100644 sys/solaris/aarp.c create mode 100644 sys/solaris/ddp.c create mode 100644 sys/solaris/ddp.conf create mode 100644 sys/solaris/dlpi.c create mode 100644 sys/solaris/if.c create mode 100644 sys/solaris/if.h create mode 100644 sys/solaris/ioc.c create mode 100644 sys/solaris/ioc.h create mode 100644 sys/solaris/linkage.c create mode 100644 sys/solaris/rt.c create mode 100644 sys/solaris/rt.h create mode 100644 sys/solaris/sock.c create mode 100644 sys/solaris/sock.h create mode 100644 sys/solaris/tpi.c create mode 100644 sys/sunos/Makefile create mode 100644 sys/sunos/at_sun.c create mode 100644 sys/ultrix/Makefile create mode 100644 sys/ultrix/at_ultrix.c create mode 100644 sys/ultrix/kpatch-4.1 create mode 100644 sys/ultrix/kpatch-4.2 diff --git a/BUGS b/BUGS new file mode 100644 index 00000000..db819773 --- /dev/null +++ b/BUGS @@ -0,0 +1,26 @@ +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. diff --git a/CHANGES b/CHANGES new file mode 100644 index 00000000..8f2a8e4b --- /dev/null +++ b/CHANGES @@ -0,0 +1,247 @@ +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 . + + 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. diff --git a/CONTRIBUTORS b/CONTRIBUTORS new file mode 100644 index 00000000..e1c268d9 --- /dev/null +++ b/CONTRIBUTORS @@ -0,0 +1,37 @@ +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 diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 00000000..7400d7df --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,46 @@ +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 diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..a5c5b5f3 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1823 @@ +2000-02-28 a sun + + * etc/afpd/directory.h (CNID_INODE): xor the inode a little + differently. + +2000-02-23 a sun + + * 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 + + * distrib/initscripts/rc.atalk.redhat/cobalt: added changes to + make redhat 6.x happier. + +2000-02-17 a sun + + * 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 + + * etc/afpd/fork.c (afp_bytelock): only error on bytelocks + positioned at 0x7FFFFFFF if there's no resource fork. + +2000-02-14 a sun + + * 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 + + * bin/nbp/Makefile (install): make nbprgstr/nbpunrgstr with 700 + permissions. + + * include/atalk/adouble.h (sendfile): change to deal with + + +2000-01-25 a sun + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * etc/afpd/directory.c (setdirparams): don't error if we can't set + the desktop owner/permisssions. + +1999-11-04 a sun + + * etc/afpd/fork.c (afp_openfork): had the ordering wrong on an + openfork. + +1999-11-02 a sun + + * etc/afpd/afp_dsi.c (afp_over_dsi): flush data for unknown dsi + commands. + +1999-10-28 a sun + + * etc/uams/*.c: return FPError_PARAM if the user is unknown. + +1999-10-27 a sun + + * etc/afpd/fork.c (afp_read): if sendfile isn't supported, use the + old looping method. + +1999-10-25 a sun + + * libatalk/nbp/nbp_unrgstr.c (nbp_unrgstr): fix nbp unregisters. + +1999-10-21 a sun + + * etc/afpd/Makefile (install): moved install of afpd earlier per + suggestion by steven michaud. + +1999-10-05 a sun + + * etc/uams/uams_randnum.c (afppasswd): for ~/.passwd's, turn + ourselves into the user so that nfs is happy. + +1999-09-19 a sun + + * 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 + + * 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 + + * 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 + + * etc/afpd/desktop.c (mtoupath): fixed a bug in codepage support + that accidentally crept in. + +1999-08-31 Adrian Sun + + * 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 + + * 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 + + * 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 + + * etc/afpd/filedir.c,file.c,directory.c: changed error for + illegal filenames to AFPERR_EXIST. + +1999-08-11 a sun + + * 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 + + * 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 + + * etc/afpd/volume.c (readvolfile): changed volume options into an + array of structs to ease maintenance. + +1999-08-05 a sun + + * 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 + + * etc/atalkd/main.c (setaddr): made a failure with setaddr a + little more informative. + +1999-08-03 Adrian Sun + + * 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 + + * 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 + + * moved rc.atalk.* scripts to distrib/initscripts. + +1999-07-27 a sun + + * 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 + + * 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 + + * create links/mangle files in the compile rather than the install + phase so that rpm will be happier. + +1999-07-17 a sun + + * 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 + + * rc.atalk.redhat: incorporated chkconfig from inoue. + +1999-07-15 a sun + + * 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 + + * 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 + + * 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 + + * libatalk/nbp/nbp_rgstr.c (nbp_rgstr): return EADDRINUSE if the + address already exists. + +1999-07-06 a sun + + * rc.atalk.redhat: changed netatalk.config to netatalk.conf + +1999-07-05 a sun + + * 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 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 + + * 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 + + * 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 + + * etc/afpd/status.c (status_netaddress): added support for fqdn + (not available in the appleshare client yet). + +1999-07-01 a sun + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * etc/uams/Makefile: openssl-0.9.3c uses 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * sys/freebsd/Makefile: turn on sendfile support if running on a + FreeBSD 3+ machine. + +1999-06-02 a sun + + * 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 + + * 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 + + * 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 + + * 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 + + * sys/sunos/Makefile: various bits to make stuff work with sunos + again. + +1999-05-25 a sun + + * 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 + + * bin/afppasswd/afppasswd.c: global password updating utility for + the randnum authentication method. + +1999-05-22 a sun + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * etc/afpd/auth.c: got rid of global clrtxtname and switched to + using obj->username. + +1999-05-04 a sun + + * 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 + + * etc/afpd/directory.c (renamedir,copydir,deletedir): added bits + so that renaming a directory works across filesystems. + +1999-04-27 a sun + + * 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 + + * etc/afpd/fork.c (afp_openfork): always try to create a resource + fork if asked. + +1999-04-21 a sun + + * 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 + + * 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 + + * sys/generic/Makefile: AFP/tcp now compiles on irix with quota + support. + +1999-04-09 a sun + + * sys/generic/Makefile: AFP/tcp now compiles on aix with quota + support. + +1999-04-09 a sun + + * sys/generic/Makefile: AFP/tcp part now compiles on digital unix + with quota support enabled. + +1999-04-08 a sun + + * 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 + + * bin/megatron/macbin.c (bin_write): only pad if we need to do so + (from ). + (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 . + + +1999-04-02 a sun + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * sys/{*bsd,ultrix,solaris,linux}/Makefile (depend): surround + DEPS with double quotes so that multiple defines work. + +1999-03-06 a sun + + * 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 + + * 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 + . + +1999-03-03 a sun + + * 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 + + * etc/afpd/volume.c (afp_openvol): erk. the volume password gets + aligned along an even boundary. + +1999-02-23 a sun + + * etc/afpd/volume.c (readvolfile): added volume password support. + +1999-02-14 a sun + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * etc/afpd/fork.c (afp_flush), ofork.c (of_flush): FPFlush + operates on a per-volume basis. + +1999-01-31 a sun + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * sys/solaris/ddp.c (ddp_rput): added a couple htons()'s for the + net addresses. + +1999-01-11 a sun + + * 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 + + * 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 + + * 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 + + * 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 + + * bin/adv1tov2/adv1tov2.c: turn non-printable ascii characters + into hex code as well. + +1998-12-21 a sun + + * etc/afpd/auth.c: fixed FPChangePW for 2-way randnums. + +1998-12-15 a sun + + * 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 + + * 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 + + * 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 + + * libatalk/adouble/ad_open.c (ad_refresh): punt if we get a file + that we don't understand. + +1998-12-10 a sun + + * 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 + + * 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 + + * etc/afpd/directory.c, fork.c, volume.c, file.c: added unix<->afp + time conversion code. + +1998-12-05 a sun + + * etc/afpd/volume.c (volset): changed prodos setting to + prodos=true. + +1998-12-04 a sun + + * 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 + + * 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 + + * 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=, + utomfile=, casefold= 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 + + * etc/afpd/volume.c (getvolparams): added AFP2.1 volume attribute + bits. + +1998-11-24 a sun + + * etc/atalkd/config.c (readconf, getifconf): added IFF_SLAVE to + prevent atalkd from trying to use a slave channel. + +1998-11-23 a sun + + * 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 + + * 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 ). + + * 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 + + * 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 + + * 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 + + * 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 + + * libatalk/dsi/dsi_read.c (dsi_readinit): make sure to stick in + the error code. + +1998-11-15 a sun + + * 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 + + * etc/afpd/afp_dsi.c (afp_over_dsi): moved the dsi->noreply toggle + check to here from dsi_cmdreply. + +1998-11-11 a sun + + * etc/atalkd/zip.c (zip_packet): make sure to copy multicast zone + back out. (reported by Michael Zuelsdorff ) + +1998-11-09 a sun + + * etc/afpd/directory.c (getdirparams): changed unknown bit reply + code to AFPERR_BITMAP instead of AFPERR_PARAM. + +1998-11-06 a sun + + * 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 + + * etc/afpd/auth.c: fixed randnum password changing check. + +1998-10-27 a sun + + * 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 + + * 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 + + * etc/afpd/filedir.c (afp_rename, afp_moveandrename): make sure + to check against mac name. + +1998-10-19 a sun + + * 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 + + * 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 + + * 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 + + * etc/afpd/auth.c (afp_changepw): don't kill the connection here + if there's a problem. + +1998-10-10 a sun + + * 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 + + * 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 + + * 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 + + * etc/afpd/config.c (configinit): do the right thing if + AFPConfigInit fails. + +1998-09-18 a sun + + * etc/afpd/config.c (ASP/DSIConfigInit, configfree): how + embarrassing. i wasn't doing refcounts correctly. + +1998-09-17 a sun + + * 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 + + * etc/afpd/status.c (status_flags): forgot to turn on password + setting if randnum passwords are being used. + +1998-09-11 a sun + + * etc/afpd/unix.c (setdirmode): erk. make sure only to setgid on + directories. + + * bin/aecho/aecho.c (main): incorporated -c (ala ping) patch + from "Fred Lindberg" . + +1998-09-03 a sun + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * etc/atalkd/nbp.c (nbp_packet): forgot to handle another local + zone case. + +1998-08-25 a sun + + * 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 + + * 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 + + * etc/atalkd/multicast.c (zone_bcast): fixed to do the right thing + with zip multicast info. + +1998-08-15 a sun + + * etc/afpd/nfsquota.c: made the old-style rquota fields dependent + upon -DUSE_OLD_RQUOTA and defined that for sunos. also included + for ultrix breakage. + +1998-08-13 a sun + + * 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 + + * etc/afpd/ofork.c (of_dealloc,of_alloc): sped up dealloc by + sticking refnum in ofork. + +1998-08-12 a sun + + * 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 + + * etc/afpd/fork.c (afp_openfork): fixed a problem with opening + forks from read-only non-appledouble media. + +1998-07-23 a sun + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * include/atalk/adouble.h: marked out space for appledouble v2. + +1998-07-04 a sun + + * 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 + + * etc/afpd/nfsquota.c (getnfsquota): added nfs rquota + support. Robert J. Marinchick + 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 + + * 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 + + * 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 and + so that we rely upon adouble.h. + +1998-06-19 a sun + + * 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 + + * 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 + + * 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. + diff --git a/INSTALL/README.AFS b/INSTALL/README.AFS new file mode 100644 index 00000000..3aae8398 --- /dev/null +++ b/INSTALL/README.AFS @@ -0,0 +1,51 @@ +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 diff --git a/INSTALL/README.FREEBSD b/INSTALL/README.FREEBSD new file mode 100644 index 00000000..cb0368e2 --- /dev/null +++ b/INSTALL/README.FREEBSD @@ -0,0 +1,18 @@ +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 diff --git a/INSTALL/README.GENERIC b/INSTALL/README.GENERIC new file mode 100644 index 00000000..acb82c7a --- /dev/null +++ b/INSTALL/README.GENERIC @@ -0,0 +1,49 @@ +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. + diff --git a/INSTALL/README.LINUX b/INSTALL/README.LINUX new file mode 100644 index 00000000..ce1b33bd --- /dev/null +++ b/INSTALL/README.LINUX @@ -0,0 +1,37 @@ +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 diff --git a/INSTALL/README.NETBSD b/INSTALL/README.NETBSD new file mode 100644 index 00000000..cb32bfce --- /dev/null +++ b/INSTALL/README.NETBSD @@ -0,0 +1,18 @@ +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 diff --git a/INSTALL/README.OPENBSD b/INSTALL/README.OPENBSD new file mode 100644 index 00000000..c4d4a987 --- /dev/null +++ b/INSTALL/README.OPENBSD @@ -0,0 +1,18 @@ +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 diff --git a/INSTALL/README.SOLARIS b/INSTALL/README.SOLARIS new file mode 100644 index 00000000..a51f1c82 --- /dev/null +++ b/INSTALL/README.SOLARIS @@ -0,0 +1,68 @@ +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 diff --git a/INSTALL/README.SUNOS b/INSTALL/README.SUNOS new file mode 100644 index 00000000..42aead00 --- /dev/null +++ b/INSTALL/README.SUNOS @@ -0,0 +1,18 @@ +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 diff --git a/INSTALL/README.ULTRIX b/INSTALL/README.ULTRIX new file mode 100644 index 00000000..a6747138 --- /dev/null +++ b/INSTALL/README.ULTRIX @@ -0,0 +1,85 @@ +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- + + 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 .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- + + where 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 diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..da9d5c2d --- /dev/null +++ b/Makefile @@ -0,0 +1,124 @@ +# 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 diff --git a/README b/README new file mode 100644 index 00000000..1a82dc8e --- /dev/null +++ b/README @@ -0,0 +1,116 @@ +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 diff --git a/README.ASUN b/README.ASUN new file mode 100644 index 00000000..ab6aec58 --- /dev/null +++ b/README.ASUN @@ -0,0 +1,164 @@ +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 +. + +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: + + i've merged in 's apple II ProDOS support. + + i've added Randnum and 2-Way Randnum support. part of the code is + compliments of. 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 + + 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 + . + + 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 diff --git a/TODO b/TODO new file mode 100644 index 00000000..53dbf646 --- /dev/null +++ b/TODO @@ -0,0 +1,82 @@ +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 ): + 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. diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..b3f2fbef --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.4b2+asun2.1.4 diff --git a/bin/Makefile b/bin/Makefile new file mode 100644 index 00000000..842f044b --- /dev/null +++ b/bin/Makefile @@ -0,0 +1,40 @@ +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 diff --git a/bin/adv1tov2/Makefile b/bin/adv1tov2/Makefile new file mode 100644 index 00000000..331d24a6 --- /dev/null +++ b/bin/adv1tov2/Makefile @@ -0,0 +1,49 @@ +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 + diff --git a/bin/adv1tov2/adv1tov2.c b/bin/adv1tov2/adv1tov2.c new file mode 100644 index 00000000..2e8d12a3 --- /dev/null +++ b/bin/adv1tov2/adv1tov2.c @@ -0,0 +1,135 @@ +/* v1tov2: given a root directory, run down and convert all the + * files/directories into appledouble v2. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 \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 diff --git a/bin/aecho/Makefile b/bin/aecho/Makefile new file mode 100644 index 00000000..56f26aef --- /dev/null +++ b/bin/aecho/Makefile @@ -0,0 +1,48 @@ +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 + diff --git a/bin/aecho/aecho.c b/bin/aecho/aecho.c new file mode 100644 index 00000000..dc88e31b --- /dev/null +++ b/bin/aecho/aecho.c @@ -0,0 +1,257 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* 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 ); +} diff --git a/bin/afppasswd/Makefile b/bin/afppasswd/Makefile new file mode 100644 index 00000000..b4e417c0 --- /dev/null +++ b/bin/afppasswd/Makefile @@ -0,0 +1,83 @@ +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 + diff --git a/bin/afppasswd/afppasswd.c b/bin/afppasswd/afppasswd.c new file mode 100644 index 00000000..ae47e890 --- /dev/null +++ b/bin/afppasswd/afppasswd.c @@ -0,0 +1,329 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef UAM_RNDNUM +#include + +#ifdef USE_CRACKLIB +#include +#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 + diff --git a/bin/getzones/Makefile b/bin/getzones/Makefile new file mode 100644 index 00000000..19020237 --- /dev/null +++ b/bin/getzones/Makefile @@ -0,0 +1,48 @@ +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 + diff --git a/bin/getzones/getzones.c b/bin/getzones/getzones.c new file mode 100644 index 00000000..604f1aaa --- /dev/null +++ b/bin/getzones/getzones.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 ); + } +} diff --git a/bin/megatron/Makefile b/bin/megatron/Makefile new file mode 100644 index 00000000..5e4dd0cf --- /dev/null +++ b/bin/megatron/Makefile @@ -0,0 +1,55 @@ +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 + diff --git a/bin/megatron/asingle.c b/bin/megatron/asingle.c new file mode 100644 index 00000000..bf0c0b3d --- /dev/null +++ b/bin/megatron/asingle.c @@ -0,0 +1,446 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 ); +} diff --git a/bin/megatron/hqx.c b/bin/megatron/hqx.c new file mode 100644 index 00000000..5ab164f3 --- /dev/null +++ b/bin/megatron/hqx.c @@ -0,0 +1,692 @@ +#include +#include +#include +#include +#include +#ifdef notdef +#if BSD >= 199006 +# include +#else +# include +#endif +#endif notdef +#include +#include +#include +#include +#include +#include +#include +#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 ); +} diff --git a/bin/megatron/macbin.c b/bin/megatron/macbin.c new file mode 100644 index 00000000..f149dc7b --- /dev/null +++ b/bin/megatron/macbin.c @@ -0,0 +1,560 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#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 ); +} diff --git a/bin/megatron/megatron.c b/bin/megatron/megatron.c new file mode 100644 index 00000000..61049eda --- /dev/null +++ b/bin/megatron/megatron.c @@ -0,0 +1,340 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 ); +} + diff --git a/bin/megatron/megatron.h b/bin/megatron/megatron.h new file mode 100644 index 00000000..9190d499 --- /dev/null +++ b/bin/megatron/megatron.h @@ -0,0 +1,86 @@ +#include + +#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 *); diff --git a/bin/megatron/nad.c b/bin/megatron/nad.c new file mode 100644 index 00000000..b82a8d52 --- /dev/null +++ b/bin/megatron/nad.c @@ -0,0 +1,435 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#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 ); +} diff --git a/bin/megatron/updcrc.c b/bin/megatron/updcrc.c new file mode 100644 index 00000000..75079fb2 --- /dev/null +++ b/bin/megatron/updcrc.c @@ -0,0 +1,191 @@ +/* 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<>(W-B)) ^ *cp++]; +#else + crc = (crc>>B) ^ crctab[(crc & ((1< +main() +{ + initcrctab(); +} + +initcrctab() +{ + register int b, i; + WTYPE v; + + + for( b = 0; b <= (1<= 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< +#include + +#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 diff --git a/bin/nbp/Makefile b/bin/nbp/Makefile new file mode 100644 index 00000000..44d16964 --- /dev/null +++ b/bin/nbp/Makefile @@ -0,0 +1,56 @@ +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 + diff --git a/bin/nbp/nbplkup.c b/bin/nbp/nbplkup.c new file mode 100644 index 00000000..bdd10331 --- /dev/null +++ b/bin/nbp/nbplkup.c @@ -0,0 +1,153 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#if !defined( sun ) || !defined( i386 ) +#include +#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 ); + } + +} diff --git a/bin/nbp/nbprgstr.c b/bin/nbp/nbprgstr.c new file mode 100644 index 00000000..c4f47e73 --- /dev/null +++ b/bin/nbp/nbprgstr.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +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); +} diff --git a/bin/nbp/nbpunrgstr.c b/bin/nbp/nbpunrgstr.c new file mode 100644 index 00000000..8b147bff --- /dev/null +++ b/bin/nbp/nbpunrgstr.c @@ -0,0 +1,87 @@ +/* + * 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 +#include +#include +#include +#include + +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 ); + } +} diff --git a/bin/pap/Makefile b/bin/pap/Makefile new file mode 100644 index 00000000..2dae36b4 --- /dev/null +++ b/bin/pap/Makefile @@ -0,0 +1,52 @@ +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 + diff --git a/bin/pap/pap.c b/bin/pap/pap.c new file mode 100644 index 00000000..fdf4e4e0 --- /dev/null +++ b/bin/pap/pap.c @@ -0,0 +1,844 @@ +/* + * Copyright (c) 1990,1994 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ); +} diff --git a/bin/pap/papstatus.c b/bin/pap/papstatus.c new file mode 100644 index 00000000..c9076bd2 --- /dev/null +++ b/bin/pap/papstatus.c @@ -0,0 +1,197 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ); +} diff --git a/bin/psorder/Makefile b/bin/psorder/Makefile new file mode 100644 index 00000000..ed7b933d --- /dev/null +++ b/bin/psorder/Makefile @@ -0,0 +1,55 @@ +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 + diff --git a/bin/psorder/pa.c b/bin/psorder/pa.c new file mode 100644 index 00000000..e78e5b5d --- /dev/null +++ b/bin/psorder/pa.c @@ -0,0 +1,92 @@ +/* + * 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 + +#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)) ); +} diff --git a/bin/psorder/pa.h b/bin/psorder/pa.h new file mode 100644 index 00000000..d7684e24 --- /dev/null +++ b/bin/psorder/pa.h @@ -0,0 +1,70 @@ +/* + * 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 +#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)) diff --git a/bin/psorder/psorder.c b/bin/psorder/psorder.c new file mode 100644 index 00000000..34248512 --- /dev/null +++ b/bin/psorder/psorder.c @@ -0,0 +1,561 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pa.h" +#include "psorder.h" + +#include + +#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 ); +} diff --git a/bin/psorder/psorder.h b/bin/psorder/psorder.h new file mode 100644 index 00000000..ab99076e --- /dev/null +++ b/bin/psorder/psorder.h @@ -0,0 +1,103 @@ +/* + * 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(); diff --git a/config/AppleVolumes.default b/config/AppleVolumes.default new file mode 100644 index 00000000..02f7f18a --- /dev/null +++ b/config/AppleVolumes.default @@ -0,0 +1,72 @@ +# 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 and 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) +# +~ diff --git a/config/AppleVolumes.system b/config/AppleVolumes.system new file mode 100644 index 00000000..403ba246 --- /dev/null +++ b/config/AppleVolumes.system @@ -0,0 +1,282 @@ +# 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 diff --git a/config/AppleVolumes.system.cobalt b/config/AppleVolumes.system.cobalt new file mode 100644 index 00000000..bfcaa8d9 --- /dev/null +++ b/config/AppleVolumes.system.cobalt @@ -0,0 +1,285 @@ +# 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 diff --git a/config/afpd.conf b/config/afpd.conf new file mode 100644 index 00000000..74594d6f --- /dev/null +++ b/config/afpd.conf @@ -0,0 +1,125 @@ +# +# 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 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 +# 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 Specifies the TCP port the server should respond +# to (default is 548) +# -fqdn 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 Use this path to look for User Authentication Modules. +# (default: :RESDIR:/uams) +# -uamlist 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 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 Specifies path to AppleVolumes.default file +# (default :ETCDIR:/AppleVolumes.default, +# same as -f on command line) +# -systemvol 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 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 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 -systemvol +# +# default: +# - -transall -uamlist uams_guest.so,uams_clrtxt.so,uams_dhx.so -nosavepassword diff --git a/config/atalkd.conf b/config/atalkd.conf new file mode 100644 index 00000000..4648e3a6 --- /dev/null +++ b/config/atalkd.conf @@ -0,0 +1,37 @@ +# +# 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. +# diff --git a/config/atalkd.conf.cobalt b/config/atalkd.conf.cobalt new file mode 100644 index 00000000..1363a711 --- /dev/null +++ b/config/atalkd.conf.cobalt @@ -0,0 +1,38 @@ +# +# 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 diff --git a/config/netatalk.conf b/config/netatalk.conf new file mode 100644 index 00000000..1e94cc6c --- /dev/null +++ b/config/netatalk.conf @@ -0,0 +1,25 @@ +# 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 diff --git a/config/netatalk.conf.cobalt b/config/netatalk.conf.cobalt new file mode 100644 index 00000000..6c1f3623 --- /dev/null +++ b/config/netatalk.conf.cobalt @@ -0,0 +1,23 @@ +# 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 + diff --git a/config/netatalk.pamd b/config/netatalk.pamd new file mode 100644 index 00000000..45776270 --- /dev/null +++ b/config/netatalk.pamd @@ -0,0 +1,6 @@ +#%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 diff --git a/config/papd.conf b/config/papd.conf new file mode 100644 index 00000000..09f453a0 --- /dev/null +++ b/config/papd.conf @@ -0,0 +1,20 @@ +# 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. diff --git a/contrib/ICDumpSuffixMap b/contrib/ICDumpSuffixMap new file mode 100644 index 00000000..3a8283fc --- /dev/null +++ b/contrib/ICDumpSuffixMap @@ -0,0 +1 @@ +#!perl # # ICDumpMap # --- Dump suffix mappings from your Internet Config extension. # # iNOUE Koich! # use Mac::InternetConfig; open MAP, ">AppleVolumes"; printf MAP "%-9s \"%4s\" \"%4s\" %-30s %-25s %-15s\n\n", ".", "TEXT", "ttxt", "ASCII Text", "SimpleText", "text/plain"; print MAP "\# The following lines are extracted from Internet Config Preference.\n\n"; for my $entry (keys %InternetConfigMap) { next unless $entry->extension =~ /^\./; $_ = sprintf "%-9s \"%4s\" \"%4s\" %-30s %-25s %-15s", $entry->extension, $entry->file_type, $entry->file_creator, $entry->entry_name, $entry->creator_app_name, $entry->MIME_type; s/\s*$/\n/; print MAP; } close MAP; \ No newline at end of file diff --git a/contrib/printing/add_netatalk_printer b/contrib/printing/add_netatalk_printer new file mode 100644 index 00000000..71436db3 --- /dev/null +++ b/contrib/printing/add_netatalk_printer @@ -0,0 +1,364 @@ +#!/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 +# 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 '' + 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 " service is already defined." +else + echo "Defining /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 " service is already defined." +else + echo "Defining /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 diff --git a/contrib/printing/netatalk.template b/contrib/printing/netatalk.template new file mode 100644 index 00000000..f639914a --- /dev/null +++ b/contrib/printing/netatalk.template @@ -0,0 +1,610 @@ +#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 <&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 < +#include +#include +#include +#include +#include + +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); + } +} diff --git a/distrib/initscripts/rc.atalk.bsd b/distrib/initscripts/rc.atalk.bsd new file mode 100755 index 00000000..de1bdc32 --- /dev/null +++ b/distrib/initscripts/rc.atalk.bsd @@ -0,0 +1,39 @@ +# +# 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 '.' diff --git a/distrib/initscripts/rc.atalk.cobalt b/distrib/initscripts/rc.atalk.cobalt new file mode 100644 index 00000000..7ccbb5be --- /dev/null +++ b/distrib/initscripts/rc.atalk.cobalt @@ -0,0 +1,148 @@ +#!/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 diff --git a/distrib/initscripts/rc.atalk.redhat b/distrib/initscripts/rc.atalk.redhat new file mode 100755 index 00000000..1c36ccf7 --- /dev/null +++ b/distrib/initscripts/rc.atalk.redhat @@ -0,0 +1,127 @@ +#! /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 diff --git a/distrib/initscripts/rc.atalk.sysv b/distrib/initscripts/rc.atalk.sysv new file mode 100755 index 00000000..3845f8ed --- /dev/null +++ b/distrib/initscripts/rc.atalk.sysv @@ -0,0 +1,91 @@ +#! /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 diff --git a/distrib/rpm/buildrpm b/distrib/rpm/buildrpm new file mode 100755 index 00000000..37e04380 --- /dev/null +++ b/distrib/rpm/buildrpm @@ -0,0 +1,26 @@ +#!/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 + diff --git a/distrib/rpm/netatalk-asun-cobalt.spec b/distrib/rpm/netatalk-asun-cobalt.spec new file mode 100644 index 00000000..ebd1569e --- /dev/null +++ b/distrib/rpm/netatalk-asun-cobalt.spec @@ -0,0 +1,287 @@ +Summary: AppleTalk networking programs +Name: netatalk +Version: 1.4b2+asun2.1.4 +Release: pre39 +Packager: iNOUE Koichi +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' \ + /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 + - updated to latest build from Adrian + +* Thu Jul 22 1999 iNOUE Koich! +- /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! +- 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! +- [pre-asun2.1.4-30] +* Sun Jun 20 1999 iNOUE Koich! +- [pre-asun2.1.4-28] +* Thu Jun 3 1999 iNOUE Koich! +- [pre-asun2.1.4-22] +* Wed May 19 1999 iNOUE Koich! +- [pre-asun2.1.4-15] + Make BerkleyDB=/usr. +* Sun May 2 1999 iNOUE Koich! +- [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! +- [pre-asun2.1.4-9] + Move %chengelog section last. +* Wed Mar 31 1999 iNOUE Koich! +- Comment out -DNEED_QUOTA_WRAPPER in sys/linux/Makefile. +* Sat Mar 20 1999 iNOUE Koich! +- Correct symbolic links to psf. + Remove asciize function from nbplkup so as to display Japanese hostname. +* Thu Mar 11 1999 iNOUE Koich! +- Included MacPerl 5 script ICDumpSuffixMap which dumps suffix mapping + containd in Internet Config Preference. +* Tue Mar 2 1999 iNOUE Koich! +- [asun2.1.3] +* Mon Feb 15 1999 iNOUE Koich! +- [pre-asun2.1.2-8] +* Sun Feb 7 1999 iNOUE Koich! +- [pre-asun2.1.2-6] +* Mon Jan 25 1999 iNOUE Koichi +- [pre-asun2.1.2-3] +* Thu Dec 17 1998 INOUE Koichi +- [pre-asun2.1.2] + Remove crlf patch. It is now a server's option. +* Thu Dec 3 1998 INOUE Koichi +- Use stable version source netatalk-1.4b2+asun2.1.1.tar.gz + Add uams directory +* Sat Nov 28 1998 INOUE Koichi +- Use pre-asun2.1.1-3 source. +* Mon Nov 23 1998 INOUE Koichi +- Use pre-asun2.1.1-2 source. +* Mon Nov 16 1998 INOUE Koichi +- Fix rcX.d's symbolic links. +* Wed Oct 28 1998 INOUE Koichi +- Use pre-asun2.1.0a-2 source. Remove '%exclusiveos linux' line. +* Sat Oct 24 1998 INOUE Koichi +- Use stable version source netatalk-1.4b2+asun2.1.0.tar.gz. +* Mon Oct 5 1998 INOUE Koichi +- Use pre-asun2.1.0-10a source. +* Thu Sep 19 1998 INOUE Koichi +- Use pre-asun2.1.0-8 source. Add chkconfig support. +* Sat Sep 12 1998 INOUE Koichi +- Comment out -DCRLF. Use RPM_OPT_FLAGS. +* Mon Sep 8 1998 INOUE Koichi +- Use pre-asun2.1.0-7 source. Rename atalk.init to atalk. +* Mon Aug 22 1998 INOUE Koichi +- Use pre-asun2.1.0-6 source. +* Mon Jul 27 1998 INOUE Koichi +- Use pre-asun2.1.0-5 source. +* Tue Jul 21 1998 INOUE Koichi +- Use pre-asun2.1.0-3 source. +* Tue Jul 7 1998 INOUE Koichi +- Add afpovertcp entries to /etc/services +- Remove BuildRoot in man8 pages +* Mon Jun 29 1998 INOUE Koichi +- Use modified sources 1.4b2+asun2.1.0 produced by Adrian Sun + to provide an AppleShareIP file server +- Included AppleVolumes.system file maintained by Johnson + +* Mon Aug 25 1997 David Gibson +- 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 +- 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 +Updated for /etc/pam.d diff --git a/distrib/rpm/netatalk-asun.makefile.patch b/distrib/rpm/netatalk-asun.makefile.patch new file mode 100644 index 00000000..42e70a11 --- /dev/null +++ b/distrib/rpm/netatalk-asun.makefile.patch @@ -0,0 +1,65 @@ +--- 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 diff --git a/distrib/rpm/netatalk-asun.spec b/distrib/rpm/netatalk-asun.spec new file mode 100644 index 00000000..4e3d9925 --- /dev/null +++ b/distrib/rpm/netatalk-asun.spec @@ -0,0 +1,297 @@ +Summary: AppleTalk networking programs +Name: netatalk +Version: 1.4b2+asun2.1.4 +Release: pre39 +Packager: iNOUE Koichi +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' \ + /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! +- /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! +- 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! +- [pre-asun2.1.4-30] +* Sun Jun 20 1999 iNOUE Koich! +- [pre-asun2.1.4-28] +* Thu Jun 3 1999 iNOUE Koich! +- [pre-asun2.1.4-22] +* Wed May 19 1999 iNOUE Koich! +- [pre-asun2.1.4-15] + Make BerkleyDB=/usr. +* Sun May 2 1999 iNOUE Koich! +- [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! +- [pre-asun2.1.4-9] + Move %chengelog section last. +* Wed Mar 31 1999 iNOUE Koich! +- Comment out -DNEED_QUOTA_WRAPPER in sys/linux/Makefile. +* Sat Mar 20 1999 iNOUE Koich! +- Correct symbolic links to psf. + Remove asciize function from nbplkup so as to display Japanese hostname. +* Thu Mar 11 1999 iNOUE Koich! +- Included MacPerl 5 script ICDumpSuffixMap which dumps suffix mapping + containd in Internet Config Preference. +* Tue Mar 2 1999 iNOUE Koich! +- [asun2.1.3] +* Mon Feb 15 1999 iNOUE Koich! +- [pre-asun2.1.2-8] +* Sun Feb 7 1999 iNOUE Koich! +- [pre-asun2.1.2-6] +* Mon Jan 25 1999 iNOUE Koichi +- [pre-asun2.1.2-3] +* Thu Dec 17 1998 INOUE Koichi +- [pre-asun2.1.2] + Remove crlf patch. It is now a server's option. +* Thu Dec 3 1998 INOUE Koichi +- Use stable version source netatalk-1.4b2+asun2.1.1.tar.gz + Add uams directory +* Sat Nov 28 1998 INOUE Koichi +- Use pre-asun2.1.1-3 source. +* Mon Nov 23 1998 INOUE Koichi +- Use pre-asun2.1.1-2 source. +* Mon Nov 16 1998 INOUE Koichi +- Fix rcX.d's symbolic links. +* Wed Oct 28 1998 INOUE Koichi +- Use pre-asun2.1.0a-2 source. Remove '%exclusiveos linux' line. +* Sat Oct 24 1998 INOUE Koichi +- Use stable version source netatalk-1.4b2+asun2.1.0.tar.gz. +* Mon Oct 5 1998 INOUE Koichi +- Use pre-asun2.1.0-10a source. +* Thu Sep 19 1998 INOUE Koichi +- Use pre-asun2.1.0-8 source. Add chkconfig support. +* Sat Sep 12 1998 INOUE Koichi +- Comment out -DCRLF. Use RPM_OPT_FLAGS. +* Mon Sep 8 1998 INOUE Koichi +- Use pre-asun2.1.0-7 source. Rename atalk.init to atalk. +* Mon Aug 22 1998 INOUE Koichi +- Use pre-asun2.1.0-6 source. +* Mon Jul 27 1998 INOUE Koichi +- Use pre-asun2.1.0-5 source. +* Tue Jul 21 1998 INOUE Koichi +- Use pre-asun2.1.0-3 source. +* Tue Jul 7 1998 INOUE Koichi +- Add afpovertcp entries to /etc/services +- Remove BuildRoot in man8 pages +* Mon Jun 29 1998 INOUE Koichi +- Use modified sources 1.4b2+asun2.1.0 produced by Adrian Sun + to provide an AppleShareIP file server +- Included AppleVolumes.system file maintained by Johnson + +* Mon Aug 25 1997 David Gibson +- 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 +- 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 +Updated for /etc/pam.d diff --git a/etc/Makefile b/etc/Makefile new file mode 100644 index 00000000..48067198 --- /dev/null +++ b/etc/Makefile @@ -0,0 +1,46 @@ +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 diff --git a/etc/afpd/Makefile b/etc/afpd/Makefile new file mode 100644 index 00000000..3bff97eb --- /dev/null +++ b/etc/afpd/Makefile @@ -0,0 +1,141 @@ + +########################################################################## + +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 + diff --git a/etc/afpd/afp_asp.c b/etc/afpd/afp_asp.c new file mode 100644 index 00000000..73f46e8a --- /dev/null +++ b/etc/afpd/afp_asp.c @@ -0,0 +1,223 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 diff --git a/etc/afpd/afp_dsi.c b/etc/afpd/afp_dsi.c new file mode 100644 index 00000000..176921b8 --- /dev/null +++ b/etc/afpd/afp_dsi.c @@ -0,0 +1,280 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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); +} diff --git a/etc/afpd/afp_options.c b/etc/afpd/afp_options.c new file mode 100644 index 00000000..13ccf7f6 --- /dev/null +++ b/etc/afpd/afp_options.c @@ -0,0 +1,331 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "globals.h" +#include "status.h" +#include "auth.h" + +#include + +#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; +} diff --git a/etc/afpd/afs.c b/etc/afpd/afs.c new file mode 100644 index 00000000..82f35d9d --- /dev/null +++ b/etc/afpd/afs.c @@ -0,0 +1,338 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#ifdef AFS + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include + +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 diff --git a/etc/afpd/appl.c b/etc/afpd/appl.c new file mode 100644 index 00000000..dcefdaff --- /dev/null +++ b/etc/afpd/appl.c @@ -0,0 +1,452 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 ); +} + + diff --git a/etc/afpd/auth.c b/etc/afpd/auth.c new file mode 100644 index 00000000..3f229285 --- /dev/null +++ b/etc/afpd/auth.c @@ -0,0 +1,411 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + } +} diff --git a/etc/afpd/auth.h b/etc/afpd/auth.h new file mode 100644 index 00000000..4a5894b7 --- /dev/null +++ b/etc/afpd/auth.h @@ -0,0 +1,40 @@ +/* + * 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 +#include +#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 */ diff --git a/etc/afpd/codepage.c b/etc/afpd/codepage.c new file mode 100644 index 00000000..b2d0e5ae --- /dev/null +++ b/etc/afpd/codepage.c @@ -0,0 +1,277 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#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; +} diff --git a/etc/afpd/codepage.h b/etc/afpd/codepage.h new file mode 100644 index 00000000..130e0cea --- /dev/null +++ b/etc/afpd/codepage.h @@ -0,0 +1,47 @@ +#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: bytes + * xlate/rule code: 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 diff --git a/etc/afpd/config.c b/etc/afpd/config.c new file mode 100644 index 00000000..d57b22d7 --- /dev/null +++ b/etc/afpd/config.c @@ -0,0 +1,353 @@ +/* + * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu) + * All Rights Reserved. See COPYRIGHT. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/etc/afpd/config.h b/etc/afpd/config.h new file mode 100644 index 00000000..74c49406 --- /dev/null +++ b/etc/afpd/config.h @@ -0,0 +1,23 @@ +#ifndef AFPD_CONFIG_H +#define AFPD_CONFIG_H 1 + +#include +#include +#include +#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 diff --git a/etc/afpd/desktop.c b/etc/afpd/desktop.c new file mode 100644 index 00000000..6316ee35 --- /dev/null +++ b/etc/afpd/desktop.c @@ -0,0 +1,873 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ); +} diff --git a/etc/afpd/desktop.h b/etc/afpd/desktop.h new file mode 100644 index 00000000..0427a181 --- /dev/null +++ b/etc/afpd/desktop.h @@ -0,0 +1,67 @@ +/* + * 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 +#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 diff --git a/etc/afpd/directory.c b/etc/afpd/directory.c new file mode 100644 index 00000000..6d849ea8 --- /dev/null +++ b/etc/afpd/directory.c @@ -0,0 +1,1773 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * 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. */ + 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<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. */ + 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; +} diff --git a/etc/afpd/directory.h b/etc/afpd/directory.h new file mode 100644 index 00000000..87acca8f --- /dev/null +++ b/etc/afpd/directory.h @@ -0,0 +1,175 @@ +/* + * 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 +#include +/*#include */ /* including it here causes some confusion */ +#include + +/* sys/types.h usually snarfs in major/minor macros. if they don't + * try this file. */ +#ifndef major +#include +#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 diff --git a/etc/afpd/enumerate.c b/etc/afpd/enumerate.c new file mode 100644 index 00000000..64b97d43 --- /dev/null +++ b/etc/afpd/enumerate.c @@ -0,0 +1,428 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 \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; +} diff --git a/etc/afpd/file.c b/etc/afpd/file.c new file mode 100644 index 00000000..af6da8b1 --- /dev/null +++ b/etc/afpd/file.c @@ -0,0 +1,1489 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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. + */ + 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<>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". */ + 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; +} diff --git a/etc/afpd/file.h b/etc/afpd/file.h new file mode 100644 index 00000000..4848b506 --- /dev/null +++ b/etc/afpd/file.h @@ -0,0 +1,98 @@ +/* + * 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 */ /* including it here causes some confusion */ +#include +#include +#include +#include + +#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 diff --git a/etc/afpd/filedir.c b/etc/afpd/filedir.c new file mode 100644 index 00000000..f342b78b --- /dev/null +++ b/etc/afpd/filedir.c @@ -0,0 +1,556 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ); +} + diff --git a/etc/afpd/filedir.h b/etc/afpd/filedir.h new file mode 100644 index 00000000..c20bc61c --- /dev/null +++ b/etc/afpd/filedir.h @@ -0,0 +1,19 @@ +#ifndef AFPD_FILEDIR_H +#define AFPD_FILEDIR_H 1 + +#include +#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 diff --git a/etc/afpd/fork.c b/etc/afpd/fork.c new file mode 100644 index 00000000..7b1b8abb --- /dev/null +++ b/etc/afpd/fork.c @@ -0,0 +1,1297 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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<of_flags & AFPFORK_RSRC)) || + ((bitmap & (1<of_flags & AFPFORK_DATA))) { + return( AFPERR_BITMAP ); + } + + if ( bitmap & ( 1<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<of_flags & AFPFORK_DATA)) { + err = ad_dtruncate( ofork->of_ad, size ); + if (err < 0) + goto afp_setfork_err; + } else if ((bitmap == (1<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 ); +} + diff --git a/etc/afpd/fork.h b/etc/afpd/fork.h new file mode 100644 index 00000000..78f07cce --- /dev/null +++ b/etc/afpd/fork.h @@ -0,0 +1,72 @@ +/* + * 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 +#include + +#include +#include +#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 diff --git a/etc/afpd/gettok.c b/etc/afpd/gettok.c new file mode 100644 index 00000000..ea0a8e38 --- /dev/null +++ b/etc/afpd/gettok.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 1990,1994 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include + +#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 diff --git a/etc/afpd/globals.h b/etc/afpd/globals.h new file mode 100644 index 00000000..e4d68911 --- /dev/null +++ b/etc/afpd/globals.h @@ -0,0 +1,82 @@ +/* + * 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 +#include +#include /* this isn't header-protected under ultrix */ +#include +#include +#include + +/* 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 */ diff --git a/etc/afpd/icon.h b/etc/afpd/icon.h new file mode 100644 index 00000000..03839805 --- /dev/null +++ b/etc/afpd/icon.h @@ -0,0 +1,268 @@ +/* + * 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 +#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 diff --git a/etc/afpd/main.c b/etc/afpd/main.c new file mode 100644 index 00000000..512091a8 --- /dev/null +++ b/etc/afpd/main.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/etc/afpd/messages.c b/etc/afpd/messages.c new file mode 100644 index 00000000..982a7064 --- /dev/null +++ b/etc/afpd/messages.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu) + * All Rights Reserved. See COPYRIGHT. + */ +#include +#include +#include +#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; +} diff --git a/etc/afpd/misc.h b/etc/afpd/misc.h new file mode 100644 index 00000000..16b7c330 --- /dev/null +++ b/etc/afpd/misc.h @@ -0,0 +1,26 @@ +#ifndef AFPD_MISC_H +#define AFPD_MISC_H 1 + +#include +#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 diff --git a/etc/afpd/nfsquota.c b/etc/afpd/nfsquota.c new file mode 100644 index 00000000..03754deb --- /dev/null +++ b/etc/afpd/nfsquota.c @@ -0,0 +1,163 @@ +/* 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 +#include +#include +#include +#include /* for DEV_BSIZE */ +#include /* on ultrix doesn't include this */ +#include + +#include +#include +#include +#include +#include + +#include + +#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 diff --git a/etc/afpd/nls/Makefile b/etc/afpd/nls/Makefile new file mode 100644 index 00000000..1d375eb3 --- /dev/null +++ b/etc/afpd/nls/Makefile @@ -0,0 +1,25 @@ +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.* diff --git a/etc/afpd/nls/makecode.c b/etc/afpd/nls/makecode.c new file mode 100644 index 00000000..da7b93fa --- /dev/null +++ b/etc/afpd/nls/makecode.c @@ -0,0 +1,136 @@ +/* quick-and-dirty way of creating code pages */ + +#include +#include +#include + +#include +#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); + } +} diff --git a/etc/afpd/nls/parsecode.c b/etc/afpd/nls/parsecode.c new file mode 100644 index 00000000..025c0e03 --- /dev/null +++ b/etc/afpd/nls/parsecode.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +#include +#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 \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; +} diff --git a/etc/afpd/ofork.c b/etc/afpd/ofork.c new file mode 100644 index 00000000..71eb5140 --- /dev/null +++ b/etc/afpd/ofork.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 1996 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include /* works around a bug */ +#include +#include + +#include + +#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 ); +} diff --git a/etc/afpd/passwd.c b/etc/afpd/passwd.c new file mode 100644 index 00000000..99a2af6b --- /dev/null +++ b/etc/afpd/passwd.c @@ -0,0 +1,107 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ); +} diff --git a/etc/afpd/quota.c b/etc/afpd/quota.c new file mode 100644 index 00000000..25b7114e --- /dev/null +++ b/etc/afpd/quota.c @@ -0,0 +1,365 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 diff --git a/etc/afpd/status.c b/etc/afpd/status.c new file mode 100644 index 00000000..a770c1ad --- /dev/null +++ b/etc/afpd/status.c @@ -0,0 +1,368 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ +#include +#include +#include +#include +#include + +#ifdef BSD4_4 +#include +#ifndef USE_GETHOSTID +#include +#endif +#endif + +#include +#include +#include +#include +#include +#include + +#include "globals.h" /* includes */ +#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; +} diff --git a/etc/afpd/status.h b/etc/afpd/status.h new file mode 100644 index 00000000..a59280a4 --- /dev/null +++ b/etc/afpd/status.h @@ -0,0 +1,32 @@ +#ifndef AFPD_STATUS_H +#define AFPD_STATUS_H 1 + +#include +#include +#include +#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 diff --git a/etc/afpd/switch.c b/etc/afpd/switch.c new file mode 100644 index 00000000..854510b0 --- /dev/null +++ b/etc/afpd/switch.c @@ -0,0 +1,222 @@ +/* + * 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 /* to pick up NULL */ +#include /* works around a bug */ +#include + +#include +#include + +#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; +} diff --git a/etc/afpd/switch.h b/etc/afpd/switch.h new file mode 100644 index 00000000..38eaf67c --- /dev/null +++ b/etc/afpd/switch.h @@ -0,0 +1,29 @@ +/* + * 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 diff --git a/etc/afpd/uam.c b/etc/afpd/uam.c new file mode 100644 index 00000000..af39f358 --- /dev/null +++ b/etc/afpd/uam.c @@ -0,0 +1,362 @@ +/* Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu) + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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; +} diff --git a/etc/afpd/uam_auth.h b/etc/afpd/uam_auth.h new file mode 100644 index 00000000..eedc6003 --- /dev/null +++ b/etc/afpd/uam_auth.h @@ -0,0 +1,63 @@ +/* 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 +#include + +#include +#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 */ diff --git a/etc/afpd/unix.c b/etc/afpd/unix.c new file mode 100644 index 00000000..4dfde805 --- /dev/null +++ b/etc/afpd/unix.c @@ -0,0 +1,450 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 ); +} diff --git a/etc/afpd/unix.h b/etc/afpd/unix.h new file mode 100644 index 00000000..79ce68f0 --- /dev/null +++ b/etc/afpd/unix.h @@ -0,0 +1,100 @@ +#ifndef AFPD_UNIX_H +#define AFPD_UNIX_H + +#include +#include +#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 +#endif + +#if defined(_IBMR2) || defined(USE_STATFS_H) +#include +/* this might not be right. */ +#define f_mntfromname f_fname +#endif + +#if defined(USE_STATVFS_H) || defined(__svr4__) +#include +#define statfs statvfs +#else +#define f_frsize f_bsize +#endif + +#if defined(__svr4__) || defined(USE_MNTTAB_H) +#include +#endif + +#if defined(USE_MOUNT_H) || defined(BSD4_4) || \ + defined(linux) || defined(ultrix) +#include +#endif + +#if defined(linux) || defined(USE_MNTENT_H) +#include +#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 +#else +#include +#include +#include +#endif +#endif + +#ifdef __svr4__ +#include +#endif + +#ifdef BSD4_4 +#include +#endif + +#ifdef USE_UFS_QUOTA_H +#include +#endif + +#ifdef _IBMR2 +#include +#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 */ diff --git a/etc/afpd/volume.c b/etc/afpd/volume.c new file mode 100644 index 00000000..8b73be59 --- /dev/null +++ b/etc/afpd/volume.c @@ -0,0 +1,1293 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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: + * [] [allow:,<@group>,...] \ + * [codepage:] [casefold:] + * 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<>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.. */ + *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<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 ); +} diff --git a/etc/afpd/volume.h b/etc/afpd/volume.h new file mode 100644 index 00000000..86274f34 --- /dev/null +++ b/etc/afpd/volume.h @@ -0,0 +1,146 @@ +/* + * 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 +#include +#include +#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 diff --git a/etc/atalkd/Makefile b/etc/atalkd/Makefile new file mode 100644 index 00000000..65cedac1 --- /dev/null +++ b/etc/atalkd/Makefile @@ -0,0 +1,56 @@ +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 + diff --git a/etc/atalkd/aep.c b/etc/atalkd/aep.c new file mode 100644 index 00000000..83a53230 --- /dev/null +++ b/etc/atalkd/aep.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/etc/atalkd/atserv.h b/etc/atalkd/atserv.h new file mode 100644 index 00000000..64474206 --- /dev/null +++ b/etc/atalkd/atserv.h @@ -0,0 +1,21 @@ +/* + * 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; diff --git a/etc/atalkd/config.c b/etc/atalkd/config.c new file mode 100644 index 00000000..1590d825 --- /dev/null +++ b/etc/atalkd/config.c @@ -0,0 +1,753 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __svr4__ +#include +#include +#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__ diff --git a/etc/atalkd/gate.h b/etc/atalkd/gate.h new file mode 100644 index 00000000..ca09fb7b --- /dev/null +++ b/etc/atalkd/gate.h @@ -0,0 +1,13 @@ +/* + * 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; +}; diff --git a/etc/atalkd/interface.h b/etc/atalkd/interface.h new file mode 100644 index 00000000..3c682088 --- /dev/null +++ b/etc/atalkd/interface.h @@ -0,0 +1,55 @@ +/* + * 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 + +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 diff --git a/etc/atalkd/list.h b/etc/atalkd/list.h new file mode 100644 index 00000000..8d05b345 --- /dev/null +++ b/etc/atalkd/list.h @@ -0,0 +1,10 @@ +/* + * 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; +}; diff --git a/etc/atalkd/main.c b/etc/atalkd/main.c new file mode 100644 index 00000000..a05e908f --- /dev/null +++ b/etc/atalkd/main.c @@ -0,0 +1,1493 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#if defined( sun ) && defined( __svr4__ ) +#include +#else sun __svr4__ +#include +#endif sun __svr4__ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __svr4__ +#include +#include +#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 diff --git a/etc/atalkd/multicast.c b/etc/atalkd/multicast.c new file mode 100644 index 00000000..365bcecd --- /dev/null +++ b/etc/atalkd/multicast.c @@ -0,0 +1,395 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* work around for FreeBSD */ +#if defined(__FreeBSD__) && (__FreeBSD__ >= 2) +#include +#if __FreeBSD_version >= 300000 +#include +#define NO_DATA_LINK_PASSTHROUGH +#endif +#endif + +#ifdef __svr4__ +#include +#endif __svr4__ + +#include +#include +#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; +} diff --git a/etc/atalkd/multicast.h b/etc/atalkd/multicast.h new file mode 100644 index 00000000..a3ff56d0 --- /dev/null +++ b/etc/atalkd/multicast.h @@ -0,0 +1,19 @@ +/* + * 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 +#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 */ diff --git a/etc/atalkd/nbp.c b/etc/atalkd/nbp.c new file mode 100644 index 00000000..e94130d8 --- /dev/null +++ b/etc/atalkd/nbp.c @@ -0,0 +1,633 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __svr4__ +#include +#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; +} diff --git a/etc/atalkd/nbp.h b/etc/atalkd/nbp.h new file mode 100644 index 00000000..42961f02 --- /dev/null +++ b/etc/atalkd/nbp.h @@ -0,0 +1,12 @@ +/* + * 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; diff --git a/etc/atalkd/route.c b/etc/atalkd/route.c new file mode 100644 index 00000000..94ee92c3 --- /dev/null +++ b/etc/atalkd/route.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1990,1996 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include + +#include + +#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 diff --git a/etc/atalkd/rtmp.c b/etc/atalkd/rtmp.c new file mode 100644 index 00000000..f5db9ef0 --- /dev/null +++ b/etc/atalkd/rtmp.c @@ -0,0 +1,957 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __svr4__ +#include +#endif __svr4__ + +#include +#include +#include + +#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 ); +} diff --git a/etc/atalkd/rtmp.h b/etc/atalkd/rtmp.h new file mode 100644 index 00000000..edc3e743 --- /dev/null +++ b/etc/atalkd/rtmp.h @@ -0,0 +1,85 @@ +/* + * 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 + +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 */ diff --git a/etc/atalkd/zip.c b/etc/atalkd/zip.c new file mode 100644 index 00000000..246fcb59 --- /dev/null +++ b/etc/atalkd/zip.c @@ -0,0 +1,1049 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __svr4__ +#include +#endif __svr4__ + +#include +#include +#include +#include + +#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; + } +} diff --git a/etc/atalkd/zip.h b/etc/atalkd/zip.h new file mode 100644 index 00000000..dacba24b --- /dev/null +++ b/etc/atalkd/zip.h @@ -0,0 +1,23 @@ +/* + * 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 + +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 */ diff --git a/etc/papd/Makefile b/etc/papd/Makefile new file mode 100644 index 00000000..648e2b45 --- /dev/null +++ b/etc/papd/Makefile @@ -0,0 +1,88 @@ +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 diff --git a/etc/papd/comment.c b/etc/papd/comment.c new file mode 100644 index 00000000..db89202d --- /dev/null +++ b/etc/papd/comment.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 1990,1994 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include + +#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 ); + } +} diff --git a/etc/papd/comment.h b/etc/papd/comment.h new file mode 100644 index 00000000..2e12765f --- /dev/null +++ b/etc/papd/comment.h @@ -0,0 +1,48 @@ +/* + * 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 */ diff --git a/etc/papd/file.c b/etc/papd/file.c new file mode 100644 index 00000000..63f08a06 --- /dev/null +++ b/etc/papd/file.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 1990,1994 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include + +#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 )); +} diff --git a/etc/papd/file.h b/etc/papd/file.h new file mode 100644 index 00000000..e6ae71e0 --- /dev/null +++ b/etc/papd/file.h @@ -0,0 +1,31 @@ +/* + * 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 diff --git a/etc/papd/headers.c b/etc/papd/headers.c new file mode 100644 index 00000000..12104bea --- /dev/null +++ b/etc/papd/headers.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1990,1994 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include + +#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 }, +}; diff --git a/etc/papd/lp.c b/etc/papd/lp.c new file mode 100644 index 00000000..be1e6c71 --- /dev/null +++ b/etc/papd/lp.c @@ -0,0 +1,719 @@ +/* + * 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 +#include +#include +#include +#include +#if defined( sun ) && defined( __svr4__ ) +#include +#else sun __svr4__ +#include +#endif sun __svr4__ +#include +#include +#undef s_net +#include +#include +#include + +#ifdef ABS_PRINT +#include +#endif ABS_PRINT + +#include +#include +#include +#include +#include + +#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 ); + } + } +} diff --git a/etc/papd/magics.c b/etc/papd/magics.c new file mode 100644 index 00000000..dea2c009 --- /dev/null +++ b/etc/papd/magics.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 1990,1994 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include + +#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 }, +}; diff --git a/etc/papd/main.c b/etc/papd/main.c new file mode 100644 index 00000000..4b2239b3 --- /dev/null +++ b/etc/papd/main.c @@ -0,0 +1,727 @@ +/* + * Copyright (c) 1990,1995 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#if defined( sun ) && defined( __svr4__ ) +#include +#else sun __svr4__ +#include +#endif sun __svr4__ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 ); +} diff --git a/etc/papd/ppd.c b/etc/papd/ppd.c new file mode 100644 index 00000000..783719fe --- /dev/null +++ b/etc/papd/ppd.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 1995 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ); +} diff --git a/etc/papd/ppd.h b/etc/papd/ppd.h new file mode 100644 index 00000000..b519533c --- /dev/null +++ b/etc/papd/ppd.h @@ -0,0 +1,17 @@ +/* + * 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(); diff --git a/etc/papd/printcap.c b/etc/papd/printcap.c new file mode 100644 index 00000000..2459e1ab --- /dev/null +++ b/etc/papd/printcap.c @@ -0,0 +1,513 @@ +/* + * 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 +#include +#include +#include + +#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 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 )); +} diff --git a/etc/papd/printer.h b/etc/papd/printer.h new file mode 100644 index 00000000..72ce5b6c --- /dev/null +++ b/etc/papd/printer.h @@ -0,0 +1,55 @@ +/* + * 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; diff --git a/etc/papd/queries.c b/etc/papd/queries.c new file mode 100644 index 00000000..28df0800 --- /dev/null +++ b/etc/papd/queries.c @@ -0,0 +1,592 @@ +/* + * Copyright (c) 1990,1994 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef KRB +#ifdef SOLARIS +#include +#else +#include +#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 }, +}; diff --git a/etc/papd/session.c b/etc/papd/session.c new file mode 100644 index 00000000..c58ac9a7 --- /dev/null +++ b/etc/papd/session.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 1990,1994 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + } + } +} diff --git a/etc/psf/Makefile b/etc/psf/Makefile new file mode 100644 index 00000000..e666e5eb --- /dev/null +++ b/etc/psf/Makefile @@ -0,0 +1,72 @@ +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 + diff --git a/etc/psf/etc2ps.sh b/etc/psf/etc2ps.sh new file mode 100644 index 00000000..5c3c60d5 --- /dev/null +++ b/etc/psf/etc2ps.sh @@ -0,0 +1,56 @@ +#!/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 diff --git a/etc/psf/pagecount.ps b/etc/psf/pagecount.ps new file mode 100644 index 00000000..fe650e1c --- /dev/null +++ b/etc/psf/pagecount.ps @@ -0,0 +1,5 @@ +%!PS-Adobe-3.0 Query +%%?BeginQuery: PageCount +statusdict begin pagecount (*) print = flush end +%%?EndQuery: Unknown +%%EOF diff --git a/etc/psf/psa.c b/etc/psf/psa.c new file mode 100644 index 00000000..32675c2c --- /dev/null +++ b/etc/psf/psa.c @@ -0,0 +1,125 @@ +/* + * 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 + +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 ); +} diff --git a/etc/psf/psf.c b/etc/psf/psf.c new file mode 100644 index 00000000..e92c56fe --- /dev/null +++ b/etc/psf/psf.c @@ -0,0 +1,694 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 ); + } +} diff --git a/etc/uams/Makefile b/etc/uams/Makefile new file mode 100644 index 00000000..ca97c7da --- /dev/null +++ b/etc/uams/Makefile @@ -0,0 +1,171 @@ + +########################################################################## + +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 + diff --git a/etc/uams/uams_dhx_pam.c b/etc/uams/uams_dhx_pam.c new file mode 100644 index 00000000..e65b6f6a --- /dev/null +++ b/etc/uams/uams_dhx_pam.c @@ -0,0 +1,523 @@ +/* + * 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 +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#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 */ diff --git a/etc/uams/uams_dhx_passwd.c b/etc/uams/uams_dhx_passwd.c new file mode 100644 index 00000000..934847ce --- /dev/null +++ b/etc/uams/uams_dhx_passwd.c @@ -0,0 +1,269 @@ +/* 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 +#include +#include +#include +#ifndef NO_CRYPT_H +#include +#endif +#include +#include + +#ifdef SOLARIS +#define SHADOWPW +#endif SOLARIS + +#ifdef SHADOWPW +#include +#endif SHADOWPW + +#include +#include +#include + +#include +#include + +#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 diff --git a/etc/uams/uams_guest.c b/etc/uams/uams_guest.c new file mode 100644 index 00000000..41e2d70b --- /dev/null +++ b/etc/uams/uams_guest.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include + +#include +#include + +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 +}; diff --git a/etc/uams/uams_krb4/kuam.c b/etc/uams/uams_krb4/kuam.c new file mode 100644 index 00000000..a78f4aeb --- /dev/null +++ b/etc/uams/uams_krb4/kuam.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 1990,1994 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +# ifdef UAM_AFSKRB + +#include +#include +#include +#include + +#include +#include +#include + +/* use the bsd time.h struct defs for PC too! */ +#include +#include + +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 diff --git a/etc/uams/uams_krb4/lifetime.c b/etc/uams/uams_krb4/lifetime.c new file mode 100644 index 00000000..582533fe --- /dev/null +++ b/etc/uams/uams_krb4/lifetime.c @@ -0,0 +1,145 @@ +/* + * 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 +#include + +#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. + */ + +#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 + +#include +#include + +#include +#include +#include +#include +#include +#ifdef lint +#include /* struct iovec to make lint happy */ +#endif /* lint */ +#include +#include +#include +#include + +#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 diff --git a/etc/uams/uams_krb4/uams_krb4.c b/etc/uams/uams_krb4/uams_krb4.c new file mode 100644 index 00000000..e0cd40a9 --- /dev/null +++ b/etc/uams/uams_krb4/uams_krb4.c @@ -0,0 +1,590 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + + +#if defined( KRB ) || defined( UAM_AFSKRB ) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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 +#include +#include +#include +#include + +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 +#include + +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 */ diff --git a/etc/uams/uams_pam.c b/etc/uams/uams_pam.c new file mode 100644 index 00000000..3475913f --- /dev/null +++ b/etc/uams/uams_pam.c @@ -0,0 +1,289 @@ +/* + * 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 +#include +#include +#include +#include + +#include + +#include +#include + +#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 */ diff --git a/etc/uams/uams_passwd.c b/etc/uams/uams_passwd.c new file mode 100644 index 00000000..67699a97 --- /dev/null +++ b/etc/uams/uams_passwd.c @@ -0,0 +1,153 @@ +/* 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 +#include +#include +#include +#ifndef NO_CRYPT_H +#include +#endif +#include +#include + +#ifdef SOLARIS +#define SHADOWPW +#endif SOLARIS + +#ifdef SHADOWPW +#include +#endif SHADOWPW + +#include +#include + +#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 +}; diff --git a/etc/uams/uams_pgp.c b/etc/uams/uams_pgp.c new file mode 100644 index 00000000..823b060d --- /dev/null +++ b/etc/uams/uams_pgp.c @@ -0,0 +1,172 @@ +/* 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 +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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 diff --git a/etc/uams/uams_randnum.c b/etc/uams/uams_randnum.c new file mode 100644 index 00000000..42ac4530 --- /dev/null +++ b/etc/uams/uams_randnum.c @@ -0,0 +1,522 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + + +#ifdef UAM_RNDNUM +#include + +#ifdef USE_CRACKLIB +#include +#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 */ diff --git a/include/Makefile b/include/Makefile new file mode 100644 index 00000000..a120b5b5 --- /dev/null +++ b/include/Makefile @@ -0,0 +1,23 @@ +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 + diff --git a/include/atalk/adouble.h b/include/atalk/adouble.h new file mode 100644 index 00000000..931a40b0 --- /dev/null +++ b/include/atalk/adouble.h @@ -0,0 +1,313 @@ +/* + * 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 + +#if defined(sun) && defined(__svr4__) +#include +#else +#include +#endif +#include +#include +#include +#include +#include + + +/* 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 +#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 +#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 diff --git a/include/atalk/aep.h b/include/atalk/aep.h new file mode 100644 index 00000000..69a419af --- /dev/null +++ b/include/atalk/aep.h @@ -0,0 +1,30 @@ +/* + * 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 diff --git a/include/atalk/afp.h b/include/atalk/afp.h new file mode 100644 index 00000000..3ee2f104 --- /dev/null +++ b/include/atalk/afp.h @@ -0,0 +1,125 @@ +/* + * 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 +#include + +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/ . + * 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 diff --git a/include/atalk/asp.h b/include/atalk/asp.h new file mode 100644 index 00000000..fd569df5 --- /dev/null +++ b/include/atalk/asp.h @@ -0,0 +1,103 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#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 diff --git a/include/atalk/atp.h b/include/atalk/atp.h new file mode 100644 index 00000000..24548c94 --- /dev/null +++ b/include/atalk/atp.h @@ -0,0 +1,191 @@ +/* + * 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 +#include +#include +#include +#include + +/* 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 diff --git a/include/atalk/cnid.h b/include/atalk/cnid.h new file mode 100644 index 00000000..19c84171 --- /dev/null +++ b/include/atalk/cnid.h @@ -0,0 +1,44 @@ +/* + * 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 +#include +#include +#include + +#include + +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 */ diff --git a/include/atalk/compat.h b/include/atalk/compat.h new file mode 100644 index 00000000..e283220d --- /dev/null +++ b/include/atalk/compat.h @@ -0,0 +1,81 @@ +/* + * 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 +#include + +#ifdef __svr4__ +/* + * SunOS 5 (solaris) has SA_RESTART, but no SA_INTERRUPT. + */ +#ifndef SA_INTERRUPT +#define SA_INTERRUPT 0 +#endif + +#include +#include +#include + +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 +#include +#include + +/* + * 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 diff --git a/include/atalk/ddp.h b/include/atalk/ddp.h new file mode 100644 index 00000000..1c3fff1c --- /dev/null +++ b/include/atalk/ddp.h @@ -0,0 +1,35 @@ +/* + * 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 diff --git a/include/atalk/dsi.h b/include/atalk/dsi.h new file mode 100644 index 00000000..63509c1f --- /dev/null +++ b/include/atalk/dsi.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu) + * All rights reserved. + */ + +#ifndef _ATALK_DSI_H +#define _ATALK_DSI_H + +#include +#include +#include +#include + +#include +#include +#include +#include + +/* 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 . + * 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 */ diff --git a/include/atalk/nbp.h b/include/atalk/nbp.h new file mode 100644 index 00000000..56334fee --- /dev/null +++ b/include/atalk/nbp.h @@ -0,0 +1,94 @@ +/* + * 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 +#include +#include +#include + +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 diff --git a/include/atalk/netddp.h b/include/atalk/netddp.h new file mode 100644 index 00000000..ed9ddbc9 --- /dev/null +++ b/include/atalk/netddp.h @@ -0,0 +1,36 @@ +/* + * 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 +#include +#include + +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 +#include + +#define netddp_close(a) close(a) +#define netddp_sendto sendto +#define netddp_recvfrom recvfrom +#endif + +#endif /* netddp.h */ + diff --git a/include/atalk/pap.h b/include/atalk/pap.h new file mode 100644 index 00000000..4b0e7a8e --- /dev/null +++ b/include/atalk/pap.h @@ -0,0 +1,40 @@ +/* + * 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 diff --git a/include/atalk/paths.h b/include/atalk/paths.h new file mode 100644 index 00000000..ac77f150 --- /dev/null +++ b/include/atalk/paths.h @@ -0,0 +1,68 @@ +#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 */ diff --git a/include/atalk/rtmp.h b/include/atalk/rtmp.h new file mode 100644 index 00000000..e5921c26 --- /dev/null +++ b/include/atalk/rtmp.h @@ -0,0 +1,45 @@ +/* + * 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 + +#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 diff --git a/include/atalk/server_child.h b/include/atalk/server_child.h new file mode 100644 index 00000000..c983a863 --- /dev/null +++ b/include/atalk/server_child.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu) + * All rights reserved. + */ + +#ifndef _ATALK_SERVER_CHILD_H +#define _ATALK_SERVER_CHILD_H 1 + +#include +#include +#include + +/* 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 diff --git a/include/atalk/uam.h b/include/atalk/uam.h new file mode 100644 index 00000000..9e6f9f95 --- /dev/null +++ b/include/atalk/uam.h @@ -0,0 +1,71 @@ +/* Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu) + * All Rights Reserved. See COPYRIGHT. + */ + +#ifndef UAM_H +#define UAM_H 1 + +#include +#include +#include + +/* 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 diff --git a/include/atalk/util.h b/include/atalk/util.h new file mode 100644 index 00000000..01309beb --- /dev/null +++ b/include/atalk/util.h @@ -0,0 +1,46 @@ +#ifndef _ATALK_UTIL_H +#define _ATALK_UTIL_H 1 + +#include +#include +#include +#include + +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 +#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 diff --git a/include/atalk/zip.h b/include/atalk/zip.h new file mode 100644 index 00000000..9079fc1d --- /dev/null +++ b/include/atalk/zip.h @@ -0,0 +1,58 @@ +/* + * 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 + +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 diff --git a/libatalk/Makefile b/libatalk/Makefile new file mode 100644 index 00000000..08be0554 --- /dev/null +++ b/libatalk/Makefile @@ -0,0 +1,77 @@ +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 diff --git a/libatalk/adouble/Makefile b/libatalk/adouble/Makefile new file mode 100644 index 00000000..1f549e3f --- /dev/null +++ b/libatalk/adouble/Makefile @@ -0,0 +1,58 @@ +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 + diff --git a/libatalk/adouble/ad_attr.c b/libatalk/adouble/ad_attr.c new file mode 100644 index 00000000..fd00a101 --- /dev/null +++ b/libatalk/adouble/ad_attr.c @@ -0,0 +1,33 @@ +#include +#include + +#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; +} diff --git a/libatalk/adouble/ad_date.c b/libatalk/adouble/ad_date.c new file mode 100644 index 00000000..0302cc42 --- /dev/null +++ b/libatalk/adouble/ad_date.c @@ -0,0 +1,54 @@ +#include +#include +#include + +#include + +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; +} diff --git a/libatalk/adouble/ad_flush.c b/libatalk/adouble/ad_flush.c new file mode 100644 index 00000000..180b33d6 --- /dev/null +++ b/libatalk/adouble/ad_flush.c @@ -0,0 +1,162 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 ); +} diff --git a/libatalk/adouble/ad_lock.c b/libatalk/adouble/ad_lock.c new file mode 100644 index 00000000..d848ccd5 --- /dev/null +++ b/libatalk/adouble/ad_lock.c @@ -0,0 +1,405 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include + +#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); +} + + diff --git a/libatalk/adouble/ad_mmap.c b/libatalk/adouble/ad_mmap.c new file mode 100644 index 00000000..433249c6 --- /dev/null +++ b/libatalk/adouble/ad_mmap.c @@ -0,0 +1,104 @@ +/* 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 +#include +#include +#include +#include +#include + +#include + +#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); +} diff --git a/libatalk/adouble/ad_open.c b/libatalk/adouble/ad_open.c new file mode 100644 index 00000000..e685a347 --- /dev/null +++ b/libatalk/adouble/ad_open.c @@ -0,0 +1,792 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#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 +} diff --git a/libatalk/adouble/ad_private.h b/libatalk/adouble/ad_private.h new file mode 100644 index 00000000..3814e649 --- /dev/null +++ b/libatalk/adouble/ad_private.h @@ -0,0 +1,35 @@ +#ifndef LIBATALK_ADOUBLE_AD_PRIVATE_H +#define LIBATALK_ADOUBLE_AD_PRIVATE_H 1 + +#include + +#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 */ diff --git a/libatalk/adouble/ad_read.c b/libatalk/adouble/ad_read.c new file mode 100644 index 00000000..b286f556 --- /dev/null +++ b/libatalk/adouble/ad_read.c @@ -0,0 +1,103 @@ +/* + * 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 +#include +#include +#include +#include + +#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 ); +} diff --git a/libatalk/adouble/ad_sendfile.c b/libatalk/adouble/ad_sendfile.c new file mode 100644 index 00000000..9a5ac2a8 --- /dev/null +++ b/libatalk/adouble/ad_sendfile.c @@ -0,0 +1,93 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include + +#include + +#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 diff --git a/libatalk/adouble/ad_size.c b/libatalk/adouble/ad_size.c new file mode 100644 index 00000000..5773cae1 --- /dev/null +++ b/libatalk/adouble/ad_size.c @@ -0,0 +1,27 @@ +/* + * 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 +#include +#include +#include +#include + +#include + +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); +} diff --git a/libatalk/adouble/ad_write.c b/libatalk/adouble/ad_write.c new file mode 100644 index 00000000..b0e69ef2 --- /dev/null +++ b/libatalk/adouble/ad_write.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 1990,1995 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#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; +} diff --git a/libatalk/asp/Makefile b/libatalk/asp/Makefile new file mode 100644 index 00000000..5c5af687 --- /dev/null +++ b/libatalk/asp/Makefile @@ -0,0 +1,60 @@ +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 + diff --git a/libatalk/asp/asp_attn.c b/libatalk/asp/asp_attn.c new file mode 100644 index 00000000..9aa95bdd --- /dev/null +++ b/libatalk/asp/asp_attn.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu) + * All rights reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +/* 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; +} diff --git a/libatalk/asp/asp_child.h b/libatalk/asp/asp_child.h new file mode 100644 index 00000000..d3d3b834 --- /dev/null +++ b/libatalk/asp/asp_child.h @@ -0,0 +1,32 @@ +/* + * 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 diff --git a/libatalk/asp/asp_close.c b/libatalk/asp/asp_close.c new file mode 100644 index 00000000..b830788d --- /dev/null +++ b/libatalk/asp/asp_close.c @@ -0,0 +1,56 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/libatalk/asp/asp_cmdreply.c b/libatalk/asp/asp_cmdreply.c new file mode 100644 index 00000000..9acd7d9a --- /dev/null +++ b/libatalk/asp/asp_cmdreply.c @@ -0,0 +1,81 @@ +/* + * 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 +#include +#include +#include +#include + +#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 ); +} diff --git a/libatalk/asp/asp_getreq.c b/libatalk/asp/asp_getreq.c new file mode 100644 index 00000000..60cdd0cd --- /dev/null +++ b/libatalk/asp/asp_getreq.c @@ -0,0 +1,61 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +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 */ +} diff --git a/libatalk/asp/asp_getsess.c b/libatalk/asp/asp_getsess.c new file mode 100644 index 00000000..df6c39fd --- /dev/null +++ b/libatalk/asp/asp_getsess.c @@ -0,0 +1,267 @@ +/* + * Copyright (c) 1990,1996 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#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; +} diff --git a/libatalk/asp/asp_init.c b/libatalk/asp/asp_init.c new file mode 100644 index 00000000..431d80d1 --- /dev/null +++ b/libatalk/asp/asp_init.c @@ -0,0 +1,70 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/libatalk/asp/asp_shutdown.c b/libatalk/asp/asp_shutdown.c new file mode 100644 index 00000000..afe3581e --- /dev/null +++ b/libatalk/asp/asp_shutdown.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 1996 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +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 ); +} diff --git a/libatalk/asp/asp_tickle.c b/libatalk/asp/asp_tickle.c new file mode 100644 index 00000000..a591db48 --- /dev/null +++ b/libatalk/asp/asp_tickle.c @@ -0,0 +1,23 @@ +#include +#include +#include + +/* 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" ); + } +} diff --git a/libatalk/asp/asp_write.c b/libatalk/asp/asp_write.c new file mode 100644 index 00000000..411eb0b2 --- /dev/null +++ b/libatalk/asp/asp_write.c @@ -0,0 +1,94 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/libatalk/atp/Makefile b/libatalk/atp/Makefile new file mode 100644 index 00000000..8ef895cf --- /dev/null +++ b/libatalk/atp/Makefile @@ -0,0 +1,58 @@ +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 + diff --git a/libatalk/atp/atp_bprint.c b/libatalk/atp/atp_bprint.c new file mode 100644 index 00000000..6d141b7c --- /dev/null +++ b/libatalk/atp/atp_bprint.c @@ -0,0 +1,63 @@ +/* + * 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 +#include +#include +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++ ] = ' '; + } +} diff --git a/libatalk/atp/atp_bufs.c b/libatalk/atp/atp_bufs.c new file mode 100644 index 00000000..ae79074f --- /dev/null +++ b/libatalk/atp/atp_bufs.c @@ -0,0 +1,144 @@ +/* + * 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 +#include +#include + +#include +#include + +#include +#include +#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; +} + + diff --git a/libatalk/atp/atp_close.c b/libatalk/atp/atp_close.c new file mode 100644 index 00000000..47aa9abb --- /dev/null +++ b/libatalk/atp/atp_close.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1990,1997 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include "atp_internals.h" +#ifdef EBUG +#include +#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; +} diff --git a/libatalk/atp/atp_internals.h b/libatalk/atp/atp_internals.h new file mode 100644 index 00000000..70959d8e --- /dev/null +++ b/libatalk/atp/atp_internals.h @@ -0,0 +1,58 @@ +/* + * 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 +#include +#include +#include + +/* + * 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 diff --git a/libatalk/atp/atp_open.c b/libatalk/atp/atp_open.c new file mode 100644 index 00000000..cc690f79 --- /dev/null +++ b/libatalk/atp/atp_open.c @@ -0,0 +1,80 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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; +} diff --git a/libatalk/atp/atp_packet.c b/libatalk/atp/atp_packet.c new file mode 100644 index 00000000..6e029227 --- /dev/null +++ b/libatalk/atp/atp_packet.c @@ -0,0 +1,329 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#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 + +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 )); +} + diff --git a/libatalk/atp/atp_rreq.c b/libatalk/atp/atp_rreq.c new file mode 100644 index 00000000..be11f429 --- /dev/null +++ b/libatalk/atp/atp_rreq.c @@ -0,0 +1,106 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 ); +} diff --git a/libatalk/atp/atp_rresp.c b/libatalk/atp/atp_rresp.c new file mode 100644 index 00000000..dd9cb176 --- /dev/null +++ b/libatalk/atp/atp_rresp.c @@ -0,0 +1,92 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef EBUG +#include +#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 ); +} diff --git a/libatalk/atp/atp_rsel.c b/libatalk/atp/atp_rsel.c new file mode 100644 index 00000000..a183f490 --- /dev/null +++ b/libatalk/atp/atp_rsel.c @@ -0,0 +1,355 @@ +/* + * Copyright (c) 1990,1997 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#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<atph_rbitmap &= ~( 1<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 ); +} diff --git a/libatalk/atp/atp_sreq.c b/libatalk/atp/atp_sreq.c new file mode 100644 index 00000000..0ec1b194 --- /dev/null +++ b/libatalk/atp/atp_sreq.c @@ -0,0 +1,131 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#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 ); +} diff --git a/libatalk/atp/atp_sresp.c b/libatalk/atp/atp_sresp.c new file mode 100644 index 00000000..3d73be7c --- /dev/null +++ b/libatalk/atp/atp_sresp.c @@ -0,0 +1,149 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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; +} diff --git a/libatalk/cnid/Makefile b/libatalk/cnid/Makefile new file mode 100644 index 00000000..fdeafe82 --- /dev/null +++ b/libatalk/cnid/Makefile @@ -0,0 +1,58 @@ +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 diff --git a/libatalk/cnid/README b/libatalk/cnid/README new file mode 100644 index 00000000..51e85ac1 --- /dev/null +++ b/libatalk/cnid/README @@ -0,0 +1,37 @@ +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. diff --git a/libatalk/cnid/cnid_add.c b/libatalk/cnid/cnid_add.c new file mode 100644 index 00000000..81ac2a7c --- /dev/null +++ b/libatalk/cnid/cnid_add.c @@ -0,0 +1,185 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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; +} diff --git a/libatalk/cnid/cnid_close.c b/libatalk/cnid/cnid_close.c new file mode 100644 index 00000000..f5e1d096 --- /dev/null +++ b/libatalk/cnid/cnid_close.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include + +#include + +#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); +} diff --git a/libatalk/cnid/cnid_delete.c b/libatalk/cnid/cnid_delete.c new file mode 100644 index 00000000..73545762 --- /dev/null +++ b/libatalk/cnid/cnid_delete.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu) + * All Rights Reserved. See COPYRIGHT. + * + * cnid_delete: delete a CNID from the database + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/libatalk/cnid/cnid_get.c b/libatalk/cnid/cnid_get.c new file mode 100644 index 00000000..d1f82977 --- /dev/null +++ b/libatalk/cnid/cnid_get.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/libatalk/cnid/cnid_lookup.c b/libatalk/cnid/cnid_lookup.c new file mode 100644 index 00000000..d8625444 --- /dev/null +++ b/libatalk/cnid/cnid_lookup.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/libatalk/cnid/cnid_meta.c b/libatalk/cnid/cnid_meta.c new file mode 100644 index 00000000..803a7aca --- /dev/null +++ b/libatalk/cnid/cnid_meta.c @@ -0,0 +1,7 @@ +/* + * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu) + * All Rights Reserved. See COPYRIGHT. + * + * deal with metadata + * + */ diff --git a/libatalk/cnid/cnid_meta.h b/libatalk/cnid/cnid_meta.h new file mode 100644 index 00000000..5eb3d64c --- /dev/null +++ b/libatalk/cnid/cnid_meta.h @@ -0,0 +1,47 @@ +#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) + diff --git a/libatalk/cnid/cnid_nextid.c b/libatalk/cnid/cnid_nextid.c new file mode 100644 index 00000000..f26418fb --- /dev/null +++ b/libatalk/cnid/cnid_nextid.c @@ -0,0 +1,22 @@ +#include + +#include +#include + +#include + +#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; +} diff --git a/libatalk/cnid/cnid_open.c b/libatalk/cnid/cnid_open.c new file mode 100644 index 00000000..10dda27d --- /dev/null +++ b/libatalk/cnid/cnid_open.c @@ -0,0 +1,369 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#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; +} diff --git a/libatalk/cnid/cnid_private.h b/libatalk/cnid/cnid_private.h new file mode 100644 index 00000000..3eacb1a7 --- /dev/null +++ b/libatalk/cnid/cnid_private.h @@ -0,0 +1,120 @@ +#ifndef LIBATALK_CNID_PRIVATE_H +#define LIBATALK_CNID_PRIVATE_H 1 + +#include +#include +#include +#include + +#include + +#include +#include + +#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 */ diff --git a/libatalk/cnid/cnid_resolve.c b/libatalk/cnid/cnid_resolve.c new file mode 100644 index 00000000..744a7d70 --- /dev/null +++ b/libatalk/cnid/cnid_resolve.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/libatalk/cnid/cnid_update.c b/libatalk/cnid/cnid_update.c new file mode 100644 index 00000000..1c7a0217 --- /dev/null +++ b/libatalk/cnid/cnid_update.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/libatalk/compat/Makefile b/libatalk/compat/Makefile new file mode 100644 index 00000000..bb606def --- /dev/null +++ b/libatalk/compat/Makefile @@ -0,0 +1,58 @@ +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 + diff --git a/libatalk/compat/flock.c b/libatalk/compat/flock.c new file mode 100644 index 00000000..45ec45da --- /dev/null +++ b/libatalk/compat/flock.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1996 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +static int _flock_dummy; + +# if defined( sun ) && defined( __svr4__ ) + +#include +#include +#include + +#include + +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__ diff --git a/libatalk/compat/getusershell.c b/libatalk/compat/getusershell.c new file mode 100644 index 00000000..bd79d4fb --- /dev/null +++ b/libatalk/compat/getusershell.c @@ -0,0 +1,133 @@ +/* + * 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 +#include +#include +#include +#include + +#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 diff --git a/libatalk/compat/inet_aton.c b/libatalk/compat/inet_aton.c new file mode 100644 index 00000000..b073b525 --- /dev/null +++ b/libatalk/compat/inet_aton.c @@ -0,0 +1,19 @@ +#include +#include +#include + +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 diff --git a/libatalk/compat/mktemp.c b/libatalk/compat/mktemp.c new file mode 100644 index 00000000..f08bd1ec --- /dev/null +++ b/libatalk/compat/mktemp.c @@ -0,0 +1,116 @@ +/* + * 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 +#include +#include +#include +#include +#include + +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 diff --git a/libatalk/compat/rquota_xdr.c b/libatalk/compat/rquota_xdr.c new file mode 100644 index 00000000..f06da23e --- /dev/null +++ b/libatalk/compat/rquota_xdr.c @@ -0,0 +1,115 @@ +/* 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 /* 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 +#include + +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 diff --git a/libatalk/compat/strcasecmp.c b/libatalk/compat/strcasecmp.c new file mode 100644 index 00000000..46acac36 --- /dev/null +++ b/libatalk/compat/strcasecmp.c @@ -0,0 +1,105 @@ +/* + * 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 +#include + +#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 diff --git a/libatalk/compat/strdup.c b/libatalk/compat/strdup.c new file mode 100644 index 00000000..04a42758 --- /dev/null +++ b/libatalk/compat/strdup.c @@ -0,0 +1,17 @@ +#include +#include +#include + +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 diff --git a/libatalk/compat/strstr.c b/libatalk/compat/strstr.c new file mode 100644 index 00000000..2c3b2b43 --- /dev/null +++ b/libatalk/compat/strstr.c @@ -0,0 +1,73 @@ +/*- + * 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 +#include + +/* + * 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 diff --git a/libatalk/dsi/Makefile b/libatalk/dsi/Makefile new file mode 100644 index 00000000..507b22c6 --- /dev/null +++ b/libatalk/dsi/Makefile @@ -0,0 +1,69 @@ +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 diff --git a/libatalk/dsi/README b/libatalk/dsi/README new file mode 100644 index 00000000..f34b81d2 --- /dev/null +++ b/libatalk/dsi/README @@ -0,0 +1,45 @@ +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). + diff --git a/libatalk/dsi/dsi_attn.c b/libatalk/dsi/dsi_attn.c new file mode 100644 index 00000000..1bbfd7ee --- /dev/null +++ b/libatalk/dsi/dsi_attn.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu) + * All rights reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#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; +} diff --git a/libatalk/dsi/dsi_close.c b/libatalk/dsi/dsi_close.c new file mode 100644 index 00000000..3e7a2fbd --- /dev/null +++ b/libatalk/dsi/dsi_close.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu) + * All rights reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include + +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); +} diff --git a/libatalk/dsi/dsi_cmdreply.c b/libatalk/dsi/dsi_cmdreply.c new file mode 100644 index 00000000..4459206f --- /dev/null +++ b/libatalk/dsi/dsi_cmdreply.c @@ -0,0 +1,21 @@ +/* + * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu) + * All rights reserved. See COPYRIGHT. + */ + +#include +#include +#include + +/* 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); +} diff --git a/libatalk/dsi/dsi_getsess.c b/libatalk/dsi/dsi_getsess.c new file mode 100644 index 00000000..d4c1c6f8 --- /dev/null +++ b/libatalk/dsi/dsi_getsess.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu) + * All rights reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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); + } +} diff --git a/libatalk/dsi/dsi_getstat.c b/libatalk/dsi/dsi_getstat.c new file mode 100644 index 00000000..268caf44 --- /dev/null +++ b/libatalk/dsi/dsi_getstat.c @@ -0,0 +1,22 @@ +/* + * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu) + * All rights reserved. See COPYRIGHT. + */ + +#include +#include + +#include +#include + +/* 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); +} diff --git a/libatalk/dsi/dsi_init.c b/libatalk/dsi/dsi_init.c new file mode 100644 index 00000000..2fcc1780 --- /dev/null +++ b/libatalk/dsi/dsi_init.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu) + * All rights reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#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; +} diff --git a/libatalk/dsi/dsi_opensess.c b/libatalk/dsi/dsi_opensess.c new file mode 100644 index 00000000..8f16b131 --- /dev/null +++ b/libatalk/dsi/dsi_opensess.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu) + * All rights reserved. See COPYRIGHT. + */ + +#include +#include +#include + +#include + +/* 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); +} diff --git a/libatalk/dsi/dsi_private.h b/libatalk/dsi/dsi_private.h new file mode 100644 index 00000000..70509f76 --- /dev/null +++ b/libatalk/dsi/dsi_private.h @@ -0,0 +1,23 @@ +/* + * 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_.c should + * include it. + */ + +#include +#include +#include + +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 */ diff --git a/libatalk/dsi/dsi_read.c b/libatalk/dsi/dsi_read.c new file mode 100644 index 00000000..ca922e7b --- /dev/null +++ b/libatalk/dsi/dsi_read.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu) + * All rights reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#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; +} diff --git a/libatalk/dsi/dsi_stream.c b/libatalk/dsi/dsi_stream.c new file mode 100644 index 00000000..5b275fae --- /dev/null +++ b/libatalk/dsi/dsi_stream.c @@ -0,0 +1,191 @@ +/* + * 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 +#include +#include +#include +#include +#include +#ifdef USE_WRITEV +#include +#endif +#include + +#include +#include + +#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]; +} diff --git a/libatalk/dsi/dsi_tcp.c b/libatalk/dsi/dsi_tcp.c new file mode 100644 index 00000000..39c7b6ae --- /dev/null +++ b/libatalk/dsi/dsi_tcp.c @@ -0,0 +1,298 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#ifdef __svr4__ +#include +#endif + +#ifdef TCPWRAP +#include +int allow_severity = LOG_INFO; +int deny_severity = LOG_WARNING; +#endif + +#include +#include +#include +#include +#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; + +} + diff --git a/libatalk/dsi/dsi_tickle.c b/libatalk/dsi/dsi_tickle.c new file mode 100644 index 00000000..c0ba6558 --- /dev/null +++ b/libatalk/dsi/dsi_tickle.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu) + * All rights reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include + +#include +#include + +/* 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); +} + diff --git a/libatalk/dsi/dsi_write.c b/libatalk/dsi/dsi_write.c new file mode 100644 index 00000000..0acad6c7 --- /dev/null +++ b/libatalk/dsi/dsi_write.c @@ -0,0 +1,89 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#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); +} diff --git a/libatalk/nbp/Makefile b/libatalk/nbp/Makefile new file mode 100644 index 00000000..95befb5b --- /dev/null +++ b/libatalk/nbp/Makefile @@ -0,0 +1,56 @@ +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 + diff --git a/libatalk/nbp/nbp_conf.h b/libatalk/nbp/nbp_conf.h new file mode 100644 index 00000000..4ea004f2 --- /dev/null +++ b/libatalk/nbp/nbp_conf.h @@ -0,0 +1,39 @@ +/* + * 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 +#include + +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 diff --git a/libatalk/nbp/nbp_lkup.c b/libatalk/nbp/nbp_lkup.c new file mode 100644 index 00000000..d3327a42 --- /dev/null +++ b/libatalk/nbp/nbp_lkup.c @@ -0,0 +1,216 @@ +/* + * Copyright (c) 1990,1997 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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; +} diff --git a/libatalk/nbp/nbp_rgstr.c b/libatalk/nbp/nbp_rgstr.c new file mode 100644 index 00000000..6d820693 --- /dev/null +++ b/libatalk/nbp/nbp_rgstr.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#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; +} diff --git a/libatalk/nbp/nbp_unrgstr.c b/libatalk/nbp/nbp_unrgstr.c new file mode 100644 index 00000000..49cf2ace --- /dev/null +++ b/libatalk/nbp/nbp_unrgstr.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 1990,1997 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#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; +} diff --git a/libatalk/nbp/nbp_util.c b/libatalk/nbp/nbp_util.c new file mode 100644 index 00000000..83e9180f --- /dev/null +++ b/libatalk/nbp/nbp_util.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 1990,1997 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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 ); +} diff --git a/libatalk/netddp/Makefile b/libatalk/netddp/Makefile new file mode 100644 index 00000000..b13668f0 --- /dev/null +++ b/libatalk/netddp/Makefile @@ -0,0 +1,56 @@ +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 + diff --git a/libatalk/netddp/netddp_open.c b/libatalk/netddp/netddp_open.c new file mode 100644 index 00000000..999a2434 --- /dev/null +++ b/libatalk/netddp/netddp_open.c @@ -0,0 +1,77 @@ +/* + * 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 +#include +#include +#include + +#ifdef MACOSX_SERVER +#include +#include +#endif + +#include +#include + +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; +} diff --git a/libatalk/netddp/netddp_recvfrom.c b/libatalk/netddp/netddp_recvfrom.c new file mode 100644 index 00000000..b7974614 --- /dev/null +++ b/libatalk/netddp/netddp_recvfrom.c @@ -0,0 +1,61 @@ +/* + * 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 +#include +#include +#include +#include + +#ifdef MACOSX_SERVER +#include +#include +#endif + +#include +#include +#include +#include + +#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 */ diff --git a/libatalk/netddp/netddp_sendto.c b/libatalk/netddp/netddp_sendto.c new file mode 100644 index 00000000..3e26e338 --- /dev/null +++ b/libatalk/netddp/netddp_sendto.c @@ -0,0 +1,64 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#ifdef MACOSX_SERVER +#include +#include +#endif + +#include +#include +#include +#include + +#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 */ diff --git a/libatalk/pap/pap_child.h b/libatalk/pap/pap_child.h new file mode 100644 index 00000000..3d3bab80 --- /dev/null +++ b/libatalk/pap/pap_child.h @@ -0,0 +1,32 @@ +/* + * 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 diff --git a/libatalk/pap/pap_close.c b/libatalk/pap/pap_close.c new file mode 100644 index 00000000..4257a466 --- /dev/null +++ b/libatalk/pap/pap_close.c @@ -0,0 +1,44 @@ +/* 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; +} diff --git a/libatalk/pap/pap_init.c b/libatalk/pap/pap_init.c new file mode 100644 index 00000000..94d20a1b --- /dev/null +++ b/libatalk/pap/pap_init.c @@ -0,0 +1,25 @@ + +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 ); +} diff --git a/libatalk/pap/pap_open.c b/libatalk/pap/pap_open.c new file mode 100644 index 00000000..9d296250 --- /dev/null +++ b/libatalk/pap/pap_open.c @@ -0,0 +1,119 @@ +/* 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 */ + +} diff --git a/libatalk/pap/pap_read.c b/libatalk/pap/pap_read.c new file mode 100644 index 00000000..abd9014e --- /dev/null +++ b/libatalk/pap/pap_read.c @@ -0,0 +1,25 @@ +/* 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; +} diff --git a/libatalk/pap/pap_sendstatus.c b/libatalk/pap/pap_sendstatus.c new file mode 100644 index 00000000..6c3ae9e9 --- /dev/null +++ b/libatalk/pap/pap_sendstatus.c @@ -0,0 +1,21 @@ + + +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; +} diff --git a/libatalk/pap/pap_slinit.c b/libatalk/pap/pap_slinit.c new file mode 100644 index 00000000..07403e62 --- /dev/null +++ b/libatalk/pap/pap_slinit.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 1990,1996 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#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; +} diff --git a/libatalk/pap/pap_tickle.c b/libatalk/pap/pap_tickle.c new file mode 100644 index 00000000..b7e9a971 --- /dev/null +++ b/libatalk/pap/pap_tickle.c @@ -0,0 +1,19 @@ +/* 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"); + } +} diff --git a/libatalk/util/Makefile b/libatalk/util/Makefile new file mode 100644 index 00000000..fa0839e4 --- /dev/null +++ b/libatalk/util/Makefile @@ -0,0 +1,58 @@ +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 + diff --git a/libatalk/util/atalk_addr.c b/libatalk/util/atalk_addr.c new file mode 100644 index 00000000..0cf4cc8e --- /dev/null +++ b/libatalk/util/atalk_addr.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include + +/* + * 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); +} diff --git a/libatalk/util/bprint.c b/libatalk/util/bprint.c new file mode 100644 index 00000000..c192ba75 --- /dev/null +++ b/libatalk/util/bprint.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +#include + +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"); +} diff --git a/libatalk/util/getiface.c b/libatalk/util/getiface.c new file mode 100644 index 00000000..28aa0305 --- /dev/null +++ b/libatalk/util/getiface.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 1990,1993 Regents of The University of Michigan. + * Copyright (c) 1999-2000 Adrian Sun. + * All Rights Reserved. See COPYRIGHT. + */ + + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __svr4__ +#include +#endif + +#include + +/* 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); +} diff --git a/libatalk/util/module.c b/libatalk/util/module.c new file mode 100644 index 00000000..d3255349 --- /dev/null +++ b/libatalk/util/module.c @@ -0,0 +1,66 @@ +#include +#include +#include + +static int _mod_dummy; + +#ifdef NO_DLFCN_H +#ifdef MACOSX_SERVER +#include + +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 + +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 */ diff --git a/libatalk/util/server_child.c b/libatalk/util/server_child.c new file mode 100644 index 00000000..82b6199d --- /dev/null +++ b/libatalk/util/server_child.c @@ -0,0 +1,249 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#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); + } + } +} diff --git a/libatalk/util/server_lock.c b/libatalk/util/server_lock.c new file mode 100644 index 00000000..2a5d43dc --- /dev/null +++ b/libatalk/util/server_lock.c @@ -0,0 +1,80 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* 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; +} diff --git a/libatalk/util/strdicasecmp.c b/libatalk/util/strdicasecmp.c new file mode 100644 index 00000000..6f3b996d --- /dev/null +++ b/libatalk/util/strdicasecmp.c @@ -0,0 +1,544 @@ +#include + +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 ] ); +} diff --git a/lp2pap.sh b/lp2pap.sh new file mode 100755 index 00000000..fea0cf40 --- /dev/null +++ b/lp2pap.sh @@ -0,0 +1,11 @@ +#!/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 diff --git a/man/Makefile b/man/Makefile new file mode 100644 index 00000000..9028cca8 --- /dev/null +++ b/man/Makefile @@ -0,0 +1,41 @@ +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 diff --git a/man/man1/Makefile b/man/man1/Makefile new file mode 100644 index 00000000..9276cbf4 --- /dev/null +++ b/man/man1/Makefile @@ -0,0 +1,46 @@ +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 + diff --git a/man/man1/aecho.1 b/man/man1/aecho.1 new file mode 100644 index 00000000..c8162001 --- /dev/null +++ b/man/man1/aecho.1 @@ -0,0 +1,68 @@ +.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). diff --git a/man/man1/getzones.1 b/man/man1/getzones.1 new file mode 100644 index 00000000..188df2d1 --- /dev/null +++ b/man/man1/getzones.1 @@ -0,0 +1,39 @@ +.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). diff --git a/man/man1/hqx2bin.1 b/man/man1/hqx2bin.1 new file mode 100644 index 00000000..40dd5c2a --- /dev/null +++ b/man/man1/hqx2bin.1 @@ -0,0 +1 @@ +.so man1/megatron.1 diff --git a/man/man1/macbinary.1 b/man/man1/macbinary.1 new file mode 100644 index 00000000..40dd5c2a --- /dev/null +++ b/man/man1/macbinary.1 @@ -0,0 +1 @@ +.so man1/megatron.1 diff --git a/man/man1/megatron.1 b/man/man1/megatron.1 new file mode 100644 index 00000000..1b8be127 --- /dev/null +++ b/man/man1/megatron.1 @@ -0,0 +1,104 @@ +.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) diff --git a/man/man1/nbp.1 b/man/man1/nbp.1 new file mode 100644 index 00000000..fe2607ec --- /dev/null +++ b/man/man1/nbp.1 @@ -0,0 +1,82 @@ +.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). diff --git a/man/man1/nbplkup.1 b/man/man1/nbplkup.1 new file mode 100644 index 00000000..ac418085 --- /dev/null +++ b/man/man1/nbplkup.1 @@ -0,0 +1 @@ +.so man1/nbp.1 diff --git a/man/man1/nbprgstr.1 b/man/man1/nbprgstr.1 new file mode 100644 index 00000000..ac418085 --- /dev/null +++ b/man/man1/nbprgstr.1 @@ -0,0 +1 @@ +.so man1/nbp.1 diff --git a/man/man1/nbpunrgstr.1 b/man/man1/nbpunrgstr.1 new file mode 100644 index 00000000..ac418085 --- /dev/null +++ b/man/man1/nbpunrgstr.1 @@ -0,0 +1 @@ +.so man1/nbp.1 diff --git a/man/man1/pap.1 b/man/man1/pap.1 new file mode 100644 index 00000000..8e3a05bf --- /dev/null +++ b/man/man1/pap.1 @@ -0,0 +1,123 @@ +.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). diff --git a/man/man1/papstatus.1 b/man/man1/papstatus.1 new file mode 100644 index 00000000..631797d8 --- /dev/null +++ b/man/man1/papstatus.1 @@ -0,0 +1 @@ +.so man1/pap.1 diff --git a/man/man1/psorder.1 b/man/man1/psorder.1 new file mode 100644 index 00000000..41aa36b3 --- /dev/null +++ b/man/man1/psorder.1 @@ -0,0 +1,48 @@ +.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). diff --git a/man/man1/single2bin.1 b/man/man1/single2bin.1 new file mode 100644 index 00000000..40dd5c2a --- /dev/null +++ b/man/man1/single2bin.1 @@ -0,0 +1 @@ +.so man1/megatron.1 diff --git a/man/man1/unbin.1 b/man/man1/unbin.1 new file mode 100644 index 00000000..40dd5c2a --- /dev/null +++ b/man/man1/unbin.1 @@ -0,0 +1 @@ +.so man1/megatron.1 diff --git a/man/man1/unhex.1 b/man/man1/unhex.1 new file mode 100644 index 00000000..40dd5c2a --- /dev/null +++ b/man/man1/unhex.1 @@ -0,0 +1 @@ +.so man1/megatron.1 diff --git a/man/man1/unsingle.1 b/man/man1/unsingle.1 new file mode 100644 index 00000000..40dd5c2a --- /dev/null +++ b/man/man1/unsingle.1 @@ -0,0 +1 @@ +.so man1/megatron.1 diff --git a/man/man3/Makefile b/man/man3/Makefile new file mode 100644 index 00000000..a002eb97 --- /dev/null +++ b/man/man3/Makefile @@ -0,0 +1,40 @@ +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 + diff --git a/man/man3/atalk_aton.3 b/man/man3/atalk_aton.3 new file mode 100644 index 00000000..f7949eb4 --- /dev/null +++ b/man/man3/atalk_aton.3 @@ -0,0 +1,24 @@ +.TH ATALK_ATON 3 "12 Jan 1994" "netatalk 1.3" +.SH NAME +atalk_aton \- AppleTalk address parsing +.SH SYNOPSIS +.nf +#include +#include +.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). diff --git a/man/man3/nbp_name.3 b/man/man3/nbp_name.3 new file mode 100644 index 00000000..9c41cc47 --- /dev/null +++ b/man/man3/nbp_name.3 @@ -0,0 +1,79 @@ +.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. diff --git a/man/man4/Makefile b/man/man4/Makefile new file mode 100644 index 00000000..4b9a7ac1 --- /dev/null +++ b/man/man4/Makefile @@ -0,0 +1,39 @@ +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 + diff --git a/man/man4/atalk.4 b/man/man4/atalk.4 new file mode 100644 index 00000000..ecb5c270 --- /dev/null +++ b/man/man4/atalk.4 @@ -0,0 +1,63 @@ +.TH ATALK 4F "17 Dec 1991" "netatalk 1.2" +.SH NAME +atalk \- AppleTalk protocol family +.SH SYNOPSIS +.B #include +.br +.B #include +.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). diff --git a/man/man8/Makefile b/man/man8/Makefile new file mode 100644 index 00000000..8e4bce64 --- /dev/null +++ b/man/man8/Makefile @@ -0,0 +1,40 @@ +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 + diff --git a/man/man8/afpd.8 b/man/man8/afpd.8 new file mode 100644 index 00000000..e0be1b9b --- /dev/null +++ b/man/man8/afpd.8 @@ -0,0 +1,261 @@ +.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. diff --git a/man/man8/atalkd.8 b/man/man8/atalkd.8 new file mode 100644 index 00000000..c6e4d8b3 --- /dev/null +++ b/man/man8/atalkd.8 @@ -0,0 +1,123 @@ +.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. diff --git a/man/man8/pap.8 b/man/man8/pap.8 new file mode 100644 index 00000000..54bdb454 --- /dev/null +++ b/man/man8/pap.8 @@ -0,0 +1,165 @@ +.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. diff --git a/man/man8/papd.8 b/man/man8/papd.8 new file mode 100644 index 00000000..d9d7a4ea --- /dev/null +++ b/man/man8/papd.8 @@ -0,0 +1,142 @@ +'\" 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. diff --git a/man/man8/papstatus.8 b/man/man8/papstatus.8 new file mode 100644 index 00000000..8d8ab006 --- /dev/null +++ b/man/man8/papstatus.8 @@ -0,0 +1,84 @@ +.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) diff --git a/man/man8/psf.8 b/man/man8/psf.8 new file mode 100644 index 00000000..9fcb7110 --- /dev/null +++ b/man/man8/psf.8 @@ -0,0 +1,124 @@ +.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). diff --git a/services.atalk b/services.atalk new file mode 100644 index 00000000..2d8555e1 --- /dev/null +++ b/services.atalk @@ -0,0 +1,7 @@ +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 diff --git a/sys/freebsd/Makefile b/sys/freebsd/Makefile new file mode 100644 index 00000000..c3bf7cf3 --- /dev/null +++ b/sys/freebsd/Makefile @@ -0,0 +1,88 @@ +# 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 + diff --git a/sys/generic/Makefile b/sys/generic/Makefile new file mode 100644 index 00000000..23e5eb9f --- /dev/null +++ b/sys/generic/Makefile @@ -0,0 +1,142 @@ +# 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 + diff --git a/sys/generic/sys/cdefs.h b/sys/generic/sys/cdefs.h new file mode 100644 index 00000000..a5eaedee --- /dev/null +++ b/sys/generic/sys/cdefs.h @@ -0,0 +1,10 @@ +#ifndef _SYS_CDEFS_H +#define _SYS_CDEFS_H 1 + +#ifdef __STDC__ +#define __P(args) args +#else +#define __P(args) () +#endif + +#endif /* sys/cdefs.h */ diff --git a/sys/linux/Makefile b/sys/linux/Makefile new file mode 100644 index 00000000..cce91f51 --- /dev/null +++ b/sys/linux/Makefile @@ -0,0 +1,138 @@ +# 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 + diff --git a/sys/netatalk/aarp.c b/sys/netatalk/aarp.c new file mode 100644 index 00000000..ece86b1a --- /dev/null +++ b/sys/netatalk/aarp.c @@ -0,0 +1,796 @@ +/* + * Copyright (c) 1990,1991 Regents of The University of Michigan. + * All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#ifndef _IBMR2 +#include +#endif _IBMR2 +#include +#include +#include +#include +#undef s_net +#include +#ifdef _IBMR2 +#include +#include +#include +#include +#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 ); + } + } +} diff --git a/sys/netatalk/aarp.h b/sys/netatalk/aarp.h new file mode 100644 index 00000000..646a13ac --- /dev/null +++ b/sys/netatalk/aarp.h @@ -0,0 +1,58 @@ +/* + * 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 diff --git a/sys/netatalk/at.h b/sys/netatalk/at.h new file mode 100644 index 00000000..4f075879 --- /dev/null +++ b/sys/netatalk/at.h @@ -0,0 +1,113 @@ +/* + * 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 +#include +#include +#else + +#include +#include /* 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__ diff --git a/sys/netatalk/at_control.c b/sys/netatalk/at_control.c new file mode 100644 index 00000000..a57c2aa1 --- /dev/null +++ b/sys/netatalk/at_control.c @@ -0,0 +1,580 @@ +/* + * Copyright (c) 1990,1991 Regents of The University of Michigan. + * All Rights Reserved. + */ + +#include +#include +#ifdef ibm032 +#include +#endif ibm032 +#include +#include +#include +#include +#include +#ifndef _IBMR2 +#include +#endif _IBMR2 +#include +#include +#include +#include +#include +#include +#undef s_net +#include +#ifdef _IBMR2 +#include +#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" ); + } + } + } +} diff --git a/sys/netatalk/at_proto.c b/sys/netatalk/at_proto.c new file mode 100644 index 00000000..f1d6f5e9 --- /dev/null +++ b/sys/netatalk/at_proto.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1990,1991 Regents of The University of Michigan. + * + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include + +#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 ] ) ] +}; diff --git a/sys/netatalk/at_var.h b/sys/netatalk/at_var.h new file mode 100644 index 00000000..606c2418 --- /dev/null +++ b/sys/netatalk/at_var.h @@ -0,0 +1,51 @@ +/* + * 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 diff --git a/sys/netatalk/ddp.h b/sys/netatalk/ddp.h new file mode 100644 index 00000000..5a20d5ea --- /dev/null +++ b/sys/netatalk/ddp.h @@ -0,0 +1,125 @@ +/* + * 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 + +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 */ diff --git a/sys/netatalk/ddp_input.c b/sys/netatalk/ddp_input.c new file mode 100644 index 00000000..8cb4dd56 --- /dev/null +++ b/sys/netatalk/ddp_input.c @@ -0,0 +1,401 @@ +/* + * Copyright (c) 1990,1994 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _IBMR2 +#include +#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 +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; + } + } +} diff --git a/sys/netatalk/ddp_output.c b/sys/netatalk/ddp_output.c new file mode 100644 index 00000000..b37c051f --- /dev/null +++ b/sys/netatalk/ddp_output.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 1990,1991 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#undef s_net +#include + +#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 )); +} diff --git a/sys/netatalk/ddp_usrreq.c b/sys/netatalk/ddp_usrreq.c new file mode 100644 index 00000000..f678b87d --- /dev/null +++ b/sys/netatalk/ddp_usrreq.c @@ -0,0 +1,530 @@ +/* + * Copyright (c) 1990,1994 Regents of The University of Michigan. + * All Rights Reserved. See COPYRIGHT. + */ + +#include +#include +#include +#include +#ifdef ibm032 +#include +#endif ibm032 +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _IBMR2 +#include +#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 ); + } +} diff --git a/sys/netatalk/ddp_var.h b/sys/netatalk/ddp_var.h new file mode 100644 index 00000000..3a8f6231 --- /dev/null +++ b/sys/netatalk/ddp_var.h @@ -0,0 +1,41 @@ +/* + * 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 + +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 */ diff --git a/sys/netatalk/endian.h b/sys/netatalk/endian.h new file mode 100644 index 00000000..e602ec56 --- /dev/null +++ b/sys/netatalk/endian.h @@ -0,0 +1,129 @@ +/* + * 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 +#include + +#ifdef _IBMR2 +#include +#endif /*_IBMR2*/ + +#ifdef _ISOC9X_SOURCE +#include +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 +#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 /* 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_*/ diff --git a/sys/netatalk/phase2.h b/sys/netatalk/phase2.h new file mode 100644 index 00000000..54eac749 --- /dev/null +++ b/sys/netatalk/phase2.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 1990,1991 Regents of The University of Michigan. + * All Rights Reserved. + */ + +# if defined( ultrix ) || defined( BSD4_4 ) +#include +# else ultrix BSD4_4 + +#if defined( sun ) && !defined( __svr4__ ) +#include +#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 diff --git a/sys/netbsd/Makefile b/sys/netbsd/Makefile new file mode 100644 index 00000000..c31d3b42 --- /dev/null +++ b/sys/netbsd/Makefile @@ -0,0 +1,91 @@ +# 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 + diff --git a/sys/netbsd/netatalk/endian.h b/sys/netbsd/netatalk/endian.h new file mode 100644 index 00000000..a1957e21 --- /dev/null +++ b/sys/netbsd/netatalk/endian.h @@ -0,0 +1,9 @@ + +/* + * 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. + */ diff --git a/sys/openbsd/Makefile b/sys/openbsd/Makefile new file mode 100644 index 00000000..c25583cb --- /dev/null +++ b/sys/openbsd/Makefile @@ -0,0 +1,83 @@ +# 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 + diff --git a/sys/osx/Makefile b/sys/osx/Makefile new file mode 100644 index 00000000..57df17aa --- /dev/null +++ b/sys/osx/Makefile @@ -0,0 +1,95 @@ +# 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 + diff --git a/sys/solaris/Makefile b/sys/solaris/Makefile new file mode 100644 index 00000000..3576c475 --- /dev/null +++ b/sys/solaris/Makefile @@ -0,0 +1,194 @@ +# 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 + diff --git a/sys/solaris/aarp.c b/sys/solaris/aarp.c new file mode 100644 index 00000000..02a8524c --- /dev/null +++ b/sys/solaris/aarp.c @@ -0,0 +1,343 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#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 ); +} diff --git a/sys/solaris/ddp.c b/sys/solaris/ddp.c new file mode 100644 index 00000000..ea3285eb --- /dev/null +++ b/sys/solaris/ddp.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#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 ); +} diff --git a/sys/solaris/ddp.conf b/sys/solaris/ddp.conf new file mode 100644 index 00000000..e0257ad7 --- /dev/null +++ b/sys/solaris/ddp.conf @@ -0,0 +1,5 @@ +# +# Netatalk driver's configuration file +# + +name="ddp" parent="pseudo" instance=0; diff --git a/sys/solaris/dlpi.c b/sys/solaris/dlpi.c new file mode 100644 index 00000000..e70b465c --- /dev/null +++ b/sys/solaris/dlpi.c @@ -0,0 +1,473 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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, +}; diff --git a/sys/solaris/if.c b/sys/solaris/if.c new file mode 100644 index 00000000..810e7485 --- /dev/null +++ b/sys/solaris/if.c @@ -0,0 +1,368 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 )); + } +} diff --git a/sys/solaris/if.h b/sys/solaris/if.h new file mode 100644 index 00000000..f1ed7089 --- /dev/null +++ b/sys/solaris/if.h @@ -0,0 +1,77 @@ +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 * ); diff --git a/sys/solaris/ioc.c b/sys/solaris/ioc.c new file mode 100644 index 00000000..936336fe --- /dev/null +++ b/sys/solaris/ioc.c @@ -0,0 +1,126 @@ +#include +#include +#include + +#include + +#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; +} diff --git a/sys/solaris/ioc.h b/sys/solaris/ioc.h new file mode 100644 index 00000000..4b1424ab --- /dev/null +++ b/sys/solaris/ioc.h @@ -0,0 +1,4 @@ +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 ); diff --git a/sys/solaris/linkage.c b/sys/solaris/linkage.c new file mode 100644 index 00000000..7917607f --- /dev/null +++ b/sys/solaris/linkage.c @@ -0,0 +1,46 @@ +/* + * 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 +#include +#include + +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 )); +} diff --git a/sys/solaris/rt.c b/sys/solaris/rt.c new file mode 100644 index 00000000..95bd1e27 --- /dev/null +++ b/sys/solaris/rt.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ); +} diff --git a/sys/solaris/rt.h b/sys/solaris/rt.h new file mode 100644 index 00000000..ef7e17fb --- /dev/null +++ b/sys/solaris/rt.h @@ -0,0 +1,3 @@ +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 * ); diff --git a/sys/solaris/sock.c b/sys/solaris/sock.c new file mode 100644 index 00000000..2c197993 --- /dev/null +++ b/sys/solaris/sock.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#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 ); +} diff --git a/sys/solaris/sock.h b/sys/solaris/sock.h new file mode 100644 index 00000000..2397bd70 --- /dev/null +++ b/sys/solaris/sock.h @@ -0,0 +1,12 @@ +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 * ); diff --git a/sys/solaris/tpi.c b/sys/solaris/tpi.c new file mode 100644 index 00000000..078ebe5c --- /dev/null +++ b/sys/solaris/tpi.c @@ -0,0 +1,714 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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, +}; diff --git a/sys/sunos/Makefile b/sys/sunos/Makefile new file mode 100644 index 00000000..e489af11 --- /dev/null +++ b/sys/sunos/Makefile @@ -0,0 +1,148 @@ +# 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 + diff --git a/sys/sunos/at_sun.c b/sys/sunos/at_sun.c new file mode 100644 index 00000000..507d9eff --- /dev/null +++ b/sys/sunos/at_sun.c @@ -0,0 +1,374 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#undef s_net +#include + +#include +#include +#include +#include + +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 ); + } +} diff --git a/sys/ultrix/Makefile b/sys/ultrix/Makefile new file mode 100644 index 00000000..e3aa638b --- /dev/null +++ b/sys/ultrix/Makefile @@ -0,0 +1,143 @@ +# 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 + diff --git a/sys/ultrix/at_ultrix.c b/sys/ultrix/at_ultrix.c new file mode 100644 index 00000000..e53fb391 --- /dev/null +++ b/sys/ultrix/at_ultrix.c @@ -0,0 +1,211 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef s_net +#include + +#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 ); +} diff --git a/sys/ultrix/kpatch-4.1 b/sys/ultrix/kpatch-4.1 new file mode 100644 index 00000000..8a30e432 --- /dev/null +++ b/sys/ultrix/kpatch-4.1 @@ -0,0 +1,151 @@ +*** ../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(); } diff --git a/sys/ultrix/kpatch-4.2 b/sys/ultrix/kpatch-4.2 new file mode 100644 index 00000000..0c8fa8e3 --- /dev/null +++ b/sys/ultrix/kpatch-4.2 @@ -0,0 +1,143 @@ +*** ../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(); } -- 2.39.2