diff --git a/etc/Makefile b/etc/Makefile index 9933975620a0..e2fa4bbeb2eb 100644 --- a/etc/Makefile +++ b/etc/Makefile @@ -1,244 +1,248 @@ # from: @(#)Makefile 5.11 (Berkeley) 5/21/91 # $FreeBSD$ .include .if ${MK_SENDMAIL} != "no" SUBDIR= sendmail .endif BIN1= amd.map apmd.conf auth.conf \ crontab csh.cshrc csh.login csh.logout devd.conf devfs.conf \ dhclient.conf disktab fbtab ftpusers gettytab group \ hosts hosts.allow hosts.equiv hosts.lpd \ inetd.conf login.access login.conf \ mac.conf motd netconfig network.subr networks newsyslog.conf \ portsnap.conf pf.conf pf.os phones profile protocols \ rc rc.bsdextended rc.firewall rc.firewall6 rc.initdiskless \ rc.sendmail rc.shutdown \ rc.subr remote rpc services shells \ snmpd.config sysctl.conf syslog.conf \ etc.${MACHINE_ARCH}/ttys \ ${.CURDIR}/../gnu/usr.bin/man/manpath/manpath.config \ ${.CURDIR}/../usr.bin/mail/misc/mail.rc \ ${.CURDIR}/../usr.bin/locate/locate/locate.rc OPENBSMDIR= ${.CURDIR}/../contrib/openbsm BSM_ETC_OPEN_FILES= ${OPENBSMDIR}/etc/audit_class \ ${OPENBSMDIR}/etc/audit_event BSM_ETC_RESTRICTED_FILES= ${OPENBSMDIR}/etc/audit_control \ ${OPENBSMDIR}/etc/audit_user BSM_ETC_EXEC_FILES= ${OPENBSMDIR}/etc/audit_warn BSM_ETC_DIR= ${DESTDIR}/etc/security .if ${MK_LPR} != "no" BIN1+= printcap .endif +.if ${MK_NS_CACHING} != "no" +BIN1+= cached.conf +.endif + .if ${MK_OPENSSH} != "no" SSH= ${.CURDIR}/../crypto/openssh/ssh_config \ ${.CURDIR}/../crypto/openssh/sshd_config \ ${.CURDIR}/../crypto/openssh/moduli .endif .if ${MK_OPENSSL} != "no" SSL= ${.CURDIR}/../crypto/openssl/apps/openssl.cnf .endif # -rwxr-xr-x root:wheel, for the new cron root:wheel BIN2= netstart pccard_ether rc.suspend rc.resume MTREE= BSD.include.dist BSD.local.dist BSD.root.dist BSD.usr.dist \ BSD.var.dist BSD.x11.dist BSD.x11-4.dist .if ${MK_SENDMAIL} != "no" MTREE+= BSD.sendmail.dist .endif .if ${MK_BIND} != "no" MTREE+= BIND.chroot.dist .if ${MK_BIND_LIBS} != "no" MTREE+= BIND.include.dist .endif .endif .if ${MK_BIND_ETC} != "no" NAMEDB= PROTO.localhost.rev PROTO.localhost-v6.rev named.conf named.root \ make-localhost .endif PPPCNF= ppp.conf .if ${MK_SENDMAIL} == "no" ETCMAIL=mailer.conf aliases .else ETCMAIL=Makefile README mailer.conf access.sample virtusertable.sample \ mailertable.sample aliases .endif # Special top level files for FreeBSD FREEBSD=COPYRIGHT afterinstall: .if ${MK_MAN} != "no" cd ${.CURDIR}/../share/man; ${MAKE} makedb .endif distribute: cd ${.CURDIR} ; ${MAKE} install DESTDIR=${DISTDIR}/${DISTRIBUTION} cd ${.CURDIR} ; ${MAKE} distribution DESTDIR=${DISTDIR}/${DISTRIBUTION} .include .if ${TARGET_ENDIANNESS} == "1234" CAP_MKDB_ENDIAN?= -l PWD_MKDB_ENDIAN?= -L .elif ${TARGET_ENDIANNESS} == "4321" CAP_MKDB_ENDIAN?= -b PWD_MKDB_ENDIAN?= -B .else CAP_MKDB_ENDIAN?= PWD_MKDB_ENDIAN?= .endif distribution: .if !defined(DESTDIR) @echo "set DESTDIR before running \"make ${.TARGET}\"" @false .endif cd ${.CURDIR}; \ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 \ ${BIN1} ${DESTDIR}/etc; \ cap_mkdb ${CAP_MKDB_ENDIAN} ${DESTDIR}/etc/login.conf; \ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 755 \ ${BIN2} ${DESTDIR}/etc; \ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 600 \ master.passwd nsmb.conf opieaccess ${DESTDIR}/etc; \ pwd_mkdb ${PWD_MKDB_ENDIAN} -i -p -d ${DESTDIR}/etc \ ${DESTDIR}/etc/master.passwd cd ${.CURDIR}/bluetooth; ${MAKE} install cd ${.CURDIR}/defaults; ${MAKE} install cd ${.CURDIR}/gss; ${MAKE} install cd ${.CURDIR}/periodic; ${MAKE} install cd ${.CURDIR}/rc.d; ${MAKE} install cd ${.CURDIR}/../gnu/usr.bin/send-pr; ${MAKE} etc-gnats-freefall cd ${.CURDIR}/../share/termcap; ${MAKE} etc-termcap cd ${.CURDIR}/../usr.sbin/rmt; ${MAKE} etc-rmt cd ${.CURDIR}/pam.d; ${MAKE} install cd ${.CURDIR}; ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 0444 \ ${BSM_ETC_OPEN_FILES} ${BSM_ETC_DIR} cd ${.CURDIR}; ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 0600 \ ${BSM_ETC_RESTRICTED_FILES} ${BSM_ETC_DIR} cd ${.CURDIR}; ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 0500 \ ${BSM_ETC_EXEC_FILES} ${BSM_ETC_DIR} .if ${MK_I4B} != "no" cd ${.CURDIR}/isdn; ${MAKE} install .endif .if ${MK_SENDMAIL} != "no" cd ${.CURDIR}/sendmail; ${MAKE} distribution .endif .if ${MK_OPENSSH} != "no" cd ${.CURDIR}; ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 \ ${SSH} ${DESTDIR}/etc/ssh .endif .if ${MK_OPENSSL} != "no" cd ${.CURDIR}; ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 \ ${SSL} ${DESTDIR}/etc/ssl .endif .if ${MK_KERBEROS} != "no" cd ${.CURDIR}/root; \ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 \ dot.k5login ${DESTDIR}/root/.k5login; .endif cd ${.CURDIR}/root; \ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 \ dot.cshrc ${DESTDIR}/root/.cshrc; \ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 \ dot.login ${DESTDIR}/root/.login; \ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 \ dot.profile ${DESTDIR}/root/.profile; \ rm -f ${DESTDIR}/.cshrc ${DESTDIR}/.profile; \ ln ${DESTDIR}/root/.cshrc ${DESTDIR}/.cshrc; \ ln ${DESTDIR}/root/.profile ${DESTDIR}/.profile cd ${.CURDIR}/mtree; ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 444 \ ${MTREE} ${DESTDIR}/etc/mtree .if ${MK_BIND_ETC} != "no" cd ${.CURDIR}/namedb; ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 \ ${NAMEDB} ${DESTDIR}/var/named/etc/namedb .endif .if ${MK_BIND_MTREE} != "no" @if [ ! -e ${DESTDIR}/etc/namedb ]; then \ set -x; \ ln -s ../var/named/etc/namedb ${DESTDIR}/etc/namedb; \ fi .endif cd ${.CURDIR}/ppp; ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 600 \ ${PPPCNF} ${DESTDIR}/etc/ppp cd ${.CURDIR}/mail; ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 \ ${ETCMAIL} ${DESTDIR}/etc/mail @if [ -d ${DESTDIR}/etc/mail -a -f ${DESTDIR}/etc/mail/aliases -a \ ! -f ${DESTDIR}/etc/aliases ]; then \ set -x; \ ln -s mail/aliases ${DESTDIR}/etc/aliases; \ fi ${INSTALL} -o ${BINOWN} -g operator -m 664 /dev/null \ ${DESTDIR}/etc/dumpdates ${INSTALL} -o nobody -g ${BINGRP} -m 644 /dev/null \ ${DESTDIR}/var/db/locate.database ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 ${.CURDIR}/minfree \ ${DESTDIR}/var/crash cd ${.CURDIR}/..; ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 444 \ ${FREEBSD} ${DESTDIR}/ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 444 \ ${.CURDIR}/../sys/${MACHINE}/conf/GENERIC.hints \ ${DESTDIR}/boot/device.hints distrib-dirs: mtree -eU ${MTREE_FOLLOWS_SYMLINKS} -f ${.CURDIR}/mtree/BSD.root.dist -p ${DESTDIR}/ mtree -eU ${MTREE_FOLLOWS_SYMLINKS} -f ${.CURDIR}/mtree/BSD.var.dist -p ${DESTDIR}/var mtree -eU ${MTREE_FOLLOWS_SYMLINKS} -f ${.CURDIR}/mtree/BSD.usr.dist -p ${DESTDIR}/usr mtree -eU ${MTREE_FOLLOWS_SYMLINKS} -f ${.CURDIR}/mtree/BSD.include.dist \ -p ${DESTDIR}/usr/include .if ${MK_BIND_LIBS} != "no" mtree -deU ${MTREE_FOLLOWS_SYMLINKS} -f ${.CURDIR}/mtree/BIND.include.dist \ -p ${DESTDIR}/usr/include .endif .if ${MK_BIND_MTREE} != "no" mtree -deU ${MTREE_FOLLOWS_SYMLINKS} -f ${.CURDIR}/mtree/BIND.chroot.dist \ -p ${DESTDIR}/var/named .endif .if ${MK_SENDMAIL} != "no" mtree -deU ${MTREE_FOLLOWS_SYMLINKS} -f ${.CURDIR}/mtree/BSD.sendmail.dist -p ${DESTDIR}/ .endif cd ${DESTDIR}/; rm -f ${DESTDIR}/sys; ln -s usr/src/sys sys cd ${DESTDIR}/usr/share/man/en.ISO8859-1; ln -sf ../man* . cd ${DESTDIR}/usr/share/man; \ set - `grep "^[a-zA-Z]" ${.CURDIR}/man.alias`; \ while [ $$# -gt 0 ] ; \ do \ rm -rf "$$1"; \ ln -s "$$2" "$$1"; \ shift; shift; \ done cd ${DESTDIR}/usr/share/openssl/man; \ set - `grep "^[a-zA-Z]" ${.CURDIR}/man.alias`; \ while [ $$# -gt 0 ] ; \ do \ rm -rf "$$1"; \ ln -s "$$2" "$$1"; \ shift; shift; \ done cd ${DESTDIR}/usr/share/openssl/man/en.ISO8859-1; ln -sf ../man* . cd ${DESTDIR}/usr/share/nls; \ set - `grep "^[a-zA-Z]" ${.CURDIR}/nls.alias`; \ while [ $$# -gt 0 ] ; \ do \ rm -rf "$$1"; \ ln -s "$$2" "$$1"; \ shift; shift; \ done etc-examples: cd ${.CURDIR}; ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 444 \ ${BIN1} ${BIN2} nsmb.conf opieaccess \ ${DESTDIR}/usr/share/examples/etc cd ${.CURDIR}/defaults; ${MAKE} install \ DESTDIR=${DESTDIR}/usr/share/examples .include diff --git a/etc/cached.conf b/etc/cached.conf new file mode 100644 index 000000000000..19a8b5b95452 --- /dev/null +++ b/etc/cached.conf @@ -0,0 +1,12 @@ +# +# Default caching daemon configuration file +# $FreeBSD$ +# + +enable-cache passwd yes +enable-cache group yes +enable-cache hosts yes +enable-cache services yes +enable-cache protocols yes +enable-cache rpc yes +enable-cache networks yes diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf index 316705e341fa..6d929197d206 100644 --- a/etc/defaults/rc.conf +++ b/etc/defaults/rc.conf @@ -1,594 +1,595 @@ #!/bin/sh # This is rc.conf - a file full of useful variables that you can set # to change the default startup behavior of your system. You should # not edit this file! Put any overrides into one of the ${rc_conf_files} # instead and you will be able to update these defaults later without # spamming your local configuration information. # # The ${rc_conf_files} files should only contain values which override # values set in this file. This eases the upgrade path when defaults # are changed and new features are added. # # All arguments must be in double or single quotes. # # For a more detailed explanation of all the rc.conf variables, please # refer to the rc.conf(5) manual page. # # $FreeBSD$ ############################################################## ### Important initial Boot-time options #################### ############################################################## rc_debug="NO" # Set to YES to enable debugging output from rc.d rc_info="NO" # Enables display of informational messages at boot. rcshutdown_timeout="30" # Seconds to wait before terminating rc.shutdown early_late_divider="mountcritlocal" # Script that separates early/late # stages of the boot process. Make sure you know # the ramifications if you change this. # See rc.conf(5) for more details. swapfile="NO" # Set to name of swapfile if aux swapfile desired. apm_enable="NO" # Set to YES to enable APM BIOS functions (or NO). apmd_enable="NO" # Run apmd to handle APM event from userland. apmd_flags="" # Flags to apmd (if enabled). devd_enable="YES" # Run devd, to trigger programs on device tree changes. devd_flags="" # Additional flags for devd(8). kldxref_enable="NO" # Build linker.hints files with kldxref(8). kldxref_clobber="NO" # Overwrite old linker.hints at boot. kldxref_module_path="" # Override kern.module_path. A ';'-delimited list. powerd_enable="NO" # Run powerd to lower our power usage. powerd_flags="" # Flags to powerd (if enabled). removable_route_flush="YES" # Flush routes when removing an interface tmpmfs="AUTO" # Set to YES to always create an mfs /tmp, NO to never tmpsize="20m" # Size of mfs /tmp if created tmpmfs_flags="-S -M" # Extra mdmfs options for the mfs /tmp varmfs="AUTO" # Set to YES to always create an mfs /var, NO to never varsize="32m" # Size of mfs /var if created varmfs_flags="-S -M" # Extra mount options for the mfs /var populate_var="AUTO" # Set to YES to always (re)populate /var, NO to never cleanvar_enable="YES" # Clean the /var directory local_startup="/usr/local/etc/rc.d /usr/X11R6/etc/rc.d" # startup script dirs. script_name_sep=" " # Change if your startup scripts' names contain spaces rc_conf_files="/etc/rc.conf /etc/rc.conf.local" # Experimental - test before enabling gbde_autoattach_all="NO" # YES automatically mounts gbde devices from fstab gbde_devices="NO" # Devices to automatically attach (list, or AUTO) gbde_attach_attempts="3" # Number of times to attempt attaching gbde devices gbde_lockdir="/etc" # Where to look for gbde lockfiles # GELI disk encryption configuration. geli_devices="" # List of devices to automatically attach in addition to # GELI devices listed in /etc/fstab. geli_tries="" # Number of times to attempt attaching geli device. # If empty, kern.geom.eli.tries will be used. geli_default_flags="" # Default flags for geli(8). geli_autodetach="YES" # Automatically detach on last close. # Providers are marked as such when all file systems are # mounted. # Example use. #geli_devices="da1 mirror/home" #geli_da1_flags="-p -k /etc/geli/da1.keys" #geli_da1_autodetach="NO" #geli_mirror_home_flags="-k /etc/geli/home.keys" geli_swap_flags="-a aes -l 256 -s 4096 -d" # Options for GELI-encrypted # swap partitions. root_rw_mount="YES" # Set to NO to inhibit remounting root read-write. fsck_y_enable="NO" # Set to YES to do fsck -y if the initial preen fails. background_fsck="YES" # Attempt to run fsck in the background where possible. background_fsck_delay="60" # Time to wait (seconds) before starting the fsck. netfs_types="nfs:NFS nfs4:NFS4 smbfs:SMB portalfs:PORTAL nwfs:NWFS" # Net filesystems. extra_netfs_types="NO" # List of network extra filesystem types for delayed # mount at startup (or NO). ############################################################## ### Network configuration sub-section ###################### ############################################################## ### Basic network and firewall/security options: ### hostname="" # Set this! nisdomainname="NO" # Set to NIS domain if using NIS (or NO). dhclient_program="/sbin/dhclient" # Path to dhcp client program. dhclient_flags="" # Additional flags to pass to dhcp client. background_dhclient="NO" # Start dhcp client in the background. synchronous_dhclient="YES" # Start dhclient directly on configured # interfaces during startup. firewall_enable="NO" # Set to YES to enable firewall functionality firewall_script="/etc/rc.firewall" # Which script to run to set up the firewall firewall_type="UNKNOWN" # Firewall type (see /etc/rc.firewall) firewall_quiet="NO" # Set to YES to suppress rule display firewall_logging="NO" # Set to YES to enable events logging firewall_flags="" # Flags passed to ipfw when type is a file ip_portrange_first="NO" # Set first dynamically allocated port ip_portrange_last="NO" # Set last dynamically allocated port ike_enable="NO" # Enable IKE daemon (usually racoon or isakmpd) ike_program="/usr/local/sbin/isakmpd" # Path to IKE daemon ike_flags="" # Additional flags for IKE daemon ipsec_enable="NO" # Set to YES to run setkey on ipsec_file ipsec_file="/etc/ipsec.conf" # Name of config file for setkey natd_program="/sbin/natd" # path to natd, if you want a different one. natd_enable="NO" # Enable natd (if firewall_enable == YES). natd_interface="" # Public interface or IPaddress to use. natd_flags="" # Additional flags for natd. ipfilter_enable="NO" # Set to YES to enable ipfilter functionality ipfilter_program="/sbin/ipf" # where the ipfilter program lives ipfilter_rules="/etc/ipf.rules" # rules definition file for ipfilter, see # /usr/src/contrib/ipfilter/rules for examples ipfilter_flags="" # additional flags for ipfilter ipnat_enable="NO" # Set to YES to enable ipnat functionality ipnat_program="/sbin/ipnat" # where the ipnat program lives ipnat_rules="/etc/ipnat.rules" # rules definition file for ipnat ipnat_flags="" # additional flags for ipnat ipmon_enable="NO" # Set to YES for ipmon; needs ipfilter or ipnat ipmon_program="/sbin/ipmon" # where the ipfilter monitor program lives ipmon_flags="-Ds" # typically "-Ds" or "-D /var/log/ipflog" ipfs_enable="NO" # Set to YES to enable saving and restoring # of state tables at shutdown and boot ipfs_program="/sbin/ipfs" # where the ipfs program lives ipfs_flags="" # additional flags for ipfs pf_enable="NO" # Set to YES to enable packet filter (pf) pf_rules="/etc/pf.conf" # rules definition file for pf pf_program="/sbin/pfctl" # where the pfctl program lives pf_flags="" # additional flags for pfctl pflog_enable="NO" # Set to YES to enable packet filter logging pflog_logfile="/var/log/pflog" # where pflogd should store the logfile pflog_program="/sbin/pflogd" # where the pflogd program lives pflog_flags="" # additional flags for pflogd pfsync_enable="NO" # Expose pf state to other hosts for syncing pfsync_syncdev="" # Interface for pfsync to work through pfsync_ifconfig="" # Additional options to ifconfig(8) for pfsync tcp_extensions="YES" # Set to NO to turn off RFC1323 extensions. log_in_vain="0" # >=1 to log connects to ports w/o listeners. tcp_keepalive="YES" # Enable stale TCP connection timeout (or NO). # For the following option you need to have TCP_DROP_SYNFIN set in your # kernel. Please refer to LINT and NOTES for details. tcp_drop_synfin="NO" # Set to YES to drop TCP packets with SYN+FIN # NOTE: this violates the TCP specification icmp_drop_redirect="NO" # Set to YES to ignore ICMP REDIRECT packets icmp_log_redirect="NO" # Set to YES to log ICMP REDIRECT packets network_interfaces="auto" # List of network interfaces (or "auto"). cloned_interfaces="" # List of cloned network interfaces to create. #cloned_interfaces="gif0 gif1 gif2 gif3" # Pre-cloning GENERIC config. ifconfig_lo0="inet 127.0.0.1" # default loopback device configuration. #ifconfig_lo0_alias0="inet 127.0.0.254 netmask 0xffffffff" # Sample alias entry. #ifconfig_ed0_ipx="ipx 0x00010010" # Sample IPX address family entry. #ifconfig_fxp0_name="net0" # Change interface name from fxp0 to net0. #ipv4_addrs_fxp0="192.168.0.1/24 192.168.1.1-5/28" # example IPv4 address entry. # # If you have any sppp(4) interfaces above, you might also want to set # the following parameters. Refer to spppcontrol(8) for their meaning. sppp_interfaces="" # List of sppp interfaces. #sppp_interfaces="isp0" # example: sppp over ISDN #spppconfig_isp0="authproto=chap myauthname=foo myauthsecret='top secret' hisauthname=some-gw hisauthsecret='another secret'" gif_interfaces="NO" # List of GIF tunnels (or "NO"). #gif_interfaces="gif0 gif1" # Examples typically for a router. # Choose correct tunnel addrs. #gifconfig_gif0="10.1.1.1 10.1.2.1" # Examples typically for a router. #gifconfig_gif1="10.1.1.2 10.1.2.2" # Examples typically for a router. # User ppp configuration. ppp_enable="NO" # Start user-ppp (or NO). ppp_program="/usr/sbin/ppp" # Path to user-ppp program. ppp_mode="auto" # Choice of "auto", "ddial", "direct" or "dedicated". # For details see man page for ppp(8). Default is auto. ppp_nat="YES" # Use PPP's internal network address translation or NO. ppp_profile="papchap" # Which profile to use from /etc/ppp/ppp.conf. ppp_user="root" # Which user to run ppp as ### Network daemon (miscellaneous) ### hostapd_enable="NO" # Run hostap daemon. syslogd_enable="YES" # Run syslog daemon (or NO). syslogd_program="/usr/sbin/syslogd" # path to syslogd, if you want a different one. syslogd_flags="-s" # Flags to syslogd (if enabled). inetd_enable="NO" # Run the network daemon dispatcher (YES/NO). inetd_program="/usr/sbin/inetd" # path to inetd, if you want a different one. inetd_flags="-wW -C 60" # Optional flags to inetd # # named. It may be possible to run named in a sandbox, man security for # details. # named_enable="NO" # Run named, the DNS server (or NO). named_program="/usr/sbin/named" # path to named, if you want a different one. #named_flags="" # Flags for named named_pidfile="/var/run/named/pid" # Must set this in named.conf as well named_uid="bind" # User to run named as named_chrootdir="/var/named" # Chroot directory (or "" not to auto-chroot it) named_chroot_autoupdate="YES" # Automatically install/update chrooted # components of named. See /etc/rc.d/named. named_symlink_enable="YES" # Symlink the chrooted pid file # # kerberos. Do not run the admin daemons on slave servers # kerberos5_server_enable="NO" # Run a kerberos 5 master server (or NO). kerberos5_server="/usr/libexec/kdc" # path to kerberos 5 KDC kerberos5_server_flags="" # Additional flags to the kerberos 5 server kadmind5_server_enable="NO" # Run kadmind (or NO) kadmind5_server="/usr/libexec/kadmind" # path to kerberos 5 admin daemon kpasswdd_server_enable="NO" # Run kpasswdd (or NO) kpasswdd_server="/usr/libexec/kpasswdd" # path to kerberos 5 passwd daemon rwhod_enable="NO" # Run the rwho daemon (or NO). rwhod_flags="" # Flags for rwhod rarpd_enable="NO" # Run rarpd (or NO). rarpd_flags="" # Flags to rarpd. bootparamd_enable="NO" # Run bootparamd (or NO). bootparamd_flags="" # Flags to bootparamd pppoed_enable="NO" # Run the PPP over Ethernet daemon. pppoed_provider="*" # Provider and ppp(8) config file entry. pppoed_flags="-P /var/run/pppoed.pid" # Flags to pppoed (if enabled). pppoed_interface="fxp0" # The interface that pppoed runs on. sshd_enable="NO" # Enable sshd sshd_program="/usr/sbin/sshd" # path to sshd, if you want a different one. sshd_flags="" # Additional flags for sshd. ftpd_enable="NO" # Enable stand-alone ftpd. ftpd_program="/usr/libexec/ftpd" # Path to ftpd, if you want a different one. ftpd_flags="" # Additional flags to stand-alone ftpd. ### Network daemon (NFS): All need rpcbind_enable="YES" ### amd_enable="NO" # Run amd service with $amd_flags (or NO). amd_program="/usr/sbin/amd" # path to amd, if you want a different one. amd_flags="-a /.amd_mnt -l syslog /host /etc/amd.map /net /etc/amd.map" amd_map_program="NO" # Can be set to "ypcat -k amd.master" nfs_client_enable="NO" # This host is an NFS client (or NO). nfs_access_cache="2" # Client cache timeout in seconds nfs_server_enable="NO" # This host is an NFS server (or NO). nfs_server_flags="-u -t -n 4" # Flags to nfsd (if enabled). mountd_enable="NO" # Run mountd (or NO). mountd_flags="-r" # Flags to mountd (if NFS server enabled). weak_mountd_authentication="NO" # Allow non-root mount requests to be served. nfs_reserved_port_only="NO" # Provide NFS only on secure port (or NO). nfs_bufpackets="" # bufspace (in packets) for client rpc_lockd_enable="NO" # Run NFS rpc.lockd needed for client/server. rpc_statd_enable="NO" # Run NFS rpc.statd needed for client/server. rpcbind_enable="NO" # Run the portmapper service (YES/NO). rpcbind_program="/usr/sbin/rpcbind" # path to rpcbind, if you want a different one. rpcbind_flags="" # Flags to rpcbind (if enabled). rpc_ypupdated_enable="NO" # Run if NIS master and SecureRPC (or NO). keyserv_enable="NO" # Run the SecureRPC keyserver (or NO). keyserv_flags="" # Flags to keyserv (if enabled). ### Network Time Services options: ### timed_enable="NO" # Run the time daemon (or NO). timed_flags="" # Flags to timed (if enabled). ntpdate_enable="NO" # Run ntpdate to sync time on boot (or NO). ntpdate_program="/usr/sbin/ntpdate" # path to ntpdate, if you want a different one. ntpdate_flags="-b" # Flags to ntpdate (if enabled). ntpd_enable="NO" # Run ntpd Network Time Protocol (or NO). ntpd_program="/usr/sbin/ntpd" # path to ntpd, if you want a different one. ntpd_config="/etc/ntp.conf" # ntpd(8) configuration file ntpd_sync_on_start="NO" # Sync time on ntpd startup, even if offset is high ntpd_flags="-p /var/run/ntpd.pid -f /var/db/ntpd.drift" # Flags to ntpd (if enabled). # Network Information Services (NIS) options: All need rpcbind_enable="YES" ### nis_client_enable="NO" # We're an NIS client (or NO). nis_client_flags="" # Flags to ypbind (if enabled). nis_ypset_enable="NO" # Run ypset at boot time (or NO). nis_ypset_flags="" # Flags to ypset (if enabled). nis_server_enable="NO" # We're an NIS server (or NO). nis_server_flags="" # Flags to ypserv (if enabled). nis_ypxfrd_enable="NO" # Run rpc.ypxfrd at boot time (or NO). nis_ypxfrd_flags="" # Flags to rpc.ypxfrd (if enabled). nis_yppasswdd_enable="NO" # Run rpc.yppasswdd at boot time (or NO). nis_yppasswdd_flags="" # Flags to rpc.yppasswdd (if enabled). ### SNMP daemon ### # Be sure to understand the security implications of running SNMP v1/v2 # in your network. bsnmpd_enable="NO" # Run the SNMP daemon (or NO). bsnmpd_flags="" # Flags for bsnmpd. ### Network routing options: ### defaultrouter="NO" # Set to default gateway (or NO). static_routes="" # Set to static route list (or leave empty). natm_static_routes="" # Set to static route list for NATM (or leave empty). gateway_enable="NO" # Set to YES if this host will be a gateway. router_enable="NO" # Set to YES to enable a routing daemon. router="/sbin/routed" # Name of routing daemon to use if enabled. router_flags="-q" # Flags for routing daemon. mrouted_enable="NO" # Do multicast routing (see /etc/mrouted.conf). mrouted_flags="" # Flags for multicast routing daemon. ipxgateway_enable="NO" # Set to YES to enable IPX routing. ipxrouted_enable="NO" # Set to YES to run the IPX routing daemon. ipxrouted_flags="" # Flags for IPX routing daemon. arpproxy_all="NO" # replaces obsolete kernel option ARP_PROXYALL. forward_sourceroute="NO" # do source routing (only if gateway_enable is set to "YES") accept_sourceroute="NO" # accept source routed packets to us ### ATM interface options: ### atm_enable="NO" # Configure ATM interfaces (or NO). #atm_netif_hea0="atm 1" # Network interfaces for physical interface. #atm_sigmgr_hea0="uni31" # Signalling manager for physical interface. #atm_prefix_hea0="ILMI" # NSAP prefix (UNI interfaces only) (or ILMI). #atm_macaddr_hea0="NO" # Override physical MAC address (or NO). #atm_arpserver_atm0="0x47.0005.80.999999.9999.9999.9999.999999999999.00" # ATMARP server address (or local). #atm_scsparp_atm0="NO" # Run SCSP/ATMARP on network interface (or NO). atm_pvcs="" # Set to PVC list (or leave empty). atm_arps="" # Set to permanent ARP list (or leave empty). ### ISDN interface options: (see also: /usr/share/examples/isdn) ### isdn_enable="NO" # Enable the ISDN subsystem (or NO). isdn_fsdev="NO" # Output device for fullscreen mode (or NO for daemon mode). isdn_flags="-dn -d0x1f9" # Flags for isdnd isdn_ttype="cons25" # terminal type for fullscreen mode isdn_screenflags="NO" # screenflags for ${isdn_fsdev} isdn_trace="NO" # Enable the ISDN trace subsystem (or NO). isdn_traceflags="-f /var/tmp/isdntrace0" # Flags for isdntrace ### Bluetooth ### hcsecd_enable="NO" # Enable hcsecd(8) (or NO) hcsecd_config="/etc/bluetooth/hcsecd.conf" # hcsecd(8) configuration file sdpd_enable="NO" # Enable sdpd(8) (or NO) sdpd_control="/var/run/sdp" # sdpd(8) control socket sdpd_groupname="nobody" # set spdp(8) user/group to run as after sdpd_username="nobody" # it initializes ### Miscellaneous network options: ### icmp_bmcastecho="NO" # respond to broadcast ping packets ### IPv6 options: ### ipv6_enable="NO" # Set to YES to set up for IPv6. ipv6_network_interfaces="auto" # List of network interfaces (or "auto"). ipv6_defaultrouter="NO" # Set to IPv6 default gateway (or NO). #ipv6_defaultrouter="2002:c058:6301::" # Use this for 6to4 (RFC 3068) ipv6_static_routes="" # Set to static route list (or leave empty). #ipv6_static_routes="xxx" # An example to set fec0:0000:0000:0006::/64 # route toward loopback interface. #ipv6_route_xxx="fec0:0000:0000:0006:: -prefixlen 64 ::1" ipv6_gateway_enable="NO" # Set to YES if this host will be a gateway. ipv6_router_enable="NO" # Set to YES to enable an IPv6 routing daemon. ipv6_router="/usr/sbin/route6d" # Name of IPv6 routing daemon. ipv6_router_flags="" # Flags to IPv6 routing daemon. #ipv6_router_flags="-l" # Example for route6d with only IPv6 site local # addrs. #ipv6_router_flags="-q" # If you want to run a routing daemon on an end # node, you should stop advertisement. #ipv6_network_interfaces="ed0 ep0" # Examples for router # or static configuration for end node. # Choose correct prefix value. #ipv6_prefix_ed0="fec0:0000:0000:0001 fec0:0000:0000:0002" # Examples for rtr. #ipv6_prefix_ep0="fec0:0000:0000:0003 fec0:0000:0000:0004" # Examples for rtr. #ipv6_ifconfig_ed0="fec0:0:0:5::1 prefixlen 64" # Sample manual assign entry #ipv6_ifconfig_ed0_alias0="fec0:0:0:5::2 prefixlen 64" # Sample alias entry. ipv6_default_interface="NO" # Default output interface for scoped addrs. # Now this works only for IPv6 link local # multicast addrs. rtsol_flags="" # Flags to IPv6 router solicitation. rtadvd_enable="NO" # Set to YES to enable an IPv6 router # advertisement daemon. If set to YES, # this router becomes a possible candidate # IPv6 default router for local subnets. rtadvd_interfaces="" # Interfaces rtadvd sends RA packets. mroute6d_enable="NO" # Do IPv6 multicast routing. mroute6d_program="/usr/local/sbin/pim6dd" # Name of IPv6 multicast # routing daemon. You need to # install it from package or # port. mroute6d_flags="" # Flags to IPv6 multicast routing daemon. stf_interface_ipv4addr="" # Local IPv4 addr for 6to4 IPv6 over IPv4 # tunneling interface. Specify this entry # to enable 6to4 interface. stf_interface_ipv4plen="0" # Prefix length for 6to4 IPv4 addr, # to limit peer addr range. Effective value # is 0-31. stf_interface_ipv6_ifid="0:0:0:1" # IPv6 interface id for stf0. # If you like, you can set "AUTO" for this. stf_interface_ipv6_slaid="0000" # IPv6 Site Level Aggregator for stf0 ipv6_faith_prefix="NO" # Set faith prefix to enable a FAITH # IPv6-to-IPv4 TCP translator. You also need # faithd(8) setup. ipv6_ipv4mapping="NO" # Set to "YES" to enable IPv4 mapped IPv6 addr # communication. (like ::ffff:a.b.c.d) ipv6_firewall_enable="NO" # Set to YES to enable IPv6 firewall # functionality ipv6_firewall_script="/etc/rc.firewall6" # Which script to run to set up the IPv6 firewall ipv6_firewall_type="UNKNOWN" # IPv6 Firewall type (see /etc/rc.firewall6) ipv6_firewall_quiet="NO" # Set to YES to suppress rule display ipv6_firewall_logging="NO" # Set to YES to enable events logging ipv6_firewall_flags="" # Flags passed to ip6fw when type is a file ipv6_ipfilter_rules="/etc/ipf6.rules" # rules definition file for ipfilter, # see /usr/src/contrib/ipfilter/rules # for examples ip6addrctl_enable="NO" # Set to YES to enable default address selection ip6addrctl_verbose="NO" # Set to YES to enable verbose configuration messages ############################################################## ### System console options ################################# ############################################################## keyboard="" # keyboard device to use (default /dev/kbd0). keymap="NO" # keymap in /usr/share/syscons/keymaps/* (or NO). keyrate="NO" # keyboard rate to: slow, normal, fast (or NO). keybell="NO" # See kbdcontrol(1) for options. Use "off" to disable. keychange="NO" # function keys default values (or NO). cursor="NO" # cursor type {normal|blink|destructive} (or NO). scrnmap="NO" # screen map in /usr/share/syscons/scrnmaps/* (or NO). font8x16="NO" # font 8x16 from /usr/share/syscons/fonts/* (or NO). font8x14="NO" # font 8x14 from /usr/share/syscons/fonts/* (or NO). font8x8="NO" # font 8x8 from /usr/share/syscons/fonts/* (or NO). blanktime="300" # blank time (in seconds) or "NO" to turn it off. saver="NO" # screen saver: Uses /boot/kernel/${saver}_saver.ko moused_nondefault_enable="YES" # Treat non-default mice as enabled unless # specifically overriden in rc.conf(5). moused_enable="NO" # Run the mouse daemon. moused_type="auto" # See man page for rc.conf(5) for available settings. moused_port="/dev/psm0" # Set to your mouse port. moused_flags="" # Any additional flags to moused. mousechar_start="NO" # if 0xd0-0xd3 default range is occupied in your # language code table, specify alternative range # start like mousechar_start=3, see vidcontrol(1) allscreens_flags="" # Set this vidcontrol mode for all virtual screens allscreens_kbdflags="" # Set this kbdcontrol mode for all virtual screens ############################################################## ### pcvt console driver options ############################ ############################################################## pcvt_verbose="NO" # set to YES to enable verbose configuration messages pcvt_keymap="NO" # keyboard map in /usr/share/misc/keycap.pcvt (or NO). pcvt_keydel="NO" # key repeat delay, 0-3 (250,500,750,1000 msec) (or NO). pcvt_keyrate="NO" # keyboard repetition rate 31-0 (2-30 char/sec) (or NO). pcvt_keyrepeat="NO" # keyboard repeat ON or OFF (or NO). pcvt_force24="NO" # force a 24 line display (when 25 possible) (or NO). pcvt_hpext="NO" # use HP extensions (function keys labels) (or NO). pcvt_lines="NO" # lines (25, 28, 40, 50 or NO). pcvt_blanktime="NO" # blank time (in seconds) (or NO). pcvt_cursorh="NO" # cursor top scanline (topmost line is 0) (or NO). pcvt_cursorl="NO" # cursor low scanline (bottom line is 16) (or NO). pcvt_monohigh="NO" # set intensity to high on monochrome monitors (or NO). ############################################################## ### Mail Transfer Agent (MTA) options ###################### ############################################################## mta_start_script="/etc/rc.sendmail" # Script to start your chosen MTA, called by /etc/rc. # Settings for /etc/rc.sendmail and /etc/rc.d/sendmail: sendmail_enable="NO" # Run the sendmail inbound daemon (YES/NO). sendmail_pidfile="/var/run/sendmail.pid" # sendmail pid file sendmail_procname="/usr/sbin/sendmail" # sendmail process name sendmail_flags="-L sm-mta -bd -q30m" # Flags to sendmail (as a server) sendmail_submit_enable="YES" # Start a localhost-only MTA for mail submission sendmail_submit_flags="-L sm-mta -bd -q30m -ODaemonPortOptions=Addr=localhost" # Flags for localhost-only MTA sendmail_outbound_enable="YES" # Dequeue stuck mail (YES/NO). sendmail_outbound_flags="-L sm-queue -q30m" # Flags to sendmail (outbound only) sendmail_msp_queue_enable="YES" # Dequeue stuck clientmqueue mail (YES/NO). sendmail_msp_queue_flags="-L sm-msp-queue -Ac -q30m" # Flags for sendmail_msp_queue daemon. ############################################################## ### Miscellaneous administrative options ################### ############################################################## auditd_enable="NO" # Run the audit daemon. auditd_flags="" # Which options to pass to the audit daemon. +cached_enable="NO" # Run the nsswitch caching daemon. cron_enable="YES" # Run the periodic job daemon. cron_program="/usr/sbin/cron" # Which cron executable to run (if enabled). cron_dst="YES" # Handle DST transitions intelligently (YES/NO) cron_flags="" # Which options to pass to the cron daemon. lpd_enable="NO" # Run the line printer daemon. lpd_program="/usr/sbin/lpd" # path to lpd, if you want a different one. lpd_flags="" # Flags to lpd (if enabled). chkprintcap_enable="NO" # Run chkprintcap(8) before running lpd. chkprintcap_flags="-d" # Create missing directories by default. dumpdev="AUTO" # Device to crashdump to (device name, AUTO, or NO). dumpdir="/var/crash" # Directory where crash dumps are to be stored savecore_flags="" # Used if dumpdev is enabled above, and present. enable_quotas="NO" # turn on quotas on startup (or NO). check_quotas="YES" # Check quotas on startup (or NO). accounting_enable="NO" # Turn on process accounting (or NO). ibcs2_enable="NO" # Ibcs2 (SCO) emulation loaded at startup (or NO). ibcs2_loaders="coff" # List of additional Ibcs2 loaders (or NO). # Emulation/compatibility services provided by /etc/rc.d/abi sysvipc_enable="NO" # Load System V IPC primitives at startup (or NO). linux_enable="NO" # Linux binary compatibility loaded at startup (or NO). svr4_enable="NO" # SysVR4 emulation loaded at startup (or NO). osf1_enable="NO" # Alpha OSF/1 emulation loaded at startup (or NO). clear_tmp_enable="NO" # Clear /tmp at startup. clear_tmp_X="YES" # Clear and recreate X11-related directories in /tmp ldconfig_insecure="NO" # Set to YES to disable ldconfig security checks ldconfig_paths="/usr/lib/compat /usr/X11R6/lib /usr/local/lib /usr/local/lib/compat/pkg" # shared library search paths ldconfig32_paths="/usr/lib32" # 32-bit compatibility shared library search paths ldconfig_paths_aout="/usr/lib/compat/aout /usr/X11R6/lib/aout /usr/local/lib/aout" # a.out shared library search paths ldconfig_local_dirs="/usr/local/libdata/ldconfig /usr/X11R6/libdata/ldconfig" # Local directories with ldconfig configuration files. ldconfig_local32_dirs="/usr/local/libdata/ldconfig32 /usr/X11R6/libdata/ldconfig32" # Local directories with 32-bit compatibility ldconfig # configuration files. kern_securelevel_enable="NO" # kernel security level (see init(8)), kern_securelevel="-1" # range: -1..3 ; `-1' is the most insecure # Note that setting securelevel to 0 will result # in the system booting with securelevel set to 1, as # init(8) will raise the level when rc(8) completes. update_motd="YES" # update version info in /etc/motd (or NO) unaligned_print="YES" # print unaligned access warnings on the alpha (or NO). entropy_file="/entropy" # Set to NO to disable caching entropy through reboots. # /var/db/entropy-file is preferred if / is not avail. entropy_dir="/var/db/entropy" # Set to NO to disable caching entropy via cron. entropy_save_sz="2048" # Size of the entropy cache files. entropy_save_num="8" # Number of entropy cache files to save. harvest_interrupt="YES" # Entropy device harvests interrupt randomness harvest_ethernet="YES" # Entropy device harvests ethernet randomness harvest_p_to_p="YES" # Entropy device harvests point-to-point randomness dmesg_enable="YES" # Save dmesg(8) to /var/run/dmesg.boot watchdogd_enable="NO" # Start the software watchdog daemon devfs_rulesets="/etc/defaults/devfs.rules /etc/devfs.rules" # Files containing # devfs(8) rules. devfs_system_ruleset="" # The name of a ruleset to apply to /dev devfs_set_rulesets="" # A list of /mount/dev=ruleset_name settings to # apply (must be mounted already, i.e. fstab(5)) performance_cx_lowest="LOW" # Online CPU idle state performance_cpu_freq="NONE" # Online CPU frequency economy_cx_lowest="LOW" # Offline CPU idle state economy_cpu_freq="NONE" # Offline CPU frequency virecover_enable="YES" # Perform housekeeping for the vi(1) editor ugidfw_enable="NO" # Load mac_bsdextended(4) rules on boot bsdextended_script="/etc/rc.bsdextended" # Default mac_bsdextended(4) # ruleset file. newsyslog_enable="YES" # Run newsyslog at startup. newsyslog_flags="-CN" # Newsyslog flags to create marked files ############################################################## ### Jail Configuration ####################################### ############################################################## jail_enable="NO" # Set to NO to disable starting of any jails jail_list="" # Space separated list of names of jails jail_set_hostname_allow="YES" # Allow root user in a jail to change its hostname jail_socket_unixiproute_only="YES" # Route only TCP/IP within a jail jail_sysvipc_allow="NO" # Allow SystemV IPC use from within a jail # # To use rc's built-in jail infrastructure create entries for # each jail, specified in jail_list, with the following variables. # NOTE: replace 'example' with the jail's name. # #jail_example_rootdir="/usr/jail/default" # Jail's root directory #jail_example_hostname="default.domain.com" # Jail's hostname #jail_example_ip="192.168.0.10" # Jail's IP number #jail_example_exec_start="/bin/sh /etc/rc" # command to execute in jail for starting #jail_example_exec_stop="/bin/sh /etc/rc.shutdown" # command to execute in jail for stopping #jail_example_devfs_enable="NO" # mount devfs in the jail #jail_example_fdescfs_enable="NO" # mount fdescfs in the jail #jail_example_procfs_enable="NO" # mount procfs in jail #jail_example_mount_enable="NO" # mount/umount jail's fs #jail_example_devfs_ruleset="ruleset_name" # devfs ruleset to apply to jail #jail_example_fstab="" # fstab(5) for mount/umount #jail_example_flags="-l -U root" # flags for jail(8) ############################################################## ### Define source_rc_confs, the mechanism used by /etc/rc.* ## ### scripts to source rc_conf_files overrides safely. ## ############################################################## if [ -z "${source_rc_confs_defined}" ]; then source_rc_confs_defined=yes source_rc_confs () { local i sourced_files for i in ${rc_conf_files}; do case ${sourced_files} in *:$i:*) ;; *) sourced_files="${sourced_files}:$i:" if [ -r $i ]; then . $i fi ;; esac done } fi diff --git a/etc/nscd.conf b/etc/nscd.conf new file mode 100644 index 000000000000..19a8b5b95452 --- /dev/null +++ b/etc/nscd.conf @@ -0,0 +1,12 @@ +# +# Default caching daemon configuration file +# $FreeBSD$ +# + +enable-cache passwd yes +enable-cache group yes +enable-cache hosts yes +enable-cache services yes +enable-cache protocols yes +enable-cache rpc yes +enable-cache networks yes diff --git a/etc/rc.d/Makefile b/etc/rc.d/Makefile index a955b767b58d..45ffa4a8d539 100755 --- a/etc/rc.d/Makefile +++ b/etc/rc.d/Makefile @@ -1,52 +1,56 @@ # $NetBSD: Makefile,v 1.16 2001/01/14 15:37:22 minoura Exp $ # $FreeBSD$ .include FILES= DAEMON LOGIN NETWORKING SERVERS \ abi accounting addswap adjkerntz amd \ apm apmd archdep atm1 atm2 atm3 auditd \ bgfsck bluetooth bootparams bsnmpd \ ccd cleanvar cleartmp cron \ devd devfs dhclient \ dmesg dumpon \ early.sh encswap \ fsck ftpd \ gbde geli geli2 \ hcsecd \ hostapd hostname \ inetd initrandom \ ip6addrctl ip6fw ipfilter ipfs ipfw ipmon \ ipnat ipsec ipxrouted isdnd \ jail \ kadmind kerberos kernel keyserv kldxref kpasswdd \ ldconfig local localpkg lpd \ mixer motd mountcritlocal mountcritremote \ mountd moused mroute6d mrouted msgs \ named natd netif netoptions \ network_ipv6 newsyslog nfsclient nfsd \ nfslocking nfsserver nisdomain nsswitch ntpd ntpdate \ othermta \ pcvt pf pflog pfsync \ powerd power_profile ppp pppoed pwcheck \ quota \ ramdisk ramdisk-own random rarpd resolv root \ route6d routed routing rpcbind rtadvd rwho \ savecore sdpd securelevel sendmail \ serial sppp swap1 \ syscons sysctl syslogd \ timed tmp \ ugidfw \ var virecover \ watchdogd wpa_supplicant \ ypbind yppasswdd ypserv \ ypset ypupdated ypxfrd .if ${MK_OPENSSH} != "no" FILES+= sshd .endif +.if ${MK_NS_CACHING} != "no" +FILES+= cached +.endif + FILESDIR= /etc/rc.d FILESMODE= ${BINMODE} .include diff --git a/etc/rc.d/cached b/etc/rc.d/cached new file mode 100644 index 000000000000..0376b58eed21 --- /dev/null +++ b/etc/rc.d/cached @@ -0,0 +1,30 @@ +#!/bin/sh +# +# $FreeBSD$ +# + +# PROVIDE: cached +# REQUIRE: DAEMON +# BEFORE: LOGIN + +# +# Add the following lines to /etc/rc.conf to enable cached: +# +# cached_enable="YES" +# +# See cached(8) for flags +# + +. /etc/rc.subr + +name=cached +rcvar=`set_rcvar` + +command=/usr/sbin/cached + +cached_enable=${cached_enable:-"NO"} +cached_pidfile=${cached_pidfile:-"/var/run/cached.pid"} +cached_flags=${cached_flags:-""} + +load_rc_config $name +run_rc_command "$1" diff --git a/etc/rc.d/nscd b/etc/rc.d/nscd new file mode 100644 index 000000000000..0376b58eed21 --- /dev/null +++ b/etc/rc.d/nscd @@ -0,0 +1,30 @@ +#!/bin/sh +# +# $FreeBSD$ +# + +# PROVIDE: cached +# REQUIRE: DAEMON +# BEFORE: LOGIN + +# +# Add the following lines to /etc/rc.conf to enable cached: +# +# cached_enable="YES" +# +# See cached(8) for flags +# + +. /etc/rc.subr + +name=cached +rcvar=`set_rcvar` + +command=/usr/sbin/cached + +cached_enable=${cached_enable:-"NO"} +cached_pidfile=${cached_pidfile:-"/var/run/cached.pid"} +cached_flags=${cached_flags:-""} + +load_rc_config $name +run_rc_command "$1" diff --git a/include/nsswitch.h b/include/nsswitch.h index 22e482c3a853..5e7ee0c29ba7 100644 --- a/include/nsswitch.h +++ b/include/nsswitch.h @@ -1,246 +1,248 @@ /* $NetBSD: nsswitch.h,v 1.6 1999/01/26 01:04:07 lukem Exp $ */ /* $FreeBSD$ */ /*- * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Luke Mewburn. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 _NSSWITCH_H #define _NSSWITCH_H 1 #include #include #define NSS_MODULE_INTERFACE_VERSION 1 #ifndef _PATH_NS_CONF #define _PATH_NS_CONF "/etc/nsswitch.conf" #endif /* NSS source actions */ #define NS_ACTION_CONTINUE 0 /* try the next source */ #define NS_ACTION_RETURN 1 /* look no further */ #define NS_SUCCESS (1<<0) /* entry was found */ #define NS_UNAVAIL (1<<1) /* source not responding, or corrupt */ #define NS_NOTFOUND (1<<2) /* source responded 'no such entry' */ #define NS_TRYAGAIN (1<<3) /* source busy, may respond to retry */ #define NS_RETURN (1<<4) /* stop search, e.g. for ERANGE */ #define NS_TERMINATE (NS_SUCCESS|NS_RETURN) /* flags that end search */ #define NS_STATUSMASK 0x000000ff /* bitmask to get the status flags */ /* * currently implemented sources */ #define NSSRC_FILES "files" /* local files */ #define NSSRC_DNS "dns" /* DNS; IN for hosts, HS for others */ #define NSSRC_NIS "nis" /* YP/NIS */ #define NSSRC_COMPAT "compat" /* passwd,group in YP compat mode */ +#define NSSRC_CACHE "cache" /* cache daemon */ /* * currently implemented databases */ #define NSDB_HOSTS "hosts" #define NSDB_GROUP "group" #define NSDB_GROUP_COMPAT "group_compat" #define NSDB_NETGROUP "netgroup" #define NSDB_NETWORKS "networks" #define NSDB_PASSWD "passwd" #define NSDB_PASSWD_COMPAT "passwd_compat" #define NSDB_SHELLS "shells" +#define NSDB_SERVICES "services" +#define NSDB_SERVICES_COMPAT "services_compat" +#define NSDB_SSH_HOSTKEYS "ssh_hostkeys" +#define NSDB_PROTOCOLS "protocols" +#define NSDB_RPC "rpc" /* * suggested databases to implement */ #define NSDB_ALIASES "aliases" #define NSDB_AUTH "auth" #define NSDB_AUTOMOUNT "automount" #define NSDB_BOOTPARAMS "bootparams" #define NSDB_ETHERS "ethers" #define NSDB_EXPORTS "exports" #define NSDB_NETMASKS "netmasks" #define NSDB_PHONES "phones" #define NSDB_PRINTCAP "printcap" -#define NSDB_PROTOCOLS "protocols" #define NSDB_REMOTE "remote" -#define NSDB_RPC "rpc" #define NSDB_SENDMAILVARS "sendmailvars" -#define NSDB_SERVICES "services" #define NSDB_TERMCAP "termcap" #define NSDB_TTYS "ttys" /* * ns_dtab `method' function signature. */ typedef int (*nss_method)(void *_retval, void *_mdata, va_list _ap); /* * Macro for generating method prototypes. */ #define NSS_METHOD_PROTOTYPE(method) \ int method(void *, void *, va_list) /* * ns_dtab - `nsswitch dispatch table' * Contains an entry for each source and the appropriate function to * call. ns_dtabs are used in the nsdispatch() API in order to allow * the application to override built-in actions. */ typedef struct _ns_dtab { const char *src; /* Source this entry implements */ nss_method method; /* Method to be called */ void *mdata; /* Data passed to method */ } ns_dtab; /* * macros to help build an ns_dtab[] */ #define NS_FILES_CB(F,C) { NSSRC_FILES, F, C }, #define NS_COMPAT_CB(F,C) { NSSRC_COMPAT, F, C }, #ifdef HESIOD # define NS_DNS_CB(F,C) { NSSRC_DNS, F, C }, #else # define NS_DNS_CB(F,C) #endif #ifdef YP # define NS_NIS_CB(F,C) { NSSRC_NIS, F, C }, #else # define NS_NIS_CB(F,C) #endif /* * ns_src - `nsswitch source' * used by the nsparser routines to store a mapping between a source * and its dispatch control flags for a given database. */ typedef struct _ns_src { const char *name; u_int32_t flags; } ns_src; /* * default sourcelist (if nsswitch.conf is missing, corrupt, * or the requested database doesn't have an entry. */ extern const ns_src __nsdefaultsrc[]; /* * ns_mtab - NSS method table * An NSS module provides a mapping from (database name, method name) * tuples to the nss_method and associated data. */ typedef struct _ns_mtab { const char *database; const char *name; nss_method method; void *mdata; } ns_mtab; /* * NSS module de-registration, called at module unload. */ typedef void (*nss_module_unregister_fn)(ns_mtab *, unsigned int); /* * NSS module registration, called at module load. */ typedef ns_mtab *(*nss_module_register_fn)(const char *, unsigned int *, nss_module_unregister_fn *); /* * Many NSS interfaces follow the getXXnam, getXXid, getXXent pattern. * Developers are encouraged to use nss_lookup_type where approriate. */ enum nss_lookup_type { nss_lt_name = 1, nss_lt_id = 2, nss_lt_all = 3 }; #ifdef _NS_PRIVATE - /* * private data structures for back-end nsswitch implementation */ /* * ns_dbt - `nsswitch database thang' * for each database in /etc/nsswitch.conf there is a ns_dbt, with its * name and a list of ns_src's containing the source information. */ typedef struct _ns_dbt { const char *name; /* name of database */ ns_src *srclist; /* list of sources */ int srclistsize; /* size of srclist */ } ns_dbt; /* * ns_mod - NSS module */ typedef struct _ns_mod { char *name; /* module name */ void *handle; /* handle from dlopen */ ns_mtab *mtab; /* method table */ unsigned int mtabsize; /* count of entries in method table */ nss_module_unregister_fn unregister; /* called to unload module */ } ns_mod; #endif /* _NS_PRIVATE */ #include __BEGIN_DECLS extern int nsdispatch(void *, const ns_dtab [], const char *, const char *, const ns_src [], ...); #ifdef _NS_PRIVATE extern void _nsdbtaddsrc(ns_dbt *, const ns_src *); extern void _nsdbtput(const ns_dbt *); extern void _nsyyerror(const char *); extern int _nsyylex(void); extern int _nsyyparse(void); extern int _nsyylineno; #ifdef _NSS_DEBUG extern void _nsdbtdump(const ns_dbt *); #endif #endif /* _NS_PRIVATE */ __END_DECLS #endif /* !_NSSWITCH_H */ diff --git a/include/rpc/rpcent.h b/include/rpc/rpcent.h index 4388d8b9ff88..64858b10fa06 100644 --- a/include/rpc/rpcent.h +++ b/include/rpc/rpcent.h @@ -1,69 +1,64 @@ /* $NetBSD: rpcent.h,v 1.1 2000/06/02 22:57:56 fvdl Exp $ */ /* $FreeBSD$ */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ /* * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. */ /* * rpcent.h, * For converting rpc program numbers to names etc. * */ #ifndef _RPC_RPCENT_H #define _RPC_RPCENT_H /* #pragma ident "@(#)rpcent.h 1.13 94/04/25 SMI" */ /* @(#)rpcent.h 1.1 88/12/06 SMI */ struct rpcent { char *r_name; /* name of server for this rpc program */ char **r_aliases; /* alias list */ int r_number; /* rpc program number */ }; __BEGIN_DECLS -extern struct rpcent *getrpcbyname_r(const char *, struct rpcent *, - char *, int); -extern struct rpcent *getrpcbynumber_r(int, struct rpcent *, char *, int); -extern struct rpcent *getrpcent_r(struct rpcent *, char *, int); - -/* Old interfaces that return a pointer to a static area; MT-unsafe */ +/* These interfaces are currently implemented through nsswitch and MT-safe */ extern struct rpcent *getrpcbyname(char *); extern struct rpcent *getrpcbynumber(int); extern struct rpcent *getrpcent(void); extern void setrpcent(int); extern void endrpcent(void); __END_DECLS #endif /* !_RPC_CENT_H */ diff --git a/lib/libc/Makefile b/lib/libc/Makefile index f3a7c03dde1c..496598a07064 100644 --- a/lib/libc/Makefile +++ b/lib/libc/Makefile @@ -1,116 +1,119 @@ # @(#)Makefile 8.2 (Berkeley) 2/3/94 # $FreeBSD$ SHLIBDIR?= /lib .include # All library objects contain FreeBSD revision strings by default; they may be # excluded as a space-saving measure. To produce a library that does # not contain these strings, add -DSTRIP_FBSDID (see ) to CFLAGS # below. Note: there are no IDs for syscall stubs whose sources are generated. # To include legacy CSRG sccsid strings, add -DLIBC_SCCS and -DSYSLIBC_SCCS # to CFLAGS below. -DSYSLIBC_SCCS affects just the system call stubs. LIB=c # If you bump SHLIB_MAJOR, remove the kluge from gen/gethostname.c. # If you bump SHLIB_MAJOR, see standards/55112. SHLIB_MAJOR= 6 WARNS?= 2 CFLAGS+=-I${.CURDIR}/include -I${.CURDIR}/../../include CFLAGS+=-I${.CURDIR}/${MACHINE_ARCH} CLEANFILES+=tags INSTALL_PIC_ARCHIVE= PRECIOUSLIB= # Define (empty) variables so that make doesn't give substitution # errors if the included makefiles don't change these: MDSRCS= MISRCS= MDASM= MIASM= NOASM= .include "${.CURDIR}/${MACHINE_ARCH}/Makefile.inc" .include "${.CURDIR}/db/Makefile.inc" .include "${.CURDIR}/compat-43/Makefile.inc" .include "${.CURDIR}/gdtoa/Makefile.inc" .include "${.CURDIR}/gen/Makefile.inc" .include "${.CURDIR}/gmon/Makefile.inc" .include "${.CURDIR}/inet/Makefile.inc" .include "${.CURDIR}/isc/Makefile.inc" .include "${.CURDIR}/locale/Makefile.inc" .include "${.CURDIR}/nameser/Makefile.inc" .include "${.CURDIR}/net/Makefile.inc" .include "${.CURDIR}/nls/Makefile.inc" .include "${.CURDIR}/posix1e/Makefile.inc" .if ${MACHINE_ARCH} != "alpha" && \ ${MACHINE_ARCH} != "amd64" && \ ${MACHINE_ARCH} != "ia64" && \ ${MACHINE_ARCH} != "sparc64" .include "${.CURDIR}/quad/Makefile.inc" .endif .include "${.CURDIR}/regex/Makefile.inc" .include "${.CURDIR}/resolv/Makefile.inc" .include "${.CURDIR}/stdio/Makefile.inc" .include "${.CURDIR}/stdlib/Makefile.inc" .include "${.CURDIR}/stdtime/Makefile.inc" .include "${.CURDIR}/string/Makefile.inc" .include "${.CURDIR}/sys/Makefile.inc" .include "${.CURDIR}/rpc/Makefile.inc" .include "${.CURDIR}/uuid/Makefile.inc" .include "${.CURDIR}/xdr/Makefile.inc" .if ${MACHINE_ARCH} == "arm" .include "${.CURDIR}/softfloat/Makefile.inc" .endif .if ${MK_NIS} != "no" CFLAGS+= -DYP .include "${.CURDIR}/yp/Makefile.inc" .endif .if ${MK_HESIOD} != "no" CFLAGS+= -DHESIOD .endif .if ${MK_FP_LIBC} == "no" CFLAGS+= -DNO_FLOATING_POINT .endif +.if ${MK_NS_CACHING} != "no" +CFLAGS+= -DNS_CACHING +.endif .if defined(SYMVER_ENABLED) VERSION_DEF=${.CURDIR}/Versions.def SYMBOL_MAPS=${SYM_MAPS} CFLAGS+= -DSYMBOL_VERSIONING .endif # If there are no machine dependent sources, append all the # machine-independent sources: .if empty(MDSRCS) SRCS+= ${MISRCS} .else # Append machine-dependent sources, then append machine-independent sources # for which there is no machine-dependent variant. SRCS+= ${MDSRCS} .for _src in ${MISRCS} .if ${MDSRCS:R:M${_src:R}} == "" SRCS+= ${_src} .endif .endfor .endif KQSRCS= adddi3.c anddi3.c ashldi3.c ashrdi3.c cmpdi2.c divdi3.c iordi3.c \ lshldi3.c lshrdi3.c moddi3.c muldi3.c negdi2.c notdi2.c qdivrem.c \ subdi3.c ucmpdi2.c udivdi3.c umoddi3.c xordi3.c KSRCS= bcmp.c ffs.c ffsl.c fls.c flsl.c index.c mcount.c rindex.c \ strcat.c strcmp.c strcpy.c strlen.c strncpy.c libkern: libkern.gen libkern.${MACHINE_ARCH} libkern.gen: ${KQSRCS} ${KSRCS} cp -p ${.CURDIR}/quad/quad.h ${.ALLSRC} ${DESTDIR}/sys/libkern libkern.${MACHINE_ARCH}:: ${KMSRCS} .if defined(KMSRCS) && !empty(KMSRCS) cp -p ${.ALLSRC} ${DESTDIR}/sys/libkern/${MACHINE_ARCH} .endif .include # Disable warnings in contributed sources. CWARNFLAGS:= ${.IMPSRC:Ngdtoa_*.c:C/^.+$/${CWARNFLAGS}/} diff --git a/lib/libc/gen/getgrent.c b/lib/libc/gen/getgrent.c index c9b7474e78d7..d9ea5bdbeb1b 100644 --- a/lib/libc/gen/getgrent.c +++ b/lib/libc/gen/getgrent.c @@ -1,1152 +1,1428 @@ /*- * Copyright (c) 2003 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by * Jacques A. Vidrine, Safeport Network Services, and Network * Associates Laboratories, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #ifdef YP #include #include #include #endif #include #include #ifdef HESIOD #include #endif #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "libc_private.h" #include "nss_tls.h" - +#ifdef NS_CACHING +#include "nscache.h" +#endif enum constants { GRP_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ GRP_STORAGE_MAX = 1 << 20, /* 1 MByte */ SETGRENT = 1, ENDGRENT = 2, HESIOD_NAME_MAX = 256, }; static const ns_src defaultsrc[] = { { NSSRC_COMPAT, NS_SUCCESS }, { NULL, 0 } }; int __gr_match_entry(const char *, size_t, enum nss_lookup_type, const char *, gid_t); int __gr_parse_entry(char *, size_t, struct group *, char *, size_t, int *); static int is_comment_line(const char *, size_t); union key { const char *name; gid_t gid; }; static struct group *getgr(int (*)(union key, struct group *, char *, size_t, struct group **), union key); static int wrap_getgrnam_r(union key, struct group *, char *, size_t, struct group **); static int wrap_getgrgid_r(union key, struct group *, char *, size_t, struct group **); static int wrap_getgrent_r(union key, struct group *, char *, size_t, struct group **); struct files_state { FILE *fp; int stayopen; }; static void files_endstate(void *); NSS_TLS_HANDLING(files); static int files_setgrent(void *, void *, va_list); static int files_group(void *, void *, va_list); #ifdef HESIOD struct dns_state { long counter; }; static void dns_endstate(void *); NSS_TLS_HANDLING(dns); static int dns_setgrent(void *, void *, va_list); static int dns_group(void *, void *, va_list); #endif #ifdef YP struct nis_state { char domain[MAXHOSTNAMELEN]; int done; char *key; int keylen; }; static void nis_endstate(void *); NSS_TLS_HANDLING(nis); static int nis_setgrent(void *, void *, va_list); static int nis_group(void *, void *, va_list); #endif struct compat_state { FILE *fp; int stayopen; char *name; enum _compat { COMPAT_MODE_OFF = 0, COMPAT_MODE_ALL, COMPAT_MODE_NAME } compat; }; static void compat_endstate(void *); NSS_TLS_HANDLING(compat); static int compat_setgrent(void *, void *, va_list); static int compat_group(void *, void *, va_list); +#ifdef NS_CACHING +static int grp_id_func(char *, size_t *, va_list, void *); +static int grp_marshal_func(char *, size_t *, void *, va_list, void *); +static int grp_unmarshal_func(char *, size_t, void *, va_list, void *); + +static int +grp_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) +{ + char *name; + gid_t gid; + + size_t desired_size, size; + int res = NS_UNAVAIL; + enum nss_lookup_type lookup_type; + + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + name = va_arg(ap, char *); + size = strlen(name); + desired_size = sizeof(enum nss_lookup_type) + size + 1; + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + gid = va_arg(ap, gid_t); + desired_size = sizeof(enum nss_lookup_type) + sizeof(gid_t); + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), &gid, + sizeof(gid_t)); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + +static int +grp_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *name; + gid_t gid; + struct group *grp; + char *orig_buf; + size_t orig_buf_size; + + struct group new_grp; + size_t desired_size, size, mem_size; + char *p, **mem; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + gid = va_arg(ap, gid_t); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + grp = va_arg(ap, struct group *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + + desired_size = _ALIGNBYTES + sizeof(struct group) + sizeof(char *); + + if (grp->gr_name != NULL) + desired_size += strlen(grp->gr_name) + 1; + if (grp->gr_passwd != NULL) + desired_size += strlen(grp->gr_passwd) + 1; + + if (grp->gr_mem != NULL) { + mem_size = 0; + for (mem = grp->gr_mem; *mem; ++mem) { + desired_size += strlen(*mem) + 1; + ++mem_size; + } + + desired_size += _ALIGNBYTES + (mem_size + 1) * sizeof(char *); + } + + if (desired_size > *buffer_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_grp, grp, sizeof(struct group)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct group) + sizeof(char *); + memcpy(buffer + sizeof(struct group), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_grp.gr_name != NULL) { + size = strlen(new_grp.gr_name); + memcpy(p, new_grp.gr_name, size); + new_grp.gr_name = p; + p += size + 1; + } + + if (new_grp.gr_passwd != NULL) { + size = strlen(new_grp.gr_passwd); + memcpy(p, new_grp.gr_passwd, size); + new_grp.gr_passwd = p; + p += size + 1; + } + + if (new_grp.gr_mem != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_grp.gr_mem, sizeof(char *) * mem_size); + new_grp.gr_mem = (char **)p; + p += sizeof(char *) * (mem_size + 1); + + for (mem = new_grp.gr_mem; *mem; ++mem) { + size = strlen(*mem); + memcpy(p, *mem, size); + *mem = p; + p += size + 1; + } + } + + memcpy(buffer, &new_grp, sizeof(struct group)); + return (NS_SUCCESS); +} + +static int +grp_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *name; + gid_t gid; + struct group *grp; + char *orig_buf; + size_t orig_buf_size; + int *ret_errno; + + char *p; + char **mem; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + gid = va_arg(ap, gid_t); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + grp = va_arg(ap, struct group *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + ret_errno = va_arg(ap, int *); + + if (orig_buf_size < + buffer_size - sizeof(struct group) - sizeof(char *)) { + *ret_errno = ERANGE; + return (NS_RETURN); + } + + memcpy(grp, buffer, sizeof(struct group)); + memcpy(&p, buffer + sizeof(struct group), sizeof(char *)); + + orig_buf = (char *)_ALIGN(orig_buf); + memcpy(orig_buf, buffer + sizeof(struct group) + sizeof(char *) + + _ALIGN(p) - (size_t)p, + buffer_size - sizeof(struct group) - sizeof(char *) - + _ALIGN(p) + (size_t)p); + p = (char *)_ALIGN(p); + + NS_APPLY_OFFSET(grp->gr_name, orig_buf, p, char *); + NS_APPLY_OFFSET(grp->gr_passwd, orig_buf, p, char *); + if (grp->gr_mem != NULL) { + NS_APPLY_OFFSET(grp->gr_mem, orig_buf, p, char **); + + for (mem = grp->gr_mem; *mem; ++mem) + NS_APPLY_OFFSET(*mem, orig_buf, p, char *); + } + + if (retval != NULL) + *((struct group **)retval) = grp; + + return (NS_SUCCESS); +} + +NSS_MP_CACHE_HANDLING(group); +#endif /* NS_CACHING */ + /* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */ int setgrent(void) { +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + group, (void *)nss_lt_all, + NULL, NULL); +#endif + static const ns_dtab dtab[] = { { NSSRC_FILES, files_setgrent, (void *)SETGRENT }, #ifdef HESIOD { NSSRC_DNS, dns_setgrent, (void *)SETGRENT }, #endif #ifdef YP { NSSRC_NIS, nis_setgrent, (void *)SETGRENT }, #endif { NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { NULL, NULL, NULL } }; (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc, 0); return (1); } int setgroupent(int stayopen) { +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + group, (void *)nss_lt_all, + NULL, NULL); +#endif + static const ns_dtab dtab[] = { { NSSRC_FILES, files_setgrent, (void *)SETGRENT }, #ifdef HESIOD { NSSRC_DNS, dns_setgrent, (void *)SETGRENT }, #endif #ifdef YP { NSSRC_NIS, nis_setgrent, (void *)SETGRENT }, #endif { NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { NULL, NULL, NULL } }; (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc, stayopen); return (1); } void endgrent(void) { +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + group, (void *)nss_lt_all, + NULL, NULL); +#endif + static const ns_dtab dtab[] = { { NSSRC_FILES, files_setgrent, (void *)ENDGRENT }, #ifdef HESIOD { NSSRC_DNS, dns_setgrent, (void *)ENDGRENT }, #endif #ifdef YP { NSSRC_NIS, nis_setgrent, (void *)ENDGRENT }, #endif { NSSRC_COMPAT, compat_setgrent, (void *)ENDGRENT }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { NULL, NULL, NULL } }; (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "endgrent", defaultsrc); } int getgrent_r(struct group *grp, char *buffer, size_t bufsize, struct group **result) { +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + group, (void *)nss_lt_all, + grp_marshal_func, grp_unmarshal_func); +#endif + static const ns_dtab dtab[] = { { NSSRC_FILES, files_group, (void *)nss_lt_all }, #ifdef HESIOD { NSSRC_DNS, dns_group, (void *)nss_lt_all }, #endif #ifdef YP { NSSRC_NIS, nis_group, (void *)nss_lt_all }, #endif { NSSRC_COMPAT, compat_group, (void *)nss_lt_all }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { NULL, NULL, NULL } }; int rv, ret_errno; ret_errno = 0; *result = NULL; rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrent_r", defaultsrc, grp, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } int getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize, struct group **result) { +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + group, (void *)nss_lt_name, + grp_id_func, grp_marshal_func, grp_unmarshal_func); +#endif + static const ns_dtab dtab[] = { { NSSRC_FILES, files_group, (void *)nss_lt_name }, #ifdef HESIOD { NSSRC_DNS, dns_group, (void *)nss_lt_name }, #endif #ifdef YP { NSSRC_NIS, nis_group, (void *)nss_lt_name }, #endif { NSSRC_COMPAT, compat_group, (void *)nss_lt_name }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { NULL, NULL, NULL } }; int rv, ret_errno; ret_errno = 0; *result = NULL; rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrnam_r", defaultsrc, name, grp, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } int getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize, struct group **result) { +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + group, (void *)nss_lt_id, + grp_id_func, grp_marshal_func, grp_unmarshal_func); +#endif + static const ns_dtab dtab[] = { { NSSRC_FILES, files_group, (void *)nss_lt_id }, #ifdef HESIOD { NSSRC_DNS, dns_group, (void *)nss_lt_id }, #endif #ifdef YP { NSSRC_NIS, nis_group, (void *)nss_lt_id }, #endif { NSSRC_COMPAT, compat_group, (void *)nss_lt_id }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { NULL, NULL, NULL } }; int rv, ret_errno; ret_errno = 0; *result = NULL; rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrgid_r", defaultsrc, gid, grp, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } static struct group grp; static char *grp_storage; static size_t grp_storage_size; static struct group * getgr(int (*fn)(union key, struct group *, char *, size_t, struct group **), union key key) { int rv; struct group *res; if (grp_storage == NULL) { grp_storage = malloc(GRP_STORAGE_INITIAL); if (grp_storage == NULL) return (NULL); grp_storage_size = GRP_STORAGE_INITIAL; } do { rv = fn(key, &grp, grp_storage, grp_storage_size, &res); if (res == NULL && rv == ERANGE) { free(grp_storage); if ((grp_storage_size << 1) > GRP_STORAGE_MAX) { grp_storage = NULL; errno = ERANGE; return (NULL); } grp_storage_size <<= 1; grp_storage = malloc(grp_storage_size); if (grp_storage == NULL) return (NULL); } } while (res == NULL && rv == ERANGE); if (rv != 0) errno = rv; return (res); } static int wrap_getgrnam_r(union key key, struct group *grp, char *buffer, size_t bufsize, struct group **res) { return (getgrnam_r(key.name, grp, buffer, bufsize, res)); } static int wrap_getgrgid_r(union key key, struct group *grp, char *buffer, size_t bufsize, struct group **res) { return (getgrgid_r(key.gid, grp, buffer, bufsize, res)); } static int wrap_getgrent_r(union key key __unused, struct group *grp, char *buffer, size_t bufsize, struct group **res) { return (getgrent_r(grp, buffer, bufsize, res)); } struct group * getgrnam(const char *name) { union key key; key.name = name; return (getgr(wrap_getgrnam_r, key)); } struct group * getgrgid(gid_t gid) { union key key; key.gid = gid; return (getgr(wrap_getgrgid_r, key)); } struct group * getgrent(void) { union key key; key.gid = 0; /* not used */ return (getgr(wrap_getgrent_r, key)); } static int is_comment_line(const char *s, size_t n) { const char *eom; eom = &s[n]; for (; s < eom; s++) if (*s == '#' || !isspace((unsigned char)*s)) break; return (*s == '#' || s == eom); } /* * files backend */ static void files_endstate(void *p) { if (p == NULL) return; if (((struct files_state *)p)->fp != NULL) fclose(((struct files_state *)p)->fp); free(p); } static int files_setgrent(void *retval, void *mdata, va_list ap) { struct files_state *st; int rv, stayopen; rv = files_getstate(&st); if (rv != 0) return (NS_UNAVAIL); switch ((enum constants)mdata) { case SETGRENT: stayopen = va_arg(ap, int); if (st->fp != NULL) rewind(st->fp); else if (stayopen) st->fp = fopen(_PATH_GROUP, "r"); break; case ENDGRENT: if (st->fp != NULL) { fclose(st->fp); st->fp = NULL; } break; default: break; } return (NS_UNAVAIL); } static int files_group(void *retval, void *mdata, va_list ap) { struct files_state *st; enum nss_lookup_type how; const char *name, *line; struct group *grp; gid_t gid; char *buffer; size_t bufsize, linesize; int rv, stayopen, *errnop; name = NULL; gid = (gid_t)-1; how = (enum nss_lookup_type)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); break; case nss_lt_id: gid = va_arg(ap, gid_t); break; case nss_lt_all: break; default: return (NS_NOTFOUND); } grp = va_arg(ap, struct group *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = files_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (st->fp == NULL && ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) { *errnop = errno; return (NS_UNAVAIL); } if (how == nss_lt_all) stayopen = 1; else { rewind(st->fp); stayopen = st->stayopen; } rv = NS_NOTFOUND; while ((line = fgetln(st->fp, &linesize)) != NULL) { if (line[linesize-1] == '\n') linesize--; rv = __gr_match_entry(line, linesize, how, name, gid); if (rv != NS_SUCCESS) continue; /* We need room at least for the line, a string NUL * terminator, alignment padding, and one (char *) * pointer for the member list terminator. */ if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) { *errnop = ERANGE; rv = NS_RETURN; break; } memcpy(buffer, line, linesize); buffer[linesize] = '\0'; rv = __gr_parse_entry(buffer, linesize, grp, &buffer[linesize + 1], bufsize - linesize - 1, errnop); if (rv & NS_TERMINATE) break; } if (!stayopen && st->fp != NULL) { fclose(st->fp); st->fp = NULL; } if (rv == NS_SUCCESS && retval != NULL) *(struct group **)retval = grp; return (rv); } #ifdef HESIOD /* * dns backend */ static void dns_endstate(void *p) { free(p); } static int dns_setgrent(void *retval, void *cb_data, va_list ap) { struct dns_state *st; int rv; rv = dns_getstate(&st); if (rv != 0) return (NS_UNAVAIL); st->counter = 0; return (NS_UNAVAIL); } static int dns_group(void *retval, void *mdata, va_list ap) { char buf[HESIOD_NAME_MAX]; struct dns_state *st; struct group *grp; const char *name, *label; void *ctx; char *buffer, **hes; size_t bufsize, adjsize, linesize; gid_t gid; enum nss_lookup_type how; int rv, *errnop; ctx = NULL; hes = NULL; name = NULL; gid = (gid_t)-1; how = (enum nss_lookup_type)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); break; case nss_lt_id: gid = va_arg(ap, gid_t); break; case nss_lt_all: break; } grp = va_arg(ap, struct group *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = dns_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (hesiod_init(&ctx) != 0) { *errnop = errno; rv = NS_UNAVAIL; goto fin; } do { rv = NS_NOTFOUND; switch (how) { case nss_lt_name: label = name; break; case nss_lt_id: if (snprintf(buf, sizeof(buf), "%lu", (unsigned long)gid) >= sizeof(buf)) goto fin; label = buf; break; case nss_lt_all: if (st->counter < 0) goto fin; if (snprintf(buf, sizeof(buf), "group-%ld", st->counter++) >= sizeof(buf)) goto fin; label = buf; break; } hes = hesiod_resolve(ctx, label, how == nss_lt_id ? "gid" : "group"); if ((how == nss_lt_id && hes == NULL && (hes = hesiod_resolve(ctx, buf, "group")) == NULL) || hes == NULL) { if (how == nss_lt_all) st->counter = -1; if (errno != ENOENT) *errnop = errno; goto fin; } rv = __gr_match_entry(hes[0], strlen(hes[0]), how, name, gid); if (rv != NS_SUCCESS) { hesiod_free_list(ctx, hes); hes = NULL; continue; } /* We need room at least for the line, a string NUL * terminator, alignment padding, and one (char *) * pointer for the member list terminator. */ adjsize = bufsize - _ALIGNBYTES - sizeof(char *); linesize = strlcpy(buffer, hes[0], adjsize); if (linesize >= adjsize) { *errnop = ERANGE; rv = NS_RETURN; goto fin; } hesiod_free_list(ctx, hes); hes = NULL; rv = __gr_parse_entry(buffer, linesize, grp, &buffer[linesize + 1], bufsize - linesize - 1, errnop); } while (how == nss_lt_all && !(rv & NS_TERMINATE)); fin: if (hes != NULL) hesiod_free_list(ctx, hes); if (ctx != NULL) hesiod_end(ctx); if (rv == NS_SUCCESS && retval != NULL) *(struct group **)retval = grp; return (rv); } #endif /* HESIOD */ #ifdef YP /* * nis backend */ static void nis_endstate(void *p) { if (p == NULL) return; free(((struct nis_state *)p)->key); free(p); } static int nis_setgrent(void *retval, void *cb_data, va_list ap) { struct nis_state *st; int rv; rv = nis_getstate(&st); if (rv != 0) return (NS_UNAVAIL); st->done = 0; free(st->key); st->key = NULL; return (NS_UNAVAIL); } static int nis_group(void *retval, void *mdata, va_list ap) { char *map; struct nis_state *st; struct group *grp; const char *name; char *buffer, *key, *result; size_t bufsize; gid_t gid; enum nss_lookup_type how; int *errnop, keylen, resultlen, rv; name = NULL; gid = (gid_t)-1; how = (enum nss_lookup_type)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); map = "group.byname"; break; case nss_lt_id: gid = va_arg(ap, gid_t); map = "group.bygid"; break; case nss_lt_all: map = "group.byname"; break; } grp = va_arg(ap, struct group *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = nis_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (st->domain[0] == '\0') { if (getdomainname(st->domain, sizeof(st->domain)) != 0) { *errnop = errno; return (NS_UNAVAIL); } } result = NULL; do { rv = NS_NOTFOUND; switch (how) { case nss_lt_name: if (strlcpy(buffer, name, bufsize) >= bufsize) goto erange; break; case nss_lt_id: if (snprintf(buffer, bufsize, "%lu", (unsigned long)gid) >= bufsize) goto erange; break; case nss_lt_all: if (st->done) goto fin; break; } result = NULL; if (how == nss_lt_all) { if (st->key == NULL) rv = yp_first(st->domain, map, &st->key, &st->keylen, &result, &resultlen); else { key = st->key; keylen = st->keylen; st->key = NULL; rv = yp_next(st->domain, map, key, keylen, &st->key, &st->keylen, &result, &resultlen); free(key); } if (rv != 0) { free(result); free(st->key); st->key = NULL; if (rv == YPERR_NOMORE) { st->done = 1; rv = NS_NOTFOUND; } else rv = NS_UNAVAIL; goto fin; } } else { rv = yp_match(st->domain, map, buffer, strlen(buffer), &result, &resultlen); if (rv == YPERR_KEY) { rv = NS_NOTFOUND; continue; } else if (rv != 0) { free(result); rv = NS_UNAVAIL; continue; } } /* We need room at least for the line, a string NUL * terminator, alignment padding, and one (char *) * pointer for the member list terminator. */ if (resultlen >= bufsize - _ALIGNBYTES - sizeof(char *)) goto erange; memcpy(buffer, result, resultlen); buffer[resultlen] = '\0'; free(result); rv = __gr_match_entry(buffer, resultlen, how, name, gid); if (rv == NS_SUCCESS) rv = __gr_parse_entry(buffer, resultlen, grp, &buffer[resultlen+1], bufsize - resultlen - 1, errnop); } while (how == nss_lt_all && !(rv & NS_TERMINATE)); fin: if (rv == NS_SUCCESS && retval != NULL) *(struct group **)retval = grp; return (rv); erange: *errnop = ERANGE; return (NS_RETURN); } #endif /* YP */ /* * compat backend */ static void compat_endstate(void *p) { struct compat_state *st; if (p == NULL) return; st = (struct compat_state *)p; free(st->name); if (st->fp != NULL) fclose(st->fp); free(p); } static int compat_setgrent(void *retval, void *mdata, va_list ap) { static const ns_src compatsrc[] = { #ifdef YP { NSSRC_NIS, NS_SUCCESS }, #endif { NULL, 0 } }; ns_dtab dtab[] = { #ifdef HESIOD { NSSRC_DNS, dns_setgrent, NULL }, #endif #ifdef YP { NSSRC_NIS, nis_setgrent, NULL }, #endif { NULL, NULL, NULL } }; struct compat_state *st; int rv, stayopen; #define set_setent(x, y) do { \ int i; \ \ for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++) \ x[i].mdata = (void *)y; \ } while (0) rv = compat_getstate(&st); if (rv != 0) return (NS_UNAVAIL); switch ((enum constants)mdata) { case SETGRENT: stayopen = va_arg(ap, int); if (st->fp != NULL) rewind(st->fp); else if (stayopen) st->fp = fopen(_PATH_GROUP, "r"); set_setent(dtab, mdata); (void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgrent", compatsrc, 0); break; case ENDGRENT: if (st->fp != NULL) { fclose(st->fp); st->fp = NULL; } set_setent(dtab, mdata); (void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "endgrent", compatsrc, 0); break; default: break; } st->compat = COMPAT_MODE_OFF; free(st->name); st->name = NULL; return (NS_UNAVAIL); #undef set_setent } static int compat_group(void *retval, void *mdata, va_list ap) { static const ns_src compatsrc[] = { #ifdef YP { NSSRC_NIS, NS_SUCCESS }, #endif { NULL, 0 } }; ns_dtab dtab[] = { #ifdef YP { NSSRC_NIS, nis_group, NULL }, #endif #ifdef HESIOD { NSSRC_DNS, dns_group, NULL }, #endif { NULL, NULL, NULL } }; struct compat_state *st; enum nss_lookup_type how; const char *name, *line; struct group *grp; gid_t gid; char *buffer, *p; void *discard; size_t bufsize, linesize; int rv, stayopen, *errnop; #define set_lookup_type(x, y) do { \ int i; \ \ for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++) \ x[i].mdata = (void *)y; \ } while (0) name = NULL; gid = (gid_t)-1; how = (enum nss_lookup_type)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); break; case nss_lt_id: gid = va_arg(ap, gid_t); break; case nss_lt_all: break; default: return (NS_NOTFOUND); } grp = va_arg(ap, struct group *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = compat_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (st->fp == NULL && ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) { *errnop = errno; rv = NS_UNAVAIL; goto fin; } if (how == nss_lt_all) stayopen = 1; else { rewind(st->fp); stayopen = st->stayopen; } docompat: switch (st->compat) { case COMPAT_MODE_ALL: set_lookup_type(dtab, how); switch (how) { case nss_lt_all: rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, "getgrent_r", compatsrc, grp, buffer, bufsize, errnop); break; case nss_lt_id: rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, "getgrgid_r", compatsrc, gid, grp, buffer, bufsize, errnop); break; case nss_lt_name: rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, "getgrnam_r", compatsrc, name, grp, buffer, bufsize, errnop); break; } if (rv & NS_TERMINATE) goto fin; st->compat = COMPAT_MODE_OFF; break; case COMPAT_MODE_NAME: set_lookup_type(dtab, nss_lt_name); rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, "getgrnam_r", compatsrc, st->name, grp, buffer, bufsize, errnop); switch (rv) { case NS_SUCCESS: switch (how) { case nss_lt_name: if (strcmp(name, grp->gr_name) != 0) rv = NS_NOTFOUND; break; case nss_lt_id: if (gid != grp->gr_gid) rv = NS_NOTFOUND; break; default: break; } break; case NS_RETURN: goto fin; default: break; } free(st->name); st->name = NULL; st->compat = COMPAT_MODE_OFF; if (rv == NS_SUCCESS) goto fin; break; default: break; } rv = NS_NOTFOUND; while ((line = fgetln(st->fp, &linesize)) != NULL) { if (line[linesize-1] == '\n') linesize--; if (linesize > 2 && line[0] == '+') { p = memchr(&line[1], ':', linesize); if (p == NULL || p == &line[1]) st->compat = COMPAT_MODE_ALL; else { st->name = malloc(p - line); if (st->name == NULL) { syslog(LOG_ERR, "getgrent memory allocation failure"); *errnop = ENOMEM; rv = NS_UNAVAIL; break; } memcpy(st->name, &line[1], p - line - 1); st->name[p - line - 1] = '\0'; st->compat = COMPAT_MODE_NAME; } goto docompat; } rv = __gr_match_entry(line, linesize, how, name, gid); if (rv != NS_SUCCESS) continue; /* We need room at least for the line, a string NUL * terminator, alignment padding, and one (char *) * pointer for the member list terminator. */ if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) { *errnop = ERANGE; rv = NS_RETURN; break; } memcpy(buffer, line, linesize); buffer[linesize] = '\0'; rv = __gr_parse_entry(buffer, linesize, grp, &buffer[linesize + 1], bufsize - linesize - 1, errnop); if (rv & NS_TERMINATE) break; } fin: if (!stayopen && st->fp != NULL) { fclose(st->fp); st->fp = NULL; } if (rv == NS_SUCCESS && retval != NULL) *(struct group **)retval = grp; return (rv); #undef set_lookup_type } /* * common group line matching and parsing */ int __gr_match_entry(const char *line, size_t linesize, enum nss_lookup_type how, const char *name, gid_t gid) { size_t namesize; const char *p, *eol; char *q; unsigned long n; int i, needed; if (linesize == 0 || is_comment_line(line, linesize)) return (NS_NOTFOUND); switch (how) { case nss_lt_name: needed = 1; break; case nss_lt_id: needed = 2; break; default: needed = 2; break; } eol = &line[linesize]; for (p = line, i = 0; i < needed && p < eol; p++) if (*p == ':') i++; if (i < needed) return (NS_NOTFOUND); switch (how) { case nss_lt_name: namesize = strlen(name); if (namesize + 1 == (size_t)(p - line) && memcmp(line, name, namesize) == 0) return (NS_SUCCESS); break; case nss_lt_id: n = strtoul(p, &q, 10); if (q < eol && *q == ':' && gid == (gid_t)n) return (NS_SUCCESS); break; case nss_lt_all: return (NS_SUCCESS); default: break; } return (NS_NOTFOUND); } int __gr_parse_entry(char *line, size_t linesize, struct group *grp, char *membuf, size_t membufsize, int *errnop) { char *s_gid, *s_mem, *p, **members; unsigned long n; int maxmembers; memset(grp, 0, sizeof(*grp)); members = (char **)_ALIGN(membuf); membufsize -= (char *)members - membuf; maxmembers = membufsize / sizeof(*members); if (maxmembers <= 0 || (grp->gr_name = strsep(&line, ":")) == NULL || grp->gr_name[0] == '\0' || (grp->gr_passwd = strsep(&line, ":")) == NULL || (s_gid = strsep(&line, ":")) == NULL || s_gid[0] == '\0') return (NS_NOTFOUND); s_mem = line; n = strtoul(s_gid, &s_gid, 10); if (s_gid[0] != '\0') return (NS_NOTFOUND); grp->gr_gid = (gid_t)n; grp->gr_mem = members; while (maxmembers > 1 && s_mem != NULL) { p = strsep(&s_mem, ","); if (p != NULL && *p != '\0') { *members++ = p; maxmembers--; } } *members = NULL; if (s_mem == NULL) return (NS_SUCCESS); else { *errnop = ERANGE; return (NS_RETURN); } } diff --git a/lib/libc/gen/getpwent.c b/lib/libc/gen/getpwent.c index a0867ddf8e1c..f729cdf1d417 100644 --- a/lib/libc/gen/getpwent.c +++ b/lib/libc/gen/getpwent.c @@ -1,1732 +1,2009 @@ /*- * Copyright (c) 2003 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by * Jacques A. Vidrine, Safeport Network Services, and Network * Associates Laboratories, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #ifdef YP #include #include #include #endif #include #include #include #ifdef HESIOD #include #endif #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include #include "libc_private.h" #include "pw_scan.h" #include "nss_tls.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif #ifndef CTASSERT #define CTASSERT(x) _CTASSERT(x, __LINE__) #define _CTASSERT(x, y) __CTASSERT(x, y) #define __CTASSERT(x, y) typedef char __assert_ ## y [(x) ? 1 : -1] #endif /* Counter as stored in /etc/pwd.db */ typedef int pwkeynum; CTASSERT(MAXLOGNAME > sizeof(uid_t)); CTASSERT(MAXLOGNAME > sizeof(pwkeynum)); enum constants { PWD_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ PWD_STORAGE_MAX = 1 << 20, /* 1 MByte */ SETPWENT = 1, ENDPWENT = 2, HESIOD_NAME_MAX = 256 }; static const ns_src defaultsrc[] = { { NSSRC_COMPAT, NS_SUCCESS }, { NULL, 0 } }; int __pw_match_entry(const char *, size_t, enum nss_lookup_type, const char *, uid_t); int __pw_parse_entry(char *, size_t, struct passwd *, int, int *errnop); static void pwd_init(struct passwd *); union key { const char *name; uid_t uid; }; static struct passwd *getpw(int (*fn)(union key, struct passwd *, char *, size_t, struct passwd **), union key); static int wrap_getpwnam_r(union key, struct passwd *, char *, size_t, struct passwd **); static int wrap_getpwuid_r(union key, struct passwd *, char *, size_t, struct passwd **); static int wrap_getpwent_r(union key, struct passwd *, char *, size_t, struct passwd **); static int pwdb_match_entry_v3(char *, size_t, enum nss_lookup_type, const char *, uid_t); static int pwdb_parse_entry_v3(char *, size_t, struct passwd *, int *); static int pwdb_match_entry_v4(char *, size_t, enum nss_lookup_type, const char *, uid_t); static int pwdb_parse_entry_v4(char *, size_t, struct passwd *, int *); struct { int (*match)(char *, size_t, enum nss_lookup_type, const char *, uid_t); int (*parse)(char *, size_t, struct passwd *, int *); } pwdb_versions[] = { { NULL, NULL }, /* version 0 */ { NULL, NULL }, /* version 1 */ { NULL, NULL }, /* version 2 */ { pwdb_match_entry_v3, pwdb_parse_entry_v3 }, /* version 3 */ { pwdb_match_entry_v4, pwdb_parse_entry_v4 }, /* version 4 */ }; struct files_state { DB *db; pwkeynum keynum; int stayopen; int version; }; static void files_endstate(void *); NSS_TLS_HANDLING(files); static DB *pwdbopen(int *); static void files_endstate(void *); static int files_setpwent(void *, void *, va_list); static int files_passwd(void *, void *, va_list); #ifdef HESIOD struct dns_state { long counter; }; static void dns_endstate(void *); NSS_TLS_HANDLING(dns); static int dns_setpwent(void *, void *, va_list); static int dns_passwd(void *, void *, va_list); #endif #ifdef YP struct nis_state { char domain[MAXHOSTNAMELEN]; int done; char *key; int keylen; }; static void nis_endstate(void *); NSS_TLS_HANDLING(nis); static int nis_setpwent(void *, void *, va_list); static int nis_passwd(void *, void *, va_list); static int nis_map(char *, enum nss_lookup_type, char *, size_t, int *); static int nis_adjunct(char *, const char *, char *, size_t); #endif struct compat_state { DB *db; pwkeynum keynum; int stayopen; int version; DB *exclude; struct passwd template; char *name; enum _compat { COMPAT_MODE_OFF = 0, COMPAT_MODE_ALL, COMPAT_MODE_NAME, COMPAT_MODE_NETGROUP } compat; }; static void compat_endstate(void *); NSS_TLS_HANDLING(compat); static int compat_setpwent(void *, void *, va_list); static int compat_passwd(void *, void *, va_list); static void compat_clear_template(struct passwd *); static int compat_set_template(struct passwd *, struct passwd *); static int compat_use_template(struct passwd *, struct passwd *, char *, size_t); static int compat_redispatch(struct compat_state *, enum nss_lookup_type, enum nss_lookup_type, const char *, const char *, uid_t, struct passwd *, char *, size_t, int *); + +#ifdef NS_CACHING +static int pwd_id_func(char *, size_t *, va_list ap, void *); +static int pwd_marshal_func(char *, size_t *, void *, va_list, void *); +static int pwd_unmarshal_func(char *, size_t, void *, va_list, void *); + +static int +pwd_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) +{ + char *name; + uid_t uid; + size_t size, desired_size; + int res = NS_UNAVAIL; + enum nss_lookup_type lookup_type; + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + name = va_arg(ap, char *); + size = strlen(name); + desired_size = sizeof(enum nss_lookup_type) + size + 1; + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + uid = va_arg(ap, uid_t); + desired_size = sizeof(enum nss_lookup_type) + sizeof(uid_t); + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), &uid, + sizeof(uid_t)); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + +static int +pwd_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *name; + uid_t uid; + struct passwd *pwd; + char *orig_buf; + size_t orig_buf_size; + + struct passwd new_pwd; + size_t desired_size, size; + char *p; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + uid = va_arg(ap, uid_t); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + pwd = va_arg(ap, struct passwd *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + + desired_size = sizeof(struct passwd) + sizeof(char *) + + strlen(pwd->pw_name) + 1; + if (pwd->pw_passwd != NULL) + desired_size += strlen(pwd->pw_passwd) + 1; + if (pwd->pw_class != NULL) + desired_size += strlen(pwd->pw_class) + 1; + if (pwd->pw_gecos != NULL) + desired_size += strlen(pwd->pw_gecos) + 1; + if (pwd->pw_dir != NULL) + desired_size += strlen(pwd->pw_dir) + 1; + if (pwd->pw_shell != NULL) + desired_size += strlen(pwd->pw_shell) + 1; + + if (*buffer_size < desired_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_pwd, pwd, sizeof(struct passwd)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct passwd) + sizeof(char *); + memcpy(buffer + sizeof(struct passwd), &p, sizeof(char *)); + + if (new_pwd.pw_name != NULL) { + size = strlen(new_pwd.pw_name); + memcpy(p, new_pwd.pw_name, size); + new_pwd.pw_name = p; + p += size + 1; + } + + if (new_pwd.pw_passwd != NULL) { + size = strlen(new_pwd.pw_passwd); + memcpy(p, new_pwd.pw_passwd, size); + new_pwd.pw_passwd = p; + p += size + 1; + } + + if (new_pwd.pw_class != NULL) { + size = strlen(new_pwd.pw_class); + memcpy(p, new_pwd.pw_class, size); + new_pwd.pw_class = p; + p += size + 1; + } + + if (new_pwd.pw_gecos != NULL) { + size = strlen(new_pwd.pw_gecos); + memcpy(p, new_pwd.pw_gecos, size); + new_pwd.pw_gecos = p; + p += size + 1; + } + + if (new_pwd.pw_dir != NULL) { + size = strlen(new_pwd.pw_dir); + memcpy(p, new_pwd.pw_dir, size); + new_pwd.pw_dir = p; + p += size + 1; + } + + if (new_pwd.pw_shell != NULL) { + size = strlen(new_pwd.pw_shell); + memcpy(p, new_pwd.pw_shell, size); + new_pwd.pw_shell = p; + p += size + 1; + } + + memcpy(buffer, &new_pwd, sizeof(struct passwd)); + return (NS_SUCCESS); +} + +static int +pwd_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *name; + uid_t uid; + struct passwd *pwd; + char *orig_buf; + size_t orig_buf_size; + int *ret_errno; + + char *p; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + uid = va_arg(ap, uid_t); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + pwd = va_arg(ap, struct passwd *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + ret_errno = va_arg(ap, int *); + + if (orig_buf_size < + buffer_size - sizeof(struct passwd) - sizeof(char *)) { + *ret_errno = ERANGE; + return (NS_RETURN); + } + + memcpy(pwd, buffer, sizeof(struct passwd)); + memcpy(&p, buffer + sizeof(struct passwd), sizeof(char *)); + memcpy(orig_buf, buffer + sizeof(struct passwd) + sizeof(char *), + buffer_size - sizeof(struct passwd) - sizeof(char *)); + + NS_APPLY_OFFSET(pwd->pw_name, orig_buf, p, char *); + NS_APPLY_OFFSET(pwd->pw_passwd, orig_buf, p, char *); + NS_APPLY_OFFSET(pwd->pw_class, orig_buf, p, char *); + NS_APPLY_OFFSET(pwd->pw_gecos, orig_buf, p, char *); + NS_APPLY_OFFSET(pwd->pw_dir, orig_buf, p, char *); + NS_APPLY_OFFSET(pwd->pw_shell, orig_buf, p, char *); + + if (retval != NULL) + *((struct passwd **)retval) = pwd; + + return (NS_SUCCESS); +} + +NSS_MP_CACHE_HANDLING(passwd); +#endif /* NS_CACHING */ + void setpwent(void) { +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + passwd, (void *)nss_lt_all, + NULL, NULL); +#endif + static const ns_dtab dtab[] = { { NSSRC_FILES, files_setpwent, (void *)SETPWENT }, #ifdef HESIOD { NSSRC_DNS, dns_setpwent, (void *)SETPWENT }, #endif #ifdef YP { NSSRC_NIS, nis_setpwent, (void *)SETPWENT }, #endif { NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { NULL, NULL, NULL } }; (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc, 0); } int setpassent(int stayopen) { +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + passwd, (void *)nss_lt_all, + NULL, NULL); +#endif + static const ns_dtab dtab[] = { { NSSRC_FILES, files_setpwent, (void *)SETPWENT }, #ifdef HESIOD { NSSRC_DNS, dns_setpwent, (void *)SETPWENT }, #endif #ifdef YP { NSSRC_NIS, nis_setpwent, (void *)SETPWENT }, #endif { NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { NULL, NULL, NULL } }; (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc, stayopen); return (1); } void endpwent(void) { +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + passwd, (void *)nss_lt_all, + NULL, NULL); +#endif + static const ns_dtab dtab[] = { { NSSRC_FILES, files_setpwent, (void *)ENDPWENT }, #ifdef HESIOD { NSSRC_DNS, dns_setpwent, (void *)ENDPWENT }, #endif #ifdef YP { NSSRC_NIS, nis_setpwent, (void *)ENDPWENT }, #endif { NSSRC_COMPAT, compat_setpwent, (void *)ENDPWENT }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { NULL, NULL, NULL } }; (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "endpwent", defaultsrc); } int getpwent_r(struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + passwd, (void *)nss_lt_all, + pwd_marshal_func, pwd_unmarshal_func); +#endif + static const ns_dtab dtab[] = { { NSSRC_FILES, files_passwd, (void *)nss_lt_all }, #ifdef HESIOD { NSSRC_DNS, dns_passwd, (void *)nss_lt_all }, #endif #ifdef YP { NSSRC_NIS, nis_passwd, (void *)nss_lt_all }, #endif { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_all }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { NULL, NULL, NULL } }; int rv, ret_errno; pwd_init(pwd); ret_errno = 0; *result = NULL; rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwent_r", defaultsrc, pwd, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } int getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + passwd, (void *)nss_lt_name, + pwd_id_func, pwd_marshal_func, pwd_unmarshal_func); +#endif + static const ns_dtab dtab[] = { { NSSRC_FILES, files_passwd, (void *)nss_lt_name }, #ifdef HESIOD { NSSRC_DNS, dns_passwd, (void *)nss_lt_name }, #endif #ifdef YP { NSSRC_NIS, nis_passwd, (void *)nss_lt_name }, #endif { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_name }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { NULL, NULL, NULL } }; int rv, ret_errno; pwd_init(pwd); ret_errno = 0; *result = NULL; rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwnam_r", defaultsrc, name, pwd, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } int getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + passwd, (void *)nss_lt_id, + pwd_id_func, pwd_marshal_func, pwd_unmarshal_func); +#endif + static const ns_dtab dtab[] = { { NSSRC_FILES, files_passwd, (void *)nss_lt_id }, #ifdef HESIOD { NSSRC_DNS, dns_passwd, (void *)nss_lt_id }, #endif #ifdef YP { NSSRC_NIS, nis_passwd, (void *)nss_lt_id }, #endif { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_id }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { NULL, NULL, NULL } }; int rv, ret_errno; pwd_init(pwd); ret_errno = 0; *result = NULL; rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwuid_r", defaultsrc, uid, pwd, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } static void pwd_init(struct passwd *pwd) { static char nul[] = ""; memset(pwd, 0, sizeof(*pwd)); pwd->pw_uid = (uid_t)-1; /* Considered least likely to lead to */ pwd->pw_gid = (gid_t)-1; /* a security issue. */ pwd->pw_name = nul; pwd->pw_passwd = nul; pwd->pw_class = nul; pwd->pw_gecos = nul; pwd->pw_dir = nul; pwd->pw_shell = nul; } static struct passwd pwd; static char *pwd_storage; static size_t pwd_storage_size; static struct passwd * getpw(int (*fn)(union key, struct passwd *, char *, size_t, struct passwd **), union key key) { int rv; struct passwd *res; if (pwd_storage == NULL) { pwd_storage = malloc(PWD_STORAGE_INITIAL); if (pwd_storage == NULL) return (NULL); pwd_storage_size = PWD_STORAGE_INITIAL; } do { rv = fn(key, &pwd, pwd_storage, pwd_storage_size, &res); if (res == NULL && rv == ERANGE) { free(pwd_storage); if ((pwd_storage_size << 1) > PWD_STORAGE_MAX) { pwd_storage = NULL; errno = ERANGE; return (NULL); } pwd_storage_size <<= 1; pwd_storage = malloc(pwd_storage_size); if (pwd_storage == NULL) return (NULL); } } while (res == NULL && rv == ERANGE); if (rv != 0) errno = rv; return (res); } static int wrap_getpwnam_r(union key key, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **res) { return (getpwnam_r(key.name, pwd, buffer, bufsize, res)); } static int wrap_getpwuid_r(union key key, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **res) { return (getpwuid_r(key.uid, pwd, buffer, bufsize, res)); } static int wrap_getpwent_r(union key key __unused, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **res) { return (getpwent_r(pwd, buffer, bufsize, res)); } struct passwd * getpwnam(const char *name) { union key key; key.name = name; return (getpw(wrap_getpwnam_r, key)); } struct passwd * getpwuid(uid_t uid) { union key key; key.uid = uid; return (getpw(wrap_getpwuid_r, key)); } struct passwd * getpwent(void) { union key key; key.uid = 0; /* not used */ return (getpw(wrap_getpwent_r, key)); } /* * files backend */ static DB * pwdbopen(int *version) { DB *res; DBT key, entry; int rv; if (geteuid() != 0 || (res = dbopen(_PATH_SMP_DB, O_RDONLY, 0, DB_HASH, NULL)) == NULL) res = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL); if (res == NULL) return (NULL); key.data = _PWD_VERSION_KEY; key.size = strlen(_PWD_VERSION_KEY); rv = res->get(res, &key, &entry, 0); if (rv == 0) *version = *(unsigned char *)entry.data; else *version = 3; if (*version < 3 || *version >= sizeof(pwdb_versions)/sizeof(pwdb_versions[0])) { syslog(LOG_CRIT, "Unsupported password database version %d", *version); res->close(res); res = NULL; } return (res); } static void files_endstate(void *p) { DB *db; if (p == NULL) return; db = ((struct files_state *)p)->db; if (db != NULL) db->close(db); free(p); } static int files_setpwent(void *retval, void *mdata, va_list ap) { struct files_state *st; int rv, stayopen; rv = files_getstate(&st); if (rv != 0) return (NS_UNAVAIL); switch ((enum constants)mdata) { case SETPWENT: stayopen = va_arg(ap, int); st->keynum = 0; if (stayopen) st->db = pwdbopen(&st->version); st->stayopen = stayopen; break; case ENDPWENT: if (st->db != NULL) { (void)st->db->close(st->db); st->db = NULL; } break; default: break; } return (NS_UNAVAIL); } static int files_passwd(void *retval, void *mdata, va_list ap) { char keybuf[MAXLOGNAME + 1]; DBT key, entry; struct files_state *st; enum nss_lookup_type how; const char *name; struct passwd *pwd; char *buffer; size_t bufsize, namesize; uid_t uid; uint32_t store; int rv, stayopen, *errnop; name = NULL; uid = (uid_t)-1; how = (enum nss_lookup_type)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); keybuf[0] = _PW_KEYBYNAME; break; case nss_lt_id: uid = va_arg(ap, uid_t); keybuf[0] = _PW_KEYBYUID; break; case nss_lt_all: keybuf[0] = _PW_KEYBYNUM; break; default: rv = NS_NOTFOUND; goto fin; } pwd = va_arg(ap, struct passwd *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = files_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (how == nss_lt_all && st->keynum < 0) { rv = NS_NOTFOUND; goto fin; } if (st->db == NULL && (st->db = pwdbopen(&st->version)) == NULL) { *errnop = errno; rv = NS_UNAVAIL; goto fin; } if (how == nss_lt_all) stayopen = 1; else stayopen = st->stayopen; key.data = keybuf; do { switch (how) { case nss_lt_name: /* MAXLOGNAME includes NUL byte, but we do not * include the NUL byte in the key. */ namesize = strlcpy(&keybuf[1], name, sizeof(keybuf)-1); if (namesize >= sizeof(keybuf)-1) { *errnop = EINVAL; rv = NS_NOTFOUND; goto fin; } key.size = namesize + 1; break; case nss_lt_id: if (st->version < _PWD_CURRENT_VERSION) { memcpy(&keybuf[1], &uid, sizeof(uid)); key.size = sizeof(uid) + 1; } else { store = htonl(uid); memcpy(&keybuf[1], &store, sizeof(store)); key.size = sizeof(store) + 1; } break; case nss_lt_all: st->keynum++; if (st->version < _PWD_CURRENT_VERSION) { memcpy(&keybuf[1], &st->keynum, sizeof(st->keynum)); key.size = sizeof(st->keynum) + 1; } else { store = htonl(st->keynum); memcpy(&keybuf[1], &store, sizeof(store)); key.size = sizeof(store) + 1; } break; } keybuf[0] = _PW_VERSIONED(keybuf[0], st->version); rv = st->db->get(st->db, &key, &entry, 0); if (rv < 0 || rv > 1) { /* should never return > 1 */ *errnop = errno; rv = NS_UNAVAIL; goto fin; } else if (rv == 1) { if (how == nss_lt_all) st->keynum = -1; rv = NS_NOTFOUND; goto fin; } rv = pwdb_versions[st->version].match(entry.data, entry.size, how, name, uid); if (rv != NS_SUCCESS) continue; if (entry.size > bufsize) { *errnop = ERANGE; rv = NS_RETURN; break; } memcpy(buffer, entry.data, entry.size); rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, errnop); } while (how == nss_lt_all && !(rv & NS_TERMINATE)); fin: if (!stayopen && st->db != NULL) { (void)st->db->close(st->db); st->db = NULL; } if (rv == NS_SUCCESS) { pwd->pw_fields &= ~_PWF_SOURCE; pwd->pw_fields |= _PWF_FILES; if (retval != NULL) *(struct passwd **)retval = pwd; } return (rv); } static int pwdb_match_entry_v3(char *entry, size_t entrysize, enum nss_lookup_type how, const char *name, uid_t uid) { const char *p, *eom; uid_t uid2; eom = &entry[entrysize]; for (p = entry; p < eom; p++) if (*p == '\0') break; if (*p != '\0') return (NS_NOTFOUND); if (how == nss_lt_all) return (NS_SUCCESS); if (how == nss_lt_name) return (strcmp(name, entry) == 0 ? NS_SUCCESS : NS_NOTFOUND); for (p++; p < eom; p++) if (*p == '\0') break; if (*p != '\0' || (++p) + sizeof(uid) >= eom) return (NS_NOTFOUND); memcpy(&uid2, p, sizeof(uid2)); return (uid == uid2 ? NS_SUCCESS : NS_NOTFOUND); } static int pwdb_parse_entry_v3(char *buffer, size_t bufsize, struct passwd *pwd, int *errnop) { char *p, *eom; int32_t pw_change, pw_expire; /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */ p = buffer; eom = &buffer[bufsize]; #define STRING(field) do { \ (field) = p; \ while (p < eom && *p != '\0') \ p++; \ if (p >= eom) \ return (NS_NOTFOUND); \ p++; \ } while (0) #define SCALAR(field) do { \ if (p + sizeof(field) > eom) \ return (NS_NOTFOUND); \ memcpy(&(field), p, sizeof(field)); \ p += sizeof(field); \ } while (0) STRING(pwd->pw_name); STRING(pwd->pw_passwd); SCALAR(pwd->pw_uid); SCALAR(pwd->pw_gid); SCALAR(pw_change); STRING(pwd->pw_class); STRING(pwd->pw_gecos); STRING(pwd->pw_dir); STRING(pwd->pw_shell); SCALAR(pw_expire); SCALAR(pwd->pw_fields); #undef STRING #undef SCALAR pwd->pw_change = pw_change; pwd->pw_expire = pw_expire; return (NS_SUCCESS); } static int pwdb_match_entry_v4(char *entry, size_t entrysize, enum nss_lookup_type how, const char *name, uid_t uid) { const char *p, *eom; uint32_t uid2; eom = &entry[entrysize]; for (p = entry; p < eom; p++) if (*p == '\0') break; if (*p != '\0') return (NS_NOTFOUND); if (how == nss_lt_all) return (NS_SUCCESS); if (how == nss_lt_name) return (strcmp(name, entry) == 0 ? NS_SUCCESS : NS_NOTFOUND); for (p++; p < eom; p++) if (*p == '\0') break; if (*p != '\0' || (++p) + sizeof(uid) >= eom) return (NS_NOTFOUND); memcpy(&uid2, p, sizeof(uid2)); uid2 = ntohl(uid2); return (uid == (uid_t)uid2 ? NS_SUCCESS : NS_NOTFOUND); } static int pwdb_parse_entry_v4(char *buffer, size_t bufsize, struct passwd *pwd, int *errnop) { char *p, *eom; uint32_t n; /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */ p = buffer; eom = &buffer[bufsize]; #define STRING(field) do { \ (field) = p; \ while (p < eom && *p != '\0') \ p++; \ if (p >= eom) \ return (NS_NOTFOUND); \ p++; \ } while (0) #define SCALAR(field) do { \ if (p + sizeof(n) > eom) \ return (NS_NOTFOUND); \ memcpy(&n, p, sizeof(n)); \ (field) = ntohl(n); \ p += sizeof(n); \ } while (0) STRING(pwd->pw_name); STRING(pwd->pw_passwd); SCALAR(pwd->pw_uid); SCALAR(pwd->pw_gid); SCALAR(pwd->pw_change); STRING(pwd->pw_class); STRING(pwd->pw_gecos); STRING(pwd->pw_dir); STRING(pwd->pw_shell); SCALAR(pwd->pw_expire); SCALAR(pwd->pw_fields); #undef STRING #undef SCALAR return (NS_SUCCESS); } #ifdef HESIOD /* * dns backend */ static void dns_endstate(void *p) { free(p); } static int dns_setpwent(void *retval, void *mdata, va_list ap) { struct dns_state *st; int rv; rv = dns_getstate(&st); if (rv != 0) return (NS_UNAVAIL); st->counter = 0; return (NS_UNAVAIL); } static int dns_passwd(void *retval, void *mdata, va_list ap) { char buf[HESIOD_NAME_MAX]; struct dns_state *st; struct passwd *pwd; const char *name, *label; void *ctx; char *buffer, **hes; size_t bufsize, linesize; uid_t uid; enum nss_lookup_type how; int rv, *errnop; ctx = NULL; hes = NULL; name = NULL; uid = (uid_t)-1; how = (enum nss_lookup_type)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); break; case nss_lt_id: uid = va_arg(ap, uid_t); break; case nss_lt_all: break; } pwd = va_arg(ap, struct passwd *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = dns_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (hesiod_init(&ctx) != 0) { *errnop = errno; rv = NS_UNAVAIL; goto fin; } do { rv = NS_NOTFOUND; switch (how) { case nss_lt_name: label = name; break; case nss_lt_id: if (snprintf(buf, sizeof(buf), "%lu", (unsigned long)uid) >= sizeof(buf)) goto fin; label = buf; break; case nss_lt_all: if (st->counter < 0) goto fin; if (snprintf(buf, sizeof(buf), "passwd-%ld", st->counter++) >= sizeof(buf)) goto fin; label = buf; break; } hes = hesiod_resolve(ctx, label, how == nss_lt_id ? "uid" : "passwd"); if (hes == NULL) { if (how == nss_lt_all) st->counter = -1; if (errno != ENOENT) *errnop = errno; goto fin; } rv = __pw_match_entry(hes[0], strlen(hes[0]), how, name, uid); if (rv != NS_SUCCESS) { hesiod_free_list(ctx, hes); hes = NULL; continue; } linesize = strlcpy(buffer, hes[0], bufsize); if (linesize >= bufsize) { *errnop = ERANGE; rv = NS_RETURN; continue; } hesiod_free_list(ctx, hes); hes = NULL; rv = __pw_parse_entry(buffer, bufsize, pwd, 0, errnop); } while (how == nss_lt_all && !(rv & NS_TERMINATE)); fin: if (hes != NULL) hesiod_free_list(ctx, hes); if (ctx != NULL) hesiod_end(ctx); if (rv == NS_SUCCESS) { pwd->pw_fields &= ~_PWF_SOURCE; pwd->pw_fields |= _PWF_HESIOD; if (retval != NULL) *(struct passwd **)retval = pwd; } return (rv); } #endif /* HESIOD */ #ifdef YP /* * nis backend */ static void nis_endstate(void *p) { free(((struct nis_state *)p)->key); free(p); } /* * Test for the presence of special FreeBSD-specific master.passwd.by* * maps. We do this using yp_order(). If it fails, then either the server * doesn't have the map, or the YPPROC_ORDER procedure isn't supported by * the server (Sun NIS+ servers in YP compat mode behave this way). If * the master.passwd.by* maps don't exist, then let the lookup routine try * the regular passwd.by* maps instead. If the lookup routine fails, it * can return an error as needed. */ static int nis_map(char *domain, enum nss_lookup_type how, char *buffer, size_t bufsize, int *master) { int rv, order; *master = 0; if (geteuid() == 0) { if (snprintf(buffer, bufsize, "master.passwd.by%s", (how == nss_lt_id) ? "uid" : "name") >= bufsize) return (NS_UNAVAIL); rv = yp_order(domain, buffer, &order); if (rv == 0) { *master = 1; return (NS_SUCCESS); } } if (snprintf(buffer, bufsize, "passwd.by%s", (how == nss_lt_id) ? "uid" : "name") >= bufsize) return (NS_UNAVAIL); return (NS_SUCCESS); } static int nis_adjunct(char *domain, const char *name, char *buffer, size_t bufsize) { int rv; char *result, *p, *q, *eor; int resultlen; result = NULL; rv = yp_match(domain, "passwd.adjunct.byname", name, strlen(name), &result, &resultlen); if (rv != 0) rv = 1; else { eor = &result[resultlen]; p = memchr(result, ':', eor - result); if (p != NULL && ++p < eor && (q = memchr(p, ':', eor - p)) != NULL) { if (q - p >= bufsize) rv = -1; else { memcpy(buffer, p, q - p); buffer[q - p] ='\0'; } } else rv = 1; } free(result); return (rv); } static int nis_setpwent(void *retval, void *mdata, va_list ap) { struct nis_state *st; int rv; rv = nis_getstate(&st); if (rv != 0) return (NS_UNAVAIL); st->done = 0; free(st->key); st->key = NULL; return (NS_UNAVAIL); } static int nis_passwd(void *retval, void *mdata, va_list ap) { char map[YPMAXMAP]; struct nis_state *st; struct passwd *pwd; const char *name; char *buffer, *key, *result; size_t bufsize; uid_t uid; enum nss_lookup_type how; int *errnop, keylen, resultlen, rv, master; name = NULL; uid = (uid_t)-1; how = (enum nss_lookup_type)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); break; case nss_lt_id: uid = va_arg(ap, uid_t); break; case nss_lt_all: break; } pwd = va_arg(ap, struct passwd *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = nis_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (st->domain[0] == '\0') { if (getdomainname(st->domain, sizeof(st->domain)) != 0) { *errnop = errno; return (NS_UNAVAIL); } } rv = nis_map(st->domain, how, map, sizeof(map), &master); if (rv != NS_SUCCESS) return (rv); result = NULL; do { rv = NS_NOTFOUND; switch (how) { case nss_lt_name: if (strlcpy(buffer, name, bufsize) >= bufsize) goto erange; break; case nss_lt_id: if (snprintf(buffer, bufsize, "%lu", (unsigned long)uid) >= bufsize) goto erange; break; case nss_lt_all: if (st->done) goto fin; break; } result = NULL; if (how == nss_lt_all) { if (st->key == NULL) rv = yp_first(st->domain, map, &st->key, &st->keylen, &result, &resultlen); else { key = st->key; keylen = st->keylen; st->key = NULL; rv = yp_next(st->domain, map, key, keylen, &st->key, &st->keylen, &result, &resultlen); free(key); } if (rv != 0) { free(result); free(st->key); st->key = NULL; if (rv == YPERR_NOMORE) st->done = 1; else rv = NS_UNAVAIL; goto fin; } } else { rv = yp_match(st->domain, map, buffer, strlen(buffer), &result, &resultlen); if (rv == YPERR_KEY) { rv = NS_NOTFOUND; continue; } else if (rv != 0) { free(result); rv = NS_UNAVAIL; continue; } } if (resultlen >= bufsize) goto erange; memcpy(buffer, result, resultlen); buffer[resultlen] = '\0'; free(result); rv = __pw_match_entry(buffer, resultlen, how, name, uid); if (rv == NS_SUCCESS) rv = __pw_parse_entry(buffer, resultlen, pwd, master, errnop); } while (how == nss_lt_all && !(rv & NS_TERMINATE)); fin: if (rv == NS_SUCCESS) { if (strstr(pwd->pw_passwd, "##") != NULL) { rv = nis_adjunct(st->domain, pwd->pw_name, &buffer[resultlen+1], bufsize-resultlen-1); if (rv < 0) goto erange; else if (rv == 0) pwd->pw_passwd = &buffer[resultlen+1]; } pwd->pw_fields &= ~_PWF_SOURCE; pwd->pw_fields |= _PWF_NIS; if (retval != NULL) *(struct passwd **)retval = pwd; rv = NS_SUCCESS; } return (rv); erange: *errnop = ERANGE; return (NS_RETURN); } #endif /* YP */ /* * compat backend */ static void compat_clear_template(struct passwd *template) { free(template->pw_passwd); free(template->pw_gecos); free(template->pw_dir); free(template->pw_shell); memset(template, 0, sizeof(*template)); } static int compat_set_template(struct passwd *src, struct passwd *template) { compat_clear_template(template); #ifdef PW_OVERRIDE_PASSWD if ((src->pw_fields & _PWF_PASSWD) && (template->pw_passwd = strdup(src->pw_passwd)) == NULL) goto enomem; #endif if (src->pw_fields & _PWF_UID) template->pw_uid = src->pw_uid; if (src->pw_fields & _PWF_GID) template->pw_gid = src->pw_gid; if ((src->pw_fields & _PWF_GECOS) && (template->pw_gecos = strdup(src->pw_gecos)) == NULL) goto enomem; if ((src->pw_fields & _PWF_DIR) && (template->pw_dir = strdup(src->pw_dir)) == NULL) goto enomem; if ((src->pw_fields & _PWF_SHELL) && (template->pw_shell = strdup(src->pw_shell)) == NULL) goto enomem; template->pw_fields = src->pw_fields; return (0); enomem: syslog(LOG_ERR, "getpwent memory allocation failure"); return (-1); } static int compat_use_template(struct passwd *pwd, struct passwd *template, char *buffer, size_t bufsize) { struct passwd hold; char *copy, *p, *q, *eob; size_t n; /* We cannot know the layout of the password fields in `buffer', * so we have to copy everything. */ if (template->pw_fields == 0) /* nothing to fill-in */ return (0); n = 0; n += pwd->pw_name != NULL ? strlen(pwd->pw_name) + 1 : 0; n += pwd->pw_passwd != NULL ? strlen(pwd->pw_passwd) + 1 : 0; n += pwd->pw_class != NULL ? strlen(pwd->pw_class) + 1 : 0; n += pwd->pw_gecos != NULL ? strlen(pwd->pw_gecos) + 1 : 0; n += pwd->pw_dir != NULL ? strlen(pwd->pw_dir) + 1 : 0; n += pwd->pw_shell != NULL ? strlen(pwd->pw_shell) + 1 : 0; copy = malloc(n); if (copy == NULL) { syslog(LOG_ERR, "getpwent memory allocation failure"); return (ENOMEM); } p = copy; eob = ©[n]; #define COPY(field) do { \ if (pwd->field == NULL) \ hold.field = NULL; \ else { \ hold.field = p; \ p += strlcpy(p, pwd->field, eob-p) + 1; \ } \ } while (0) COPY(pw_name); COPY(pw_passwd); COPY(pw_class); COPY(pw_gecos); COPY(pw_dir); COPY(pw_shell); #undef COPY p = buffer; eob = &buffer[bufsize]; #define COPY(field, flag) do { \ q = (template->pw_fields & flag) ? template->field : hold.field; \ if (q == NULL) \ pwd->field = NULL; \ else { \ pwd->field = p; \ if ((n = strlcpy(p, q, eob-p)) >= eob-p) { \ free(copy); \ return (ERANGE); \ } \ p += n + 1; \ } \ } while (0) COPY(pw_name, 0); #ifdef PW_OVERRIDE_PASSWD COPY(pw_passwd, _PWF_PASSWD); #else COPY(pw_passwd, 0); #endif COPY(pw_class, 0); COPY(pw_gecos, _PWF_GECOS); COPY(pw_dir, _PWF_DIR); COPY(pw_shell, _PWF_SHELL); #undef COPY #define COPY(field, flag) do { \ if (template->pw_fields & flag) \ pwd->field = template->field; \ } while (0) COPY(pw_uid, _PWF_UID); COPY(pw_gid, _PWF_GID); #undef COPY free(copy); return (0); } static int compat_exclude(const char *name, DB **db) { DBT key, data; if (*db == NULL && (*db = dbopen(NULL, O_RDWR, 600, DB_HASH, 0)) == NULL) return (errno); key.size = strlen(name); key.data = (char *)name; data.size = 0; data.data = NULL; if ((*db)->put(*db, &key, &data, 0) == -1) return (errno); return (0); } static int compat_is_excluded(const char *name, DB *db) { DBT key, data; if (db == NULL) return (0); key.size = strlen(name); key.data = (char *)name; return (db->get(db, &key, &data, 0) == 0); } static int compat_redispatch(struct compat_state *st, enum nss_lookup_type how, enum nss_lookup_type lookup_how, const char *name, const char *lookup_name, uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, int *errnop) { static const ns_src compatsrc[] = { #ifdef YP { NSSRC_NIS, NS_SUCCESS }, #endif { NULL, 0 } }; ns_dtab dtab[] = { #ifdef YP { NSSRC_NIS, nis_passwd, NULL }, #endif #ifdef HESIOD { NSSRC_DNS, dns_passwd, NULL }, #endif { NULL, NULL, NULL } }; void *discard; int rv, e, i; for (i = 0; i < sizeof(dtab)/sizeof(dtab[0]) - 1; i++) dtab[i].mdata = (void *)lookup_how; more: pwd_init(pwd); switch (lookup_how) { case nss_lt_all: rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, "getpwent_r", compatsrc, pwd, buffer, bufsize, errnop); break; case nss_lt_id: rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, "getpwuid_r", compatsrc, uid, pwd, buffer, bufsize, errnop); break; case nss_lt_name: rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, "getpwnam_r", compatsrc, lookup_name, pwd, buffer, bufsize, errnop); break; default: return (NS_UNAVAIL); } if (rv != NS_SUCCESS) return (rv); if (compat_is_excluded(pwd->pw_name, st->exclude)) { if (how == nss_lt_all) goto more; return (NS_NOTFOUND); } e = compat_use_template(pwd, &st->template, buffer, bufsize); if (e != 0) { *errnop = e; if (e == ERANGE) return (NS_RETURN); else return (NS_UNAVAIL); } switch (how) { case nss_lt_name: if (strcmp(name, pwd->pw_name) != 0) return (NS_NOTFOUND); break; case nss_lt_id: if (uid != pwd->pw_uid) return (NS_NOTFOUND); break; default: break; } return (NS_SUCCESS); } static void compat_endstate(void *p) { struct compat_state *st; if (p == NULL) return; st = (struct compat_state *)p; if (st->db != NULL) st->db->close(st->db); if (st->exclude != NULL) st->exclude->close(st->exclude); compat_clear_template(&st->template); free(p); } static int compat_setpwent(void *retval, void *mdata, va_list ap) { static const ns_src compatsrc[] = { #ifdef YP { NSSRC_NIS, NS_SUCCESS }, #endif { NULL, 0 } }; ns_dtab dtab[] = { #ifdef YP { NSSRC_NIS, nis_setpwent, NULL }, #endif #ifdef HESIOD { NSSRC_DNS, dns_setpwent, NULL }, #endif { NULL, NULL, NULL } }; struct compat_state *st; int rv, stayopen; #define set_setent(x, y) do { \ int i; \ \ for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++) \ x[i].mdata = (void *)y; \ } while (0) rv = compat_getstate(&st); if (rv != 0) return (NS_UNAVAIL); switch ((enum constants)mdata) { case SETPWENT: stayopen = va_arg(ap, int); st->keynum = 0; if (stayopen) st->db = pwdbopen(&st->version); st->stayopen = stayopen; set_setent(dtab, mdata); (void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "setpwent", compatsrc, 0); break; case ENDPWENT: if (st->db != NULL) { (void)st->db->close(st->db); st->db = NULL; } set_setent(dtab, mdata); (void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "endpwent", compatsrc, 0); break; default: break; } return (NS_UNAVAIL); #undef set_setent } static int compat_passwd(void *retval, void *mdata, va_list ap) { char keybuf[MAXLOGNAME + 1]; DBT key, entry; struct compat_state *st; enum nss_lookup_type how; const char *name; struct passwd *pwd; char *buffer, *pw_name; char *host, *user, *domain; size_t bufsize; uid_t uid; uint32_t store; int rv, from_compat, stayopen, *errnop; from_compat = 0; name = NULL; uid = (uid_t)-1; how = (enum nss_lookup_type)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); break; case nss_lt_id: uid = va_arg(ap, uid_t); break; case nss_lt_all: break; default: rv = NS_NOTFOUND; goto fin; } pwd = va_arg(ap, struct passwd *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = compat_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (how == nss_lt_all && st->keynum < 0) { rv = NS_NOTFOUND; goto fin; } if (st->db == NULL && (st->db = pwdbopen(&st->version)) == NULL) { *errnop = errno; rv = NS_UNAVAIL; goto fin; } if (how == nss_lt_all) { if (st->keynum < 0) { rv = NS_NOTFOUND; goto fin; } stayopen = 1; } else { st->keynum = 0; stayopen = st->stayopen; } docompat: rv = NS_NOTFOUND; switch (st->compat) { case COMPAT_MODE_ALL: rv = compat_redispatch(st, how, how, name, name, uid, pwd, buffer, bufsize, errnop); if (rv != NS_SUCCESS) st->compat = COMPAT_MODE_OFF; break; case COMPAT_MODE_NETGROUP: /* XXX getnetgrent is not thread-safe. */ do { rv = getnetgrent(&host, &user, &domain); if (rv == 0) { endnetgrent(); st->compat = COMPAT_MODE_OFF; rv = NS_NOTFOUND; continue; } else if (user == NULL || user[0] == '\0') continue; rv = compat_redispatch(st, how, nss_lt_name, name, user, uid, pwd, buffer, bufsize, errnop); } while (st->compat == COMPAT_MODE_NETGROUP && !(rv & NS_TERMINATE)); break; case COMPAT_MODE_NAME: rv = compat_redispatch(st, how, nss_lt_name, name, st->name, uid, pwd, buffer, bufsize, errnop); free(st->name); st->name = NULL; st->compat = COMPAT_MODE_OFF; break; default: break; } if (rv & NS_TERMINATE) { from_compat = 1; goto fin; } key.data = keybuf; rv = NS_NOTFOUND; while (st->keynum >= 0) { st->keynum++; if (st->version < _PWD_CURRENT_VERSION) { memcpy(&keybuf[1], &st->keynum, sizeof(st->keynum)); key.size = sizeof(st->keynum) + 1; } else { store = htonl(st->keynum); memcpy(&keybuf[1], &store, sizeof(store)); key.size = sizeof(store) + 1; } keybuf[0] = _PW_VERSIONED(_PW_KEYBYNUM, st->version); rv = st->db->get(st->db, &key, &entry, 0); if (rv < 0 || rv > 1) { /* should never return > 1 */ *errnop = errno; rv = NS_UNAVAIL; goto fin; } else if (rv == 1) { st->keynum = -1; rv = NS_NOTFOUND; goto fin; } pw_name = (char *)entry.data; switch (pw_name[0]) { case '+': switch (pw_name[1]) { case '\0': st->compat = COMPAT_MODE_ALL; break; case '@': setnetgrent(&pw_name[2]); st->compat = COMPAT_MODE_NETGROUP; break; default: st->name = strdup(&pw_name[1]); if (st->name == NULL) { syslog(LOG_ERR, "getpwent memory allocation failure"); *errnop = ENOMEM; rv = NS_UNAVAIL; break; } st->compat = COMPAT_MODE_NAME; } if (entry.size > bufsize) { *errnop = ERANGE; rv = NS_RETURN; goto fin; } memcpy(buffer, entry.data, entry.size); rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, errnop); if (rv != NS_SUCCESS) ; else if (compat_set_template(pwd, &st->template) < 0) { *errnop = ENOMEM; rv = NS_UNAVAIL; goto fin; } goto docompat; case '-': switch (pw_name[1]) { case '\0': /* XXX Maybe syslog warning */ continue; case '@': setnetgrent(&pw_name[2]); while (getnetgrent(&host, &user, &domain) != 0) { if (user != NULL && user[0] != '\0') compat_exclude(user, &st->exclude); } endnetgrent(); continue; default: compat_exclude(&pw_name[1], &st->exclude); continue; } break; default: break; } if (compat_is_excluded((char *)entry.data, st->exclude)) continue; rv = pwdb_versions[st->version].match(entry.data, entry.size, how, name, uid); if (rv == NS_RETURN) break; else if (rv != NS_SUCCESS) continue; if (entry.size > bufsize) { *errnop = ERANGE; rv = NS_RETURN; break; } memcpy(buffer, entry.data, entry.size); rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, errnop); if (rv & NS_TERMINATE) break; } fin: if (!stayopen && st->db != NULL) { (void)st->db->close(st->db); st->db = NULL; } if (rv == NS_SUCCESS) { if (!from_compat) { pwd->pw_fields &= ~_PWF_SOURCE; pwd->pw_fields |= _PWF_FILES; } if (retval != NULL) *(struct passwd **)retval = pwd; } return (rv); } /* * common passwd line matching and parsing */ int __pw_match_entry(const char *entry, size_t entrysize, enum nss_lookup_type how, const char *name, uid_t uid) { const char *p, *eom; char *q; size_t len; unsigned long m; eom = entry + entrysize; for (p = entry; p < eom; p++) if (*p == ':') break; if (*p != ':') return (NS_NOTFOUND); if (how == nss_lt_all) return (NS_SUCCESS); if (how == nss_lt_name) { len = strlen(name); if (len == (p - entry) && memcmp(name, entry, len) == 0) return (NS_SUCCESS); else return (NS_NOTFOUND); } for (p++; p < eom; p++) if (*p == ':') break; if (*p != ':') return (NS_NOTFOUND); m = strtoul(++p, &q, 10); if (q[0] != ':' || (uid_t)m != uid) return (NS_NOTFOUND); else return (NS_SUCCESS); } /* XXX buffer must be NUL-terminated. errnop is not set correctly. */ int __pw_parse_entry(char *buffer, size_t bufsize __unused, struct passwd *pwd, int master, int *errnop __unused) { if (__pw_scan(buffer, pwd, master ? _PWSCAN_MASTER : 0) == 0) return (NS_NOTFOUND); else return (NS_SUCCESS); } diff --git a/lib/libc/include/nscache.h b/lib/libc/include/nscache.h new file mode 100644 index 000000000000..015c4ec74ffe --- /dev/null +++ b/lib/libc/include/nscache.h @@ -0,0 +1,197 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __NS_CACHE_H__ +#define __NS_CACHE_H__ + +#include "nscachedcli.h" + +typedef int (*nss_cache_id_func_t)(char *, size_t *, va_list, void *); +typedef int (*nss_cache_marshal_func_t)(char *, size_t *, void *, va_list, + void *); +typedef int (*nss_cache_unmarshal_func_t)(char *, size_t, void *, va_list, + void *); + +typedef void (*nss_set_mp_ws_func_t)(cached_mp_write_session); +typedef cached_mp_write_session (*nss_get_mp_ws_func_t)(void); + +typedef void (*nss_set_mp_rs_func_t)(cached_mp_read_session); +typedef cached_mp_read_session (*nss_get_mp_rs_func_t)(void); + +typedef struct _nss_cache_info { + char *entry_name; + void *mdata; + + /* + * These 3 functions should be implemented specifically for each + * nsswitch database. + */ + nss_cache_id_func_t id_func; /* marshals the request parameters */ + nss_cache_marshal_func_t marshal_func; /* marshals response */ + nss_cache_unmarshal_func_t unmarshal_func; /* unmarshals response */ + + /* + * These 4 functions should be generated with NSS_MP_CACHE_HANDLING + * macro. + */ + nss_set_mp_ws_func_t set_mp_ws_func; /* sets current write session */ + nss_get_mp_ws_func_t get_mp_ws_func; /* gets current write session */ + + nss_set_mp_rs_func_t set_mp_rs_func; /* sets current read session */ + nss_get_mp_rs_func_t get_mp_rs_func; /* gets current read session */ +} nss_cache_info; + +/* + * NSS_MP_CACHE_HANDLING implements the set_mp_ws, get_mp_ws, set_mp_rs, + * get_mp_rs functions, that are used in _nss_cache_info. It uses + * NSS_TLS_HANDLING macro to organize thread local storage. + */ +#define NSS_MP_CACHE_HANDLING(name) \ +struct name##_mp_state { \ + cached_mp_write_session mp_write_session; \ + cached_mp_read_session mp_read_session; \ +}; \ + \ +static void \ +name##_mp_endstate(void *s) { \ + struct name##_mp_state *mp_state; \ + \ + mp_state = (struct name##_mp_state *)s; \ + if (mp_state->mp_write_session != INVALID_CACHED_MP_WRITE_SESSION)\ + __abandon_cached_mp_write_session(mp_state->mp_write_session);\ + \ + if (mp_state->mp_read_session != INVALID_CACHED_MP_READ_SESSION)\ + __close_cached_mp_read_session(mp_state->mp_read_session);\ +} \ +NSS_TLS_HANDLING(name##_mp); \ + \ +static void \ +name##_set_mp_ws(cached_mp_write_session ws) \ +{ \ + struct name##_mp_state *mp_state; \ + int res; \ + \ + res = name##_mp_getstate(&mp_state); \ + if (res != 0) \ + return; \ + \ + mp_state->mp_write_session = ws; \ +} \ + \ +static cached_mp_write_session \ +name##_get_mp_ws(void) \ +{ \ + struct name##_mp_state *mp_state; \ + int res; \ + \ + res = name##_mp_getstate(&mp_state); \ + if (res != 0) \ + return (INVALID_CACHED_MP_WRITE_SESSION); \ + \ + return (mp_state->mp_write_session); \ +} \ + \ +static void \ +name##_set_mp_rs(cached_mp_read_session rs) \ +{ \ + struct name##_mp_state *mp_state; \ + int res; \ + \ + res = name##_mp_getstate(&mp_state); \ + if (res != 0) \ + return; \ + \ + mp_state->mp_read_session = rs; \ +} \ + \ +static cached_mp_read_session \ +name##_get_mp_rs(void) \ +{ \ + struct name##_mp_state *mp_state; \ + int res; \ + \ + res = name##_mp_getstate(&mp_state); \ + if (res != 0) \ + return (INVALID_CACHED_MP_READ_SESSION); \ + \ + return (mp_state->mp_read_session); \ +} + +/* + * These macros should be used to initialize _nss_cache_info structure. For + * multipart queries in setXXXent and getXXXent functions mf and uf + * (marshal function and unmarshal function) should be both NULL. + */ +#define NS_COMMON_CACHE_INFO_INITIALIZER(name, mdata, if, mf, uf) \ + {#name, mdata, if, mf, uf, NULL, NULL, NULL, NULL} +#define NS_MP_CACHE_INFO_INITIALIZER(name, mdata, mf, uf) \ + {#name, mdata, NULL, mf, uf, name##_set_mp_ws, name##_get_mp_ws,\ + name##_set_mp_rs, name##_get_mp_rs } + +/* + * Analog of other XXX_CB macros. Has the pointer to _nss_cache_info + * structure as the only argument. + */ +#define NS_CACHE_CB(cinfo) {NSSRC_CACHE, __nss_cache_handler, (void *)(cinfo) }, + +/* args are: current pointer, current buffer, initial buffer, pointer type */ +#define NS_APPLY_OFFSET(cp, cb, ib, p_type) \ + if ((cp) != NULL) \ + (cp) = (p_type)((char *)(cb) + (size_t)(cp) - (size_t)(ib)) +/* + * Gets new pointer from the marshalled buffer by uisng initial address + * and initial buffer address + */ +#define NS_GET_NEWP(cp, cb, ib) \ + ((char *)(cb) + (size_t)(cp) - (size_t)(ib)) + +typedef struct _nss_cache_data { + char *key; + size_t key_size; + + nss_cache_info const *info; +} nss_cache_data; + +__BEGIN_DECLS +/* dummy function, which is needed to make nss_method_lookup happy */ +extern int __nss_cache_handler(void *, void *, va_list); + +#ifdef _NS_PRIVATE +extern int __nss_common_cache_read(void *, void *, va_list); +extern int __nss_common_cache_write(void *, void *, va_list); +extern int __nss_common_cache_write_negative(void *); + +extern int __nss_mp_cache_read(void *, void *, va_list); +extern int __nss_mp_cache_write(void *, void *, va_list); +extern int __nss_mp_cache_write_submit(void *, void *, va_list); +extern int __nss_mp_cache_end(void *, void *, va_list); +#endif /* _NS_PRIVATE */ + +__END_DECLS + +#endif diff --git a/lib/libc/include/nscachedcli.h b/lib/libc/include/nscachedcli.h new file mode 100644 index 000000000000..b0f79bd9d05c --- /dev/null +++ b/lib/libc/include/nscachedcli.h @@ -0,0 +1,107 @@ +/*- + * Copyright (c) 2004 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __NS_CACHED_CLI_H__ +#define __NS_CACHED_CLI_H__ + +/* + * This file contains API for working with caching daemon + */ + +enum comm_element_t { + CET_UNDEFINED = 0, + CET_WRITE_REQUEST = 1, + CET_WRITE_RESPONSE = 2, + CET_READ_REQUEST = 3, + CET_READ_RESPONSE = 4, + CET_TRANSFORM_REQUEST = 5, + CET_TRANSFORM_RESPONSE = 6, + CET_MP_WRITE_SESSION_REQUEST = 7, + CET_MP_WRITE_SESSION_RESPONSE = 8, + CET_MP_WRITE_SESSION_WRITE_REQUEST = 9, + CET_MP_WRITE_SESSION_WRITE_RESPONSE = 10, + CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION = 11, + CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION = 12, + CET_MP_READ_SESSION_REQUEST = 13, + CET_MP_READ_SESSION_RESPONSE = 14, + CET_MP_READ_SESSION_READ_REQUEST = 15, + CET_MP_READ_SESSION_READ_RESPONSE = 16, + CET_MP_READ_SESSION_CLOSE_NOTIFICATION = 17 +}; + +struct cached_connection_params { + char *socket_path; + struct timeval timeout; +}; + +struct cached_connection_ { + int sockfd; + int read_queue; + int write_queue; + + int mp_flag; /* shows if the connection is used for + * multipart operations */ +}; + +/* simple abstractions for not to write "struct" every time */ +typedef struct cached_connection_ *cached_connection; +typedef struct cached_connection_ *cached_mp_write_session; +typedef struct cached_connection_ *cached_mp_read_session; + +#define INVALID_CACHED_CONNECTION (NULL) +#define INVALID_CACHED_MP_WRITE_SESSION (NULL) +#define INVALID_CACHED_MP_READ_SESSION (NULL) + +__BEGIN_DECLS + +/* initialization/destruction routines */ +extern cached_connection __open_cached_connection( + struct cached_connection_params const *); +extern void __close_cached_connection(cached_connection); + +/* simple read/write operations */ +extern int __cached_write(cached_connection, const char *, const char *, + size_t, const char *, size_t); +extern int __cached_read(cached_connection, const char *, const char *, + size_t, char *, size_t *); + +/* multipart read/write operations */ +extern cached_mp_write_session __open_cached_mp_write_session( + struct cached_connection_params const *, const char *); +extern int __cached_mp_write(cached_mp_write_session, const char *, size_t); +extern int __abandon_cached_mp_write_session(cached_mp_write_session); +extern int __close_cached_mp_write_session(cached_mp_write_session); + +extern cached_mp_read_session __open_cached_mp_read_session( + struct cached_connection_params const *, const char *); +extern int __cached_mp_read(cached_mp_read_session, char *, size_t *); +extern int __close_cached_mp_read_session(cached_mp_read_session); + +__END_DECLS + +#endif diff --git a/lib/libc/net/Makefile.inc b/lib/libc/net/Makefile.inc index 3c363cd1400f..9921f8d61d17 100644 --- a/lib/libc/net/Makefile.inc +++ b/lib/libc/net/Makefile.inc @@ -1,123 +1,126 @@ # from @(#)Makefile.inc 8.2 (Berkeley) 9/5/93 # $FreeBSD$ # machine-independent net sources .PATH: ${.CURDIR}/${MACHINE_ARCH}/net ${.CURDIR}/net SRCS+= addr2ascii.c ascii2addr.c base64.c ether_addr.c eui64.c \ gai_strerror.c getaddrinfo.c \ gethostbydns.c gethostbyht.c gethostbynis.c gethostnamadr.c \ getifaddrs.c getifmaddrs.c getnameinfo.c \ getnetbydns.c getnetbyht.c getnetbynis.c getnetnamadr.c \ - getproto.c getprotoent.c getprotoname.c getservbyname.c \ - getservbyport.c getservent.c \ + getproto.c getprotoent.c getprotoname.c getservent.c \ if_indextoname.c if_nameindex.c if_nametoindex.c \ ip6opt.c linkaddr.c map_v4v6.c name6.c \ nsdispatch.c nslexer.c nsparser.c nss_compat.c \ rcmd.c rcmdsh.c recv.c rthdr.c send.c sockatmark.c vars.c +.if ${MK_NS_CACHING} != "no" +SRCS+= nscache.c nscachedcli.c +.endif + # for binary backward compatibility against FreeBSD 6.X and earlier SRCS+= res_mkupdate.c res_update.c SYM_MAPS+=${.CURDIR}/net/Symbol.map CFLAGS+=-DINET6 -I${.OBJDIR} # name6.c refers res_private.h CFLAGS+=-I${.CURDIR}/resolv YFLAGS+=-p_nsyy LFLAGS+=-P_nsyy CLEANFILES+=nsparser.c nslexer.c nsparser.h nsparser.h: nsparser.c mv y.tab.h ${.TARGET} nslexer.c: nslexer.l nsparser.h ${LEX} ${LFLAGS} -o/dev/stdout ${.IMPSRC} | \ sed -e '/YY_BUF_SIZE/s/16384/1024/' >${.TARGET} # machine-dependent net sources .if exists(${.CURDIR}/${MACHINE_ARCH}/net/Makefile.inc) .include "${.CURDIR}/${MACHINE_ARCH}/net/Makefile.inc" .endif MAN+= addr2ascii.3 byteorder.3 ethers.3 eui64.3 \ getaddrinfo.3 gai_strerror.3 gethostbyname.3 \ getifaddrs.3 getifmaddrs.3 getipnodebyname.3 \ getnameinfo.3 getnetent.3 getprotoent.3 getservent.3 \ if_indextoname.3 \ inet.3 inet_net.3 \ inet6_opt_init.3 inet6_option_space.3 inet6_rth_space.3 \ inet6_rthdr_space.3 linkaddr.3 \ nsdispatch.3 rcmd.3 rcmdsh.3 resolver.3 sockatmark.3 MLINKS+=addr2ascii.3 ascii2addr.3 MLINKS+=byteorder.3 htonl.3 byteorder.3 htons.3 byteorder.3 ntohl.3 \ byteorder.3 ntohs.3 MLINKS+=ethers.3 ether_aton.3 ethers.3 ether_hostton.3 ethers.3 ether_line.3 \ ethers.3 ether_ntoa.3 ethers.3 ether_ntohost.3 MLINKS+=eui64.3 eui64_aton.3 eui64.3 eui64_hostton.3 \ eui64.3 eui64_ntoa.3 eui64.3 eui64_ntohost.3 MLINKS+=getaddrinfo.3 freeaddrinfo.3 MLINKS+=gethostbyname.3 endhostent.3 gethostbyname.3 gethostbyaddr.3 \ gethostbyname.3 gethostbyname2.3 gethostbyname.3 gethostent.3 \ gethostbyname.3 herror.3 gethostbyname.3 hstrerror.3 \ gethostbyname.3 sethostent.3 MLINKS+=getifaddrs.3 freeifaddrs.3 MLINKS+=getifmaddrs.3 freeifmaddrs.3 MLINKS+=getipnodebyname.3 getipnodebyaddr.3 getipnodebyname.3 freehostent.3 MLINKS+=getnetent.3 endnetent.3 getnetent.3 getnetbyaddr.3 \ getnetent.3 getnetbyname.3 getnetent.3 setnetent.3 MLINKS+=getprotoent.3 endprotoent.3 getprotoent.3 getprotobyname.3 \ getprotoent.3 getprotobynumber.3 getprotoent.3 setprotoent.3 MLINKS+=getservent.3 endservent.3 getservent.3 getservbyname.3 \ getservent.3 getservbyport.3 getservent.3 setservent.3 MLINKS+=if_indextoname.3 if_nametoindex.3 if_indextoname.3 if_nameindex.3 \ if_indextoname.3 if_freenameindex.3 MLINKS+=inet.3 addr.3 inet.3 inet_addr.3 inet.3 inet_aton.3 \ inet.3 inet_lnaof.3 inet.3 inet_makeaddr.3 inet.3 inet_netof.3 \ inet.3 inet_network.3 inet.3 inet_ntoa.3 \ inet.3 inet_ntop.3 inet.3 inet_pton.3 \ inet.3 network.3 inet.3 ntoa.3 MLINKS+=inet_net.3 inet_net_ntop.3 inet_net.3 inet_net_pton.3 MLINKS+=inet6_opt_init.3 inet6_opt_append.3 \ inet6_opt_init.3 inet6_opt_find.3 \ inet6_opt_init.3 inet6_opt_finish.3 \ inet6_opt_init.3 inet6_opt_get_val.3 \ inet6_opt_init.3 inet6_opt_next.3 \ inet6_opt_init.3 inet6_opt_set_val.3 \ inet6_option_space.3 inet6_option_alloc.3 \ inet6_option_space.3 inet6_option_append.3 \ inet6_option_space.3 inet6_option_find.3 \ inet6_option_space.3 inet6_option_init.3 \ inet6_option_space.3 inet6_option_next.3 \ inet6_rth_space.3 inet6_rth_add.3 \ inet6_rth_space.3 inet6_rth_getaddr.3 \ inet6_rth_space.3 inet6_rth_init.3 \ inet6_rth_space.3 inet6_rth_reverse.3 \ inet6_rth_space.3 inet6_rth_segments.3 \ inet6_rthdr_space.3 inet6_rthdr_add.3 \ inet6_rthdr_space.3 inet6_rthdr_getaddr.3 \ inet6_rthdr_space.3 inet6_rthdr_getflags.3 \ inet6_rthdr_space.3 inet6_rthdr_init.3 \ inet6_rthdr_space.3 inet6_rthdr_lasthop.3 \ inet6_rthdr_space.3 inet6_rthdr_reverse.3 \ inet6_rthdr_space.3 inet6_rthdr_segments.3 MLINKS+=linkaddr.3 link_addr.3 linkaddr.3 link_ntoa.3 MLINKS+=rcmd.3 iruserok.3 rcmd.3 iruserok_sa.3 \ rcmd.3 rcmd_af.3 \ rcmd.3 rresvport.3 rcmd.3 rresvport_af.3 \ rcmd.3 ruserok.3 MLINKS+=resolver.3 dn_comp.3 resolver.3 dn_expand.3 resolver.3 res_init.3 \ resolver.3 res_mkquery.3 resolver.3 res_query.3 \ resolver.3 res_search.3 resolver.3 res_send.3 resolver.3 dn_skipname.3 \ resolver.3 ns_get16.3 resolver.3 ns_get32.3 \ resolver.3 ns_put16.3 resolver.3 ns_put32.3 .if ${MK_HESIOD} != "no" SRCS+= hesiod.c MAN+= hesiod.3 .endif diff --git a/lib/libc/net/getaddrinfo.c b/lib/libc/net/getaddrinfo.c index a2552a9ec819..b422c4bb6838 100644 --- a/lib/libc/net/getaddrinfo.c +++ b/lib/libc/net/getaddrinfo.c @@ -1,2575 +1,2772 @@ /* $KAME: getaddrinfo.c,v 1.15 2000/07/09 04:37:24 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * 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. Neither the name of the project 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 PROJECT 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 PROJECT 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. */ /* * "#ifdef FAITH" part is local hack for supporting IPv4-v6 translator. * * Issues to be discussed: * - Thread safe-ness must be checked. * - Return values. There are nonstandard return values defined and used * in the source code. This is because RFC2553 is silent about which error * code must be returned for which situation. * - freeaddrinfo(NULL). RFC2553 is silent about it. XNET 5.2 says it is * invalid. current code - SEGV on freeaddrinfo(NULL) * * Note: * - The code filters out AFs that are not supported by the kernel, * when globbing NULL hostname (to loopback, or wildcard). Is it the right * thing to do? What is the relationship with post-RFC2553 AI_ADDRCONFIG * in ai_flags? * - (post-2553) semantics of AI_ADDRCONFIG itself is too vague. * (1) what should we do against numeric hostname (2) what should we do * against NULL hostname (3) what is AI_ADDRCONFIG itself. AF not ready? * non-loopback address configured? global address configured? * * OS specific notes for netbsd/openbsd/freebsd4/bsdi4: * - To avoid search order issue, we have a big amount of code duplicate * from gethnamaddr.c and some other places. The issues that there's no * lower layer function to lookup "IPv4 or IPv6" record. Calling * gethostbyname2 from getaddrinfo will end up in wrong search order, as * presented above. * * OS specific notes for freebsd4: * - FreeBSD supported $GAI. The code does not. * - FreeBSD allowed classful IPv4 numeric (127.1), the code does not. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #ifdef INET6 #include #include #include #include /* XXX */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "res_config.h" #ifdef DEBUG #include #endif #include #include #include "un-namespace.h" #include "libc_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif #if defined(__KAME__) && defined(INET6) # define FAITH #endif #define SUCCESS 0 #define ANY 0 #define YES 1 #define NO 0 static const char in_addrany[] = { 0, 0, 0, 0 }; static const char in_loopback[] = { 127, 0, 0, 1 }; #ifdef INET6 static const char in6_addrany[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static const char in6_loopback[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; #endif struct policyqueue { TAILQ_ENTRY(policyqueue) pc_entry; #ifdef INET6 struct in6_addrpolicy pc_policy; #endif }; TAILQ_HEAD(policyhead, policyqueue); static const struct afd { int a_af; int a_addrlen; socklen_t a_socklen; int a_off; const char *a_addrany; const char *a_loopback; int a_scoped; } afdl [] = { #ifdef INET6 #define N_INET6 0 {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), offsetof(struct sockaddr_in6, sin6_addr), in6_addrany, in6_loopback, 1}, #define N_INET 1 #else #define N_INET 0 #endif {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), offsetof(struct sockaddr_in, sin_addr), in_addrany, in_loopback, 0}, {0, 0, 0, 0, NULL, NULL, 0}, }; struct explore { int e_af; int e_socktype; int e_protocol; const char *e_protostr; int e_wild; #define WILD_AF(ex) ((ex)->e_wild & 0x01) #define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02) #define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04) }; static const struct explore explore[] = { #if 0 { PF_LOCAL, 0, ANY, ANY, NULL, 0x01 }, #endif #ifdef INET6 { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 }, #endif { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, { PF_INET, SOCK_RAW, ANY, NULL, 0x05 }, { PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, { PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, { PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 }, { -1, 0, 0, NULL, 0 }, }; #ifdef INET6 #define PTON_MAX 16 #else #define PTON_MAX 4 #endif #define AIO_SRCFLAG_DEPRECATED 0x1 struct ai_order { union { struct sockaddr_storage aiou_ss; struct sockaddr aiou_sa; } aio_src_un; #define aio_srcsa aio_src_un.aiou_sa u_int32_t aio_srcflag; int aio_srcscope; int aio_dstscope; struct policyqueue *aio_srcpolicy; struct policyqueue *aio_dstpolicy; struct addrinfo *aio_ai; int aio_matchlen; }; static const ns_src default_dns_files[] = { { NSSRC_FILES, NS_SUCCESS }, { NSSRC_DNS, NS_SUCCESS }, { 0 } }; struct res_target { struct res_target *next; const char *name; /* domain name */ int qclass, qtype; /* class and type of query */ u_char *answer; /* buffer to put answer */ int anslen; /* size of answer buffer */ int n; /* result length */ }; #define MAXPACKET (64*1024) typedef union { HEADER hdr; u_char buf[MAXPACKET]; } querybuf; static int str2number(const char *); static int explore_null(const struct addrinfo *, const char *, struct addrinfo **); static int explore_numeric(const struct addrinfo *, const char *, const char *, struct addrinfo **, const char *); static int explore_numeric_scope(const struct addrinfo *, const char *, const char *, struct addrinfo **); static int get_canonname(const struct addrinfo *, struct addrinfo *, const char *); static struct addrinfo *get_ai(const struct addrinfo *, const struct afd *, const char *); static int get_portmatch(const struct addrinfo *, const char *); static int get_port(struct addrinfo *, const char *, int); static const struct afd *find_afd(int); static int addrconfig(struct addrinfo *); static void set_source(struct ai_order *, struct policyhead *); static int comp_dst(const void *, const void *); #ifdef INET6 static int ip6_str2scopeid(char *, struct sockaddr_in6 *, u_int32_t *); #endif static int gai_addr2scopetype(struct sockaddr *); static int explore_fqdn(const struct addrinfo *, const char *, const char *, struct addrinfo **); static int reorder(struct addrinfo *); static int get_addrselectpolicy(struct policyhead *); static void free_addrselectpolicy(struct policyhead *); static struct policyqueue *match_addrselectpolicy(struct sockaddr *, struct policyhead *); static int matchlen(struct sockaddr *, struct sockaddr *); static struct addrinfo *getanswer(const querybuf *, int, const char *, int, const struct addrinfo *, res_state); #if defined(RESOLVSORT) static int addr4sort(struct addrinfo *, res_state); #endif static int _dns_getaddrinfo(void *, void *, va_list); static void _sethtent(FILE **); static void _endhtent(FILE **); static struct addrinfo *_gethtent(FILE **, const char *, const struct addrinfo *); static int _files_getaddrinfo(void *, void *, va_list); #ifdef YP static struct addrinfo *_yphostent(char *, const struct addrinfo *); static int _yp_getaddrinfo(void *, void *, va_list); #endif +#ifdef NS_CACHING +static int addrinfo_id_func(char *, size_t *, va_list, void *); +static int addrinfo_marshal_func(char *, size_t *, void *, va_list, void *); +static int addrinfo_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif static int res_queryN(const char *, struct res_target *, res_state); static int res_searchN(const char *, struct res_target *, res_state); static int res_querydomainN(const char *, const char *, struct res_target *, res_state); /* XXX macros that make external reference is BAD. */ #define GET_AI(ai, afd, addr) \ do { \ /* external reference: pai, error, and label free */ \ (ai) = get_ai(pai, (afd), (addr)); \ if ((ai) == NULL) { \ error = EAI_MEMORY; \ goto free; \ } \ } while (/*CONSTCOND*/0) #define GET_PORT(ai, serv) \ do { \ /* external reference: error and label free */ \ error = get_port((ai), (serv), 0); \ if (error != 0) \ goto free; \ } while (/*CONSTCOND*/0) #define GET_CANONNAME(ai, str) \ do { \ /* external reference: pai, error and label free */ \ error = get_canonname(pai, (ai), (str)); \ if (error != 0) \ goto free; \ } while (/*CONSTCOND*/0) #define ERR(err) \ do { \ /* external reference: error, and label bad */ \ error = (err); \ goto bad; \ /*NOTREACHED*/ \ } while (/*CONSTCOND*/0) #define MATCH_FAMILY(x, y, w) \ ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC))) #define MATCH(x, y, w) \ ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY))) void freeaddrinfo(struct addrinfo *ai) { struct addrinfo *next; do { next = ai->ai_next; if (ai->ai_canonname) free(ai->ai_canonname); /* no need to free(ai->ai_addr) */ free(ai); ai = next; } while (ai); } static int str2number(const char *p) { char *ep; unsigned long v; if (*p == '\0') return -1; ep = NULL; errno = 0; v = strtoul(p, &ep, 10); if (errno == 0 && ep && *ep == '\0' && v <= UINT_MAX) return v; else return -1; } int getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res) { struct addrinfo sentinel; struct addrinfo *cur; int error = 0; struct addrinfo ai; struct addrinfo ai0; struct addrinfo *pai; const struct explore *ex; int numeric = 0; memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; pai = &ai; pai->ai_flags = 0; pai->ai_family = PF_UNSPEC; pai->ai_socktype = ANY; pai->ai_protocol = ANY; pai->ai_addrlen = 0; pai->ai_canonname = NULL; pai->ai_addr = NULL; pai->ai_next = NULL; if (hostname == NULL && servname == NULL) return EAI_NONAME; if (hints) { /* error check for hints */ if (hints->ai_addrlen || hints->ai_canonname || hints->ai_addr || hints->ai_next) ERR(EAI_BADHINTS); /* xxx */ if (hints->ai_flags & ~AI_MASK) ERR(EAI_BADFLAGS); switch (hints->ai_family) { case PF_UNSPEC: case PF_INET: #ifdef INET6 case PF_INET6: #endif break; default: ERR(EAI_FAMILY); } memcpy(pai, hints, sizeof(*pai)); /* * if both socktype/protocol are specified, check if they * are meaningful combination. */ if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) { for (ex = explore; ex->e_af >= 0; ex++) { if (pai->ai_family != ex->e_af) continue; if (ex->e_socktype == ANY) continue; if (ex->e_protocol == ANY) continue; if (pai->ai_socktype == ex->e_socktype && pai->ai_protocol != ex->e_protocol) { ERR(EAI_BADHINTS); } } } } /* * post-2553: AI_ALL and AI_V4MAPPED are effective only against * AF_INET6 query. They need to be ignored if specified in other * occassions. */ switch (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) { case AI_V4MAPPED: case AI_ALL | AI_V4MAPPED: if (pai->ai_family != AF_INET6) pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); break; case AI_ALL: #if 1 /* illegal */ ERR(EAI_BADFLAGS); #else pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); #endif break; } /* * check for special cases. (1) numeric servname is disallowed if * socktype/protocol are left unspecified. (2) servname is disallowed * for raw and other inet{,6} sockets. */ if (MATCH_FAMILY(pai->ai_family, PF_INET, 1) #ifdef PF_INET6 || MATCH_FAMILY(pai->ai_family, PF_INET6, 1) #endif ) { ai0 = *pai; /* backup *pai */ if (pai->ai_family == PF_UNSPEC) { #ifdef PF_INET6 pai->ai_family = PF_INET6; #else pai->ai_family = PF_INET; #endif } error = get_portmatch(pai, servname); if (error) ERR(error); *pai = ai0; } ai0 = *pai; /* NULL hostname, or numeric hostname */ for (ex = explore; ex->e_af >= 0; ex++) { *pai = ai0; /* PF_UNSPEC entries are prepared for DNS queries only */ if (ex->e_af == PF_UNSPEC) continue; if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) continue; if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) continue; if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) continue; if (pai->ai_family == PF_UNSPEC) pai->ai_family = ex->e_af; if (pai->ai_socktype == ANY && ex->e_socktype != ANY) pai->ai_socktype = ex->e_socktype; if (pai->ai_protocol == ANY && ex->e_protocol != ANY) pai->ai_protocol = ex->e_protocol; if (hostname == NULL) error = explore_null(pai, servname, &cur->ai_next); else error = explore_numeric_scope(pai, hostname, servname, &cur->ai_next); if (error) goto free; while (cur && cur->ai_next) cur = cur->ai_next; } /* * XXX * If numreic representation of AF1 can be interpreted as FQDN * representation of AF2, we need to think again about the code below. */ if (sentinel.ai_next) { numeric = 1; goto good; } if (hostname == NULL) ERR(EAI_NONAME); /* used to be EAI_NODATA */ if (pai->ai_flags & AI_NUMERICHOST) ERR(EAI_NONAME); if ((pai->ai_flags & AI_ADDRCONFIG) != 0 && !addrconfig(&ai0)) ERR(EAI_FAIL); /* * hostname as alphabetical name. * we would like to prefer AF_INET6 than AF_INET, so we'll make a * outer loop by AFs. */ for (ex = explore; ex->e_af >= 0; ex++) { *pai = ai0; /* require exact match for family field */ if (pai->ai_family != ex->e_af) continue; if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) { continue; } if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) { continue; } if (pai->ai_socktype == ANY && ex->e_socktype != ANY) pai->ai_socktype = ex->e_socktype; if (pai->ai_protocol == ANY && ex->e_protocol != ANY) pai->ai_protocol = ex->e_protocol; error = explore_fqdn(pai, hostname, servname, &cur->ai_next); while (cur && cur->ai_next) cur = cur->ai_next; } /* XXX inhibit errors if we have the result */ if (sentinel.ai_next) error = 0; good: /* * ensure we return either: * - error == 0, non-NULL *res * - error != 0, NULL *res */ if (error == 0) { if (sentinel.ai_next) { /* * If the returned entry is for an active connection, * and the given name is not numeric, reorder the * list, so that the application would try the list * in the most efficient order. */ if (hints == NULL || !(hints->ai_flags & AI_PASSIVE)) { if (!numeric) (void)reorder(&sentinel); } *res = sentinel.ai_next; return SUCCESS; } else error = EAI_FAIL; } free: bad: if (sentinel.ai_next) freeaddrinfo(sentinel.ai_next); *res = NULL; return error; } static int reorder(struct addrinfo *sentinel) { struct addrinfo *ai, **aip; struct ai_order *aio; int i, n; struct policyhead policyhead; /* count the number of addrinfo elements for sorting. */ for (n = 0, ai = sentinel->ai_next; ai != NULL; ai = ai->ai_next, n++) ; /* * If the number is small enough, we can skip the reordering process. */ if (n <= 1) return(n); /* allocate a temporary array for sort and initialization of it. */ if ((aio = malloc(sizeof(*aio) * n)) == NULL) return(n); /* give up reordering */ memset(aio, 0, sizeof(*aio) * n); /* retrieve address selection policy from the kernel */ TAILQ_INIT(&policyhead); if (!get_addrselectpolicy(&policyhead)) { /* no policy is installed into kernel, we don't sort. */ free(aio); return (n); } for (i = 0, ai = sentinel->ai_next; i < n; ai = ai->ai_next, i++) { aio[i].aio_ai = ai; aio[i].aio_dstscope = gai_addr2scopetype(ai->ai_addr); aio[i].aio_dstpolicy = match_addrselectpolicy(ai->ai_addr, &policyhead); set_source(&aio[i], &policyhead); } /* perform sorting. */ qsort(aio, n, sizeof(*aio), comp_dst); /* reorder the addrinfo chain. */ for (i = 0, aip = &sentinel->ai_next; i < n; i++) { *aip = aio[i].aio_ai; aip = &aio[i].aio_ai->ai_next; } *aip = NULL; /* cleanup and return */ free(aio); free_addrselectpolicy(&policyhead); return(n); } static int get_addrselectpolicy(struct policyhead *head) { #ifdef INET6 int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY }; size_t l; char *buf; struct in6_addrpolicy *pol, *ep; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) return (0); if ((buf = malloc(l)) == NULL) return (0); if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { free(buf); return (0); } ep = (struct in6_addrpolicy *)(buf + l); for (pol = (struct in6_addrpolicy *)buf; pol + 1 <= ep; pol++) { struct policyqueue *new; if ((new = malloc(sizeof(*new))) == NULL) { free_addrselectpolicy(head); /* make the list empty */ break; } new->pc_policy = *pol; TAILQ_INSERT_TAIL(head, new, pc_entry); } free(buf); return (1); #else return (0); #endif } static void free_addrselectpolicy(struct policyhead *head) { struct policyqueue *ent, *nent; for (ent = TAILQ_FIRST(head); ent; ent = nent) { nent = TAILQ_NEXT(ent, pc_entry); TAILQ_REMOVE(head, ent, pc_entry); free(ent); } } static struct policyqueue * match_addrselectpolicy(struct sockaddr *addr, struct policyhead *head) { #ifdef INET6 struct policyqueue *ent, *bestent = NULL; struct in6_addrpolicy *pol; int matchlen, bestmatchlen = -1; u_char *mp, *ep, *k, *p, m; struct sockaddr_in6 key; switch(addr->sa_family) { case AF_INET6: key = *(struct sockaddr_in6 *)addr; break; case AF_INET: /* convert the address into IPv4-mapped IPv6 address. */ memset(&key, 0, sizeof(key)); key.sin6_family = AF_INET6; key.sin6_len = sizeof(key); key.sin6_addr.s6_addr[10] = 0xff; key.sin6_addr.s6_addr[11] = 0xff; memcpy(&key.sin6_addr.s6_addr[12], &((struct sockaddr_in *)addr)->sin_addr, 4); break; default: return(NULL); } for (ent = TAILQ_FIRST(head); ent; ent = TAILQ_NEXT(ent, pc_entry)) { pol = &ent->pc_policy; matchlen = 0; mp = (u_char *)&pol->addrmask.sin6_addr; ep = mp + 16; /* XXX: scope field? */ k = (u_char *)&key.sin6_addr; p = (u_char *)&pol->addr.sin6_addr; for (; mp < ep && *mp; mp++, k++, p++) { m = *mp; if ((*k & m) != *p) goto next; /* not match */ if (m == 0xff) /* short cut for a typical case */ matchlen += 8; else { while (m >= 0x80) { matchlen++; m <<= 1; } } } /* matched. check if this is better than the current best. */ if (matchlen > bestmatchlen) { bestent = ent; bestmatchlen = matchlen; } next: continue; } return(bestent); #else return(NULL); #endif } static void set_source(struct ai_order *aio, struct policyhead *ph) { struct addrinfo ai = *aio->aio_ai; struct sockaddr_storage ss; socklen_t srclen; int s; /* set unspec ("no source is available"), just in case */ aio->aio_srcsa.sa_family = AF_UNSPEC; aio->aio_srcscope = -1; switch(ai.ai_family) { case AF_INET: #ifdef INET6 case AF_INET6: #endif break; default: /* ignore unsupported AFs explicitly */ return; } /* XXX: make a dummy addrinfo to call connect() */ ai.ai_socktype = SOCK_DGRAM; ai.ai_protocol = IPPROTO_UDP; /* is UDP too specific? */ ai.ai_next = NULL; memset(&ss, 0, sizeof(ss)); memcpy(&ss, ai.ai_addr, ai.ai_addrlen); ai.ai_addr = (struct sockaddr *)&ss; get_port(&ai, "1", 0); /* open a socket to get the source address for the given dst */ if ((s = _socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol)) < 0) return; /* give up */ if (_connect(s, ai.ai_addr, ai.ai_addrlen) < 0) goto cleanup; srclen = ai.ai_addrlen; if (_getsockname(s, &aio->aio_srcsa, &srclen) < 0) { aio->aio_srcsa.sa_family = AF_UNSPEC; goto cleanup; } aio->aio_srcscope = gai_addr2scopetype(&aio->aio_srcsa); aio->aio_srcpolicy = match_addrselectpolicy(&aio->aio_srcsa, ph); aio->aio_matchlen = matchlen(&aio->aio_srcsa, aio->aio_ai->ai_addr); #ifdef INET6 if (ai.ai_family == AF_INET6) { struct in6_ifreq ifr6; u_int32_t flags6; /* XXX: interface name should not be hardcoded */ strncpy(ifr6.ifr_name, "lo0", sizeof(ifr6.ifr_name)); memset(&ifr6, 0, sizeof(ifr6)); memcpy(&ifr6.ifr_addr, ai.ai_addr, ai.ai_addrlen); if (_ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) == 0) { flags6 = ifr6.ifr_ifru.ifru_flags6; if ((flags6 & IN6_IFF_DEPRECATED)) aio->aio_srcflag |= AIO_SRCFLAG_DEPRECATED; } } #endif cleanup: _close(s); return; } static int matchlen(struct sockaddr *src, struct sockaddr *dst) { int match = 0; u_char *s, *d; u_char *lim, r; int addrlen; switch (src->sa_family) { #ifdef INET6 case AF_INET6: s = (u_char *)&((struct sockaddr_in6 *)src)->sin6_addr; d = (u_char *)&((struct sockaddr_in6 *)dst)->sin6_addr; addrlen = sizeof(struct in6_addr); lim = s + addrlen; break; #endif case AF_INET: s = (u_char *)&((struct sockaddr_in *)src)->sin_addr; d = (u_char *)&((struct sockaddr_in *)dst)->sin_addr; addrlen = sizeof(struct in_addr); lim = s + addrlen; break; default: return(0); } while (s < lim) if ((r = (*d++ ^ *s++)) != 0) { while (r < addrlen * 8) { match++; r <<= 1; } break; } else match += 8; return(match); } static int comp_dst(const void *arg1, const void *arg2) { const struct ai_order *dst1 = arg1, *dst2 = arg2; /* * Rule 1: Avoid unusable destinations. * XXX: we currently do not consider if an appropriate route exists. */ if (dst1->aio_srcsa.sa_family != AF_UNSPEC && dst2->aio_srcsa.sa_family == AF_UNSPEC) { return(-1); } if (dst1->aio_srcsa.sa_family == AF_UNSPEC && dst2->aio_srcsa.sa_family != AF_UNSPEC) { return(1); } /* Rule 2: Prefer matching scope. */ if (dst1->aio_dstscope == dst1->aio_srcscope && dst2->aio_dstscope != dst2->aio_srcscope) { return(-1); } if (dst1->aio_dstscope != dst1->aio_srcscope && dst2->aio_dstscope == dst2->aio_srcscope) { return(1); } /* Rule 3: Avoid deprecated addresses. */ if (dst1->aio_srcsa.sa_family != AF_UNSPEC && dst2->aio_srcsa.sa_family != AF_UNSPEC) { if (!(dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && (dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { return(-1); } if ((dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && !(dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { return(1); } } /* Rule 4: Prefer home addresses. */ /* XXX: not implemented yet */ /* Rule 5: Prefer matching label. */ #ifdef INET6 if (dst1->aio_srcpolicy && dst1->aio_dstpolicy && dst1->aio_srcpolicy->pc_policy.label == dst1->aio_dstpolicy->pc_policy.label && (dst2->aio_srcpolicy == NULL || dst2->aio_dstpolicy == NULL || dst2->aio_srcpolicy->pc_policy.label != dst2->aio_dstpolicy->pc_policy.label)) { return(-1); } if (dst2->aio_srcpolicy && dst2->aio_dstpolicy && dst2->aio_srcpolicy->pc_policy.label == dst2->aio_dstpolicy->pc_policy.label && (dst1->aio_srcpolicy == NULL || dst1->aio_dstpolicy == NULL || dst1->aio_srcpolicy->pc_policy.label != dst1->aio_dstpolicy->pc_policy.label)) { return(1); } #endif /* Rule 6: Prefer higher precedence. */ #ifdef INET6 if (dst1->aio_dstpolicy && (dst2->aio_dstpolicy == NULL || dst1->aio_dstpolicy->pc_policy.preced > dst2->aio_dstpolicy->pc_policy.preced)) { return(-1); } if (dst2->aio_dstpolicy && (dst1->aio_dstpolicy == NULL || dst2->aio_dstpolicy->pc_policy.preced > dst1->aio_dstpolicy->pc_policy.preced)) { return(1); } #endif /* Rule 7: Prefer native transport. */ /* XXX: not implemented yet */ /* Rule 8: Prefer smaller scope. */ if (dst1->aio_dstscope >= 0 && dst1->aio_dstscope < dst2->aio_dstscope) { return(-1); } if (dst2->aio_dstscope >= 0 && dst2->aio_dstscope < dst1->aio_dstscope) { return(1); } /* * Rule 9: Use longest matching prefix. * We compare the match length in a same AF only. */ if (dst1->aio_ai->ai_addr->sa_family == dst2->aio_ai->ai_addr->sa_family) { if (dst1->aio_matchlen > dst2->aio_matchlen) { return(-1); } if (dst1->aio_matchlen < dst2->aio_matchlen) { return(1); } } /* Rule 10: Otherwise, leave the order unchanged. */ return(-1); } /* * Copy from scope.c. * XXX: we should standardize the functions and link them as standard * library. */ static int gai_addr2scopetype(struct sockaddr *sa) { #ifdef INET6 struct sockaddr_in6 *sa6; #endif struct sockaddr_in *sa4; switch(sa->sa_family) { #ifdef INET6 case AF_INET6: sa6 = (struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) { /* just use the scope field of the multicast address */ return(sa6->sin6_addr.s6_addr[2] & 0x0f); } /* * Unicast addresses: map scope type to corresponding scope * value defined for multcast addresses. * XXX: hardcoded scope type values are bad... */ if (IN6_IS_ADDR_LOOPBACK(&sa6->sin6_addr)) return(1); /* node local scope */ if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) return(2); /* link-local scope */ if (IN6_IS_ADDR_SITELOCAL(&sa6->sin6_addr)) return(5); /* site-local scope */ return(14); /* global scope */ break; #endif case AF_INET: /* * IPv4 pseudo scoping according to RFC 3484. */ sa4 = (struct sockaddr_in *)sa; /* IPv4 autoconfiguration addresses have link-local scope. */ if (((u_char *)&sa4->sin_addr)[0] == 169 && ((u_char *)&sa4->sin_addr)[1] == 254) return(2); /* Private addresses have site-local scope. */ if (((u_char *)&sa4->sin_addr)[0] == 10 || (((u_char *)&sa4->sin_addr)[0] == 172 && (((u_char *)&sa4->sin_addr)[1] & 0xf0) == 16) || (((u_char *)&sa4->sin_addr)[0] == 192 && ((u_char *)&sa4->sin_addr)[1] == 168)) return(14); /* XXX: It should be 5 unless NAT */ /* Loopback addresses have link-local scope. */ if (((u_char *)&sa4->sin_addr)[0] == 127) return(2); return(14); break; default: errno = EAFNOSUPPORT; /* is this a good error? */ return(-1); } } /* * hostname == NULL. * passive socket -> anyaddr (0.0.0.0 or ::) * non-passive socket -> localhost (127.0.0.1 or ::1) */ static int explore_null(const struct addrinfo *pai, const char *servname, struct addrinfo **res) { int s; const struct afd *afd; struct addrinfo *cur; struct addrinfo sentinel; int error; *res = NULL; sentinel.ai_next = NULL; cur = &sentinel; /* * filter out AFs that are not supported by the kernel * XXX errno? */ s = _socket(pai->ai_family, SOCK_DGRAM, 0); if (s < 0) { if (errno != EMFILE) return 0; } else _close(s); /* * if the servname does not match socktype/protocol, ignore it. */ if (get_portmatch(pai, servname) != 0) return 0; afd = find_afd(pai->ai_family); if (afd == NULL) return 0; if (pai->ai_flags & AI_PASSIVE) { GET_AI(cur->ai_next, afd, afd->a_addrany); /* xxx meaningless? * GET_CANONNAME(cur->ai_next, "anyaddr"); */ GET_PORT(cur->ai_next, servname); } else { GET_AI(cur->ai_next, afd, afd->a_loopback); /* xxx meaningless? * GET_CANONNAME(cur->ai_next, "localhost"); */ GET_PORT(cur->ai_next, servname); } cur = cur->ai_next; *res = sentinel.ai_next; return 0; free: if (sentinel.ai_next) freeaddrinfo(sentinel.ai_next); return error; } /* * numeric hostname */ static int explore_numeric(const struct addrinfo *pai, const char *hostname, const char *servname, struct addrinfo **res, const char *canonname) { const struct afd *afd; struct addrinfo *cur; struct addrinfo sentinel; int error; char pton[PTON_MAX]; *res = NULL; sentinel.ai_next = NULL; cur = &sentinel; /* * if the servname does not match socktype/protocol, ignore it. */ if (get_portmatch(pai, servname) != 0) return 0; afd = find_afd(pai->ai_family); if (afd == NULL) return 0; switch (afd->a_af) { #if 1 /*X/Open spec*/ case AF_INET: if (inet_aton(hostname, (struct in_addr *)pton) == 1) { if (pai->ai_family == afd->a_af || pai->ai_family == PF_UNSPEC /*?*/) { GET_AI(cur->ai_next, afd, pton); GET_PORT(cur->ai_next, servname); if ((pai->ai_flags & AI_CANONNAME)) { /* * Set the numeric address itself as * the canonical name, based on a * clarification in rfc3493. */ GET_CANONNAME(cur->ai_next, canonname); } while (cur && cur->ai_next) cur = cur->ai_next; } else ERR(EAI_FAMILY); /*xxx*/ } break; #endif default: if (inet_pton(afd->a_af, hostname, pton) == 1) { if (pai->ai_family == afd->a_af || pai->ai_family == PF_UNSPEC /*?*/) { GET_AI(cur->ai_next, afd, pton); GET_PORT(cur->ai_next, servname); if ((pai->ai_flags & AI_CANONNAME)) { /* * Set the numeric address itself as * the canonical name, based on a * clarification in rfc3493. */ GET_CANONNAME(cur->ai_next, canonname); } while (cur && cur->ai_next) cur = cur->ai_next; } else ERR(EAI_FAMILY); /* XXX */ } break; } *res = sentinel.ai_next; return 0; free: bad: if (sentinel.ai_next) freeaddrinfo(sentinel.ai_next); return error; } /* * numeric hostname with scope */ static int explore_numeric_scope(const struct addrinfo *pai, const char *hostname, const char *servname, struct addrinfo **res) { #if !defined(SCOPE_DELIMITER) || !defined(INET6) return explore_numeric(pai, hostname, servname, res, hostname); #else const struct afd *afd; struct addrinfo *cur; int error; char *cp, *hostname2 = NULL, *scope, *addr; struct sockaddr_in6 *sin6; /* * if the servname does not match socktype/protocol, ignore it. */ if (get_portmatch(pai, servname) != 0) return 0; afd = find_afd(pai->ai_family); if (afd == NULL) return 0; if (!afd->a_scoped) return explore_numeric(pai, hostname, servname, res, hostname); cp = strchr(hostname, SCOPE_DELIMITER); if (cp == NULL) return explore_numeric(pai, hostname, servname, res, hostname); /* * Handle special case of */ hostname2 = strdup(hostname); if (hostname2 == NULL) return EAI_MEMORY; /* terminate at the delimiter */ hostname2[cp - hostname] = '\0'; addr = hostname2; scope = cp + 1; error = explore_numeric(pai, addr, servname, res, hostname); if (error == 0) { u_int32_t scopeid; for (cur = *res; cur; cur = cur->ai_next) { if (cur->ai_family != AF_INET6) continue; sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr; if (ip6_str2scopeid(scope, sin6, &scopeid) == -1) { free(hostname2); return(EAI_NONAME); /* XXX: is return OK? */ } sin6->sin6_scope_id = scopeid; } } free(hostname2); return error; #endif } static int get_canonname(const struct addrinfo *pai, struct addrinfo *ai, const char *str) { if ((pai->ai_flags & AI_CANONNAME) != 0) { ai->ai_canonname = strdup(str); if (ai->ai_canonname == NULL) return EAI_MEMORY; } return 0; } static struct addrinfo * get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr) { char *p; struct addrinfo *ai; #ifdef FAITH struct in6_addr faith_prefix; char *fp_str; int translate = 0; #endif #ifdef FAITH /* * Transfrom an IPv4 addr into a special IPv6 addr format for * IPv6->IPv4 translation gateway. (only TCP is supported now) * * +-----------------------------------+------------+ * | faith prefix part (12 bytes) | embedded | * | | IPv4 addr part (4 bytes) * +-----------------------------------+------------+ * * faith prefix part is specified as ascii IPv6 addr format * in environmental variable GAI. * For FAITH to work correctly, routing to faith prefix must be * setup toward a machine where a FAITH daemon operates. * Also, the machine must enable some mechanizm * (e.g. faith interface hack) to divert those packet with * faith prefixed destination addr to user-land FAITH daemon. */ fp_str = getenv("GAI"); if (fp_str && inet_pton(AF_INET6, fp_str, &faith_prefix) == 1 && afd->a_af == AF_INET && pai->ai_socktype == SOCK_STREAM) { u_int32_t v4a; u_int8_t v4a_top; memcpy(&v4a, addr, sizeof v4a); v4a_top = v4a >> IN_CLASSA_NSHIFT; if (!IN_MULTICAST(v4a) && !IN_EXPERIMENTAL(v4a) && v4a_top != 0 && v4a != IN_LOOPBACKNET) { afd = &afdl[N_INET6]; memcpy(&faith_prefix.s6_addr[12], addr, sizeof(struct in_addr)); translate = 1; } } #endif ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) + (afd->a_socklen)); if (ai == NULL) return NULL; memcpy(ai, pai, sizeof(struct addrinfo)); ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); memset(ai->ai_addr, 0, (size_t)afd->a_socklen); ai->ai_addr->sa_len = afd->a_socklen; ai->ai_addrlen = afd->a_socklen; ai->ai_addr->sa_family = ai->ai_family = afd->a_af; p = (char *)(void *)(ai->ai_addr); #ifdef FAITH if (translate == 1) memcpy(p + afd->a_off, &faith_prefix, (size_t)afd->a_addrlen); else #endif memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen); return ai; } static int get_portmatch(const struct addrinfo *ai, const char *servname) { /* get_port does not touch first argument when matchonly == 1. */ /* LINTED const cast */ return get_port((struct addrinfo *)ai, servname, 1); } static int get_port(struct addrinfo *ai, const char *servname, int matchonly) { const char *proto; struct servent *sp; int port; int allownumeric; if (servname == NULL) return 0; switch (ai->ai_family) { case AF_INET: #ifdef AF_INET6 case AF_INET6: #endif break; default: return 0; } switch (ai->ai_socktype) { case SOCK_RAW: return EAI_SERVICE; case SOCK_DGRAM: case SOCK_STREAM: allownumeric = 1; break; case ANY: allownumeric = 0; break; default: return EAI_SOCKTYPE; } port = str2number(servname); if (port >= 0) { if (!allownumeric) return EAI_SERVICE; if (port < 0 || port > 65535) return EAI_SERVICE; port = htons(port); } else { if (ai->ai_flags & AI_NUMERICSERV) return EAI_NONAME; switch (ai->ai_socktype) { case SOCK_DGRAM: proto = "udp"; break; case SOCK_STREAM: proto = "tcp"; break; default: proto = NULL; break; } if ((sp = getservbyname(servname, proto)) == NULL) return EAI_SERVICE; port = sp->s_port; } if (!matchonly) { switch (ai->ai_family) { case AF_INET: ((struct sockaddr_in *)(void *) ai->ai_addr)->sin_port = port; break; #ifdef INET6 case AF_INET6: ((struct sockaddr_in6 *)(void *) ai->ai_addr)->sin6_port = port; break; #endif } } return 0; } static const struct afd * find_afd(int af) { const struct afd *afd; if (af == PF_UNSPEC) return NULL; for (afd = afdl; afd->a_af; afd++) { if (afd->a_af == af) return afd; } return NULL; } /* * post-2553: AI_ADDRCONFIG check. if we use getipnodeby* as backend, backend * will take care of it. * the semantics of AI_ADDRCONFIG is not defined well. we are not sure * if the code is right or not. * * XXX PF_UNSPEC -> PF_INET6 + PF_INET mapping needs to be in sync with * _dns_getaddrinfo. */ static int addrconfig(struct addrinfo *pai) { int s, af; /* * TODO: * Note that implementation dependent test for address * configuration should be done everytime called * (or apropriate interval), * because addresses will be dynamically assigned or deleted. */ af = pai->ai_family; if (af == AF_UNSPEC) { if ((s = _socket(AF_INET6, SOCK_DGRAM, 0)) < 0) af = AF_INET; else { _close(s); if ((s = _socket(AF_INET, SOCK_DGRAM, 0)) < 0) af = AF_INET6; else _close(s); } } if (af != AF_UNSPEC) { if ((s = _socket(af, SOCK_DGRAM, 0)) < 0) return 0; _close(s); } pai->ai_family = af; return 1; } #ifdef INET6 /* convert a string to a scope identifier. XXX: IPv6 specific */ static int ip6_str2scopeid(char *scope, struct sockaddr_in6 *sin6, u_int32_t *scopeid) { u_long lscopeid; struct in6_addr *a6; char *ep; a6 = &sin6->sin6_addr; /* empty scopeid portion is invalid */ if (*scope == '\0') return -1; if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) { /* * We currently assume a one-to-one mapping between links * and interfaces, so we simply use interface indices for * like-local scopes. */ *scopeid = if_nametoindex(scope); if (*scopeid == 0) goto trynumeric; return 0; } /* still unclear about literal, allow numeric only - placeholder */ if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6)) goto trynumeric; if (IN6_IS_ADDR_MC_ORGLOCAL(a6)) goto trynumeric; else goto trynumeric; /* global */ /* try to convert to a numeric id as a last resort */ trynumeric: errno = 0; lscopeid = strtoul(scope, &ep, 10); *scopeid = (u_int32_t)(lscopeid & 0xffffffffUL); if (errno == 0 && ep && *ep == '\0' && *scopeid == lscopeid) return 0; else return -1; } #endif + +#ifdef NS_CACHING +static int +addrinfo_id_func(char *buffer, size_t *buffer_size, va_list ap, + void *cache_mdata) +{ + res_state statp; + u_long res_options; + + const int op_id = 0; /* identifies the getaddrinfo for the cache */ + char *hostname; + struct addrinfo *hints; + + char *p; + int ai_flags, ai_family, ai_socktype, ai_protocol; + size_t desired_size, size; + + statp = __res_state(); + res_options = statp->options & (RES_RECURSE | RES_DEFNAMES | + RES_DNSRCH | RES_NOALIASES | RES_USE_INET6); + + hostname = va_arg(ap, char *); + hints = va_arg(ap, struct addrinfo *); + + desired_size = sizeof(res_options) + sizeof(int) + sizeof(int) * 4; + if (hostname != NULL) { + size = strlen(hostname); + desired_size += size + 1; + } else + size = 0; + + if (desired_size > *buffer_size) { + *buffer_size = desired_size; + return (NS_RETURN); + } + + if (hints == NULL) + ai_flags = ai_family = ai_socktype = ai_protocol = 0; + else { + ai_flags = hints->ai_flags; + ai_family = hints->ai_family; + ai_socktype = hints->ai_socktype; + ai_protocol = hints->ai_protocol; + } + + p = buffer; + memcpy(p, &res_options, sizeof(res_options)); + p += sizeof(res_options); + + memcpy(p, &op_id, sizeof(int)); + p += sizeof(int); + + memcpy(p, &ai_flags, sizeof(int)); + p += sizeof(int); + + memcpy(p, &ai_family, sizeof(int)); + p += sizeof(int); + + memcpy(p, &ai_socktype, sizeof(int)); + p += sizeof(int); + + memcpy(p, &ai_protocol, sizeof(int)); + p += sizeof(int); + + if (hostname != NULL) + memcpy(p, hostname, size); + + *buffer_size = desired_size; + return (NS_SUCCESS); +} + +static int +addrinfo_marshal_func(char *buffer, size_t *buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + struct addrinfo *ai, *cai; + char *p; + size_t desired_size, size, ai_size; + + ai = *((struct addrinfo **)retval); + + desired_size = sizeof(size_t); + ai_size = 0; + for (cai = ai; cai != NULL; cai = cai->ai_next) { + desired_size += sizeof(struct addrinfo) + cai->ai_addrlen; + if (cai->ai_canonname != NULL) + desired_size += sizeof(size_t) + + strlen(cai->ai_canonname); + ++ai_size; + } + + if (desired_size > *buffer_size) { + /* this assignment is here for future use */ + errno = ERANGE; + *buffer_size = desired_size; + return (NS_RETURN); + } + + memset(buffer, 0, desired_size); + p = buffer; + + memcpy(p, &ai_size, sizeof(size_t)); + p += sizeof(size_t); + for (cai = ai; cai != NULL; cai = cai->ai_next) { + memcpy(p, cai, sizeof(struct addrinfo)); + p += sizeof(struct addrinfo); + + memcpy(p, cai->ai_addr, cai->ai_addrlen); + p += cai->ai_addrlen; + + if (cai->ai_canonname != NULL) { + size = strlen(cai->ai_canonname); + memcpy(p, &size, sizeof(size_t)); + p += sizeof(size_t); + + memcpy(p, cai->ai_canonname, size); + p += size; + } + } + + return (NS_SUCCESS); +} + +static int +addrinfo_unmarshal_func(char *buffer, size_t buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + struct addrinfo new_ai, *result, *sentinel, *lasts; + + char *p; + size_t ai_size, ai_i, size; + + p = buffer; + memcpy(&ai_size, p, sizeof(size_t)); + p += sizeof(size_t); + + result = NULL; + lasts = NULL; + for (ai_i = 0; ai_i < ai_size; ++ai_i) { + memcpy(&new_ai, p, sizeof(struct addrinfo)); + p += sizeof(struct addrinfo); + size = new_ai.ai_addrlen + sizeof(struct addrinfo) + + _ALIGNBYTES; + + sentinel = (struct addrinfo *)malloc(size); + memset(sentinel, 0, size); + + memcpy(sentinel, &new_ai, sizeof(struct addrinfo)); + sentinel->ai_addr = (struct sockaddr *)_ALIGN((char *)sentinel + + sizeof(struct addrinfo)); + + memcpy(sentinel->ai_addr, p, new_ai.ai_addrlen); + p += new_ai.ai_addrlen; + + if (new_ai.ai_canonname != NULL) { + memcpy(&size, p, sizeof(size_t)); + p += sizeof(size_t); + + sentinel->ai_canonname = (char *)malloc(size + 1); + memset(sentinel->ai_canonname, 0, size + 1); + + memcpy(sentinel->ai_canonname, p, size); + p += size; + } + + if (result == NULL) { + result = sentinel; + lasts = sentinel; + } else { + lasts->ai_next = sentinel; + lasts = sentinel; + } + } + + *((struct addrinfo **)retval) = result; + return (NS_SUCCESS); +} +#endif /* NS_CACHING */ + /* * FQDN hostname, DNS lookup */ static int explore_fqdn(const struct addrinfo *pai, const char *hostname, const char *servname, struct addrinfo **res) { struct addrinfo *result; struct addrinfo *cur; int error = 0; + +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + hosts, NULL, addrinfo_id_func, addrinfo_marshal_func, + addrinfo_unmarshal_func); +#endif static const ns_dtab dtab[] = { NS_FILES_CB(_files_getaddrinfo, NULL) { NSSRC_DNS, _dns_getaddrinfo, NULL }, /* force -DHESIOD */ NS_NIS_CB(_yp_getaddrinfo, NULL) +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { 0 } }; result = NULL; /* * if the servname does not match socktype/protocol, ignore it. */ if (get_portmatch(pai, servname) != 0) return 0; switch (_nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo", default_dns_files, hostname, pai)) { case NS_TRYAGAIN: error = EAI_AGAIN; goto free; case NS_UNAVAIL: error = EAI_FAIL; goto free; case NS_NOTFOUND: error = EAI_NONAME; goto free; case NS_SUCCESS: error = 0; for (cur = result; cur; cur = cur->ai_next) { GET_PORT(cur, servname); /* canonname should be filled already */ } break; } *res = result; return 0; free: if (result) freeaddrinfo(result); return error; } #ifdef DEBUG static const char AskedForGot[] = "gethostby*.getanswer: asked for \"%s\", got \"%s\""; #endif static struct addrinfo * getanswer(const querybuf *answer, int anslen, const char *qname, int qtype, const struct addrinfo *pai, res_state res) { struct addrinfo sentinel, *cur; struct addrinfo ai; const struct afd *afd; char *canonname; const HEADER *hp; const u_char *cp; int n; const u_char *eom; char *bp, *ep; int type, class, ancount, qdcount; int haveanswer, had_error; char tbuf[MAXDNAME]; int (*name_ok)(const char *); char hostbuf[8*1024]; memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; canonname = NULL; eom = answer->buf + anslen; switch (qtype) { case T_A: case T_AAAA: case T_ANY: /*use T_ANY only for T_A/T_AAAA lookup*/ name_ok = res_hnok; break; default: return (NULL); /* XXX should be abort(); */ } /* * find first satisfactory answer */ hp = &answer->hdr; ancount = ntohs(hp->ancount); qdcount = ntohs(hp->qdcount); bp = hostbuf; ep = hostbuf + sizeof hostbuf; cp = answer->buf + HFIXEDSZ; if (qdcount != 1) { RES_SET_H_ERRNO(res, NO_RECOVERY); return (NULL); } n = dn_expand(answer->buf, eom, cp, bp, ep - bp); if ((n < 0) || !(*name_ok)(bp)) { RES_SET_H_ERRNO(res, NO_RECOVERY); return (NULL); } cp += n + QFIXEDSZ; if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) { /* res_send() has already verified that the query name is the * same as the one we sent; this just gets the expanded name * (i.e., with the succeeding search-domain tacked on). */ n = strlen(bp) + 1; /* for the \0 */ if (n >= MAXHOSTNAMELEN) { RES_SET_H_ERRNO(res, NO_RECOVERY); return (NULL); } canonname = bp; bp += n; /* The qname can be abbreviated, but h_name is now absolute. */ qname = canonname; } haveanswer = 0; had_error = 0; while (ancount-- > 0 && cp < eom && !had_error) { n = dn_expand(answer->buf, eom, cp, bp, ep - bp); if ((n < 0) || !(*name_ok)(bp)) { had_error++; continue; } cp += n; /* name */ type = _getshort(cp); cp += INT16SZ; /* type */ class = _getshort(cp); cp += INT16SZ + INT32SZ; /* class, TTL */ n = _getshort(cp); cp += INT16SZ; /* len */ if (class != C_IN) { /* XXX - debug? syslog? */ cp += n; continue; /* XXX - had_error++ ? */ } if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) && type == T_CNAME) { n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); if ((n < 0) || !(*name_ok)(tbuf)) { had_error++; continue; } cp += n; /* Get canonical name. */ n = strlen(tbuf) + 1; /* for the \0 */ if (n > ep - bp || n >= MAXHOSTNAMELEN) { had_error++; continue; } strlcpy(bp, tbuf, ep - bp); canonname = bp; bp += n; continue; } if (qtype == T_ANY) { if (!(type == T_A || type == T_AAAA)) { cp += n; continue; } } else if (type != qtype) { #ifdef DEBUG if (type != T_KEY && type != T_SIG) syslog(LOG_NOTICE|LOG_AUTH, "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"", qname, p_class(C_IN), p_type(qtype), p_type(type)); #endif cp += n; continue; /* XXX - had_error++ ? */ } switch (type) { case T_A: case T_AAAA: if (strcasecmp(canonname, bp) != 0) { #ifdef DEBUG syslog(LOG_NOTICE|LOG_AUTH, AskedForGot, canonname, bp); #endif cp += n; continue; /* XXX - had_error++ ? */ } if (type == T_A && n != INADDRSZ) { cp += n; continue; } if (type == T_AAAA && n != IN6ADDRSZ) { cp += n; continue; } #ifdef FILTER_V4MAPPED if (type == T_AAAA) { struct in6_addr in6; memcpy(&in6, cp, sizeof(in6)); if (IN6_IS_ADDR_V4MAPPED(&in6)) { cp += n; continue; } } #endif if (!haveanswer) { int nn; canonname = bp; nn = strlen(bp) + 1; /* for the \0 */ bp += nn; } /* don't overwrite pai */ ai = *pai; ai.ai_family = (type == T_A) ? AF_INET : AF_INET6; afd = find_afd(ai.ai_family); if (afd == NULL) { cp += n; continue; } cur->ai_next = get_ai(&ai, afd, (const char *)cp); if (cur->ai_next == NULL) had_error++; while (cur && cur->ai_next) cur = cur->ai_next; cp += n; break; default: abort(); } if (!had_error) haveanswer++; } if (haveanswer) { #if defined(RESOLVSORT) /* * We support only IPv4 address for backward * compatibility against gethostbyname(3). */ if (res->nsort && qtype == T_A) { if (addr4sort(&sentinel, res) < 0) { freeaddrinfo(sentinel.ai_next); RES_SET_H_ERRNO(res, NO_RECOVERY); return NULL; } } #endif /*RESOLVSORT*/ if (!canonname) (void)get_canonname(pai, sentinel.ai_next, qname); else (void)get_canonname(pai, sentinel.ai_next, canonname); RES_SET_H_ERRNO(res, NETDB_SUCCESS); return sentinel.ai_next; } RES_SET_H_ERRNO(res, NO_RECOVERY); return NULL; } #ifdef RESOLVSORT struct addr_ptr { struct addrinfo *ai; int aval; }; static int addr4sort(struct addrinfo *sentinel, res_state res) { struct addrinfo *ai; struct addr_ptr *addrs, addr; struct sockaddr_in *sin; int naddrs, i, j; int needsort = 0; if (!sentinel) return -1; naddrs = 0; for (ai = sentinel->ai_next; ai; ai = ai->ai_next) naddrs++; if (naddrs < 2) return 0; /* We don't need sorting. */ if ((addrs = malloc(sizeof(struct addr_ptr) * naddrs)) == NULL) return -1; i = 0; for (ai = sentinel->ai_next; ai; ai = ai->ai_next) { sin = (struct sockaddr_in *)ai->ai_addr; for (j = 0; (unsigned)j < res->nsort; j++) { if (res->sort_list[j].addr.s_addr == (sin->sin_addr.s_addr & res->sort_list[j].mask)) break; } addrs[i].ai = ai; addrs[i].aval = j; if (needsort == 0 && i > 0 && j < addrs[i - 1].aval) needsort = i; i++; } if (!needsort) { free(addrs); return 0; } while (needsort < naddrs) { for (j = needsort - 1; j >= 0; j--) { if (addrs[j].aval > addrs[j+1].aval) { addr = addrs[j]; addrs[j] = addrs[j + 1]; addrs[j + 1] = addr; } else break; } needsort++; } ai = sentinel; for (i = 0; i < naddrs; ++i) { ai->ai_next = addrs[i].ai; ai = ai->ai_next; } ai->ai_next = NULL; free(addrs); return 0; } #endif /*RESOLVSORT*/ /*ARGSUSED*/ static int _dns_getaddrinfo(void *rv, void *cb_data, va_list ap) { struct addrinfo *ai; querybuf *buf, *buf2; const char *hostname; const struct addrinfo *pai; struct addrinfo sentinel, *cur; struct res_target q, q2; res_state res; hostname = va_arg(ap, char *); pai = va_arg(ap, const struct addrinfo *); memset(&q, 0, sizeof(q)); memset(&q2, 0, sizeof(q2)); memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; buf = malloc(sizeof(*buf)); if (!buf) { RES_SET_H_ERRNO(res, NETDB_INTERNAL); return NS_NOTFOUND; } buf2 = malloc(sizeof(*buf2)); if (!buf2) { free(buf); RES_SET_H_ERRNO(res, NETDB_INTERNAL); return NS_NOTFOUND; } switch (pai->ai_family) { case AF_UNSPEC: q.name = hostname; q.qclass = C_IN; q.qtype = T_A; q.answer = buf->buf; q.anslen = sizeof(buf->buf); q.next = &q2; q2.name = hostname; q2.qclass = C_IN; q2.qtype = T_AAAA; q2.answer = buf2->buf; q2.anslen = sizeof(buf2->buf); break; case AF_INET: q.name = hostname; q.qclass = C_IN; q.qtype = T_A; q.answer = buf->buf; q.anslen = sizeof(buf->buf); break; case AF_INET6: q.name = hostname; q.qclass = C_IN; q.qtype = T_AAAA; q.answer = buf->buf; q.anslen = sizeof(buf->buf); break; default: free(buf); free(buf2); return NS_UNAVAIL; } res = __res_state(); if ((res->options & RES_INIT) == 0 && res_ninit(res) == -1) { RES_SET_H_ERRNO(res, NETDB_INTERNAL); free(buf); free(buf2); return NS_NOTFOUND; } if (res_searchN(hostname, &q, res) < 0) { free(buf); free(buf2); return NS_NOTFOUND; } /* prefer IPv6 */ if (q.next) { ai = getanswer(buf2, q2.n, q2.name, q2.qtype, pai, res); if (ai) { cur->ai_next = ai; while (cur && cur->ai_next) cur = cur->ai_next; } } ai = getanswer(buf, q.n, q.name, q.qtype, pai, res); if (ai) cur->ai_next = ai; free(buf); free(buf2); if (sentinel.ai_next == NULL) switch (res->res_h_errno) { case HOST_NOT_FOUND: return NS_NOTFOUND; case TRY_AGAIN: return NS_TRYAGAIN; default: return NS_UNAVAIL; } *((struct addrinfo **)rv) = sentinel.ai_next; return NS_SUCCESS; } static void _sethtent(FILE **hostf) { if (!*hostf) *hostf = fopen(_PATH_HOSTS, "r"); else rewind(*hostf); } static void _endhtent(FILE **hostf) { if (*hostf) { (void) fclose(*hostf); *hostf = NULL; } } static struct addrinfo * _gethtent(FILE **hostf, const char *name, const struct addrinfo *pai) { char *p; char *cp, *tname, *cname; struct addrinfo hints, *res0, *res; int error; const char *addr; char hostbuf[8*1024]; if (!*hostf && !(*hostf = fopen(_PATH_HOSTS, "r"))) return (NULL); again: if (!(p = fgets(hostbuf, sizeof hostbuf, *hostf))) return (NULL); if (*p == '#') goto again; cp = strpbrk(p, "#\n"); if (cp != NULL) *cp = '\0'; if (!(cp = strpbrk(p, " \t"))) goto again; *cp++ = '\0'; addr = p; cname = NULL; /* if this is not something we're looking for, skip it. */ while (cp && *cp) { if (*cp == ' ' || *cp == '\t') { cp++; continue; } tname = cp; if (cname == NULL) cname = cp; if ((cp = strpbrk(cp, " \t")) != NULL) *cp++ = '\0'; if (strcasecmp(name, tname) == 0) goto found; } goto again; found: /* we should not glob socktype/protocol here */ memset(&hints, 0, sizeof(hints)); hints.ai_family = pai->ai_family; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = 0; hints.ai_flags = AI_NUMERICHOST; error = getaddrinfo(addr, "0", &hints, &res0); if (error) goto again; #ifdef FILTER_V4MAPPED /* XXX should check all items in the chain */ if (res0->ai_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)res0->ai_addr)->sin6_addr)) { freeaddrinfo(res0); goto again; } #endif for (res = res0; res; res = res->ai_next) { /* cover it up */ res->ai_flags = pai->ai_flags; res->ai_socktype = pai->ai_socktype; res->ai_protocol = pai->ai_protocol; if (pai->ai_flags & AI_CANONNAME) { if (get_canonname(pai, res, cname) != 0) { freeaddrinfo(res0); goto again; } } } return res0; } /*ARGSUSED*/ static int _files_getaddrinfo(void *rv, void *cb_data, va_list ap) { const char *name; const struct addrinfo *pai; struct addrinfo sentinel, *cur; struct addrinfo *p; FILE *hostf = NULL; name = va_arg(ap, char *); pai = va_arg(ap, struct addrinfo *); memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; _sethtent(&hostf); while ((p = _gethtent(&hostf, name, pai)) != NULL) { cur->ai_next = p; while (cur && cur->ai_next) cur = cur->ai_next; } _endhtent(&hostf); *((struct addrinfo **)rv) = sentinel.ai_next; if (sentinel.ai_next == NULL) return NS_NOTFOUND; return NS_SUCCESS; } #ifdef YP /*ARGSUSED*/ static struct addrinfo * _yphostent(char *line, const struct addrinfo *pai) { struct addrinfo sentinel, *cur; struct addrinfo hints, *res, *res0; int error; char *p = line; const char *addr, *canonname; char *nextline; char *cp; addr = canonname = NULL; memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; nextline: /* terminate line */ cp = strchr(p, '\n'); if (cp) { *cp++ = '\0'; nextline = cp; } else nextline = NULL; cp = strpbrk(p, " \t"); if (cp == NULL) { if (canonname == NULL) return (NULL); else goto done; } *cp++ = '\0'; addr = p; while (cp && *cp) { if (*cp == ' ' || *cp == '\t') { cp++; continue; } if (!canonname) canonname = cp; if ((cp = strpbrk(cp, " \t")) != NULL) *cp++ = '\0'; } hints = *pai; hints.ai_flags = AI_NUMERICHOST; error = getaddrinfo(addr, NULL, &hints, &res0); if (error == 0) { for (res = res0; res; res = res->ai_next) { /* cover it up */ res->ai_flags = pai->ai_flags; if (pai->ai_flags & AI_CANONNAME) (void)get_canonname(pai, res, canonname); } } else res0 = NULL; if (res0) { cur->ai_next = res0; while (cur && cur->ai_next) cur = cur->ai_next; } if (nextline) { p = nextline; goto nextline; } done: return sentinel.ai_next; } /*ARGSUSED*/ static int _yp_getaddrinfo(void *rv, void *cb_data, va_list ap) { struct addrinfo sentinel, *cur; struct addrinfo *ai = NULL; char *ypbuf; int ypbuflen, r; const char *name; const struct addrinfo *pai; char *ypdomain; if (_yp_check(&ypdomain) == 0) return NS_UNAVAIL; name = va_arg(ap, char *); pai = va_arg(ap, const struct addrinfo *); memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; /* hosts.byname is only for IPv4 (Solaris8) */ if (pai->ai_family == PF_UNSPEC || pai->ai_family == PF_INET) { r = yp_match(ypdomain, "hosts.byname", name, (int)strlen(name), &ypbuf, &ypbuflen); if (r == 0) { struct addrinfo ai4; ai4 = *pai; ai4.ai_family = AF_INET; ai = _yphostent(ypbuf, &ai4); if (ai) { cur->ai_next = ai; while (cur && cur->ai_next) cur = cur->ai_next; } free(ypbuf); } } /* ipnodes.byname can hold both IPv4/v6 */ r = yp_match(ypdomain, "ipnodes.byname", name, (int)strlen(name), &ypbuf, &ypbuflen); if (r == 0) { ai = _yphostent(ypbuf, pai); if (ai) cur->ai_next = ai; free(ypbuf); } if (sentinel.ai_next == NULL) { RES_SET_H_ERRNO(__res_state(), HOST_NOT_FOUND); return NS_NOTFOUND; } *((struct addrinfo **)rv) = sentinel.ai_next; return NS_SUCCESS; } #endif /* resolver logic */ /* * Formulate a normal query, send, and await answer. * Returned answer is placed in supplied buffer "answer". * Perform preliminary check of answer, returning success only * if no error is indicated and the answer count is nonzero. * Return the size of the response on success, -1 on error. * Error number is left in h_errno. * * Caller must parse answer and determine whether it answers the question. */ static int res_queryN(const char *name, struct res_target *target, res_state res) { u_char *buf; HEADER *hp; int n; u_int oflags; struct res_target *t; int rcode; int ancount; rcode = NOERROR; ancount = 0; buf = malloc(MAXPACKET); if (!buf) { RES_SET_H_ERRNO(res, NETDB_INTERNAL); return -1; } for (t = target; t; t = t->next) { int class, type; u_char *answer; int anslen; hp = (HEADER *)(void *)t->answer; /* make it easier... */ class = t->qclass; type = t->qtype; answer = t->answer; anslen = t->anslen; oflags = res->_flags; again: hp->rcode = NOERROR; /* default */ #ifdef DEBUG if (res->options & RES_DEBUG) printf(";; res_query(%s, %d, %d)\n", name, class, type); #endif n = res_nmkquery(res, QUERY, name, class, type, NULL, 0, NULL, buf, MAXPACKET); if (n > 0 && (res->_flags & RES_F_EDNS0ERR) == 0 && (res->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0U) n = res_nopt(res, n, buf, MAXPACKET, anslen); if (n <= 0) { #ifdef DEBUG if (res->options & RES_DEBUG) printf(";; res_query: mkquery failed\n"); #endif free(buf); RES_SET_H_ERRNO(res, NO_RECOVERY); return (n); } n = res_nsend(res, buf, n, answer, anslen); if (n < 0) { /* * if the query choked with EDNS0, retry * without EDNS0 */ if ((res->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0U && ((oflags ^ res->_flags) & RES_F_EDNS0ERR) != 0) { res->_flags |= RES_F_EDNS0ERR; if (res->options & RES_DEBUG) printf(";; res_nquery: retry without EDNS0\n"); goto again; } rcode = hp->rcode; /* record most recent error */ #ifdef DEBUG if (res->options & RES_DEBUG) printf(";; res_query: send error\n"); #endif continue; } if (n > anslen) hp->rcode = FORMERR; /* XXX not very informative */ if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { rcode = hp->rcode; /* record most recent error */ #ifdef DEBUG if (res->options & RES_DEBUG) printf(";; rcode = %u, ancount=%u\n", hp->rcode, ntohs(hp->ancount)); #endif continue; } ancount += ntohs(hp->ancount); t->n = n; } free(buf); if (ancount == 0) { switch (rcode) { case NXDOMAIN: RES_SET_H_ERRNO(res, HOST_NOT_FOUND); break; case SERVFAIL: RES_SET_H_ERRNO(res, TRY_AGAIN); break; case NOERROR: RES_SET_H_ERRNO(res, NO_DATA); break; case FORMERR: case NOTIMP: case REFUSED: default: RES_SET_H_ERRNO(res, NO_RECOVERY); break; } return (-1); } return (ancount); } /* * Formulate a normal query, send, and retrieve answer in supplied buffer. * Return the size of the response on success, -1 on error. * If enabled, implement search rules until answer or unrecoverable failure * is detected. Error code, if any, is left in h_errno. */ static int res_searchN(const char *name, struct res_target *target, res_state res) { const char *cp, * const *domain; HEADER *hp = (HEADER *)(void *)target->answer; /*XXX*/ u_int dots; int trailing_dot, ret, saved_herrno; int got_nodata = 0, got_servfail = 0, root_on_list = 0; int tried_as_is = 0; int searched = 0; char abuf[MAXDNAME]; errno = 0; RES_SET_H_ERRNO(res, HOST_NOT_FOUND); /* default, if we never query */ dots = 0; for (cp = name; *cp; cp++) dots += (*cp == '.'); trailing_dot = 0; if (cp > name && *--cp == '.') trailing_dot++; /* * if there aren't any dots, it could be a user-level alias */ if (!dots && (cp = res_hostalias(res, name, abuf, sizeof(abuf))) != NULL) return (res_queryN(cp, target, res)); /* * If there are enough dots in the name, let's just give it a * try 'as is'. The threshold can be set with the "ndots" option. * Also, query 'as is', if there is a trailing dot in the name. */ saved_herrno = -1; if (dots >= res->ndots || trailing_dot) { ret = res_querydomainN(name, NULL, target, res); if (ret > 0 || trailing_dot) return (ret); if (errno == ECONNREFUSED) { RES_SET_H_ERRNO(res, TRY_AGAIN); return (-1); } switch (res->res_h_errno) { case NO_DATA: case HOST_NOT_FOUND: break; case TRY_AGAIN: if (hp->rcode == SERVFAIL) break; /* FALLTHROUGH */ default: return (-1); } saved_herrno = res->res_h_errno; tried_as_is++; } /* * We do at least one level of search if * - there is no dot and RES_DEFNAME is set, or * - there is at least one dot, there is no trailing dot, * and RES_DNSRCH is set. */ if ((!dots && (res->options & RES_DEFNAMES)) || (dots && !trailing_dot && (res->options & RES_DNSRCH))) { int done = 0; for (domain = (const char * const *)res->dnsrch; *domain && !done; domain++) { searched = 1; if (domain[0][0] == '\0' || (domain[0][0] == '.' && domain[0][1] == '\0')) root_on_list++; if (root_on_list && tried_as_is) continue; ret = res_querydomainN(name, *domain, target, res); if (ret > 0) return (ret); /* * If no server present, give up. * If name isn't found in this domain, * keep trying higher domains in the search list * (if that's enabled). * On a NO_DATA error, keep trying, otherwise * a wildcard entry of another type could keep us * from finding this entry higher in the domain. * If we get some other error (negative answer or * server failure), then stop searching up, * but try the input name below in case it's * fully-qualified. */ if (errno == ECONNREFUSED) { RES_SET_H_ERRNO(res, TRY_AGAIN); return (-1); } switch (res->res_h_errno) { case NO_DATA: got_nodata++; /* FALLTHROUGH */ case HOST_NOT_FOUND: /* keep trying */ break; case TRY_AGAIN: got_servfail++; if (hp->rcode == SERVFAIL) { /* try next search element, if any */ break; } /* FALLTHROUGH */ default: /* anything else implies that we're done */ done++; } /* * if we got here for some reason other than DNSRCH, * we only wanted one iteration of the loop, so stop. */ if (!(res->options & RES_DNSRCH)) done++; } } switch (res->res_h_errno) { case NO_DATA: case HOST_NOT_FOUND: break; case TRY_AGAIN: if (hp->rcode == SERVFAIL) break; /* FALLTHROUGH */ default: goto giveup; } /* * If the query has not already been tried as is then try it * unless RES_NOTLDQUERY is set and there were no dots. */ if ((dots || !searched || !(res->options & RES_NOTLDQUERY)) && !(tried_as_is || root_on_list)) { ret = res_querydomainN(name, NULL, target, res); if (ret > 0) return (ret); } /* * if we got here, we didn't satisfy the search. * if we did an initial full query, return that query's h_errno * (note that we wouldn't be here if that query had succeeded). * else if we ever got a nodata, send that back as the reason. * else send back meaningless h_errno, that being the one from * the last DNSRCH we did. */ giveup: if (saved_herrno != -1) RES_SET_H_ERRNO(res, saved_herrno); else if (got_nodata) RES_SET_H_ERRNO(res, NO_DATA); else if (got_servfail) RES_SET_H_ERRNO(res, TRY_AGAIN); return (-1); } /* * Perform a call on res_query on the concatenation of name and domain, * removing a trailing dot from name if domain is NULL. */ static int res_querydomainN(const char *name, const char *domain, struct res_target *target, res_state res) { char nbuf[MAXDNAME]; const char *longname = nbuf; size_t n, d; #ifdef DEBUG if (res->options & RES_DEBUG) printf(";; res_querydomain(%s, %s)\n", name, domain?domain:""); #endif if (domain == NULL) { /* * Check for trailing '.'; * copy without '.' if present. */ n = strlen(name); if (n >= MAXDNAME) { RES_SET_H_ERRNO(res, NO_RECOVERY); return (-1); } if (n > 0 && name[--n] == '.') { strncpy(nbuf, name, n); nbuf[n] = '\0'; } else longname = name; } else { n = strlen(name); d = strlen(domain); if (n + d + 1 >= MAXDNAME) { RES_SET_H_ERRNO(res, NO_RECOVERY); return (-1); } snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain); } return (res_queryN(longname, target, res)); } diff --git a/lib/libc/net/gethostnamadr.c b/lib/libc/net/gethostnamadr.c index c8867e54b707..965af6642b9b 100644 --- a/lib/libc/net/gethostnamadr.c +++ b/lib/libc/net/gethostnamadr.c @@ -1,421 +1,715 @@ /*- * Copyright (c) 1994, Garrett Wollman * * 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. * * THIS SOFTWARE IS PROVIDED BY THE 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. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include "reentrant.h" #include #include #include #include #include #include #include #include #include #include #include #include #include /* XXX hack for _res */ #include /* XXX hack for _res */ #include "un-namespace.h" #include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif extern int _ht_gethostbyname(void *, void *, va_list); extern int _dns_gethostbyname(void *, void *, va_list); extern int _nis_gethostbyname(void *, void *, va_list); extern int _ht_gethostbyaddr(void *, void *, va_list); extern int _dns_gethostbyaddr(void *, void *, va_list); extern int _nis_gethostbyaddr(void *, void *, va_list); static int gethostbyname_internal(const char *, int, struct hostent *, char *, size_t, struct hostent **, int *, res_state); /* Host lookup order if nsswitch.conf is broken or nonexistant */ static const ns_src default_src[] = { { NSSRC_FILES, NS_SUCCESS }, { NSSRC_DNS, NS_SUCCESS }, { 0 } }; +#ifdef NS_CACHING +static int host_id_func(char *, size_t *, va_list, void *); +static int host_marshal_func(char *, size_t *, void *, va_list, void *); +static int host_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif NETDB_THREAD_ALLOC(hostent) NETDB_THREAD_ALLOC(hostent_data) NETDB_THREAD_ALLOC(hostdata) static void hostent_free(void *ptr) { free(ptr); } static void hostent_data_free(void *ptr) { struct hostent_data *hed = ptr; if (hed == NULL) return; hed->stayopen = 0; _endhosthtent(hed); free(hed); } static void hostdata_free(void *ptr) { free(ptr); } int __copy_hostent(struct hostent *he, struct hostent *hptr, char *buf, size_t buflen) { char *cp; char **ptr; int i, n; int nptr, len; /* Find out the amount of space required to store the answer. */ nptr = 2; /* NULL ptrs */ len = (char *)ALIGN(buf) - buf; for (i = 0; he->h_addr_list[i]; i++, nptr++) { len += he->h_length; } for (i = 0; he->h_aliases[i]; i++, nptr++) { len += strlen(he->h_aliases[i]) + 1; } len += strlen(he->h_name) + 1; len += nptr * sizeof(char*); if (len > buflen) { errno = ERANGE; return (-1); } /* copy address size and type */ hptr->h_addrtype = he->h_addrtype; n = hptr->h_length = he->h_length; ptr = (char **)ALIGN(buf); cp = (char *)ALIGN(buf) + nptr * sizeof(char *); /* copy address list */ hptr->h_addr_list = ptr; for (i = 0; he->h_addr_list[i]; i++ , ptr++) { memcpy(cp, he->h_addr_list[i], n); hptr->h_addr_list[i] = cp; cp += n; } hptr->h_addr_list[i] = NULL; ptr++; /* copy official name */ n = strlen(he->h_name) + 1; strcpy(cp, he->h_name); hptr->h_name = cp; cp += n; /* copy aliases */ hptr->h_aliases = ptr; for (i = 0 ; he->h_aliases[i]; i++) { n = strlen(he->h_aliases[i]) + 1; strcpy(cp, he->h_aliases[i]); hptr->h_aliases[i] = cp; cp += n; } hptr->h_aliases[i] = NULL; return (0); } +#ifdef NS_CACHING +static int +host_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) +{ + res_state statp; + u_long res_options; + + const int op_id = 1; + char *str; + int len, type; + + size_t desired_size, size; + enum nss_lookup_type lookup_type; + char *p; + int res = NS_UNAVAIL; + + statp = __res_state(); + res_options = statp->options & (RES_RECURSE | RES_DEFNAMES | + RES_DNSRCH | RES_NOALIASES | RES_USE_INET6); + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + str = va_arg(ap, char *); + type = va_arg(ap, int); + + size = strlen(str); + desired_size = sizeof(res_options) + sizeof(int) + + sizeof(enum nss_lookup_type) + sizeof(int) + size + 1; + + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + p = buffer; + + memcpy(p, &res_options, sizeof(res_options)); + p += sizeof(res_options); + + memcpy(p, &op_id, sizeof(int)); + p += sizeof(int); + + memcpy(p, &lookup_type, sizeof(enum nss_lookup_type)); + p += sizeof(int); + + memcpy(p, &type, sizeof(int)); + p += sizeof(int); + + memcpy(p, str, size + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + str = va_arg(ap, char *); + len = va_arg(ap, int); + type = va_arg(ap, int); + + desired_size = sizeof(res_options) + sizeof(int) + + sizeof(enum nss_lookup_type) + sizeof(int) * 2 + len; + + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + p = buffer; + memcpy(p, &res_options, sizeof(res_options)); + p += sizeof(res_options); + + memcpy(p, &op_id, sizeof(int)); + p += sizeof(int); + + memcpy(p, &lookup_type, sizeof(enum nss_lookup_type)); + p += sizeof(int); + + memcpy(p, &type, sizeof(int)); + p += sizeof(int); + + memcpy(p, &len, sizeof(int)); + p += sizeof(int); + + memcpy(p, str, len); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + +static int +host_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *str; + int len, type; + struct hostent *ht; + + struct hostent new_ht; + size_t desired_size, aliases_size, addr_size, size; + char *p, **iter; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + str = va_arg(ap, char *); + type = va_arg(ap, int); + break; + case nss_lt_id: + str = va_arg(ap, char *); + len = va_arg(ap, int); + type = va_arg(ap, int); + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + ht = va_arg(ap, struct hostent *); + + desired_size = _ALIGNBYTES + sizeof(struct hostent) + sizeof(char *); + if (ht->h_name != NULL) + desired_size += strlen(ht->h_name) + 1; + + if (ht->h_aliases != NULL) { + aliases_size = 0; + for (iter = ht->h_aliases; *iter; ++iter) { + desired_size += strlen(*iter) + 1; + ++aliases_size; + } + + desired_size += _ALIGNBYTES + + (aliases_size + 1) * sizeof(char *); + } + + if (ht->h_addr_list != NULL) { + addr_size = 0; + for (iter = ht->h_addr_list; *iter; ++iter) + ++addr_size; + + desired_size += addr_size * _ALIGN(ht->h_length); + desired_size += _ALIGNBYTES + (addr_size + 1) * sizeof(char *); + } + + if (desired_size > *buffer_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_ht, ht, sizeof(struct hostent)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct hostent) + sizeof(char *); + memcpy(buffer + sizeof(struct hostent), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_ht.h_name != NULL) { + size = strlen(new_ht.h_name); + memcpy(p, new_ht.h_name, size); + new_ht.h_name = p; + p += size + 1; + } + + if (new_ht.h_aliases != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_ht.h_aliases, sizeof(char *) * aliases_size); + new_ht.h_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); + + for (iter = new_ht.h_aliases; *iter; ++iter) { + size = strlen(*iter); + memcpy(p, *iter, size); + *iter = p; + p += size + 1; + } + } + + if (new_ht.h_addr_list != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_ht.h_addr_list, sizeof(char *) * addr_size); + new_ht.h_addr_list = (char **)p; + p += sizeof(char *) * (addr_size + 1); + + size = _ALIGN(new_ht.h_length); + for (iter = new_ht.h_addr_list; *iter; ++iter) { + memcpy(p, *iter, size); + *iter = p; + p += size + 1; + } + } + memcpy(buffer, &new_ht, sizeof(struct hostent)); + return (NS_SUCCESS); +} + +static int +host_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *str; + int len, type; + struct hostent *ht; + + char *p; + char **iter; + char *orig_buf; + size_t orig_buf_size; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + str = va_arg(ap, char *); + type = va_arg(ap, int); + break; + case nss_lt_id: + str = va_arg(ap, char *); + len = va_arg(ap, int); + type = va_arg(ap, int); + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + ht = va_arg(ap, struct hostent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + + if (orig_buf_size < + buffer_size - sizeof(struct hostent) - sizeof(char *)) { + errno = ERANGE; + return (NS_RETURN); + } + + memcpy(ht, buffer, sizeof(struct hostent)); + memcpy(&p, buffer + sizeof(struct hostent), sizeof(char *)); + + orig_buf = (char *)_ALIGN(orig_buf); + memcpy(orig_buf, buffer + sizeof(struct hostent) + sizeof(char *) + + _ALIGN(p) - (size_t)p, + buffer_size - sizeof(struct hostent) - sizeof(char *) - + _ALIGN(p) + (size_t)p); + p = (char *)_ALIGN(p); + + NS_APPLY_OFFSET(ht->h_name, orig_buf, p, char *); + if (ht->h_aliases != NULL) { + NS_APPLY_OFFSET(ht->h_aliases, orig_buf, p, char **); + + for (iter = ht->h_aliases; *iter; ++iter) + NS_APPLY_OFFSET(*iter, orig_buf, p, char *); + } + + if (ht->h_addr_list != NULL) { + NS_APPLY_OFFSET(ht->h_addr_list, orig_buf, p, char **); + + for (iter = ht->h_addr_list; *iter; ++iter) + NS_APPLY_OFFSET(*iter, orig_buf, p, char *); + } + + *((struct hostent **)retval) = ht; + return (NS_SUCCESS); +} +#endif /* NS_CACHING */ + static int fakeaddr(const char *name, int af, struct hostent *hp, char *buf, -size_t buflen, res_state statp) + size_t buflen, res_state statp) { struct hostent_data *hed; struct hostent he; if ((hed = __hostent_data_init()) == NULL) { errno = ENOMEM; RES_SET_H_ERRNO(statp, NETDB_INTERNAL); return (-1); } if ((af != AF_INET || inet_aton(name, (struct in_addr *)hed->host_addr) != 1) && inet_pton(af, name, hed->host_addr) != 1) { RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); return (-1); } strncpy(hed->hostbuf, name, MAXDNAME); hed->hostbuf[MAXDNAME] = '\0'; if (af == AF_INET && (statp->options & RES_USE_INET6) != 0U) { _map_v4v6_address((char *)hed->host_addr, (char *)hed->host_addr); af = AF_INET6; } he.h_addrtype = af; switch(af) { case AF_INET: he.h_length = NS_INADDRSZ; break; case AF_INET6: he.h_length = NS_IN6ADDRSZ; break; default: errno = EAFNOSUPPORT; RES_SET_H_ERRNO(statp, NETDB_INTERNAL); return (-1); } he.h_name = hed->hostbuf; he.h_aliases = hed->host_aliases; hed->host_aliases[0] = NULL; hed->h_addr_ptrs[0] = (char *)hed->host_addr; hed->h_addr_ptrs[1] = NULL; he.h_addr_list = hed->h_addr_ptrs; RES_SET_H_ERRNO(statp, NETDB_SUCCESS); return (__copy_hostent(&he, hp, buf, buflen)); } int gethostbyname_r(const char *name, struct hostent *he, char *buffer, size_t buflen, struct hostent **result, int *h_errnop) { res_state statp; statp = __res_state(); if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); return (-1); } if (statp->options & RES_USE_INET6) { if (fakeaddr(name, AF_INET, he, buffer, buflen, statp) == 0) { *result = he; return (0); } if (gethostbyname_internal(name, AF_INET6, he, buffer, buflen, result, h_errnop, statp) == 0) return (0); } return (gethostbyname_internal(name, AF_INET, he, buffer, buflen, result, h_errnop, statp)); } int gethostbyname2_r(const char *name, int af, struct hostent *he, char *buffer, size_t buflen, struct hostent **result, int *h_errnop) { res_state statp; statp = __res_state(); if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); return (-1); } return (gethostbyname_internal(name, af, he, buffer, buflen, result, h_errnop, statp)); } int gethostbyname_internal(const char *name, int af, struct hostent *hp, char *buf, size_t buflen, struct hostent **result, int *h_errnop, res_state statp) { const char *cp; int rval, ret_errno; char abuf[MAXDNAME]; +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + hosts, (void *)nss_lt_name, + host_id_func, host_marshal_func, host_unmarshal_func); +#endif static const ns_dtab dtab[] = { NS_FILES_CB(_ht_gethostbyname, NULL) { NSSRC_DNS, _dns_gethostbyname, NULL }, NS_NIS_CB(_nis_gethostbyname, NULL) /* force -DHESIOD */ +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { 0 } }; switch (af) { case AF_INET: case AF_INET6: break; default: RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; errno = EAFNOSUPPORT; return (-1); } /* * if there aren't any dots, it could be a user-level alias. * this is also done in res_query() since we are not the only * function that looks up host names. */ if (!strchr(name, '.') && (cp = res_hostalias(statp, name, abuf, sizeof abuf))) name = cp; if (fakeaddr(name, af, hp, buf, buflen, statp) == 0) { *result = hp; return (0); } rval = _nsdispatch((void *)result, dtab, NSDB_HOSTS, "gethostbyname2_r", default_src, name, af, hp, buf, buflen, &ret_errno, h_errnop); return ((rval == NS_SUCCESS) ? 0 : -1); } int gethostbyaddr_r(const char *addr, int len, int af, struct hostent *hp, char *buf, size_t buflen, struct hostent **result, int *h_errnop) { const u_char *uaddr = (const u_char *)addr; const struct in6_addr *addr6; socklen_t size; int rval, ret_errno; res_state statp; +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + hosts, (void *)nss_lt_id, + host_id_func, host_marshal_func, host_unmarshal_func); +#endif static const ns_dtab dtab[] = { NS_FILES_CB(_ht_gethostbyaddr, NULL) { NSSRC_DNS, _dns_gethostbyaddr, NULL }, NS_NIS_CB(_nis_gethostbyaddr, NULL) /* force -DHESIOD */ +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { 0 } }; statp = __res_state(); if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; return (-1); } if (af == AF_INET6 && len == NS_IN6ADDRSZ) { addr6 = (const struct in6_addr *)(const void *)uaddr; if (IN6_IS_ADDR_LINKLOCAL(addr6)) { RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); *h_errnop = statp->res_h_errno; return (-1); } if (IN6_IS_ADDR_V4MAPPED(addr6) || IN6_IS_ADDR_V4COMPAT(addr6)) { /* Unmap. */ uaddr += NS_IN6ADDRSZ - NS_INADDRSZ; af = AF_INET; len = NS_INADDRSZ; } } switch (af) { case AF_INET: size = NS_INADDRSZ; break; case AF_INET6: size = NS_IN6ADDRSZ; break; default: errno = EAFNOSUPPORT; RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; return (-1); } if (size != len) { errno = EINVAL; RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; return (-1); } rval = _nsdispatch((void *)result, dtab, NSDB_HOSTS, "gethostbyaddr_r", default_src, uaddr, len, af, hp, buf, buflen, &ret_errno, h_errnop); return ((rval == NS_SUCCESS) ? 0 : -1); } struct hostent * gethostbyname(const char *name) { struct hostdata *hd; struct hostent *rval; int ret_h_errno; if ((hd = __hostdata_init()) == NULL) return (NULL); if (gethostbyname_r(name, &hd->host, hd->data, sizeof(hd->data), &rval, &ret_h_errno) != 0) return (NULL); return (rval); } struct hostent * gethostbyname2(const char *name, int af) { struct hostdata *hd; struct hostent *rval; int ret_h_errno; if ((hd = __hostdata_init()) == NULL) return (NULL); if (gethostbyname2_r(name, af, &hd->host, hd->data, sizeof(hd->data), &rval, &ret_h_errno) != 0) return (NULL); return (rval); } struct hostent * gethostbyaddr(const char *addr, int len, int af) { struct hostdata *hd; struct hostent *rval; int ret_h_errno; if ((hd = __hostdata_init()) == NULL) return (NULL); if (gethostbyaddr_r(addr, len, af, &hd->host, hd->data, sizeof(hd->data), &rval, &ret_h_errno) != 0) return (NULL); return (rval); } void sethostent(int stayopen) { struct hostent_data *hed; if ((hed = __hostent_data_init()) == NULL) return; _sethosthtent(stayopen, hed); _sethostdnsent(stayopen); } void endhostent(void) { struct hostent_data *hed; if ((hed = __hostent_data_init()) == NULL) return; _endhosthtent(hed); _endhostdnsent(); } diff --git a/lib/libc/net/getnetnamadr.c b/lib/libc/net/getnetnamadr.c index 633cb3077d8c..ec7e94bbda8f 100644 --- a/lib/libc/net/getnetnamadr.c +++ b/lib/libc/net/getnetnamadr.c @@ -1,217 +1,450 @@ /*- * Copyright (c) 1994, Garrett Wollman * * 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. * * THIS SOFTWARE IS PROVIDED BY THE 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. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include "reentrant.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif extern int _ht_getnetbyname(void *, void *, va_list); extern int _dns_getnetbyname(void *, void *, va_list); extern int _nis_getnetbyname(void *, void *, va_list); extern int _ht_getnetbyaddr(void *, void *, va_list); extern int _dns_getnetbyaddr(void *, void *, va_list); extern int _nis_getnetbyaddr(void *, void *, va_list); /* Network lookup order if nsswitch.conf is broken or nonexistant */ static const ns_src default_src[] = { { NSSRC_FILES, NS_SUCCESS }, { NSSRC_DNS, NS_SUCCESS }, { 0 } }; NETDB_THREAD_ALLOC(netent_data) NETDB_THREAD_ALLOC(netdata) +#ifdef NS_CACHING +static int +net_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) +{ + char *name; + uint32_t net; + int type; + + size_t desired_size, size; + enum nss_lookup_type lookup_type; + int res = NS_UNAVAIL; + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + name = va_arg(ap, char *); + + size = strlen(name); + desired_size = sizeof(enum nss_lookup_type) + size + 1; + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + net = va_arg(ap, uint32_t); + type = va_arg(ap, int); + + desired_size = sizeof(enum nss_lookup_type) + + sizeof(uint32_t) + sizeof(int); + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), &net, + sizeof(uint32_t)); + memcpy(buffer + sizeof(enum nss_lookup_type) + sizeof(uint32_t), + &type, sizeof(int)); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + + +static int +net_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *name; + uint32_t net; + int type; + struct netent *ne; + char *orig_buf; + size_t orig_buf_size; + + struct netent new_ne; + size_t desired_size, size, aliases_size; + char *p; + char **alias; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + net = va_arg(ap, uint32_t); + type = va_arg(ap, int); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + ne = va_arg(ap, struct netent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + + desired_size = _ALIGNBYTES + sizeof(struct netent) + sizeof(char *); + if (ne->n_name != NULL) + desired_size += strlen(ne->n_name) + 1; + + if (ne->n_aliases != NULL) { + aliases_size = 0; + for (alias = ne->n_aliases; *alias; ++alias) { + desired_size += strlen(*alias) + 1; + ++aliases_size; + } + + desired_size += _ALIGNBYTES + + (aliases_size + 1) * sizeof(char *); + } + + if (*buffer_size < desired_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_ne, ne, sizeof(struct netent)); + + *buffer_size = desired_size; + memset(buffer, 0, desired_size); + p = buffer + sizeof(struct netent) + sizeof(char *); + memcpy(buffer + sizeof(struct netent), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_ne.n_name != NULL) { + size = strlen(new_ne.n_name); + memcpy(p, new_ne.n_name, size); + new_ne.n_name = p; + p += size + 1; + } + + if (new_ne.n_aliases != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_ne.n_aliases, sizeof(char *) * aliases_size); + new_ne.n_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); + + for (alias = new_ne.n_aliases; *alias; ++alias) { + size = strlen(*alias); + memcpy(p, *alias, size); + *alias = p; + p += size + 1; + } + } + + memcpy(buffer, &new_ne, sizeof(struct netent)); + return (NS_SUCCESS); +} + +static int +net_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *name; + uint32_t net; + int type; + struct netent *ne; + char *orig_buf; + size_t orig_buf_size; + int *ret_errno; + + char *p; + char **alias; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + net = va_arg(ap, uint32_t); + type = va_arg(ap, int); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + ne = va_arg(ap, struct netent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + ret_errno = va_arg(ap, int *); + + if (orig_buf_size < + buffer_size - sizeof(struct netent) - sizeof(char *)) { + *ret_errno = ERANGE; + return (NS_RETURN); + } + + memcpy(ne, buffer, sizeof(struct netent)); + memcpy(&p, buffer + sizeof(struct netent), sizeof(char *)); + + orig_buf = (char *)_ALIGN(orig_buf); + memcpy(orig_buf, buffer + sizeof(struct netent) + sizeof(char *) + + _ALIGN(p) - (size_t)p, + buffer_size - sizeof(struct netent) - sizeof(char *) - + _ALIGN(p) + (size_t)p); + p = (char *)_ALIGN(p); + + NS_APPLY_OFFSET(ne->n_name, orig_buf, p, char *); + if (ne->n_aliases != NULL) { + NS_APPLY_OFFSET(ne->n_aliases, orig_buf, p, char **); + + for (alias = ne->n_aliases; *alias; ++alias) + NS_APPLY_OFFSET(*alias, orig_buf, p, char *); + } + + if (retval != NULL) + *((struct netent **)retval) = ne; + + return (NS_SUCCESS); +} +#endif /* NS_CACHING */ + static void netent_data_free(void *ptr) { struct netent_data *ned = ptr; if (ned == NULL) return; ned->stayopen = 0; _endnethtent(ned); free(ned); } static void netdata_free(void *ptr) { free(ptr); } int __copy_netent(struct netent *ne, struct netent *nptr, char *buf, size_t buflen) { char *cp; int i, n; int numptr, len; /* Find out the amount of space required to store the answer. */ numptr = 1; /* NULL ptr */ len = (char *)ALIGN(buf) - buf; for (i = 0; ne->n_aliases[i]; i++, numptr++) { len += strlen(ne->n_aliases[i]) + 1; } len += strlen(ne->n_name) + 1; len += numptr * sizeof(char*); if (len > (int)buflen) { errno = ERANGE; return (-1); } /* copy net value and type */ nptr->n_addrtype = ne->n_addrtype; nptr->n_net = ne->n_net; cp = (char *)ALIGN(buf) + numptr * sizeof(char *); /* copy official name */ n = strlen(ne->n_name) + 1; strcpy(cp, ne->n_name); nptr->n_name = cp; cp += n; /* copy aliases */ nptr->n_aliases = (char **)ALIGN(buf); for (i = 0 ; ne->n_aliases[i]; i++) { n = strlen(ne->n_aliases[i]) + 1; strcpy(cp, ne->n_aliases[i]); nptr->n_aliases[i] = cp; cp += n; } nptr->n_aliases[i] = NULL; return (0); } int getnetbyname_r(const char *name, struct netent *ne, char *buffer, size_t buflen, struct netent **result, int *h_errorp) { - int rval, ret_errno; - +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + networks, (void *)nss_lt_name, + net_id_func, net_marshal_func, net_unmarshal_func); +#endif static const ns_dtab dtab[] = { NS_FILES_CB(_ht_getnetbyname, NULL) { NSSRC_DNS, _dns_getnetbyname, NULL }, NS_NIS_CB(_nis_getnetbyname, NULL) /* force -DHESIOD */ +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { 0 } }; + int rval, ret_errno; rval = _nsdispatch((void *)result, dtab, NSDB_NETWORKS, "getnetbyname_r", default_src, name, ne, buffer, buflen, &ret_errno, h_errorp); return ((rval == NS_SUCCESS) ? 0 : -1); } int getnetbyaddr_r(uint32_t addr, int af, struct netent *ne, char *buffer, size_t buflen, struct netent **result, int *h_errorp) { - int rval, ret_errno; - +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + networks, (void *)nss_lt_id, + net_id_func, net_marshal_func, net_unmarshal_func); +#endif static const ns_dtab dtab[] = { NS_FILES_CB(_ht_getnetbyaddr, NULL) { NSSRC_DNS, _dns_getnetbyaddr, NULL }, NS_NIS_CB(_nis_getnetbyaddr, NULL) /* force -DHESIOD */ +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { 0 } }; + int rval, ret_errno; rval = _nsdispatch((void *)result, dtab, NSDB_NETWORKS, "getnetbyaddr_r", default_src, addr, af, ne, buffer, buflen, &ret_errno, h_errorp); return ((rval == NS_SUCCESS) ? 0 : -1); } struct netent * getnetbyname(const char *name) { struct netdata *nd; struct netent *rval; int ret_h_errno; if ((nd = __netdata_init()) == NULL) return (NULL); if (getnetbyname_r(name, &nd->net, nd->data, sizeof(nd->data), &rval, &ret_h_errno) != 0) return (NULL); return (rval); } struct netent * getnetbyaddr(uint32_t addr, int af) { struct netdata *nd; struct netent *rval; int ret_h_errno; if ((nd = __netdata_init()) == NULL) return (NULL); if (getnetbyaddr_r(addr, af, &nd->net, nd->data, sizeof(nd->data), &rval, &ret_h_errno) != 0) return (NULL); return (rval); } void setnetent(int stayopen) { struct netent_data *ned; if ((ned = __netent_data_init()) == NULL) return; _setnethtent(stayopen, ned); _setnetdnsent(stayopen); } void endnetent(void) { struct netent_data *ned; if ((ned = __netent_data_init()) == NULL) return; _endnethtent(ned); _endnetdnsent(); } diff --git a/lib/libc/net/getproto.c b/lib/libc/net/getproto.c index 9a3298355aed..b2a3fe7647ff 100644 --- a/lib/libc/net/getproto.c +++ b/lib/libc/net/getproto.c @@ -1,79 +1,145 @@ /* * Copyright (c) 1983, 1993 * The 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)getproto.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include +#include #include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif +#include "nss_tls.h" -int -getprotobynumber_r(int proto, struct protoent *pptr, char *buffer, - size_t buflen, struct protoent **result) +static const ns_src defaultsrc[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NULL, 0 } +}; + +#ifdef NS_CACHING +extern int __proto_id_func(char *, size_t *, va_list, void *); +extern int __proto_marshal_func(char *, size_t *, void *, va_list, void *); +extern int __proto_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif + +static int +files_getprotobynumber(void *retval, void *mdata, va_list ap) { struct protoent pe; struct protoent_data *ped; int error; - if ((ped = __protoent_data_init()) == NULL) - return (-1); + int number; + struct protoent *pptr; + char *buffer; + size_t buflen; + int *errnop; + + number = va_arg(ap, int); + pptr = va_arg(ap, struct protoent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + + if ((ped = __protoent_data_init()) == NULL) { + *errnop = -1; + return (NS_NOTFOUND); + } + __setprotoent_p(ped->stayopen, ped); while ((error = __getprotoent_p(&pe, ped)) == 0) - if (pe.p_proto == proto) + if (pe.p_proto == number) break; if (!ped->stayopen) __endprotoent_p(ped); - if (error != 0) - return (-1); - if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) - return (-1); - *result = pptr; - return (0); + if (error != 0) { + *errnop = -1; + return (NS_NOTFOUND); + } + if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) { + *errnop = -1; + return (NS_NOTFOUND); + } + + *((struct protoent **)retval) = pptr; + return (NS_SUCCESS); +} + +int +getprotobynumber_r(int proto, struct protoent *pptr, char *buffer, + size_t buflen, struct protoent **result) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + protocols, (void *)nss_lt_id, + __proto_id_func, __proto_marshal_func, __proto_unmarshal_func); +#endif + + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_getprotobynumber, NULL }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_PROTOCOLS, "getprotobynumber_r", + defaultsrc, proto, pptr, buffer, buflen, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); } struct protoent * getprotobynumber(int proto) { struct protodata *pd; struct protoent *rval; if ((pd = __protodata_init()) == NULL) return (NULL); if (getprotobynumber_r(proto, &pd->proto, pd->data, sizeof(pd->data), &rval) != 0) return (NULL); return (rval); } diff --git a/lib/libc/net/getprotoent.c b/lib/libc/net/getprotoent.c index 28f055b58163..3d3a57ea7074 100644 --- a/lib/libc/net/getprotoent.c +++ b/lib/libc/net/getprotoent.c @@ -1,246 +1,555 @@ /* * Copyright (c) 1983, 1993 * The 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)getprotoent.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include +#include #include #include #include #include "namespace.h" #include "reentrant.h" #include "un-namespace.h" #include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif +#include "nss_tls.h" + +static const ns_src defaultsrc[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NULL, 0 } +}; NETDB_THREAD_ALLOC(protoent_data) NETDB_THREAD_ALLOC(protodata) static void protoent_data_clear(struct protoent_data *ped) { if (ped->fp) { fclose(ped->fp); ped->fp = NULL; } } static void protoent_data_free(void *ptr) { struct protoent_data *ped = ptr; protoent_data_clear(ped); free(ped); } static void protodata_free(void *ptr) { free(ptr); } +#ifdef NS_CACHING +int +__proto_id_func(char *buffer, size_t *buffer_size, va_list ap, + void *cache_mdata) +{ + char *name; + int proto; + + size_t desired_size, size; + enum nss_lookup_type lookup_type; + int res = NS_UNAVAIL; + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + name = va_arg(ap, char *); + + size = strlen(name); + desired_size = sizeof(enum nss_lookup_type) + size + 1; + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + proto = va_arg(ap, int); + + desired_size = sizeof(enum nss_lookup_type) + sizeof(int); + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), &proto, + sizeof(int)); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + + +int +__proto_marshal_func(char *buffer, size_t *buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + char *name; + int num; + struct protoent *proto; + char *orig_buf; + size_t orig_buf_size; + + struct protoent new_proto; + size_t desired_size, size, aliases_size; + char *p; + char **alias; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + num = va_arg(ap, int); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + proto = va_arg(ap, struct protoent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + + desired_size = _ALIGNBYTES + sizeof(struct protoent) + sizeof(char *); + if (proto->p_name != NULL) + desired_size += strlen(proto->p_name) + 1; + + if (proto->p_aliases != NULL) { + aliases_size = 0; + for (alias = proto->p_aliases; *alias; ++alias) { + desired_size += strlen(*alias) + 1; + ++aliases_size; + } + + desired_size += _ALIGNBYTES + (aliases_size + 1) * + sizeof(char *); + } + + if (*buffer_size < desired_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_proto, proto, sizeof(struct protoent)); + + *buffer_size = desired_size; + memset(buffer, 0, desired_size); + p = buffer + sizeof(struct protoent) + sizeof(char *); + memcpy(buffer + sizeof(struct protoent), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_proto.p_name != NULL) { + size = strlen(new_proto.p_name); + memcpy(p, new_proto.p_name, size); + new_proto.p_name = p; + p += size + 1; + } + + if (new_proto.p_aliases != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_proto.p_aliases, sizeof(char *) * aliases_size); + new_proto.p_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); + + for (alias = new_proto.p_aliases; *alias; ++alias) { + size = strlen(*alias); + memcpy(p, *alias, size); + *alias = p; + p += size + 1; + } + } + + memcpy(buffer, &new_proto, sizeof(struct protoent)); + return (NS_SUCCESS); +} + +int +__proto_unmarshal_func(char *buffer, size_t buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + char *name; + int num; + struct protoent *proto; + char *orig_buf; + size_t orig_buf_size; + int *ret_errno; + + char *p; + char **alias; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + num = va_arg(ap, int); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + proto = va_arg(ap, struct protoent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + ret_errno = va_arg(ap, int *); + + if (orig_buf_size < + buffer_size - sizeof(struct protoent) - sizeof(char *)) { + *ret_errno = ERANGE; + return (NS_RETURN); + } + + memcpy(proto, buffer, sizeof(struct protoent)); + memcpy(&p, buffer + sizeof(struct protoent), sizeof(char *)); + + orig_buf = (char *)_ALIGN(orig_buf); + memcpy(orig_buf, buffer + sizeof(struct protoent) + sizeof(char *) + + _ALIGN(p) - (size_t)p, + buffer_size - sizeof(struct protoent) - sizeof(char *) - + _ALIGN(p) + (size_t)p); + p = (char *)_ALIGN(p); + + NS_APPLY_OFFSET(proto->p_name, orig_buf, p, char *); + if (proto->p_aliases != NULL) { + NS_APPLY_OFFSET(proto->p_aliases, orig_buf, p, char **); + + for (alias = proto->p_aliases; *alias; ++alias) + NS_APPLY_OFFSET(*alias, orig_buf, p, char *); + } + + if (retval != NULL) + *((struct protoent **)retval) = proto; + + return (NS_SUCCESS); +} + +NSS_MP_CACHE_HANDLING(protocols); +#endif /* NS_CACHING */ + int __copy_protoent(struct protoent *pe, struct protoent *pptr, char *buf, size_t buflen) { char *cp; int i, n; int numptr, len; /* Find out the amount of space required to store the answer. */ numptr = 1; /* NULL ptr */ len = (char *)ALIGN(buf) - buf; for (i = 0; pe->p_aliases[i]; i++, numptr++) { len += strlen(pe->p_aliases[i]) + 1; } len += strlen(pe->p_name) + 1; len += numptr * sizeof(char*); if (len > (int)buflen) { errno = ERANGE; return (-1); } /* copy protocol value*/ pptr->p_proto = pe->p_proto; cp = (char *)ALIGN(buf) + numptr * sizeof(char *); /* copy official name */ n = strlen(pe->p_name) + 1; strcpy(cp, pe->p_name); pptr->p_name = cp; cp += n; /* copy aliases */ pptr->p_aliases = (char **)ALIGN(buf); for (i = 0 ; pe->p_aliases[i]; i++) { n = strlen(pe->p_aliases[i]) + 1; strcpy(cp, pe->p_aliases[i]); pptr->p_aliases[i] = cp; cp += n; } pptr->p_aliases[i] = NULL; return (0); } void __setprotoent_p(int f, struct protoent_data *ped) { if (ped->fp == NULL) ped->fp = fopen(_PATH_PROTOCOLS, "r"); else rewind(ped->fp); ped->stayopen |= f; } void __endprotoent_p(struct protoent_data *ped) { if (ped->fp) { fclose(ped->fp); ped->fp = NULL; } ped->stayopen = 0; } int __getprotoent_p(struct protoent *pe, struct protoent_data *ped) { char *p; char *cp, **q, *endp; long l; if (ped->fp == NULL && (ped->fp = fopen(_PATH_PROTOCOLS, "r")) == NULL) return (-1); again: if ((p = fgets(ped->line, sizeof ped->line, ped->fp)) == NULL) return (-1); if (*p == '#') goto again; cp = strpbrk(p, "#\n"); if (cp != NULL) *cp = '\0'; pe->p_name = p; cp = strpbrk(p, " \t"); if (cp == NULL) goto again; *cp++ = '\0'; while (*cp == ' ' || *cp == '\t') cp++; p = strpbrk(cp, " \t"); if (p != NULL) *p++ = '\0'; l = strtol(cp, &endp, 10); if (endp == cp || *endp != '\0' || l < 0 || l > USHRT_MAX) goto again; pe->p_proto = l; q = pe->p_aliases = ped->aliases; if (p != NULL) { cp = p; while (cp && *cp) { if (*cp == ' ' || *cp == '\t') { cp++; continue; } if (q < &ped->aliases[_MAXALIASES - 1]) *q++ = cp; cp = strpbrk(cp, " \t"); if (cp != NULL) *cp++ = '\0'; } } *q = NULL; return (0); } -int -getprotoent_r(struct protoent *pptr, char *buffer, size_t buflen, - struct protoent **result) +static int +files_getprotoent_r(void *retval, void *mdata, va_list ap) { struct protoent pe; struct protoent_data *ped; + struct protoent *pptr; + char *buffer; + size_t buflen; + int *errnop; + + pptr = va_arg(ap, struct protoent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + if ((ped = __protoent_data_init()) == NULL) return (-1); - if (__getprotoent_p(&pe, ped) != 0) - return (-1); - if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) - return (-1); - *result = pptr; - return (0); + if (__getprotoent_p(&pe, ped) != 0) { + *errnop = errno; + return (NS_NOTFOUND); + } + + if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) { + *errnop = errno; + return (NS_NOTFOUND); + } + + *((struct protoent **)retval) = pptr; + return (NS_SUCCESS); } -void -setprotoent(int f) +static int +files_setprotoent(void *retval, void *mdata, va_list ap) { struct protoent_data *ped; + int f; + f = va_arg(ap, int); if ((ped = __protoent_data_init()) == NULL) - return; + return (NS_UNAVAIL); + __setprotoent_p(f, ped); + return (NS_UNAVAIL); } -void -endprotoent(void) +static int +files_endprotoent(void *retval, void *mdata, va_list ap) { struct protoent_data *ped; if ((ped = __protoent_data_init()) == NULL) - return; + return (NS_UNAVAIL); + __endprotoent_p(ped); + return (NS_UNAVAIL); +} + +int +getprotoent_r(struct protoent *pptr, char *buffer, size_t buflen, + struct protoent **result) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + protocols, (void *)nss_lt_all, + __proto_marshal_func, __proto_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_getprotoent_r, (void *)nss_lt_all }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_PROTOCOLS, "getprotoent_r", + defaultsrc, pptr, buffer, buflen, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); +} + +void +setprotoent(int stayopen) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + protocols, (void *)nss_lt_all, + NULL, NULL); +#endif + + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_setprotoent, NULL }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + + (void)nsdispatch(NULL, dtab, NSDB_PROTOCOLS, "setprotoent", defaultsrc, + stayopen); +} + +void +endprotoent(void) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + protocols, (void *)nss_lt_all, + NULL, NULL); +#endif + + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_endprotoent, NULL }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + + (void)nsdispatch(NULL, dtab, NSDB_PROTOCOLS, "endprotoent", defaultsrc); } struct protoent * getprotoent(void) { struct protodata *pd; struct protoent *rval; if ((pd = __protodata_init()) == NULL) return (NULL); if (getprotoent_r(&pd->proto, pd->data, sizeof(pd->data), &rval) != 0) return (NULL); return (rval); } diff --git a/lib/libc/net/getprotoname.c b/lib/libc/net/getprotoname.c index afe3f9d47d17..4ef50e3f4470 100644 --- a/lib/libc/net/getprotoname.c +++ b/lib/libc/net/getprotoname.c @@ -1,87 +1,153 @@ /* * Copyright (c) 1983, 1993 * The 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)getprotoname.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include +#include #include #include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif +#include "nss_tls.h" -int -getprotobyname_r(const char *name, struct protoent *pptr, char *buffer, - size_t buflen, struct protoent **result) +static const ns_src defaultsrc[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NULL, 0 } +}; + +#ifdef NS_CACHING +extern int __proto_id_func(char *, size_t *, va_list, void *); +extern int __proto_marshal_func(char *, size_t *, void *, va_list, void *); +extern int __proto_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif + +static int +files_getprotobyname(void *retval, void *mdata, va_list ap) { struct protoent pe; struct protoent_data *ped; char **cp; int error; - if ((ped = __protoent_data_init()) == NULL) - return (-1); + char *name; + struct protoent *pptr; + char *buffer; + size_t buflen; + int *errnop; + + name = va_arg(ap, char *); + pptr = va_arg(ap, struct protoent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + + + if ((ped = __protoent_data_init()) == NULL) { + *errnop = -1; + return (NS_NOTFOUND); + } __setprotoent_p(ped->stayopen, ped); while ((error = __getprotoent_p(&pe, ped)) == 0) { if (strcmp(pe.p_name, name) == 0) break; for (cp = pe.p_aliases; *cp != 0; cp++) if (strcmp(*cp, name) == 0) goto found; } found: if (!ped->stayopen) __endprotoent_p(ped); - if (error != 0) - return (-1); - if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) - return (-1); - *result = pptr; - return (0); + if (error != 0) { + *errnop = -1; + return (NS_NOTFOUND); + } + if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) { + *errnop = -1; + return (NS_NOTFOUND); + } + + *((struct protoent **)retval) = pptr; + return (NS_SUCCESS); +} + + +int +getprotobyname_r(const char *name, struct protoent *pptr, char *buffer, + size_t buflen, struct protoent **result) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + protocols, (void *)nss_lt_name, + __proto_id_func, __proto_marshal_func, __proto_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_getprotobyname, NULL }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_PROTOCOLS, "getprotobyname_r", + defaultsrc, name, pptr, buffer, buflen, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); } struct protoent * getprotobyname(const char *name) { struct protodata *pd; struct protoent *rval; if ((pd = __protodata_init()) == NULL) return (NULL); if (getprotobyname_r(name, &pd->proto, pd->data, sizeof(pd->data), &rval) != 0) return (NULL); return (rval); } diff --git a/lib/libc/net/getservbyname.c b/lib/libc/net/getservbyname.c deleted file mode 100644 index 8870c6c1d493..000000000000 --- a/lib/libc/net/getservbyname.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 1983, 1993 - * The 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. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)getservbyname.c 8.1 (Berkeley) 6/4/93"; -#endif /* LIBC_SCCS and not lint */ -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include "netdb_private.h" - -int -getservbyname_r(const char *name, const char *proto, struct servent *sptr, - char *buffer, size_t buflen, struct servent **result) -{ - struct servent se; - struct servent_data *sed; - char **cp; - int error; - - if ((sed = __servent_data_init()) == NULL) - return (-1); - -#ifdef YP - sed->yp_name = (char *)name; - sed->yp_proto = (char *)proto; -#endif - - __setservent_p(sed->stayopen, sed); - while ((error = __getservent_p(&se, sed)) == 0) { - if (strcmp(name, se.s_name) == 0) - goto gotname; - for (cp = se.s_aliases; *cp; cp++) - if (strcmp(name, *cp) == 0) - goto gotname; - continue; -gotname: - if (proto == 0 || strcmp(se.s_proto, proto) == 0) - break; - } - if (!sed->stayopen) - __endservent_p(sed); - -#ifdef YP - sed->yp_name = NULL; - sed->yp_proto = NULL; -#endif - - if (error != 0) - return (-1); - if (__copy_servent(&se, sptr, buffer, buflen) != 0) - return (-1); - *result = sptr; - return (0); -} - -struct servent * -getservbyname(const char *name, const char *proto) -{ - struct servdata *sd; - struct servent *rval; - - if ((sd = __servdata_init()) == NULL) - return (NULL); - if (getservbyname_r(name, proto, &sd->serv, sd->data, sizeof(sd->data), - &rval) != 0) - return (NULL); - return (rval); -} diff --git a/lib/libc/net/getservbyport.c b/lib/libc/net/getservbyport.c deleted file mode 100644 index 827e5bf2aa00..000000000000 --- a/lib/libc/net/getservbyport.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 1983, 1993 - * The 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. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)getservbyport.c 8.1 (Berkeley) 6/4/93"; -#endif /* LIBC_SCCS and not lint */ -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include "netdb_private.h" - -int -getservbyport_r(int port, const char *proto, struct servent *sptr, - char *buffer, size_t buflen, struct servent **result) -{ - struct servent se; - struct servent_data *sed; - int error; - - if ((sed = __servent_data_init()) == NULL) - return (-1); - -#ifdef YP - sed->yp_port = port; - sed->yp_proto = (char *)proto; -#endif - - __setservent_p(sed->stayopen, sed); - while ((error = __getservent_p(&se, sed)) == 0) { - if (se.s_port != port) - continue; - if (proto == 0 || strcmp(se.s_proto, proto) == 0) - break; - } - if (!sed->stayopen) - __endservent_p(sed); - -#ifdef YP - sed->yp_port = 0; - sed->yp_proto = NULL; -#endif - - if (error != 0) - return (-1); - if (__copy_servent(&se, sptr, buffer, buflen) != 0) - return (-1); - *result = sptr; - return (0); -} - -struct servent * -getservbyport(int port, const char *proto) -{ - struct servdata *sd; - struct servent *rval; - - if ((sd = __servdata_init()) == NULL) - return (NULL); - if (getservbyport_r(port, proto, &sd->serv, sd->data, - sizeof(sd->data), &rval) != 0) - return (NULL); - return (rval); -} diff --git a/lib/libc/net/getservent.c b/lib/libc/net/getservent.c index ff4864a9df04..b616e252ffa4 100644 --- a/lib/libc/net/getservent.c +++ b/lib/libc/net/getservent.c @@ -1,405 +1,1213 @@ /* * Copyright (c) 1983, 1993 * The 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)getservent.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include +#include #include #include #include +#include #ifdef YP #include #include #include #endif #include "namespace.h" #include "reentrant.h" #include "un-namespace.h" #include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif +#include "nss_tls.h" -NETDB_THREAD_ALLOC(servent_data) -NETDB_THREAD_ALLOC(servdata) +enum constants +{ + SETSERVENT = 1, + ENDSERVENT = 2, + SERVENT_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ + SERVENT_STORAGE_MAX = 1 << 20, /* 1 MByte */ +}; -static void -servent_data_clear(struct servent_data *sed) +struct servent_mdata { - if (sed->fp) { - fclose(sed->fp); - sed->fp = NULL; - } + enum nss_lookup_type how; + int compat_mode; +}; + +static const ns_src defaultsrc[] = { + { NSSRC_COMPAT, NS_SUCCESS }, + { NULL, 0 } +}; + +static int servent_unpack(char *, struct servent *, char **, size_t, int *); + +/* files backend declarations */ +struct files_state +{ + FILE *fp; + int stayopen; + + int compat_mode_active; +}; +static void files_endstate(void *); +NSS_TLS_HANDLING(files); + +static int files_servent(void *, void *, va_list); +static int files_setservent(void *, void *, va_list); + #ifdef YP - free(sed->yp_key); - sed->yp_key = NULL; +/* nis backend declarations */ +static int nis_servent(void *, void *, va_list); +static int nis_setservent(void *, void *, va_list); + +struct nis_state +{ + int yp_stepping; + char yp_domain[MAXHOSTNAMELEN]; + char *yp_key; + int yp_keylen; +}; +static void nis_endstate(void *); +NSS_TLS_HANDLING(nis); + +static int nis_servent(void *, void *, va_list); +static int nis_setservent(void *, void *, va_list); #endif -} -static void -servent_data_free(void *ptr) +/* compat backend declarations */ +static int compat_setservent(void *, void *, va_list); + +/* get** wrappers for get**_r functions declarations */ +struct servent_state { + struct servent serv; + char *buffer; + size_t bufsize; +}; +static void servent_endstate(void *); +NSS_TLS_HANDLING(servent); + +struct key { + const char *proto; + union { + const char *name; + int port; + }; +}; + +static int wrap_getservbyname_r(struct key, struct servent *, char *, size_t, + struct servent **); +static int wrap_getservbyport_r(struct key, struct servent *, char *, size_t, + struct servent **); +static int wrap_getservent_r(struct key, struct servent *, char *, size_t, + struct servent **); +static struct servent *getserv(int (*fn)(struct key, struct servent *, char *, + size_t, struct servent **), struct key); + +#ifdef NS_CACHING +static int serv_id_func(char *, size_t *, va_list, void *); +static int serv_marshal_func(char *, size_t *, void *, va_list, void *); +static int serv_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif + +static int +servent_unpack(char *p, struct servent *serv, char **aliases, + size_t aliases_size, int *errnop) { - struct servent_data *sed = ptr; + char *cp, **q, *endp; + long l; + + if (*p == '#') + return -1; + + memset(serv, 0, sizeof(struct servent)); + + cp = strpbrk(p, "#\n"); + if (cp != NULL) + *cp = '\0'; + serv->s_name = p; + + p = strpbrk(p, " \t"); + if (p == NULL) + return -1; + *p++ = '\0'; + while (*p == ' ' || *p == '\t') + p++; + cp = strpbrk(p, ",/"); + if (cp == NULL) + return -1; + + *cp++ = '\0'; + l = strtol(p, &endp, 10); + if (endp == p || *endp != '\0' || l < 0 || l > USHRT_MAX) + return -1; + serv->s_port = htons((in_port_t)l); + serv->s_proto = cp; + + q = serv->s_aliases = aliases; + cp = strpbrk(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < &aliases[aliases_size - 1]) { + *q++ = cp; + } else { + *q = NULL; + *errnop = ERANGE; + return -1; + } + cp = strpbrk(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + } + *q = NULL; - servent_data_clear(sed); - free(sed); + return 0; } -static void -servdata_free(void *ptr) +/* files backend implementation */ +static void +files_endstate(void *p) { - free(ptr); + FILE * f; + + if (p == NULL) + return; + + f = ((struct files_state *)p)->fp; + if (f != NULL) + fclose(f); + + free(p); } -int -__copy_servent(struct servent *se, struct servent *sptr, char *buf, - size_t buflen) +/* + * compat structures. compat and files sources functionalities are almost + * equal, so they all are managed by files_servent function + */ +static int +files_servent(void *retval, void *mdata, va_list ap) { - char *cp; - int i, n; - int numptr, len; - - /* Find out the amount of space required to store the answer. */ - numptr = 1; /* NULL ptr */ - len = (char *)ALIGN(buf) - buf; - for (i = 0; se->s_aliases[i]; i++, numptr++) { - len += strlen(se->s_aliases[i]) + 1; + static const ns_src compat_src[] = { +#ifdef YP + { NSSRC_NIS, NS_SUCCESS }, +#endif + { NULL, 0 } + }; + ns_dtab compat_dtab[] = { +#ifdef YP + { NSSRC_NIS, nis_servent, + (void *)((struct servent_mdata *)mdata)->how }, +#endif + { NULL, NULL, NULL } + }; + + struct files_state *st; + int rv; + int stayopen; + + struct servent_mdata *serv_mdata; + char *name; + char *proto; + int port; + + struct servent *serv; + char *buffer; + size_t bufsize; + int *errnop; + + char **aliases; + int aliases_size; + size_t linesize; + char *line; + char **cp; + + name = NULL; + proto = NULL; + serv_mdata = (struct servent_mdata *)mdata; + switch (serv_mdata->how) { + case nss_lt_name: + name = va_arg(ap, char *); + proto = va_arg(ap, char *); + break; + case nss_lt_id: + port = va_arg(ap, int); + proto = va_arg(ap, char *); + break; + case nss_lt_all: + break; + default: + return NS_NOTFOUND; + }; + + serv = va_arg(ap, struct servent *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap,int *); + + *errnop = files_getstate(&st); + if (*errnop != 0) + return (NS_UNAVAIL); + + if (st->fp == NULL) + st->compat_mode_active = 0; + + if (st->fp == NULL && (st->fp = fopen(_PATH_SERVICES, "r")) == NULL) { + *errnop = errno; + return (NS_UNAVAIL); } - len += strlen(se->s_name) + 1; - len += strlen(se->s_proto) + 1; - len += numptr * sizeof(char*); - if (len > (int)buflen) { - errno = ERANGE; - return (-1); + if (serv_mdata->how == nss_lt_all) + stayopen = 1; + else { + rewind(st->fp); + stayopen = st->stayopen; } - /* copy port value */ - sptr->s_port = se->s_port; + rv = NS_NOTFOUND; + do { + if (!st->compat_mode_active) { + if ((line = fgetln(st->fp, &linesize)) == NULL) { + *errnop = errno; + rv = NS_RETURN; + break; + } + + if (*line=='+') { + if (serv_mdata->compat_mode != 0) + st->compat_mode_active = 1; + } else { + if (bufsize <= linesize + _ALIGNBYTES + + sizeof(char *)) { + *errnop = ERANGE; + rv = NS_RETURN; + break; + } + aliases = (char **)_ALIGN(&buffer[linesize+1]); + aliases_size = (buffer + bufsize - + (char *)aliases) / sizeof(char *); + if (aliases_size < 1) { + *errnop = ERANGE; + rv = NS_RETURN; + break; + } + + memcpy(buffer, line, linesize); + buffer[linesize] = '\0'; + } + } + + if (st->compat_mode_active != 0) { + switch (serv_mdata->how) { + case nss_lt_name: + rv = nsdispatch(retval, compat_dtab, + NSDB_SERVICES_COMPAT, "getservbyname_r", + compat_src, name, proto, serv, buffer, + bufsize, errnop); + break; + case nss_lt_id: + rv = nsdispatch(retval, compat_dtab, + NSDB_SERVICES_COMPAT, "getservbyport_r", + compat_src, port, proto, serv, buffer, + bufsize, errnop); + break; + case nss_lt_all: + rv = nsdispatch(retval, compat_dtab, + NSDB_SERVICES_COMPAT, "getservent_r", + compat_src, serv, buffer, bufsize, errnop); + break; + } + + if (!(rv & NS_TERMINATE) || + serv_mdata->how != nss_lt_all) + st->compat_mode_active = 0; + + continue; + } + + rv = servent_unpack(buffer, serv, aliases, aliases_size, + errnop); + if (rv !=0 ) { + if (*errnop == 0) { + rv = NS_NOTFOUND; + continue; + } + else { + rv = NS_RETURN; + break; + } + } + + rv = NS_NOTFOUND; + switch (serv_mdata->how) { + case nss_lt_name: + if (strcmp(name, serv->s_name) == 0) + goto gotname; + for (cp = serv->s_aliases; *cp; cp++) + if (strcmp(name, *cp) == 0) + goto gotname; - cp = (char *)ALIGN(buf) + numptr * sizeof(char *); + continue; + gotname: + if (proto == 0 || strcmp(serv->s_proto, proto) == 0) + rv = NS_SUCCESS; + break; + case nss_lt_id: + if (port != serv->s_port) + continue; + + if (proto == 0 || strcmp(serv->s_proto, proto) == 0) + rv = NS_SUCCESS; + break; + case nss_lt_all: + rv = NS_SUCCESS; + break; + } - /* copy official name */ - n = strlen(se->s_name) + 1; - strcpy(cp, se->s_name); - sptr->s_name = cp; - cp += n; + } while (!(rv & NS_TERMINATE)); - /* copy aliases */ - sptr->s_aliases = (char **)ALIGN(buf); - for (i = 0 ; se->s_aliases[i]; i++) { - n = strlen(se->s_aliases[i]) + 1; - strcpy(cp, se->s_aliases[i]); - sptr->s_aliases[i] = cp; - cp += n; + if (!stayopen && st->fp != NULL) { + fclose(st->fp); + st->fp = NULL; } - sptr->s_aliases[i] = NULL; - /* copy proto */ - n = strlen(se->s_proto) + 1; - strcpy(cp, se->s_proto); - sptr->s_proto = cp; - cp += n; + if ((rv == NS_SUCCESS) && (retval != NULL)) + *(struct servent **)retval=serv; - return (0); + return (rv); } +static int +files_setservent(void *retval, void *mdata, va_list ap) +{ + struct files_state *st; + int rv; + int f; + + rv = files_getstate(&st); + if (rv != 0) + return (NS_UNAVAIL); + + switch ((enum constants)mdata) { + case SETSERVENT: + f = va_arg(ap,int); + if (st->fp == NULL) + st->fp = fopen(_PATH_SERVICES, "r"); + else + rewind(st->fp); + st->stayopen |= f; + break; + case ENDSERVENT: + if (st->fp != NULL) { + fclose(st->fp); + st->fp = NULL; + } + st->stayopen = 0; + break; + default: + break; + }; + + st->compat_mode_active = 0; + return (NS_UNAVAIL); +} + +/* nis backend implementation */ #ifdef YP +static void +nis_endstate(void *p) +{ + if (p == NULL) + return; + + free(((struct nis_state *)p)->yp_key); + free(p); +} + static int -_getservbyport_yp(struct servent_data *sed) +nis_servent(void *retval, void *mdata, va_list ap) { - char *result; - int resultlen; + char *resultbuf, *lastkey; + int resultbuflen; char buf[YPMAXRECORD + 2]; + + struct nis_state *st; int rv; - snprintf(buf, sizeof(buf), "%d/%s", ntohs(sed->yp_port), - sed->yp_proto); + enum nss_lookup_type how; + char *name; + char *proto; + int port; - sed->yp_port = 0; - sed->yp_proto = NULL; + struct servent *serv; + char *buffer; + size_t bufsize; + int *errnop; - if (!sed->yp_domain) { - if (yp_get_default_domain(&sed->yp_domain)) - return (0); + char **aliases; + int aliases_size; + + name = NULL; + proto = NULL; + how = (enum nss_lookup_type)mdata; + switch (how) { + case nss_lt_name: + name = va_arg(ap, char *); + proto = va_arg(ap, char *); + break; + case nss_lt_id: + port = va_arg(ap, int); + proto = va_arg(ap, char *); + break; + case nss_lt_all: + break; + default: + return NS_NOTFOUND; + }; + + serv = va_arg(ap, struct servent *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + + *errnop = nis_getstate(&st); + if (*errnop != 0) + return (NS_UNAVAIL); + + if (st->yp_domain[0] == '\0') { + if (getdomainname(st->yp_domain, sizeof st->yp_domain)) { + *errnop = errno; + return (NS_UNAVAIL); + } } - /* - * We have to be a little flexible here. Ideally you're supposed - * to have both a services.byname and a services.byport map, but - * some systems have only services.byname. FreeBSD cheats a little - * by putting the services.byport information in the same map as - * services.byname so that either case will work. We allow for both - * possibilities here: if there is no services.byport map, we try - * services.byname instead. - */ - if ((rv = yp_match(sed->yp_domain, "services.byport", buf, strlen(buf), - &result, &resultlen))) { - if (rv == YPERR_MAP) { - if (yp_match(sed->yp_domain, "services.byname", buf, - strlen(buf), &result, &resultlen)) - return(0); + do { + switch (how) { + case nss_lt_name: + snprintf(buf, sizeof(buf), "%s/%s", name, proto); + if (yp_match(st->yp_domain, "services.byname", buf, + strlen(buf), &resultbuf, &resultbuflen)) { + rv = NS_NOTFOUND; + goto fin; + } + break; + case nss_lt_id: + snprintf(buf, sizeof(buf), "%d/%s", ntohs(port), + proto); + + /* + * We have to be a little flexible + * here. Ideally you're supposed to have both + * a services.byname and a services.byport + * map, but some systems have only + * services.byname. FreeBSD cheats a little by + * putting the services.byport information in + * the same map as services.byname so that + * either case will work. We allow for both + * possibilities here: if there is no + * services.byport map, we try services.byname + * instead. + */ + rv = yp_match(st->yp_domain, "services.byport", buf, + strlen(buf), &resultbuf, &resultbuflen); + if (rv) { + if (rv == YPERR_MAP) { + if (yp_match(st->yp_domain, + "services.byname", buf, + strlen(buf), &resultbuf, + &resultbuflen)) { + rv = NS_NOTFOUND; + goto fin; + } + } else { + rv = NS_NOTFOUND; + goto fin; + } + } + + break; + case nss_lt_all: + if (!st->yp_stepping) { + free(st->yp_key); + rv = yp_first(st->yp_domain, "services.byname", + &st->yp_key, &st->yp_keylen, &resultbuf, + &resultbuflen); + if (rv) { + rv = NS_NOTFOUND; + goto fin; + } + st->yp_stepping = 1; + } else { + lastkey = st->yp_key; + rv = yp_next(st->yp_domain, "services.byname", + st->yp_key, st->yp_keylen, &st->yp_key, + &st->yp_keylen, &resultbuf, &resultbuflen); + free(lastkey); + if (rv) { + st->yp_stepping = 0; + rv = NS_NOTFOUND; + goto fin; + } + } + break; + }; + + /* we need a room for additional \n symbol */ + if (bufsize <= + resultbuflen + 1 + _ALIGNBYTES + sizeof(char *)) { + *errnop = ERANGE; + rv = NS_RETURN; + break; + } + + aliases = (char **)_ALIGN(&buffer[resultbuflen + 2]); + aliases_size = + (buffer + bufsize - (char *)aliases) / sizeof(char *); + if (aliases_size < 1) { + *errnop = ERANGE; + rv = NS_RETURN; + break; + } + + /* + * servent_unpack expects lines terminated with \n -- + * make it happy + */ + memcpy(buffer, resultbuf, resultbuflen); + buffer[resultbuflen] = '\n'; + buffer[resultbuflen + 1] = '\0'; + + if (servent_unpack(buffer, serv, aliases, aliases_size, + errnop) != 0) { + if (*errnop == 0) + rv = NS_NOTFOUND; + else + rv = NS_RETURN; } else - return(0); - } + rv = NS_SUCCESS; + free(resultbuf); + + } while (!(rv & NS_TERMINATE) && how == nss_lt_all); - /* getservent() expects lines terminated with \n -- make it happy */ - snprintf(sed->line, sizeof sed->line, "%.*s\n", resultlen, result); +fin: + if (rv == NS_SUCCESS && retval != NULL) + *(struct servent **)retval = serv; - free(result); - return(1); + return (rv); } static int -_getservbyname_yp(struct servent_data *sed) +nis_setservent(void *result, void *mdata, va_list ap) { - char *result; - int resultlen; - char buf[YPMAXRECORD + 2]; + struct nis_state *st; + int rv; - if(!sed->yp_domain) { - if(yp_get_default_domain(&sed->yp_domain)) - return (0); - } + rv = nis_getstate(&st); + if (rv != 0) + return (NS_UNAVAIL); + + switch ((enum constants)mdata) { + case SETSERVENT: + case ENDSERVENT: + free(st->yp_key); + st->yp_key = NULL; + st->yp_stepping = 0; + break; + default: + break; + }; - snprintf(buf, sizeof(buf), "%s/%s", sed->yp_name, sed->yp_proto); + return (NS_UNAVAIL); +} +#endif + +/* compat backend implementation */ +static int +compat_setservent(void *retval, void *mdata, va_list ap) +{ + static const ns_src compat_src[] = { +#ifdef YP + { NSSRC_NIS, NS_SUCCESS }, +#endif + { NULL, 0 } + }; + ns_dtab compat_dtab[] = { +#ifdef YP + { NSSRC_NIS, nis_setservent, mdata }, +#endif + { NULL, NULL, NULL } + }; + int f; - sed->yp_name = 0; - sed->yp_proto = NULL; + (void)files_setservent(retval, mdata, ap); - if (yp_match(sed->yp_domain, "services.byname", buf, strlen(buf), - &result, &resultlen)) { - return(0); + switch ((enum constants)mdata) { + case SETSERVENT: + f = va_arg(ap,int); + (void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, + "setservent", compat_src, f); + break; + case ENDSERVENT: + (void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, + "endservent", compat_src); + break; + default: + break; } - /* getservent() expects lines terminated with \n -- make it happy */ - snprintf(sed->line, sizeof sed->line, "%.*s\n", resultlen, result); - - free(result); - return(1); + return (NS_UNAVAIL); } +#ifdef NS_CACHING static int -_getservent_yp(struct servent_data *sed) +serv_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) { - char *lastkey, *result; - int resultlen; - int rv; + char *name; + char *proto; + int port; - if (!sed->yp_domain) { - if (yp_get_default_domain(&sed->yp_domain)) - return (0); - } + size_t desired_size, size, size2; + enum nss_lookup_type lookup_type; + int res = NS_UNAVAIL; + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + name = va_arg(ap, char *); + proto = va_arg(ap, char *); + + size = strlen(name); + desired_size = sizeof(enum nss_lookup_type) + size + 1; + if (proto != NULL) { + size2 = strlen(proto); + desired_size += size2 + 1; + } else + size2 = 0; - if (!sed->yp_stepping) { - free(sed->yp_key); - rv = yp_first(sed->yp_domain, "services.byname", &sed->yp_key, - &sed->yp_keylen, &result, &resultlen); - if (rv) { - sed->yp_stepping = 0; - return(0); + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; } - sed->yp_stepping = 1; - } else { - lastkey = sed->yp_key; - rv = yp_next(sed->yp_domain, "services.byname", sed->yp_key, - sed->yp_keylen, &sed->yp_key, &sed->yp_keylen, &result, - &resultlen); - free(lastkey); - if (rv) { - sed->yp_stepping = 0; - return (0); + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); + + if (proto != NULL) + memcpy(buffer + sizeof(enum nss_lookup_type) + size + 1, + proto, size2 + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + port = va_arg(ap, int); + proto = va_arg(ap, char *); + + desired_size = sizeof(enum nss_lookup_type) + sizeof(int); + if (proto != NULL) { + size = strlen(proto); + desired_size += size + 1; + } else + size = 0; + + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), &port, + sizeof(int)); + + if (proto != NULL) + memcpy(buffer + sizeof(enum nss_lookup_type) + + sizeof(int), proto, size + 1); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + +int +serv_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *name; + char *proto; + int port; + struct servent *serv; + char *orig_buf; + size_t orig_buf_size; + + struct servent new_serv; + size_t desired_size; + char **alias; + char *p; + size_t size; + size_t aliases_size; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + proto = va_arg(ap, char *); + break; + case nss_lt_id: + port = va_arg(ap, int); + proto = va_arg(ap, char *); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + serv = va_arg(ap, struct servent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + + desired_size = _ALIGNBYTES + sizeof(struct servent) + sizeof(char *); + if (serv->s_name != NULL) + desired_size += strlen(serv->s_name) + 1; + if (serv->s_proto != NULL) + desired_size += strlen(serv->s_proto) + 1; + + aliases_size = 0; + if (serv->s_aliases != NULL) { + for (alias = serv->s_aliases; *alias; ++alias) { + desired_size += strlen(*alias) + 1; + ++aliases_size; } + + desired_size += _ALIGNBYTES + + sizeof(char *) * (aliases_size + 1); + } + + if (*buffer_size < desired_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_serv, serv, sizeof(struct servent)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct servent) + sizeof(char *); + memcpy(buffer + sizeof(struct servent), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_serv.s_name != NULL) { + size = strlen(new_serv.s_name); + memcpy(p, new_serv.s_name, size); + new_serv.s_name = p; + p += size + 1; } - /* getservent() expects lines terminated with \n -- make it happy */ - snprintf(sed->line, sizeof sed->line, "%.*s\n", resultlen, result); + if (new_serv.s_proto != NULL) { + size = strlen(new_serv.s_proto); + memcpy(p, new_serv.s_proto, size); + new_serv.s_proto = p; + p += size + 1; + } - free(result); + if (new_serv.s_aliases != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size); + new_serv.s_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); - return(1); + for (alias = new_serv.s_aliases; *alias; ++alias) { + size = strlen(*alias); + memcpy(p, *alias, size); + *alias = p; + p += size + 1; + } + } + + memcpy(buffer, &new_serv, sizeof(struct servent)); + return (NS_SUCCESS); } -#endif -void -__setservent_p(int f, struct servent_data *sed) +int +serv_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, + void *cache_mdata) { - if (sed->fp == NULL) - sed->fp = fopen(_PATH_SERVICES, "r"); - else - rewind(sed->fp); - sed->stayopen |= f; + char *name; + char *proto; + int port; + struct servent *serv; + char *orig_buf; + char *p; + char **alias; + size_t orig_buf_size; + int *ret_errno; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + proto = va_arg(ap, char *); + break; + case nss_lt_id: + port = va_arg(ap, int); + proto = va_arg(ap, char *); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + serv = va_arg(ap, struct servent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + ret_errno = va_arg(ap, int *); + + if (orig_buf_size < + buffer_size - sizeof(struct servent) - sizeof(char *)) { + *ret_errno = ERANGE; + return (NS_RETURN); + } + + memcpy(serv, buffer, sizeof(struct servent)); + memcpy(&p, buffer + sizeof(struct servent), sizeof(char *)); + + orig_buf = (char *)_ALIGN(orig_buf); + memcpy(orig_buf, buffer + sizeof(struct servent) + sizeof(char *) + + (_ALIGN(p) - (size_t)p), + buffer_size - sizeof(struct servent) - sizeof(char *) - + (_ALIGN(p) - (size_t)p)); + p = (char *)_ALIGN(p); + + NS_APPLY_OFFSET(serv->s_name, orig_buf, p, char *); + NS_APPLY_OFFSET(serv->s_proto, orig_buf, p, char *); + if (serv->s_aliases != NULL) { + NS_APPLY_OFFSET(serv->s_aliases, orig_buf, p, char **); + + for (alias = serv->s_aliases; *alias; ++alias) + NS_APPLY_OFFSET(*alias, orig_buf, p, char *); + } + + if (retval != NULL) + *((struct servent **)retval) = serv; + return (NS_SUCCESS); } -void -__endservent_p(struct servent_data *sed) +NSS_MP_CACHE_HANDLING(services); +#endif /* NS_CACHING */ + +/* get**_r functions implementation */ +int +getservbyname_r(const char *name, const char *proto, struct servent *serv, + char *buffer, size_t bufsize, struct servent **result) { - servent_data_clear(sed); - sed->stayopen = 0; + static const struct servent_mdata mdata = { nss_lt_name, 0 }; + static const struct servent_mdata compat_mdata = { nss_lt_name, 1 }; +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + services, (void *)nss_lt_name, + serv_id_func, serv_marshal_func, serv_unmarshal_func); +#endif /* NS_CACHING */ + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_servent, (void *)&mdata }, #ifdef YP - sed->yp_stepping = 0; - sed->yp_domain = NULL; + { NSSRC_NIS, nis_servent, (void *)nss_lt_name }, +#endif + { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) #endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyname_r", + defaultsrc, name, proto, serv, buffer, bufsize, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); } int -__getservent_p(struct servent *se, struct servent_data *sed) +getservbyport_r(int port, const char *proto, struct servent *serv, + char *buffer, size_t bufsize, struct servent **result) { - char *p; - char *cp, **q, *endp; - long l; - -#ifdef YP - if (sed->yp_stepping && _getservent_yp(sed)) { - p = sed->line; - goto unpack; - } -tryagain: + static const struct servent_mdata mdata = { nss_lt_id, 0 }; + static const struct servent_mdata compat_mdata = { nss_lt_id, 1 }; +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + services, (void *)nss_lt_id, + serv_id_func, serv_marshal_func, serv_unmarshal_func); #endif - if (sed->fp == NULL && (sed->fp = fopen(_PATH_SERVICES, "r")) == NULL) - return (-1); -again: - if ((p = fgets(sed->line, sizeof sed->line, sed->fp)) == NULL) - return (-1); + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_servent, (void *)&mdata }, #ifdef YP - if (*p == '+' && _yp_check(NULL)) { - if (sed->yp_name != NULL) { - if (!_getservbyname_yp(sed)) - goto tryagain; - } - else if (sed->yp_port != 0) { - if (!_getservbyport_yp(sed)) - goto tryagain; - } - else if (!_getservent_yp(sed)) - goto tryagain; - } -unpack: + { NSSRC_NIS, nis_servent, (void *)nss_lt_id }, #endif - if (*p == '#') - goto again; - cp = strpbrk(p, "#\n"); - if (cp != NULL) - *cp = '\0'; - se->s_name = p; - p = strpbrk(p, " \t"); - if (p == NULL) - goto again; - *p++ = '\0'; - while (*p == ' ' || *p == '\t') - p++; - cp = strpbrk(p, ",/"); - if (cp == NULL) - goto again; - *cp++ = '\0'; - l = strtol(p, &endp, 10); - if (endp == p || *endp != '\0' || l < 0 || l > USHRT_MAX) - goto again; - se->s_port = htons((in_port_t)l); - se->s_proto = cp; - q = se->s_aliases = sed->aliases; - cp = strpbrk(cp, " \t"); - if (cp != NULL) - *cp++ = '\0'; - while (cp && *cp) { - if (*cp == ' ' || *cp == '\t') { - cp++; - continue; - } - if (q < &sed->aliases[_MAXALIASES - 1]) - *q++ = cp; - cp = strpbrk(cp, " \t"); - if (cp != NULL) - *cp++ = '\0'; - } - *q = NULL; - return (0); + { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyport_r", + defaultsrc, port, proto, serv, buffer, bufsize, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); } int -getservent_r(struct servent *sptr, char *buffer, size_t buflen, +getservent_r(struct servent *serv, char *buffer, size_t bufsize, struct servent **result) { - struct servent se; - struct servent_data *sed; - - if ((sed = __servent_data_init()) == NULL) - return (-1); - - if (__getservent_p(&se, sed) != 0) - return (-1); - if (__copy_servent(&se, sptr, buffer, buflen) != 0) - return (-1); - *result = sptr; - return (0); + static const struct servent_mdata mdata = { nss_lt_all, 0 }; + static const struct servent_mdata compat_mdata = { nss_lt_all, 1 }; +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + services, (void *)nss_lt_all, + serv_marshal_func, serv_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_servent, (void *)&mdata }, +#ifdef YP + { NSSRC_NIS, nis_servent, (void *)nss_lt_all }, +#endif + { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservent_r", + defaultsrc, serv, buffer, bufsize, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); } void -setservent(int f) +setservent(int stayopen) { - struct servent_data *sed; +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + services, (void *)nss_lt_all, + NULL, NULL); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_setservent, (void *)SETSERVENT }, +#ifdef YP + { NSSRC_NIS, nis_setservent, (void *)SETSERVENT }, +#endif + { NSSRC_COMPAT, compat_setservent, (void *)SETSERVENT }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; - if ((sed = __servent_data_init()) == NULL) - return; - __setservent_p(f, sed); + (void)nsdispatch(NULL, dtab, NSDB_SERVICES, "setservent", defaultsrc, + stayopen); } void -endservent(void) +endservent() { - struct servent_data *sed; +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + services, (void *)nss_lt_all, + NULL, NULL); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_setservent, (void *)ENDSERVENT }, +#ifdef YP + { NSSRC_NIS, nis_setservent, (void *)ENDSERVENT }, +#endif + { NSSRC_COMPAT, compat_setservent, (void *)ENDSERVENT }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; - if ((sed = __servent_data_init()) == NULL) + (void)nsdispatch(NULL, dtab, NSDB_SERVICES, "endservent", defaultsrc); +} + +/* get** wrappers for get**_r functions implementation */ +static void +servent_endstate(void *p) +{ + if (p == NULL) return; - __endservent_p(sed); + + free(((struct servent_state *)p)->buffer); + free(p); +} + +static int +wrap_getservbyname_r(struct key key, struct servent *serv, char *buffer, + size_t bufsize, struct servent **res) +{ + return (getservbyname_r(key.name, key.proto, serv, buffer, bufsize, + res)); +} + +static int +wrap_getservbyport_r(struct key key, struct servent *serv, char *buffer, + size_t bufsize, struct servent **res) +{ + return (getservbyport_r(key.port, key.proto, serv, buffer, bufsize, + res)); +} + +static int +wrap_getservent_r(struct key key, struct servent *serv, char *buffer, + size_t bufsize, struct servent **res) +{ + return (getservent_r(serv, buffer, bufsize, res)); +} + +static struct servent * +getserv(int (*fn)(struct key, struct servent *, char *, size_t, + struct servent **), struct key key) +{ + int rv; + struct servent *res; + struct servent_state * st; + + rv = servent_getstate(&st); + if (rv != 0) { + errno = rv; + return NULL; + } + + if (st->buffer == NULL) { + st->buffer = malloc(SERVENT_STORAGE_INITIAL); + if (st->buffer == NULL) + return (NULL); + st->bufsize = SERVENT_STORAGE_INITIAL; + } + do { + rv = fn(key, &st->serv, st->buffer, st->bufsize, &res); + if (res == NULL && rv == ERANGE) { + free(st->buffer); + if ((st->bufsize << 1) > SERVENT_STORAGE_MAX) { + st->buffer = NULL; + errno = ERANGE; + return (NULL); + } + st->bufsize <<= 1; + st->buffer = malloc(st->bufsize); + if (st->buffer == NULL) + return (NULL); + } + } while (res == NULL && rv == ERANGE); + if (rv != 0) + errno = rv; + + return (res); +} + +struct servent * +getservbyname(const char *name, const char *proto) +{ + struct key key; + + key.name = name; + key.proto = proto; + + return (getserv(wrap_getservbyname_r, key)); } struct servent * -getservent(void) +getservbyport(int port, const char *proto) { - struct servdata *sd; - struct servent *rval; - - if ((sd = __servdata_init()) == NULL) - return (NULL); - if (getservent_r(&sd->serv, sd->data, sizeof(sd->data), &rval) != 0) - return (NULL); - return (rval); + struct key key; + + key.port = port; + key.proto = proto; + + return (getserv(wrap_getservbyport_r, key)); +} + +struct servent * +getservent() +{ + struct key key; + + key.proto = NULL; + key.port = 0; + + return (getserv(wrap_getservent_r, key)); } diff --git a/lib/libc/net/name6.c b/lib/libc/net/name6.c index 18f2fa20814c..dc6521af3a8f 100644 --- a/lib/libc/net/name6.c +++ b/lib/libc/net/name6.c @@ -1,2095 +1,2354 @@ /* $KAME: name6.c,v 1.25 2000/06/26 16:44:40 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. * 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. Neither the name of the project 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 PROJECT 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 PROJECT 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. */ /* * ++Copyright++ 1985, 1988, 1993 * - * Copyright (c) 1985, 1988, 1993 * The 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. * - * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * - * --Copyright-- */ /* * Atsushi Onoe */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #ifdef ICMPNL #include "reentrant.h" #endif #include #include #include #include #include #ifdef INET6 #include #include #include #include #include /* XXX */ #endif #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "netdb_private.h" #include "res_config.h" #include "res_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif #ifndef _PATH_HOSTS #define _PATH_HOSTS "/etc/hosts" #endif #ifndef MAXALIASES #define MAXALIASES 10 #endif #ifndef MAXADDRS #define MAXADDRS 20 #endif #ifndef MAXDNAME #define MAXDNAME 1025 #endif #ifdef INET6 #define ADDRLEN(af) ((af) == AF_INET6 ? sizeof(struct in6_addr) : \ sizeof(struct in_addr)) #else #define ADDRLEN(af) sizeof(struct in_addr) #endif #define MAPADDR(ab, ina) \ do { \ memcpy(&(ab)->map_inaddr, ina, sizeof(struct in_addr)); \ memset((ab)->map_zero, 0, sizeof((ab)->map_zero)); \ memset((ab)->map_one, 0xff, sizeof((ab)->map_one)); \ } while (0) #define MAPADDRENABLED(flags) \ (((flags) & AI_V4MAPPED) || \ (((flags) & AI_V4MAPPED_CFG) && _mapped_addr_enabled())) union inx_addr { struct in_addr in_addr; #ifdef INET6 struct in6_addr in6_addr; #endif struct { u_char mau_zero[10]; u_char mau_one[2]; struct in_addr mau_inaddr; } map_addr_un; #define map_zero map_addr_un.mau_zero #define map_one map_addr_un.mau_one #define map_inaddr map_addr_un.mau_inaddr }; struct policyqueue { TAILQ_ENTRY(policyqueue) pc_entry; #ifdef INET6 struct in6_addrpolicy pc_policy; #endif }; TAILQ_HEAD(policyhead, policyqueue); #define AIO_SRCFLAG_DEPRECATED 0x1 struct hp_order { union { struct sockaddr_storage aiou_ss; struct sockaddr aiou_sa; } aio_src_un; #define aio_srcsa aio_src_un.aiou_sa u_int32_t aio_srcflag; int aio_srcscope; int aio_dstscope; struct policyqueue *aio_srcpolicy; struct policyqueue *aio_dstpolicy; union { struct sockaddr_storage aiou_ss; struct sockaddr aiou_sa; } aio_un; #define aio_sa aio_un.aiou_sa int aio_matchlen; char *aio_h_addr; }; static struct hostent *_hpcopy(struct hostent *, int *); static struct hostent *_hpaddr(int, const char *, void *, int *); static struct hostent *_hpmerge(struct hostent *, struct hostent *, int *); #ifdef INET6 static struct hostent *_hpmapv6(struct hostent *, int *); #endif static struct hostent *_hpsort(struct hostent *, res_state); static struct hostent *_ghbyname(const char *, int, int, int *); static char *_hgetword(char **); static int _mapped_addr_enabled(void); static struct hostent *_hpreorder(struct hostent *); static int get_addrselectpolicy(struct policyhead *); static void free_addrselectpolicy(struct policyhead *); static struct policyqueue *match_addrselectpolicy(struct sockaddr *, struct policyhead *); static void set_source(struct hp_order *, struct policyhead *); static int matchlen(struct sockaddr *, struct sockaddr *); static int comp_dst(const void *, const void *); static int gai_addr2scopetype(struct sockaddr *); static FILE *_files_open(int *); static int _files_ghbyname(void *, void *, va_list); static int _files_ghbyaddr(void *, void *, va_list); #ifdef YP static int _nis_ghbyname(void *, void *, va_list); static int _nis_ghbyaddr(void *, void *, va_list); #endif static int _dns_ghbyname(void *, void *, va_list); static int _dns_ghbyaddr(void *, void *, va_list); static void _dns_shent(int) __unused; static void _dns_ehent(void) __unused; #ifdef ICMPNL static int _icmp_ghbyaddr(void *, void *, va_list); #endif /* ICMPNL */ +#ifdef NS_CACHING +static int ipnode_id_func(char *, size_t *, va_list, void *); +static int ipnode_marshal_func(char *, size_t *, void *, va_list, void *); +static int ipnode_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif #ifdef ICMPNL static mutex_t _getipnodeby_thread_lock = MUTEX_INITIALIZER; #define THREAD_LOCK() mutex_lock(&_getipnodeby_thread_lock); #define THREAD_UNLOCK() mutex_unlock(&_getipnodeby_thread_lock); #endif /* Host lookup order if nsswitch.conf is broken or nonexistant */ static const ns_src default_src[] = { { NSSRC_FILES, NS_SUCCESS }, { NSSRC_DNS, NS_SUCCESS }, #ifdef ICMPNL #define NSSRC_ICMP "icmp" { NSSRC_ICMP, NS_SUCCESS }, #endif { 0 } }; /* * Check if kernel supports mapped address. * implementation dependent */ #ifdef __KAME__ #include #endif /* __KAME__ */ static int _mapped_addr_enabled(void) { /* implementation dependent check */ #if defined(__KAME__) && defined(IPV6CTL_MAPPED_ADDR) int mib[4]; size_t len; int val; mib[0] = CTL_NET; mib[1] = PF_INET6; mib[2] = IPPROTO_IPV6; mib[3] = IPV6CTL_MAPPED_ADDR; len = sizeof(val); if (sysctl(mib, 4, &val, &len, 0, 0) == 0 && val != 0) return 1; #endif /* __KAME__ && IPV6CTL_MAPPED_ADDR */ return 0; } +#ifdef NS_CACHING +static int +ipnode_id_func(char *buffer, size_t *buffer_size, va_list ap, + void *cache_mdata) +{ + res_state statp; + u_long res_options; + + const int op_id = 2; + char *name; + int af; + size_t len; + void *src; + + char *p; + size_t desired_size, size; + enum nss_lookup_type lookup_type; + int res = NS_UNAVAIL; + + statp = __res_state(); + res_options = statp->options & (RES_RECURSE | RES_DEFNAMES | + RES_DNSRCH | RES_NOALIASES | RES_USE_INET6); + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + name = va_arg(ap, char *); + af = va_arg(ap, int); + + size = strlen(name); + desired_size = sizeof(res_options) + sizeof(int) + + sizeof(enum nss_lookup_type) + sizeof(int) + size + 1; + + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + p = buffer; + memcpy(p, &res_options, sizeof(res_options)); + p += sizeof(res_options); + + memcpy(p, &op_id, sizeof(int)); + p += sizeof(int); + + memcpy(p, &lookup_type, sizeof(enum nss_lookup_type)); + p += sizeof(enum nss_lookup_type); + + memcpy(p, &af, sizeof(int)); + p += sizeof(int); + + memcpy(p, name, size + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + src = va_arg(ap, void *); + len = va_arg(ap, size_t); + af = va_arg(ap, int); + + desired_size = sizeof(res_options) + sizeof(int) + + sizeof(enum nss_lookup_type) + sizeof(int) + + sizeof(size_t) + len; + + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + p = buffer; + memcpy(p, &res_options, sizeof(res_options)); + p += sizeof(res_options); + + memcpy(p, &op_id, sizeof(int)); + p += sizeof(int); + + memcpy(p, &lookup_type, sizeof(enum nss_lookup_type)); + p += sizeof(enum nss_lookup_type); + + memcpy(p, &af, sizeof(int)); + p += sizeof(int); + + memcpy(p, &len, sizeof(size_t)); + p += sizeof(size_t); + + memcpy(p, src, len); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + +static int +ipnode_marshal_func(char *buffer, size_t *buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + struct hostent *ht; + + struct hostent new_ht; + size_t desired_size, aliases_size, addr_size, size; + char *p, **iter; + + ht = *((struct hostent **)retval); + + desired_size = _ALIGNBYTES + sizeof(struct hostent) + sizeof(char *); + if (ht->h_name != NULL) + desired_size += strlen(ht->h_name) + 1; + + if (ht->h_aliases != NULL) { + aliases_size = 0; + for (iter = ht->h_aliases; *iter; ++iter) { + desired_size += strlen(*iter) + 1; + ++aliases_size; + } + + desired_size += _ALIGNBYTES + + (aliases_size + 1) * sizeof(char *); + } + + if (ht->h_addr_list != NULL) { + addr_size = 0; + for (iter = ht->h_addr_list; *iter; ++iter) + ++addr_size; + + desired_size += addr_size * _ALIGN(ht->h_length); + desired_size += _ALIGNBYTES + (addr_size + 1) * sizeof(char *); + } + + if (desired_size > *buffer_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_ht, ht, sizeof(struct hostent)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct hostent) + sizeof(char *); + memcpy(buffer + sizeof(struct hostent), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_ht.h_name != NULL) { + size = strlen(new_ht.h_name); + memcpy(p, new_ht.h_name, size); + new_ht.h_name = p; + p += size + 1; + } + + if (new_ht.h_aliases != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_ht.h_aliases, sizeof(char *) * aliases_size); + new_ht.h_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); + + for (iter = new_ht.h_aliases; *iter; ++iter) { + size = strlen(*iter); + memcpy(p, *iter, size); + *iter = p; + p += size + 1; + } + } + + if (new_ht.h_addr_list != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_ht.h_addr_list, sizeof(char *) * addr_size); + new_ht.h_addr_list = (char **)p; + p += sizeof(char *) * (addr_size + 1); + + size = _ALIGN(new_ht.h_length); + for (iter = new_ht.h_addr_list; *iter; ++iter) { + memcpy(p, *iter, size); + *iter = p; + p += size + 1; + } + } + memcpy(buffer, &new_ht, sizeof(struct hostent)); + return (NS_SUCCESS); +} + +static int +ipnode_unmarshal_func(char *buffer, size_t buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + struct hostent new_ht; + struct hostent *ht; + + char *p; + char **iter; + char *orig_buf; + int err; + + ht = &new_ht; + + memcpy(ht, buffer, sizeof(struct hostent)); + memcpy(&p, buffer + sizeof(struct hostent), sizeof(char *)); + + orig_buf = buffer + sizeof(struct hostent) + sizeof(char *) + + _ALIGN(p) - (size_t)p; + p = (char *)_ALIGN(p); + + + NS_APPLY_OFFSET(ht->h_name, orig_buf, p, char *); + if (ht->h_aliases != NULL) { + NS_APPLY_OFFSET(ht->h_aliases, orig_buf, p, char **); + + for (iter = ht->h_aliases; *iter; ++iter) + NS_APPLY_OFFSET(*iter, orig_buf, p, char *); + } + + if (ht->h_addr_list != NULL) { + NS_APPLY_OFFSET(ht->h_addr_list, orig_buf, p, char **); + + for (iter = ht->h_addr_list; *iter; ++iter) + NS_APPLY_OFFSET(*iter, orig_buf, p, char *); + } + + ht = _hpcopy(ht, &err); + if (ht == NULL) + return (NS_UNAVAIL); + + *((struct hostent **)retval) = ht; + return (NS_SUCCESS); +} +#endif + /* * Functions defined in RFC2553 * getipnodebyname, getipnodebyaddr, freehostent */ static struct hostent * _ghbyname(const char *name, int af, int flags, int *errp) { struct hostent *hp; int rval; +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + hosts, (void *)nss_lt_name, + ipnode_id_func, ipnode_marshal_func, ipnode_unmarshal_func); +#endif static const ns_dtab dtab[] = { NS_FILES_CB(_files_ghbyname, NULL) { NSSRC_DNS, _dns_ghbyname, NULL }, NS_NIS_CB(_nis_ghbyname, NULL) +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif { 0 } }; if (flags & AI_ADDRCONFIG) { int s; if ((s = _socket(af, SOCK_DGRAM, 0)) < 0) return NULL; /* * TODO: * Note that implementation dependent test for address * configuration should be done everytime called * (or apropriate interval), * because addresses will be dynamically assigned or deleted. */ _close(s); } rval = _nsdispatch(&hp, dtab, NSDB_HOSTS, "ghbyname", default_src, name, af, errp); return (rval == NS_SUCCESS) ? hp : NULL; } struct hostent * getipnodebyname(const char *name, int af, int flags, int *errp) { struct hostent *hp; union inx_addr addrbuf; res_state statp; switch (af) { case AF_INET: #ifdef INET6 case AF_INET6: #endif break; default: *errp = NO_RECOVERY; return NULL; } #ifdef INET6 /* special case for literal address */ if (inet_pton(AF_INET6, name, &addrbuf) == 1) { if (af != AF_INET6) { *errp = HOST_NOT_FOUND; return NULL; } return _hpaddr(af, name, &addrbuf, errp); } #endif if (inet_aton(name, (struct in_addr *)&addrbuf) == 1) { if (af != AF_INET) { if (MAPADDRENABLED(flags)) { MAPADDR(&addrbuf, &addrbuf.in_addr); } else { *errp = HOST_NOT_FOUND; return NULL; } } return _hpaddr(af, name, &addrbuf, errp); } statp = __res_state(); if ((statp->options & RES_INIT) == 0) { if (res_ninit(statp) < 0) { *errp = NETDB_INTERNAL; return NULL; } } *errp = HOST_NOT_FOUND; hp = _ghbyname(name, af, flags, errp); #ifdef INET6 if (af == AF_INET6 && ((flags & AI_ALL) || hp == NULL) && MAPADDRENABLED(flags)) { struct hostent *hp2 = _ghbyname(name, AF_INET, flags, errp); if (hp == NULL) hp = _hpmapv6(hp2, errp); else { if (hp2 && strcmp(hp->h_name, hp2->h_name) != 0) { freehostent(hp2); hp2 = NULL; } hp = _hpmerge(hp, hp2, errp); } } #endif return _hpreorder(_hpsort(hp, statp)); } struct hostent * getipnodebyaddr(const void *src, size_t len, int af, int *errp) { struct hostent *hp; int rval; #ifdef INET6 struct in6_addr addrbuf; #else struct in_addr addrbuf; #endif +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + hosts, (void *)nss_lt_id, + ipnode_id_func, ipnode_marshal_func, ipnode_unmarshal_func); +#endif static const ns_dtab dtab[] = { NS_FILES_CB(_files_ghbyaddr, NULL) { NSSRC_DNS, _dns_ghbyaddr, NULL }, NS_NIS_CB(_nis_ghbyaddr, NULL) #ifdef ICMPNL { NSSRC_ICMP, _icmp_ghbyaddr, NULL }, +#endif +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) #endif { 0 } }; *errp = HOST_NOT_FOUND; switch (af) { case AF_INET: if (len != sizeof(struct in_addr)) { *errp = NO_RECOVERY; return NULL; } if ((long)src & ~(sizeof(struct in_addr) - 1)) { memcpy(&addrbuf, src, len); src = &addrbuf; } if (((struct in_addr *)src)->s_addr == 0) return NULL; break; #ifdef INET6 case AF_INET6: if (len != sizeof(struct in6_addr)) { *errp = NO_RECOVERY; return NULL; } if ((long)src & ~(sizeof(struct in6_addr) / 2 - 1)) { /*XXX*/ memcpy(&addrbuf, src, len); src = &addrbuf; } if (IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *)src)) return NULL; if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)src) || IN6_IS_ADDR_V4COMPAT((struct in6_addr *)src)) { src = (char *)src + (sizeof(struct in6_addr) - sizeof(struct in_addr)); af = AF_INET; len = sizeof(struct in_addr); } break; #endif default: *errp = NO_RECOVERY; return NULL; } rval = _nsdispatch(&hp, dtab, NSDB_HOSTS, "ghbyaddr", default_src, src, len, af, errp); return (rval == NS_SUCCESS) ? hp : NULL; } void freehostent(struct hostent *ptr) { free(ptr); } /* * Private utility functions */ /* * _hpcopy: allocate and copy hostent structure */ static struct hostent * _hpcopy(struct hostent *hp, int *errp) { struct hostent *nhp; char *cp, **pp; int size, addrsize; int nalias = 0, naddr = 0; int al_off; int i; if (hp == NULL) return hp; /* count size to be allocated */ size = sizeof(struct hostent); if (hp->h_name != NULL) size += strlen(hp->h_name) + 1; if ((pp = hp->h_aliases) != NULL) { for (i = 0; *pp != NULL; i++, pp++) { if (**pp != '\0') { size += strlen(*pp) + 1; nalias++; } } } /* adjust alignment */ size = ALIGN(size); al_off = size; size += sizeof(char *) * (nalias + 1); addrsize = ALIGN(hp->h_length); if ((pp = hp->h_addr_list) != NULL) { while (*pp++ != NULL) naddr++; } size += addrsize * naddr; size += sizeof(char *) * (naddr + 1); /* copy */ if ((nhp = (struct hostent *)malloc(size)) == NULL) { *errp = TRY_AGAIN; return NULL; } cp = (char *)&nhp[1]; if (hp->h_name != NULL) { nhp->h_name = cp; strcpy(cp, hp->h_name); cp += strlen(cp) + 1; } else nhp->h_name = NULL; nhp->h_aliases = (char **)((char *)nhp + al_off); if ((pp = hp->h_aliases) != NULL) { for (i = 0; *pp != NULL; pp++) { if (**pp != '\0') { nhp->h_aliases[i++] = cp; strcpy(cp, *pp); cp += strlen(cp) + 1; } } } nhp->h_aliases[nalias] = NULL; cp = (char *)&nhp->h_aliases[nalias + 1]; nhp->h_addrtype = hp->h_addrtype; nhp->h_length = hp->h_length; nhp->h_addr_list = (char **)cp; if ((pp = hp->h_addr_list) != NULL) { cp = (char *)&nhp->h_addr_list[naddr + 1]; for (i = 0; *pp != NULL; pp++) { nhp->h_addr_list[i++] = cp; memcpy(cp, *pp, hp->h_length); cp += addrsize; } } nhp->h_addr_list[naddr] = NULL; return nhp; } /* * _hpaddr: construct hostent structure with one address */ static struct hostent * _hpaddr(int af, const char *name, void *addr, int *errp) { struct hostent *hp, hpbuf; char *addrs[2]; hp = &hpbuf; hp->h_name = (char *)name; hp->h_aliases = NULL; hp->h_addrtype = af; hp->h_length = ADDRLEN(af); hp->h_addr_list = addrs; addrs[0] = (char *)addr; addrs[1] = NULL; return _hpcopy(hp, errp); } /* * _hpmerge: merge 2 hostent structure, arguments will be freed */ static struct hostent * _hpmerge(struct hostent *hp1, struct hostent *hp2, int *errp) { int i, j; int naddr, nalias; char **pp; struct hostent *hp, hpbuf; char *aliases[MAXALIASES + 1], *addrs[MAXADDRS + 1]; union inx_addr addrbuf[MAXADDRS]; if (hp1 == NULL) return hp2; if (hp2 == NULL) return hp1; #define HP(i) (i == 1 ? hp1 : hp2) hp = &hpbuf; hp->h_name = (hp1->h_name != NULL ? hp1->h_name : hp2->h_name); hp->h_aliases = aliases; nalias = 0; for (i = 1; i <= 2; i++) { if ((pp = HP(i)->h_aliases) == NULL) continue; for (; nalias < MAXALIASES && *pp != NULL; pp++) { /* check duplicates */ for (j = 0; j < nalias; j++) if (strcasecmp(*pp, aliases[j]) == 0) break; if (j == nalias) aliases[nalias++] = *pp; } } aliases[nalias] = NULL; #ifdef INET6 if (hp1->h_length != hp2->h_length) { hp->h_addrtype = AF_INET6; hp->h_length = sizeof(struct in6_addr); } else { #endif hp->h_addrtype = hp1->h_addrtype; hp->h_length = hp1->h_length; #ifdef INET6 } #endif hp->h_addr_list = addrs; naddr = 0; for (i = 1; i <= 2; i++) { if ((pp = HP(i)->h_addr_list) == NULL) continue; if (HP(i)->h_length == hp->h_length) { while (naddr < MAXADDRS && *pp != NULL) addrs[naddr++] = *pp++; } else { /* copy IPv4 addr as mapped IPv6 addr */ while (naddr < MAXADDRS && *pp != NULL) { MAPADDR(&addrbuf[naddr], *pp++); addrs[naddr] = (char *)&addrbuf[naddr]; naddr++; } } } addrs[naddr] = NULL; hp = _hpcopy(hp, errp); freehostent(hp1); freehostent(hp2); return hp; } /* * _hpmapv6: convert IPv4 hostent into IPv4-mapped IPv6 addresses */ #ifdef INET6 static struct hostent * _hpmapv6(struct hostent *hp, int *errp) { struct hostent *hp6; if (hp == NULL) return NULL; if (hp->h_addrtype == AF_INET6) return hp; /* make dummy hostent to convert IPv6 address */ if ((hp6 = (struct hostent *)malloc(sizeof(struct hostent))) == NULL) { *errp = TRY_AGAIN; return NULL; } hp6->h_name = NULL; hp6->h_aliases = NULL; hp6->h_addrtype = AF_INET6; hp6->h_length = sizeof(struct in6_addr); hp6->h_addr_list = NULL; return _hpmerge(hp6, hp, errp); } #endif /* * _hpsort: sort address by sortlist */ static struct hostent * _hpsort(struct hostent *hp, res_state statp) { int i, j, n; u_char *ap, *sp, *mp, **pp; char t; char order[MAXADDRS]; int nsort = statp->nsort; if (hp == NULL || hp->h_addr_list[1] == NULL || nsort == 0) return hp; for (i = 0; (ap = (u_char *)hp->h_addr_list[i]); i++) { for (j = 0; j < nsort; j++) { #ifdef INET6 if (statp->_u._ext.ext->sort_list[j].af != hp->h_addrtype) continue; sp = (u_char *)&statp->_u._ext.ext->sort_list[j].addr; mp = (u_char *)&statp->_u._ext.ext->sort_list[j].mask; #else sp = (u_char *)&statp->sort_list[j].addr; mp = (u_char *)&statp->sort_list[j].mask; #endif for (n = 0; n < hp->h_length; n++) { if ((ap[n] & mp[n]) != sp[n]) break; } if (n == hp->h_length) break; } order[i] = j; } n = i; pp = (u_char **)hp->h_addr_list; for (i = 0; i < n - 1; i++) { for (j = i + 1; j < n; j++) { if (order[i] > order[j]) { ap = pp[i]; pp[i] = pp[j]; pp[j] = ap; t = order[i]; order[i] = order[j]; order[j] = t; } } } return hp; } static char * _hgetword(char **pp) { char c, *p, *ret; const char *sp; static const char sep[] = "# \t\n"; ret = NULL; for (p = *pp; (c = *p) != '\0'; p++) { for (sp = sep; *sp != '\0'; sp++) { if (c == *sp) break; } if (c == '#') p[1] = '\0'; /* ignore rest of line */ if (ret == NULL) { if (*sp == '\0') ret = p; } else { if (*sp != '\0') { *p++ = '\0'; break; } } } *pp = p; if (ret == NULL || *ret == '\0') return NULL; return ret; } /* * _hpreorder: sort address by default address selection */ static struct hostent * _hpreorder(struct hostent *hp) { struct hp_order *aio; int i, n; char *ap; struct sockaddr *sa; struct policyhead policyhead; if (hp == NULL) return hp; switch (hp->h_addrtype) { case AF_INET: #ifdef INET6 case AF_INET6: #endif break; default: free_addrselectpolicy(&policyhead); return hp; } /* count the number of addrinfo elements for sorting. */ for (n = 0; hp->h_addr_list[n] != NULL; n++) ; /* * If the number is small enough, we can skip the reordering process. */ if (n <= 1) return hp; /* allocate a temporary array for sort and initialization of it. */ if ((aio = malloc(sizeof(*aio) * n)) == NULL) return hp; /* give up reordering */ memset(aio, 0, sizeof(*aio) * n); /* retrieve address selection policy from the kernel */ TAILQ_INIT(&policyhead); if (!get_addrselectpolicy(&policyhead)) { /* no policy is installed into kernel, we don't sort. */ free(aio); return hp; } for (i = 0; i < n; i++) { ap = hp->h_addr_list[i]; aio[i].aio_h_addr = ap; sa = &aio[i].aio_sa; switch (hp->h_addrtype) { case AF_INET: sa->sa_family = AF_INET; sa->sa_len = sizeof(struct sockaddr_in); memcpy(&((struct sockaddr_in *)sa)->sin_addr, ap, sizeof(struct in_addr)); break; #ifdef INET6 case AF_INET6: if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) { sa->sa_family = AF_INET; sa->sa_len = sizeof(struct sockaddr_in); memcpy(&((struct sockaddr_in *)sa)->sin_addr, &ap[12], sizeof(struct in_addr)); } else { sa->sa_family = AF_INET6; sa->sa_len = sizeof(struct sockaddr_in6); memcpy(&((struct sockaddr_in6 *)sa)->sin6_addr, ap, sizeof(struct in6_addr)); } break; #endif } aio[i].aio_dstscope = gai_addr2scopetype(sa); aio[i].aio_dstpolicy = match_addrselectpolicy(sa, &policyhead); set_source(&aio[i], &policyhead); } /* perform sorting. */ qsort(aio, n, sizeof(*aio), comp_dst); /* reorder the h_addr_list. */ for (i = 0; i < n; i++) hp->h_addr_list[i] = aio[i].aio_h_addr; /* cleanup and return */ free(aio); free_addrselectpolicy(&policyhead); return hp; } static int get_addrselectpolicy(struct policyhead *head) { #ifdef INET6 int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY }; size_t l; char *buf; struct in6_addrpolicy *pol, *ep; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) return (0); if ((buf = malloc(l)) == NULL) return (0); if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { free(buf); return (0); } ep = (struct in6_addrpolicy *)(buf + l); for (pol = (struct in6_addrpolicy *)buf; pol + 1 <= ep; pol++) { struct policyqueue *new; if ((new = malloc(sizeof(*new))) == NULL) { free_addrselectpolicy(head); /* make the list empty */ break; } new->pc_policy = *pol; TAILQ_INSERT_TAIL(head, new, pc_entry); } free(buf); return (1); #else return (0); #endif } static void free_addrselectpolicy(struct policyhead *head) { struct policyqueue *ent, *nent; for (ent = TAILQ_FIRST(head); ent; ent = nent) { nent = TAILQ_NEXT(ent, pc_entry); TAILQ_REMOVE(head, ent, pc_entry); free(ent); } } static struct policyqueue * match_addrselectpolicy(struct sockaddr *addr, struct policyhead *head) { #ifdef INET6 struct policyqueue *ent, *bestent = NULL; struct in6_addrpolicy *pol; int matchlen, bestmatchlen = -1; u_char *mp, *ep, *k, *p, m; struct sockaddr_in6 key; switch(addr->sa_family) { case AF_INET6: key = *(struct sockaddr_in6 *)addr; break; case AF_INET: /* convert the address into IPv4-mapped IPv6 address. */ memset(&key, 0, sizeof(key)); key.sin6_family = AF_INET6; key.sin6_len = sizeof(key); key.sin6_addr.s6_addr[10] = 0xff; key.sin6_addr.s6_addr[11] = 0xff; memcpy(&key.sin6_addr.s6_addr[12], &((struct sockaddr_in *)addr)->sin_addr, 4); break; default: return(NULL); } for (ent = TAILQ_FIRST(head); ent; ent = TAILQ_NEXT(ent, pc_entry)) { pol = &ent->pc_policy; matchlen = 0; mp = (u_char *)&pol->addrmask.sin6_addr; ep = mp + 16; /* XXX: scope field? */ k = (u_char *)&key.sin6_addr; p = (u_char *)&pol->addr.sin6_addr; for (; mp < ep && *mp; mp++, k++, p++) { m = *mp; if ((*k & m) != *p) goto next; /* not match */ if (m == 0xff) /* short cut for a typical case */ matchlen += 8; else { while (m >= 0x80) { matchlen++; m <<= 1; } } } /* matched. check if this is better than the current best. */ if (matchlen > bestmatchlen) { bestent = ent; bestmatchlen = matchlen; } next: continue; } return(bestent); #else return(NULL); #endif } static void set_source(struct hp_order *aio, struct policyhead *ph) { struct sockaddr_storage ss = aio->aio_un.aiou_ss; socklen_t srclen; int s; /* set unspec ("no source is available"), just in case */ aio->aio_srcsa.sa_family = AF_UNSPEC; aio->aio_srcscope = -1; switch(ss.ss_family) { case AF_INET: ((struct sockaddr_in *)&ss)->sin_port = htons(1); break; #ifdef INET6 case AF_INET6: ((struct sockaddr_in6 *)&ss)->sin6_port = htons(1); break; #endif default: /* ignore unsupported AFs explicitly */ return; } /* open a socket to get the source address for the given dst */ if ((s = _socket(ss.ss_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) return; /* give up */ if (_connect(s, (struct sockaddr *)&ss, ss.ss_len) < 0) goto cleanup; srclen = ss.ss_len; if (_getsockname(s, &aio->aio_srcsa, &srclen) < 0) { aio->aio_srcsa.sa_family = AF_UNSPEC; goto cleanup; } aio->aio_srcscope = gai_addr2scopetype(&aio->aio_srcsa); aio->aio_srcpolicy = match_addrselectpolicy(&aio->aio_srcsa, ph); aio->aio_matchlen = matchlen(&aio->aio_srcsa, (struct sockaddr *)&ss); #ifdef INET6 if (ss.ss_family == AF_INET6) { struct in6_ifreq ifr6; u_int32_t flags6; /* XXX: interface name should not be hardcoded */ strncpy(ifr6.ifr_name, "lo0", sizeof(ifr6.ifr_name)); memset(&ifr6, 0, sizeof(ifr6)); memcpy(&ifr6.ifr_addr, &ss, ss.ss_len); if (_ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) == 0) { flags6 = ifr6.ifr_ifru.ifru_flags6; if ((flags6 & IN6_IFF_DEPRECATED)) aio->aio_srcflag |= AIO_SRCFLAG_DEPRECATED; } } #endif cleanup: _close(s); return; } static int matchlen(struct sockaddr *src, struct sockaddr *dst) { int match = 0; u_char *s, *d; u_char *lim, r; int addrlen; switch (src->sa_family) { #ifdef INET6 case AF_INET6: s = (u_char *)&((struct sockaddr_in6 *)src)->sin6_addr; d = (u_char *)&((struct sockaddr_in6 *)dst)->sin6_addr; addrlen = sizeof(struct in6_addr); lim = s + addrlen; break; #endif case AF_INET: s = (u_char *)&((struct sockaddr_in *)src)->sin_addr; d = (u_char *)&((struct sockaddr_in *)dst)->sin_addr; addrlen = sizeof(struct in_addr); lim = s + addrlen; break; default: return(0); } while (s < lim) if ((r = (*d++ ^ *s++)) != 0) { while (r < addrlen * 8) { match++; r <<= 1; } break; } else match += 8; return(match); } static int comp_dst(const void *arg1, const void *arg2) { const struct hp_order *dst1 = arg1, *dst2 = arg2; /* * Rule 1: Avoid unusable destinations. * XXX: we currently do not consider if an appropriate route exists. */ if (dst1->aio_srcsa.sa_family != AF_UNSPEC && dst2->aio_srcsa.sa_family == AF_UNSPEC) { return(-1); } if (dst1->aio_srcsa.sa_family == AF_UNSPEC && dst2->aio_srcsa.sa_family != AF_UNSPEC) { return(1); } /* Rule 2: Prefer matching scope. */ if (dst1->aio_dstscope == dst1->aio_srcscope && dst2->aio_dstscope != dst2->aio_srcscope) { return(-1); } if (dst1->aio_dstscope != dst1->aio_srcscope && dst2->aio_dstscope == dst2->aio_srcscope) { return(1); } /* Rule 3: Avoid deprecated addresses. */ if (dst1->aio_srcsa.sa_family != AF_UNSPEC && dst2->aio_srcsa.sa_family != AF_UNSPEC) { if (!(dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && (dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { return(-1); } if ((dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && !(dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { return(1); } } /* Rule 4: Prefer home addresses. */ /* XXX: not implemented yet */ /* Rule 5: Prefer matching label. */ #ifdef INET6 if (dst1->aio_srcpolicy && dst1->aio_dstpolicy && dst1->aio_srcpolicy->pc_policy.label == dst1->aio_dstpolicy->pc_policy.label && (dst2->aio_srcpolicy == NULL || dst2->aio_dstpolicy == NULL || dst2->aio_srcpolicy->pc_policy.label != dst2->aio_dstpolicy->pc_policy.label)) { return(-1); } if (dst2->aio_srcpolicy && dst2->aio_dstpolicy && dst2->aio_srcpolicy->pc_policy.label == dst2->aio_dstpolicy->pc_policy.label && (dst1->aio_srcpolicy == NULL || dst1->aio_dstpolicy == NULL || dst1->aio_srcpolicy->pc_policy.label != dst1->aio_dstpolicy->pc_policy.label)) { return(1); } #endif /* Rule 6: Prefer higher precedence. */ #ifdef INET6 if (dst1->aio_dstpolicy && (dst2->aio_dstpolicy == NULL || dst1->aio_dstpolicy->pc_policy.preced > dst2->aio_dstpolicy->pc_policy.preced)) { return(-1); } if (dst2->aio_dstpolicy && (dst1->aio_dstpolicy == NULL || dst2->aio_dstpolicy->pc_policy.preced > dst1->aio_dstpolicy->pc_policy.preced)) { return(1); } #endif /* Rule 7: Prefer native transport. */ /* XXX: not implemented yet */ /* Rule 8: Prefer smaller scope. */ if (dst1->aio_dstscope >= 0 && dst1->aio_dstscope < dst2->aio_dstscope) { return(-1); } if (dst2->aio_dstscope >= 0 && dst2->aio_dstscope < dst1->aio_dstscope) { return(1); } /* * Rule 9: Use longest matching prefix. * We compare the match length in a same AF only. */ if (dst1->aio_sa.sa_family == dst2->aio_sa.sa_family) { if (dst1->aio_matchlen > dst2->aio_matchlen) { return(-1); } if (dst1->aio_matchlen < dst2->aio_matchlen) { return(1); } } /* Rule 10: Otherwise, leave the order unchanged. */ return(-1); } /* * Copy from scope.c. * XXX: we should standardize the functions and link them as standard * library. */ static int gai_addr2scopetype(struct sockaddr *sa) { #ifdef INET6 struct sockaddr_in6 *sa6; #endif struct sockaddr_in *sa4; switch(sa->sa_family) { #ifdef INET6 case AF_INET6: sa6 = (struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) { /* just use the scope field of the multicast address */ return(sa6->sin6_addr.s6_addr[2] & 0x0f); } /* * Unicast addresses: map scope type to corresponding scope * value defined for multcast addresses. * XXX: hardcoded scope type values are bad... */ if (IN6_IS_ADDR_LOOPBACK(&sa6->sin6_addr)) return(1); /* node local scope */ if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) return(2); /* link-local scope */ if (IN6_IS_ADDR_SITELOCAL(&sa6->sin6_addr)) return(5); /* site-local scope */ return(14); /* global scope */ break; #endif case AF_INET: /* * IPv4 pseudo scoping according to RFC 3484. */ sa4 = (struct sockaddr_in *)sa; /* IPv4 autoconfiguration addresses have link-local scope. */ if (((u_char *)&sa4->sin_addr)[0] == 169 && ((u_char *)&sa4->sin_addr)[1] == 254) return(2); /* Private addresses have site-local scope. */ if (((u_char *)&sa4->sin_addr)[0] == 10 || (((u_char *)&sa4->sin_addr)[0] == 172 && (((u_char *)&sa4->sin_addr)[1] & 0xf0) == 16) || (((u_char *)&sa4->sin_addr)[0] == 192 && ((u_char *)&sa4->sin_addr)[1] == 168)) return(14); /* XXX: It should be 5 unless NAT */ /* Loopback addresses have link-local scope. */ if (((u_char *)&sa4->sin_addr)[0] == 127) return(2); return(14); break; default: errno = EAFNOSUPPORT; /* is this a good error? */ return(-1); } } /* * FILES (/etc/hosts) */ static FILE * _files_open(int *errp) { FILE *fp; fp = fopen(_PATH_HOSTS, "r"); if (fp == NULL) *errp = NO_RECOVERY; return fp; } static int _files_ghbyname(void *rval, void *cb_data, va_list ap) { const char *name; int af; int *errp; int match, nalias; char *p, *line, *addrstr, *cname; FILE *fp; struct hostent *rethp, *hp, hpbuf; char *aliases[MAXALIASES + 1], *addrs[2]; union inx_addr addrbuf; char buf[BUFSIZ]; name = va_arg(ap, const char *); af = va_arg(ap, int); errp = va_arg(ap, int *); *(struct hostent **)rval = NULL; if ((fp = _files_open(errp)) == NULL) return NS_UNAVAIL; rethp = hp = NULL; while (fgets(buf, sizeof(buf), fp)) { line = buf; if ((addrstr = _hgetword(&line)) == NULL || (cname = _hgetword(&line)) == NULL) continue; match = (strcasecmp(cname, name) == 0); nalias = 0; while ((p = _hgetword(&line)) != NULL) { if (!match) match = (strcasecmp(p, name) == 0); if (nalias < MAXALIASES) aliases[nalias++] = p; } if (!match) continue; switch (af) { case AF_INET: if (inet_aton(addrstr, (struct in_addr *)&addrbuf) != 1) { *errp = NO_DATA; /* name found */ continue; } break; #ifdef INET6 case AF_INET6: if (inet_pton(af, addrstr, &addrbuf) != 1) { *errp = NO_DATA; /* name found */ continue; } break; #endif } hp = &hpbuf; hp->h_name = cname; hp->h_aliases = aliases; aliases[nalias] = NULL; hp->h_addrtype = af; hp->h_length = ADDRLEN(af); hp->h_addr_list = addrs; addrs[0] = (char *)&addrbuf; addrs[1] = NULL; hp = _hpcopy(hp, errp); rethp = _hpmerge(rethp, hp, errp); } fclose(fp); *(struct hostent **)rval = rethp; return (rethp != NULL) ? NS_SUCCESS : NS_NOTFOUND; } static int _files_ghbyaddr(void *rval, void *cb_data, va_list ap) { const void *addr; int addrlen; int af; int *errp; int nalias; char *p, *line; FILE *fp; struct hostent *hp, hpbuf; char *aliases[MAXALIASES + 1], *addrs[2]; union inx_addr addrbuf; char buf[BUFSIZ]; addr = va_arg(ap, const void *); addrlen = va_arg(ap, int); af = va_arg(ap, int); errp = va_arg(ap, int *); *(struct hostent**)rval = NULL; if ((fp = _files_open(errp)) == NULL) return NS_UNAVAIL; hp = NULL; while (fgets(buf, sizeof(buf), fp)) { line = buf; if ((p = _hgetword(&line)) == NULL || (af == AF_INET ? inet_aton(p, (struct in_addr *)&addrbuf) : inet_pton(af, p, &addrbuf)) != 1 || memcmp(addr, &addrbuf, addrlen) != 0 || (p = _hgetword(&line)) == NULL) continue; hp = &hpbuf; hp->h_name = p; hp->h_aliases = aliases; nalias = 0; while ((p = _hgetword(&line)) != NULL) { if (nalias < MAXALIASES) aliases[nalias++] = p; } aliases[nalias] = NULL; hp->h_addrtype = af; hp->h_length = addrlen; hp->h_addr_list = addrs; addrs[0] = (char *)&addrbuf; addrs[1] = NULL; hp = _hpcopy(hp, errp); break; } fclose(fp); *(struct hostent **)rval = hp; return (hp != NULL) ? NS_SUCCESS : NS_NOTFOUND; } #ifdef YP /* * NIS * * XXX actually a hack. */ static int _nis_ghbyname(void *rval, void *cb_data, va_list ap) { const char *name; int af; int *errp; struct hostent *hp = NULL; name = va_arg(ap, const char *); af = va_arg(ap, int); errp = va_arg(ap, int *); hp = _gethostbynisname(name, af); if (hp != NULL) hp = _hpcopy(hp, errp); *(struct hostent **)rval = hp; return (hp != NULL) ? NS_SUCCESS : NS_NOTFOUND; } static int _nis_ghbyaddr(void *rval, void *cb_data, va_list ap) { const void *addr; int addrlen; int af; int *errp; struct hostent *hp = NULL; addr = va_arg(ap, const void *); addrlen = va_arg(ap, int); af = va_arg(ap, int); hp = _gethostbynisaddr(addr, addrlen, af); if (hp != NULL) hp = _hpcopy(hp, errp); *(struct hostent **)rval = hp; return (hp != NULL) ? NS_SUCCESS : NS_NOTFOUND; } #endif #define MAXPACKET (64*1024) typedef union { HEADER hdr; u_char buf[MAXPACKET]; } querybuf; static struct hostent *getanswer(const querybuf *, int, const char *, int, struct hostent *, int *); /* * we don't need to take care about sorting, nor IPv4 mapped address here. */ static struct hostent * getanswer(const querybuf *answer, int anslen, const char *qname, int qtype, struct hostent *template, int *errp) { const HEADER *hp; const u_char *cp; int n; const u_char *eom, *erdata; char *bp, *ep, **ap, **hap; int type, class, ancount, qdcount; int haveanswer, had_error; char tbuf[MAXDNAME]; const char *tname; int (*name_ok)(const char *); static char *h_addr_ptrs[MAXADDRS + 1]; static char *host_aliases[MAXALIASES]; static char hostbuf[8*1024]; #define BOUNDED_INCR(x) \ do { \ cp += x; \ if (cp > eom) { \ *errp = NO_RECOVERY; \ return (NULL); \ } \ } while (0) #define BOUNDS_CHECK(ptr, count) \ do { \ if ((ptr) + (count) > eom) { \ *errp = NO_RECOVERY; \ return (NULL); \ } \ } while (0) /* XXX do {} while (0) cannot be put here */ #define DNS_ASSERT(x) \ { \ if (!(x)) { \ cp += n; \ continue; \ } \ } /* XXX do {} while (0) cannot be put here */ #define DNS_FATAL(x) \ { \ if (!(x)) { \ had_error++; \ continue; \ } \ } tname = qname; template->h_name = NULL; eom = answer->buf + anslen; switch (qtype) { case T_A: case T_AAAA: name_ok = res_hnok; break; case T_PTR: name_ok = res_dnok; break; default: return (NULL); /* XXX should be abort(); */ } /* * find first satisfactory answer */ hp = &answer->hdr; ancount = ntohs(hp->ancount); qdcount = ntohs(hp->qdcount); bp = hostbuf; ep = hostbuf + sizeof hostbuf; cp = answer->buf; BOUNDED_INCR(HFIXEDSZ); if (qdcount != 1) { *errp = NO_RECOVERY; return (NULL); } n = dn_expand(answer->buf, eom, cp, bp, ep - bp); if ((n < 0) || !(*name_ok)(bp)) { *errp = NO_RECOVERY; return (NULL); } BOUNDED_INCR(n + QFIXEDSZ); if (qtype == T_A || qtype == T_AAAA) { /* res_send() has already verified that the query name is the * same as the one we sent; this just gets the expanded name * (i.e., with the succeeding search-domain tacked on). */ n = strlen(bp) + 1; /* for the \0 */ if (n >= MAXHOSTNAMELEN) { *errp = NO_RECOVERY; return (NULL); } template->h_name = bp; bp += n; /* The qname can be abbreviated, but h_name is now absolute. */ qname = template->h_name; } ap = host_aliases; *ap = NULL; template->h_aliases = host_aliases; hap = h_addr_ptrs; *hap = NULL; template->h_addr_list = h_addr_ptrs; haveanswer = 0; had_error = 0; while (ancount-- > 0 && cp < eom && !had_error) { n = dn_expand(answer->buf, eom, cp, bp, ep - bp); DNS_FATAL(n >= 0); DNS_FATAL((*name_ok)(bp)); cp += n; /* name */ BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ); type = _getshort(cp); cp += INT16SZ; /* type */ class = _getshort(cp); cp += INT16SZ + INT32SZ; /* class, TTL */ n = _getshort(cp); cp += INT16SZ; /* len */ BOUNDS_CHECK(cp, n); erdata = cp + n; DNS_ASSERT(class == C_IN); if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) { if (ap >= &host_aliases[MAXALIASES-1]) continue; n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); DNS_FATAL(n >= 0); DNS_FATAL((*name_ok)(tbuf)); cp += n; if (cp != erdata) { *errp = NO_RECOVERY; return (NULL); } /* Store alias. */ *ap++ = bp; n = strlen(bp) + 1; /* for the \0 */ DNS_FATAL(n < MAXHOSTNAMELEN); bp += n; /* Get canonical name. */ n = strlen(tbuf) + 1; /* for the \0 */ DNS_FATAL(n <= ep - bp); DNS_FATAL(n < MAXHOSTNAMELEN); strcpy(bp, tbuf); template->h_name = bp; bp += n; continue; } if (qtype == T_PTR && type == T_CNAME) { n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); if (n < 0 || !res_dnok(tbuf)) { had_error++; continue; } cp += n; if (cp != erdata) { *errp = NO_RECOVERY; return (NULL); } /* Get canonical name. */ n = strlen(tbuf) + 1; /* for the \0 */ if (n > ep - bp || n >= MAXHOSTNAMELEN) { had_error++; continue; } strcpy(bp, tbuf); tname = bp; bp += n; continue; } DNS_ASSERT(type == qtype); switch (type) { case T_PTR: DNS_ASSERT(strcasecmp(tname, bp) == 0); n = dn_expand(answer->buf, eom, cp, bp, ep - bp); DNS_FATAL(n >= 0); DNS_FATAL(res_hnok(bp)); #if MULTI_PTRS_ARE_ALIASES cp += n; if (cp != erdata) { *errp = NO_RECOVERY; return (NULL); } if (!haveanswer) template->h_name = bp; else if (ap < &host_aliases[MAXALIASES-1]) *ap++ = bp; else n = -1; if (n != -1) { n = strlen(bp) + 1; /* for the \0 */ if (n >= MAXHOSTNAMELEN) { had_error++; break; } bp += n; } break; #else template->h_name = bp; *errp = NETDB_SUCCESS; return (template); #endif case T_A: case T_AAAA: DNS_ASSERT(strcasecmp(template->h_name, bp) == 0); DNS_ASSERT(n == template->h_length); if (!haveanswer) { int nn; template->h_name = bp; nn = strlen(bp) + 1; /* for the \0 */ bp += nn; } bp = (char *)ALIGN(bp); DNS_FATAL(bp + n < ep); DNS_ASSERT(hap < &h_addr_ptrs[MAXADDRS-1]); #ifdef FILTER_V4MAPPED if (type == T_AAAA) { struct in6_addr in6; memcpy(&in6, cp, sizeof(in6)); DNS_ASSERT(IN6_IS_ADDR_V4MAPPED(&in6) == 0); } #endif bcopy(cp, *hap++ = bp, n); bp += n; cp += n; if (cp != erdata) { *errp = NO_RECOVERY; return (NULL); } break; default: abort(); } if (!had_error) haveanswer++; } if (haveanswer) { *ap = NULL; *hap = NULL; if (!template->h_name) { n = strlen(qname) + 1; /* for the \0 */ if (n > ep - bp || n >= MAXHOSTNAMELEN) goto no_recovery; strcpy(bp, qname); template->h_name = bp; bp += n; } *errp = NETDB_SUCCESS; return (template); } no_recovery: *errp = NO_RECOVERY; return (NULL); #undef BOUNDED_INCR #undef BOUNDS_CHECK #undef DNS_ASSERT #undef DNS_FATAL } static int _dns_ghbyname(void *rval, void *cb_data, va_list ap) { const char *name; int af; int *errp; int n; struct hostent *hp; int qtype; struct hostent hbuf; querybuf *buf; res_state statp; name = va_arg(ap, const char *); af = va_arg(ap, int); errp = va_arg(ap, int *); statp = __res_state(); memset(&hbuf, 0, sizeof(hbuf)); hbuf.h_addrtype = af; hbuf.h_length = ADDRLEN(af); switch (af) { #ifdef INET6 case AF_INET6: qtype = T_AAAA; break; #endif case AF_INET: qtype = T_A; break; default: *errp = NO_RECOVERY; return NS_NOTFOUND; } buf = malloc(sizeof(*buf)); if (buf == NULL) { *errp = NETDB_INTERNAL; return NS_UNAVAIL; } n = res_nsearch(statp, name, C_IN, qtype, buf->buf, sizeof(buf->buf)); if (n < 0) { free(buf); *errp = statp->res_h_errno; return NS_UNAVAIL; } hp = getanswer(buf, n, name, qtype, &hbuf, errp); free(buf); if (!hp) { *errp = NO_RECOVERY; return NS_NOTFOUND; } *(struct hostent **)rval = _hpcopy(&hbuf, errp); if (*(struct hostent **)rval != NULL) return NS_SUCCESS; else if (*errp == TRY_AGAIN) return NS_TRYAGAIN; else return NS_NOTFOUND; } static int _dns_ghbyaddr(void *rval, void *cb_data, va_list ap) { const void *addr; int addrlen; int af; int *errp; int n; int err; struct hostent *hp; u_char c, *cp; char *bp; struct hostent hbuf; #ifdef INET6 static const char hex[] = "0123456789abcdef"; #endif querybuf *buf; char qbuf[MAXDNAME+1]; char *hlist[2]; char *tld6[] = { "ip6.arpa", NULL }; char *tld4[] = { "in-addr.arpa", NULL }; char **tld; res_state statp; addr = va_arg(ap, const void *); addrlen = va_arg(ap, int); af = va_arg(ap, int); errp = va_arg(ap, int *); *(struct hostent **)rval = NULL; #ifdef INET6 /* XXX */ if (af == AF_INET6 && IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr)) return NS_NOTFOUND; #endif switch (af) { #ifdef INET6 case AF_INET6: tld = tld6; break; #endif case AF_INET: tld = tld4; break; default: return NS_NOTFOUND; } statp = __res_state(); if ((statp->options & RES_INIT) == 0) { if (res_ninit(statp) < 0) { *errp = NETDB_INTERNAL; return NS_UNAVAIL; } } memset(&hbuf, 0, sizeof(hbuf)); hbuf.h_name = NULL; hbuf.h_addrtype = af; hbuf.h_length = addrlen; buf = malloc(sizeof(*buf)); if (buf == NULL) { *errp = NETDB_INTERNAL; return NS_UNAVAIL; } err = NS_SUCCESS; for (/* nothing */; *tld; tld++) { /* * XXX assumes that MAXDNAME is big enough - error checks * has been made by callers */ n = 0; bp = qbuf; cp = (u_char *)addr+addrlen-1; switch (af) { #ifdef INET6 case AF_INET6: for (; n < addrlen; n++, cp--) { c = *cp; *bp++ = hex[c & 0xf]; *bp++ = '.'; *bp++ = hex[c >> 4]; *bp++ = '.'; } strcpy(bp, *tld); break; #endif case AF_INET: for (; n < addrlen; n++, cp--) { c = *cp; if (c >= 100) *bp++ = '0' + c / 100; if (c >= 10) *bp++ = '0' + (c % 100) / 10; *bp++ = '0' + c % 10; *bp++ = '.'; } strcpy(bp, *tld); break; } n = res_nquery(statp, qbuf, C_IN, T_PTR, buf->buf, sizeof buf->buf); if (n < 0) { *errp = statp->res_h_errno; err = NS_UNAVAIL; continue; } else if (n > sizeof(buf->buf)) { #if 0 errno = ERANGE; /* XXX is it OK to set errno here? */ #endif *errp = NETDB_INTERNAL; err = NS_UNAVAIL; continue; } hp = getanswer(buf, n, qbuf, T_PTR, &hbuf, errp); if (!hp) { err = NS_NOTFOUND; continue; } free(buf); hbuf.h_addrtype = af; hbuf.h_length = addrlen; hbuf.h_addr_list = hlist; hlist[0] = (char *)addr; hlist[1] = NULL; *(struct hostent **)rval = _hpcopy(&hbuf, errp); return NS_SUCCESS; } free(buf); return err; } static void _dns_shent(int stayopen) { res_state statp; statp = __res_state(); if ((statp->options & RES_INIT) == 0) { if (res_ninit(statp) < 0) return; } if (stayopen) statp->options |= RES_STAYOPEN | RES_USEVC; } static void _dns_ehent(void) { res_state statp; statp = __res_state(); statp->options &= ~(RES_STAYOPEN | RES_USEVC); res_nclose(statp); } #ifdef ICMPNL /* * experimental: * draft-ietf-ipngwg-icmp-namelookups-02.txt * ifindex is assumed to be encoded in addr. */ #include #include #include struct _icmp_host_cache { struct _icmp_host_cache *hc_next; int hc_ifindex; struct in6_addr hc_addr; char *hc_name; }; static char * _icmp_fqdn_query(const struct in6_addr *addr, int ifindex) { int s; struct icmp6_filter filter; struct msghdr msg; struct cmsghdr *cmsg; struct in6_pktinfo *pkt; char cbuf[256]; char buf[1024]; int cc; struct icmp6_fqdn_query *fq; struct icmp6_fqdn_reply *fr; struct _icmp_host_cache *hc; struct sockaddr_in6 sin6; struct iovec iov; fd_set s_fds, fds; struct timeval tout; int len; char *name; static struct _icmp_host_cache *hc_head; THREAD_LOCK(); for (hc = hc_head; hc; hc = hc->hc_next) { if (hc->hc_ifindex == ifindex && IN6_ARE_ADDR_EQUAL(&hc->hc_addr, addr)) { THREAD_UNLOCK(); return hc->hc_name; /* XXX: never freed */ } } THREAD_UNLOCK(); ICMP6_FILTER_SETBLOCKALL(&filter); ICMP6_FILTER_SETPASS(ICMP6_FQDN_REPLY, &filter); FD_ZERO(&s_fds); tout.tv_sec = 0; tout.tv_usec = 200000; /*XXX: 200ms*/ fq = (struct icmp6_fqdn_query *)buf; fq->icmp6_fqdn_type = ICMP6_FQDN_QUERY; fq->icmp6_fqdn_code = 0; fq->icmp6_fqdn_cksum = 0; fq->icmp6_fqdn_id = (u_short)getpid(); fq->icmp6_fqdn_unused = 0; fq->icmp6_fqdn_cookie[0] = 0; fq->icmp6_fqdn_cookie[1] = 0; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_addr = *addr; memset(&msg, 0, sizeof(msg)); msg.msg_name = (caddr_t)&sin6; msg.msg_namelen = sizeof(sin6); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; iov.iov_base = (caddr_t)buf; iov.iov_len = sizeof(struct icmp6_fqdn_query); if (ifindex) { msg.msg_control = cbuf; msg.msg_controllen = sizeof(cbuf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; pkt = (struct in6_pktinfo *)&cmsg[1]; memset(&pkt->ipi6_addr, 0, sizeof(struct in6_addr)); pkt->ipi6_ifindex = ifindex; cmsg = CMSG_NXTHDR(&msg, cmsg); msg.msg_controllen = (char *)cmsg - cbuf; } if ((s = _socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) return NULL; (void)_setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, (char *)&filter, sizeof(filter)); cc = _sendmsg(s, &msg, 0); if (cc < 0) { _close(s); return NULL; } FD_SET(s, &s_fds); for (;;) { fds = s_fds; if (_select(s + 1, &fds, NULL, NULL, &tout) <= 0) { _close(s); return NULL; } len = sizeof(sin6); cc = _recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)&sin6, &len); if (cc <= 0) { _close(s); return NULL; } if (cc < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) continue; if (!IN6_ARE_ADDR_EQUAL(addr, &sin6.sin6_addr)) continue; fr = (struct icmp6_fqdn_reply *)(buf + sizeof(struct ip6_hdr)); if (fr->icmp6_fqdn_type == ICMP6_FQDN_REPLY) break; } _close(s); if (fr->icmp6_fqdn_cookie[1] != 0) { /* rfc1788 type */ name = buf + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) + 4; len = (buf + cc) - name; } else { len = fr->icmp6_fqdn_namelen; name = fr->icmp6_fqdn_name; } if (len <= 0) return NULL; name[len] = 0; if ((hc = (struct _icmp_host_cache *)malloc(sizeof(*hc))) == NULL) return NULL; /* XXX: limit number of cached entries */ hc->hc_ifindex = ifindex; hc->hc_addr = *addr; hc->hc_name = strdup(name); THREAD_LOCK(); hc->hc_next = hc_head; hc_head = hc; THREAD_UNLOCK(); return hc->hc_name; } static struct hostent * _icmp_ghbyaddr(const void *addr, int addrlen, int af, int *errp) { char *hname; int ifindex; struct in6_addr addr6; if (af != AF_INET6) { /* * Note: rfc1788 defines Who Are You for IPv4, * but no one implements it. */ return NULL; } memcpy(&addr6, addr, addrlen); ifindex = (addr6.s6_addr[2] << 8) | addr6.s6_addr[3]; addr6.s6_addr[2] = addr6.s6_addr[3] = 0; if (!IN6_IS_ADDR_LINKLOCAL(&addr6)) return NULL; /*XXX*/ if ((hname = _icmp_fqdn_query(&addr6, ifindex)) == NULL) return NULL; return _hpaddr(af, hname, &addr6, errp); } #endif /* ICMPNL */ diff --git a/lib/libc/net/netdb_private.h b/lib/libc/net/netdb_private.h index fa1de628fb3f..e5535f68da1a 100644 --- a/lib/libc/net/netdb_private.h +++ b/lib/libc/net/netdb_private.h @@ -1,172 +1,145 @@ /*- * Copyright (C) 2005 The FreeBSD Project. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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. * * $FreeBSD$ */ #ifndef _NETDB_PRIVATE_H_ #define _NETDB_PRIVATE_H_ #include /* XXX: for FILE */ #define NETDB_THREAD_ALLOC(name) \ static struct name name; \ static thread_key_t name##_key; \ static once_t name##_init_once = ONCE_INITIALIZER; \ static int name##_thr_keycreated = 0; \ \ static void name##_free(void *); \ \ static void \ name##_keycreate(void) \ { \ name##_thr_keycreated = \ (thr_keycreate(&name##_key, name##_free) == 0); \ } \ \ struct name * \ __##name##_init(void) \ { \ struct name *he; \ \ if (thr_main() != 0) \ return (&name); \ if (thr_once(&name##_init_once, name##_keycreate) != 0 || \ !name##_thr_keycreated) \ return (NULL); \ if ((he = thr_getspecific(name##_key)) != NULL) \ return (he); \ if ((he = calloc(1, sizeof(*he))) == NULL) \ return (NULL); \ if (thr_setspecific(name##_key, he) == 0) \ return (he); \ free(he); \ return (NULL); \ } #define _MAXALIASES 35 #define _MAXLINELEN 1024 #define _MAXADDRS 35 #define _HOSTBUFSIZE (8 * 1024) #define _NETBUFSIZE 1025 struct hostent_data { uint32_t host_addr[4]; /* IPv4 or IPv6 */ char *h_addr_ptrs[_MAXADDRS + 1]; char *host_aliases[_MAXALIASES]; char hostbuf[_HOSTBUFSIZE]; FILE *hostf; int stayopen; #ifdef YP char *yp_domain; #endif }; struct netent_data { char *net_aliases[_MAXALIASES]; char netbuf[_NETBUFSIZE]; FILE *netf; int stayopen; #ifdef YP char *yp_domain; #endif }; struct protoent_data { FILE *fp; char *aliases[_MAXALIASES]; int stayopen; char line[_MAXLINELEN + 1]; }; -struct servent_data { - FILE *fp; - char *aliases[_MAXALIASES]; - int stayopen; - char line[_MAXLINELEN + 1]; -#ifdef YP - int yp_stepping; - char *yp_name; - char *yp_proto; - int yp_port; - char *yp_domain; - char *yp_key; - int yp_keylen; -#endif -}; - struct hostdata { struct hostent host; char data[sizeof(struct hostent_data)]; }; struct netdata { struct netent net; char data[sizeof(struct netent_data)]; }; struct protodata { struct protoent proto; char data[sizeof(struct protoent_data)]; }; -struct servdata { - struct servent serv; - char data[sizeof(struct servent_data)]; -}; - struct hostdata *__hostdata_init(void); struct hostent *__hostent_init(void); struct hostent_data *__hostent_data_init(void); struct netdata *__netdata_init(void); struct netent_data *__netent_data_init(void); struct protodata *__protodata_init(void); struct protoent_data *__protoent_data_init(void); -struct servdata *__servdata_init(void); -struct servent_data *__servent_data_init(void); int __copy_hostent(struct hostent *, struct hostent *, char *, size_t); int __copy_netent(struct netent *, struct netent *, char *, size_t); int __copy_protoent(struct protoent *, struct protoent *, char *, size_t); -int __copy_servent(struct servent *, struct servent *, char *, size_t); void __endprotoent_p(struct protoent_data *); -void __endservent_p(struct servent_data *); int __getprotoent_p(struct protoent *, struct protoent_data *); -int __getservent_p(struct servent *, struct servent_data *); void __setprotoent_p(int, struct protoent_data *); -void __setservent_p(int, struct servent_data *); void _endhostdnsent(void); void _endhosthtent(struct hostent_data *); void _endnetdnsent(void); void _endnethtent(struct netent_data *); struct hostent *_gethostbynisaddr(const char *, int, int); struct hostent *_gethostbynisname(const char *, int); void _map_v4v6_address(const char *, char *); void _map_v4v6_hostent(struct hostent *, char **, char *); void _sethostdnsent(int); void _sethosthtent(int, struct hostent_data *); void _setnetdnsent(int); void _setnethtent(int, struct netent_data *); #endif /* _NETDB_PRIVATE_H_ */ diff --git a/lib/libc/net/nscache.c b/lib/libc/net/nscache.c new file mode 100644 index 000000000000..98a4367dcfb1 --- /dev/null +++ b/lib/libc/net/nscache.c @@ -0,0 +1,438 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include +#include +#include +#include "un-namespace.h" +#include "nscachedcli.h" +#include "nscache.h" + +#define NSS_CACHE_KEY_INITIAL_SIZE (256) +#define NSS_CACHE_KEY_SIZE_LIMIT (NSS_CACHE_KEY_INITIAL_SIZE << 4) + +#define NSS_CACHE_BUFFER_INITIAL_SIZE (1024) +#define NSS_CACHE_BUFFER_SIZE_LIMIT (NSS_CACHE_BUFFER_INITIAL_SIZE << 8) + +#define CACHED_SOCKET_PATH "/var/run/cached" + +int +__nss_cache_handler(void *retval, void *mdata, va_list ap) +{ + return (NS_UNAVAIL); +} + +int +__nss_common_cache_read(void *retval, void *mdata, va_list ap) +{ + struct cached_connection_params params; + cached_connection connection; + + char *buffer; + size_t buffer_size, size; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + va_list ap_new; + int res; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + memset(¶ms, 0, sizeof(struct cached_connection_params)); + params.socket_path = CACHED_SOCKET_PATH; + + cache_data->key = (char *)malloc(NSS_CACHE_KEY_INITIAL_SIZE); + memset(cache_data->key, 0, NSS_CACHE_KEY_INITIAL_SIZE); + cache_data->key_size = NSS_CACHE_KEY_INITIAL_SIZE; + va_copy(ap_new, ap); + + do { + size = cache_data->key_size; + res = cache_info->id_func(cache_data->key, &size, ap_new, + cache_info->mdata); + va_end(ap_new); + if (res == NS_RETURN) { + if (cache_data->key_size > NSS_CACHE_KEY_SIZE_LIMIT) + break; + + cache_data->key_size <<= 1; + cache_data->key = realloc(cache_data->key, + cache_data->key_size); + memset(cache_data->key, 0, cache_data->key_size); + va_copy(ap_new, ap); + } + } while (res == NS_RETURN); + + if (res != NS_SUCCESS) { + free(cache_data->key); + cache_data->key = NULL; + cache_data->key_size = 0; + return (res); + } else + cache_data->key_size = size; + + buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE; + buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE); + memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE); + + do { + connection = __open_cached_connection(¶ms); + if (connection == NULL) { + res = -1; + break; + } + res = __cached_read(connection, cache_info->entry_name, + cache_data->key, cache_data->key_size, buffer, + &buffer_size); + __close_cached_connection(connection); + if (res == -2 && buffer_size < NSS_CACHE_BUFFER_SIZE_LIMIT) { + buffer = (char *)realloc(buffer, buffer_size); + memset(buffer, 0, buffer_size); + } + } while (res == -2); + + if (res == 0) { + if (buffer_size == 0) { + free(buffer); + free(cache_data->key); + cache_data->key = NULL; + cache_data->key_size = 0; + return (NS_RETURN); + } + + va_copy(ap_new, ap); + res = cache_info->unmarshal_func(buffer, buffer_size, retval, + ap_new, cache_info->mdata); + va_end(ap_new); + + if (res != NS_SUCCESS) { + free(buffer); + free(cache_data->key); + cache_data->key = NULL; + cache_data->key_size = 0; + return (res); + } else + res = 0; + } + + if (res == 0) { + free(cache_data->key); + cache_data->key = NULL; + cache_data->key_size = 0; + } + + free(buffer); + return (res == 0 ? NS_SUCCESS : NS_NOTFOUND); +} + +int +__nss_common_cache_write(void *retval, void *mdata, va_list ap) +{ + struct cached_connection_params params; + cached_connection connection; + + char *buffer; + size_t buffer_size; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + va_list ap_new; + int res; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + if (cache_data->key == NULL) + return (NS_UNAVAIL); + + memset(¶ms, 0, sizeof(struct cached_connection_params)); + params.socket_path = CACHED_SOCKET_PATH; + + connection = __open_cached_connection(¶ms); + if (connection == NULL) { + free(cache_data->key); + return (NS_UNAVAIL); + } + + buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE; + buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE); + memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE); + + do { + size_t size; + + size = buffer_size; + va_copy(ap_new, ap); + res = cache_info->marshal_func(buffer, &size, retval, ap_new, + cache_info->mdata); + va_end(ap_new); + + if (res == NS_RETURN) { + if (buffer_size > NSS_CACHE_BUFFER_SIZE_LIMIT) + break; + + buffer_size <<= 1; + buffer = (char *)realloc(buffer, buffer_size); + memset(buffer, 0, buffer_size); + } + } while (res == NS_RETURN); + + if (res != NS_SUCCESS) { + __close_cached_connection(connection); + free(cache_data->key); + free(buffer); + return (res); + } + + res = __cached_write(connection, cache_info->entry_name, + cache_data->key, cache_data->key_size, buffer, buffer_size); + __close_cached_connection(connection); + + free(cache_data->key); + free(buffer); + + return (res == 0 ? NS_SUCCESS : NS_UNAVAIL); +} + +int +__nss_common_cache_write_negative(void *mdata) +{ + struct cached_connection_params params; + cached_connection connection; + int res; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + if (cache_data->key == NULL) + return (NS_UNAVAIL); + + memset(¶ms, 0, sizeof(struct cached_connection_params)); + params.socket_path = CACHED_SOCKET_PATH; + + connection = __open_cached_connection(¶ms); + if (connection == NULL) { + free(cache_data->key); + return (NS_UNAVAIL); + } + + res = __cached_write(connection, cache_info->entry_name, + cache_data->key, cache_data->key_size, NULL, 0); + __close_cached_connection(connection); + + free(cache_data->key); + return (res == 0 ? NS_SUCCESS : NS_UNAVAIL); +} + +int +__nss_mp_cache_read(void *retval, void *mdata, va_list ap) +{ + struct cached_connection_params params; + cached_mp_read_session rs; + + char *buffer; + size_t buffer_size; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + va_list ap_new; + int res; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + if (cache_info->get_mp_ws_func() != INVALID_CACHED_MP_WRITE_SESSION) + return (NS_UNAVAIL); + + rs = cache_info->get_mp_rs_func(); + if (rs == INVALID_CACHED_MP_READ_SESSION) { + memset(¶ms, 0, sizeof(struct cached_connection_params)); + params.socket_path = CACHED_SOCKET_PATH; + + rs = __open_cached_mp_read_session(¶ms, + cache_info->entry_name); + if (rs == INVALID_CACHED_MP_READ_SESSION) + return (NS_UNAVAIL); + + cache_info->set_mp_rs_func(rs); + } + + buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE; + buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE); + memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE); + + do { + res = __cached_mp_read(rs, buffer, &buffer_size); + if (res == -2 && buffer_size < NSS_CACHE_BUFFER_SIZE_LIMIT) { + buffer = (char *)realloc(buffer, buffer_size); + memset(buffer, 0, buffer_size); + } + } while (res == -2); + + if (res == 0) { + va_copy(ap_new, ap); + res = cache_info->unmarshal_func(buffer, buffer_size, retval, + ap_new, cache_info->mdata); + va_end(ap_new); + + if (res != NS_SUCCESS) { + free(buffer); + return (res); + } else + res = 0; + } else { + free(buffer); + __close_cached_mp_read_session(rs); + rs = INVALID_CACHED_MP_READ_SESSION; + cache_info->set_mp_rs_func(rs); + return (res == -1 ? NS_RETURN : NS_UNAVAIL); + } + + free(buffer); + return (res == 0 ? NS_SUCCESS : NS_NOTFOUND); +} + +int +__nss_mp_cache_write(void *retval, void *mdata, va_list ap) +{ + struct cached_connection_params params; + cached_mp_write_session ws; + + char *buffer; + size_t buffer_size; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + va_list ap_new; + int res; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + ws = cache_info->get_mp_ws_func(); + if (ws == INVALID_CACHED_MP_WRITE_SESSION) { + memset(¶ms, 0, sizeof(struct cached_connection_params)); + params.socket_path = CACHED_SOCKET_PATH; + + ws = __open_cached_mp_write_session(¶ms, + cache_info->entry_name); + if (ws == INVALID_CACHED_MP_WRITE_SESSION) + return (NS_UNAVAIL); + + cache_info->set_mp_ws_func(ws); + } + + buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE; + buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE); + memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE); + + do { + size_t size; + + size = buffer_size; + va_copy(ap_new, ap); + res = cache_info->marshal_func(buffer, &size, retval, ap_new, + cache_info->mdata); + va_end(ap_new); + + if (res == NS_RETURN) { + if (buffer_size > NSS_CACHE_BUFFER_SIZE_LIMIT) + break; + + buffer_size <<= 1; + buffer = (char *)realloc(buffer, buffer_size); + memset(buffer, 0, buffer_size); + } + } while (res == NS_RETURN); + + if (res != NS_SUCCESS) { + free(buffer); + return (res); + } + + res = __cached_mp_write(ws, buffer, buffer_size); + + free(buffer); + return (res == 0 ? NS_SUCCESS : NS_UNAVAIL); +} + +int +__nss_mp_cache_write_submit(void *retval, void *mdata, va_list ap) +{ + cached_mp_write_session ws; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + ws = cache_info->get_mp_ws_func(); + if (ws != INVALID_CACHED_MP_WRITE_SESSION) { + __close_cached_mp_write_session(ws); + ws = INVALID_CACHED_MP_WRITE_SESSION; + cache_info->set_mp_ws_func(ws); + } + return (NS_UNAVAIL); +} + +int +__nss_mp_cache_end(void *retval, void *mdata, va_list ap) +{ + cached_mp_write_session ws; + cached_mp_read_session rs; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + ws = cache_info->get_mp_ws_func(); + if (ws != INVALID_CACHED_MP_WRITE_SESSION) { + __abandon_cached_mp_write_session(ws); + ws = INVALID_CACHED_MP_WRITE_SESSION; + cache_info->set_mp_ws_func(ws); + } + + rs = cache_info->get_mp_rs_func(); + if (rs != INVALID_CACHED_MP_READ_SESSION) { + __close_cached_mp_read_session(rs); + rs = INVALID_CACHED_MP_READ_SESSION; + cache_info->set_mp_rs_func(rs); + } + + return (NS_UNAVAIL); +} diff --git a/lib/libc/net/nscachedcli.c b/lib/libc/net/nscachedcli.c new file mode 100644 index 000000000000..374f37031157 --- /dev/null +++ b/lib/libc/net/nscachedcli.c @@ -0,0 +1,576 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "un-namespace.h" +#include "nscachedcli.h" + +#define NS_DEFAULT_CACHED_IO_TIMEOUT 4 + +static int safe_write(struct cached_connection_ *, const void *, size_t); +static int safe_read(struct cached_connection_ *, void *, size_t); +static int send_credentials(struct cached_connection_ *, int); + +/* + * safe_write writes data to the specified connection and tries to do it in + * the very safe manner. We ensure, that we can write to the socket with + * kevent. If the data_size can't be sent in one piece, then it would be + * splitted. + */ +static int +safe_write(struct cached_connection_ *connection, const void *data, + size_t data_size) +{ + struct kevent eventlist; + int nevents; + size_t result; + ssize_t s_result; + struct timespec timeout; + + if (data_size == 0) + return (0); + + timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT; + timeout.tv_nsec = 0; + result = 0; + do { + nevents = kevent(connection->write_queue, NULL, 0, &eventlist, + 1, &timeout); + if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) { + s_result = _write(connection->sockfd, data + result, + eventlist.data < data_size - result ? + eventlist.data : data_size - result); + if (s_result == -1) + return (-1); + else + result += s_result; + + if (eventlist.flags & EV_EOF) + return (result < data_size ? -1 : 0); + } else + return (-1); + } while (result < data_size); + + return (0); +} + +/* + * safe_read reads data from connection and tries to do it in the very safe + * and stable way. It uses kevent to ensure, that the data are availabe for + * reading. If the amount of data to be read is too large, then they would + * be splitted. + */ +static int +safe_read(struct cached_connection_ *connection, void *data, size_t data_size) +{ + struct kevent eventlist; + size_t result; + ssize_t s_result; + struct timespec timeout; + int nevents; + + if (data_size == 0) + return (0); + + timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT; + timeout.tv_nsec = 0; + result = 0; + do { + nevents = kevent(connection->read_queue, NULL, 0, &eventlist, + 1, &timeout); + if (nevents == 1 && eventlist.filter == EVFILT_READ) { + s_result = _read(connection->sockfd, data + result, + eventlist.data <= data_size - result ? + eventlist.data : data_size - result); + if (s_result == -1) + return (-1); + else + result += s_result; + + if (eventlist.flags & EV_EOF) + return (result < data_size ? -1 : 0); + } else + return (-1); + } while (result < data_size); + + return (0); +} + +/* + * Sends the credentials information to the connection along with the + * communication element type. + */ +static int +send_credentials(struct cached_connection_ *connection, int type) +{ + struct kevent eventlist; + int nevents; + ssize_t result; + int res; + + struct msghdr cred_hdr; + struct iovec iov; + + struct { + struct cmsghdr hdr; + struct cmsgcred creds; + } cmsg; + + memset(&cmsg, 0, sizeof(cmsg)); + cmsg.hdr.cmsg_len = sizeof(cmsg); + cmsg.hdr.cmsg_level = SOL_SOCKET; + cmsg.hdr.cmsg_type = SCM_CREDS; + + memset(&cred_hdr, 0, sizeof(struct msghdr)); + cred_hdr.msg_iov = &iov; + cred_hdr.msg_iovlen = 1; + cred_hdr.msg_control = &cmsg; + cred_hdr.msg_controllen = sizeof(cmsg); + + iov.iov_base = &type; + iov.iov_len = sizeof(int); + + EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, + NOTE_LOWAT, sizeof(int), NULL); + res = kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); + + nevents = kevent(connection->write_queue, NULL, 0, &eventlist, 1, + NULL); + if (nevents == 1 && eventlist.filter == EVFILT_WRITE) { + result = (_sendmsg(connection->sockfd, &cred_hdr, 0) == -1) ? + -1 : 0; + EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, + 0, 0, NULL); + kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); + return (result); + } else + return (-1); +} + +/* + * Opens the connection with the specified params. Initializes all kqueues. + */ +struct cached_connection_ * +__open_cached_connection(struct cached_connection_params const *params) +{ + struct cached_connection_ *retval; + struct kevent eventlist; + struct sockaddr_un client_address; + int client_address_len, client_socket; + int res; + + assert(params != NULL); + + client_socket = _socket(PF_LOCAL, SOCK_STREAM, 0); + client_address.sun_family = PF_LOCAL; + strncpy(client_address.sun_path, params->socket_path, + sizeof(client_address.sun_path)); + client_address_len = sizeof(client_address.sun_family) + + strlen(client_address.sun_path) + 1; + + res = _connect(client_socket, (struct sockaddr *)&client_address, + client_address_len); + if (res == -1) { + _close(client_socket); + return (NULL); + } + _fcntl(client_socket, F_SETFL, O_NONBLOCK); + + retval = malloc(sizeof(struct cached_connection_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cached_connection_)); + + retval->sockfd = client_socket; + + retval->write_queue = kqueue(); + assert(retval->write_queue != -1); + + EV_SET(&eventlist, retval->sockfd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); + res = kevent(retval->write_queue, &eventlist, 1, NULL, 0, NULL); + + retval->read_queue = kqueue(); + assert(retval->read_queue != -1); + + EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL); + res = kevent(retval->read_queue, &eventlist, 1, NULL, 0, NULL); + + return (retval); +} + +void +__close_cached_connection(struct cached_connection_ *connection) +{ + assert(connection != NULL); + + _close(connection->sockfd); + _close(connection->read_queue); + _close(connection->write_queue); + free(connection); +} + +/* + * This function is very close to the cache_write function of the caching + * library, which is used in the caching daemon. It caches the data with the + * specified key in the cache entry with entry_name. + */ +int +__cached_write(struct cached_connection_ *connection, const char *entry_name, + const char *key, size_t key_size, const char *data, size_t data_size) +{ + size_t name_size; + int error_code; + int result; + + error_code = -1; + result = 0; + result = send_credentials(connection, CET_WRITE_REQUEST); + if (result != 0) + goto fin; + + name_size = strlen(entry_name); + result = safe_write(connection, &name_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, &key_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, &data_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, entry_name, name_size); + if (result != 0) + goto fin; + + result = safe_write(connection, key, key_size); + if (result != 0) + goto fin; + + result = safe_write(connection, data, data_size); + if (result != 0) + goto fin; + + result = safe_read(connection, &error_code, sizeof(int)); + if (result != 0) + error_code = -1; + +fin: + return (error_code); +} + +/* + * This function is very close to the cache_read function of the caching + * library, which is used in the caching daemon. It reads cached data with the + * specified key from the cache entry with entry_name. + */ +int +__cached_read(struct cached_connection_ *connection, const char *entry_name, + const char *key, size_t key_size, char *data, size_t *data_size) +{ + size_t name_size, result_size; + int error_code, rec_error_code; + int result; + + assert(connection != NULL); + result = 0; + error_code = -1; + + result = send_credentials(connection, CET_READ_REQUEST); + if (result != 0) + goto fin; + + name_size = strlen(entry_name); + result = safe_write(connection, &name_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, &key_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, entry_name, name_size); + if (result != 0) + goto fin; + + result = safe_write(connection, key, key_size); + if (result != 0) + goto fin; + + result = safe_read(connection, &rec_error_code, sizeof(int)); + if (result != 0) + goto fin; + + if (rec_error_code != 0) { + error_code = rec_error_code; + goto fin; + } + + result = safe_read(connection, &result_size, sizeof(size_t)); + if (result != 0) + goto fin; + + if (result_size > *data_size) { + *data_size = result_size; + error_code = -2; + goto fin; + } + + result = safe_read(connection, data, result_size); + if (result != 0) + goto fin; + + *data_size = result_size; + error_code = 0; + +fin: + return (error_code); +} + +/* + * Initializes the mp_write_session. For such a session the new connection + * would be opened. The data should be written to the session with + * __cached_mp_write function. The __close_cached_mp_write_session function + * should be used to submit session and __abandon_cached_mp_write_session - to + * abandon it. When the session is submitted, the whole se + */ +struct cached_connection_ * +__open_cached_mp_write_session(struct cached_connection_params const *params, + const char *entry_name) +{ + struct cached_connection_ *connection, *retval; + size_t name_size; + int error_code; + int result; + + retval = NULL; + connection = __open_cached_connection(params); + if (connection == NULL) + return (NULL); + connection->mp_flag = 1; + + result = send_credentials(connection, CET_MP_WRITE_SESSION_REQUEST); + if (result != 0) + goto fin; + + name_size = strlen(entry_name); + result = safe_write(connection, &name_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, entry_name, name_size); + if (result != 0) + goto fin; + + result = safe_read(connection, &error_code, sizeof(int)); + if (result != 0) + goto fin; + + if (error_code != 0) + result = error_code; + +fin: + if (result != 0) + __close_cached_connection(connection); + else + retval = connection; + return (retval); +} + +/* + * Adds new portion of data to the opened write session + */ +int +__cached_mp_write(struct cached_connection_ *ws, const char *data, + size_t data_size) +{ + int request, result; + int error_code; + + error_code = -1; + + request = CET_MP_WRITE_SESSION_WRITE_REQUEST; + result = safe_write(ws, &request, sizeof(int)); + if (result != 0) + goto fin; + + result = safe_write(ws, &data_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(ws, data, data_size); + if (result != 0) + goto fin; + + result = safe_read(ws, &error_code, sizeof(int)); + if (result != 0) + error_code = -1; + +fin: + return (error_code); +} + +/* + * Abandons all operations with the write session. All data, that were written + * to the session before, are discarded. + */ +int +__abandon_cached_mp_write_session(struct cached_connection_ *ws) +{ + int notification; + int result; + + notification = CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION; + result = safe_write(ws, ¬ification, sizeof(int)); + __close_cached_connection(ws); + return (result); +} + +/* + * Gracefully closes the write session. The data, that were previously written + * to the session, are committed. + */ +int +__close_cached_mp_write_session(struct cached_connection_ *ws) +{ + int notification; + int result; + + notification = CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION; + result = safe_write(ws, ¬ification, sizeof(int)); + __close_cached_connection(ws); + return (0); +} + +struct cached_connection_ * +__open_cached_mp_read_session(struct cached_connection_params const *params, + const char *entry_name) +{ + struct cached_connection_ *connection, *retval; + size_t name_size; + int error_code; + int result; + + retval = NULL; + connection = __open_cached_connection(params); + if (connection == NULL) + return (NULL); + connection->mp_flag = 1; + + result = send_credentials(connection, CET_MP_READ_SESSION_REQUEST); + if (result != 0) + goto fin; + + name_size = strlen(entry_name); + result = safe_write(connection, &name_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, entry_name, name_size); + if (result != 0) + goto fin; + + result = safe_read(connection, &error_code, sizeof(int)); + if (result != 0) + goto fin; + + if (error_code != 0) + result = error_code; + +fin: + if (result != 0) + __close_cached_connection(connection); + else + retval = connection; + return (retval); +} + +int +__cached_mp_read(struct cached_connection_ *rs, char *data, size_t *data_size) +{ + size_t result_size; + int error_code, rec_error_code; + int request, result; + + error_code = -1; + request = CET_MP_READ_SESSION_READ_REQUEST; + result = safe_write(rs, &request, sizeof(int)); + if (result != 0) + goto fin; + + result = safe_read(rs, &rec_error_code, sizeof(int)); + if (result != 0) + goto fin; + + if (rec_error_code != 0) { + error_code = rec_error_code; + goto fin; + } + + result = safe_read(rs, &result_size, sizeof(size_t)); + if (result != 0) + goto fin; + + if (result_size > *data_size) { + *data_size = result_size; + error_code = -2; + goto fin; + } + + result = safe_read(rs, data, result_size); + if (result != 0) + goto fin; + + *data_size = result_size; + error_code = 0; + +fin: + return (error_code); +} + +int +__close_cached_mp_read_session(struct cached_connection_ *rs) +{ + + __close_cached_connection(rs); + return (0); +} diff --git a/lib/libc/net/nsdispatch.c b/lib/libc/net/nsdispatch.c index c9be76396567..0e3c41982175 100644 --- a/lib/libc/net/nsdispatch.c +++ b/lib/libc/net/nsdispatch.c @@ -1,628 +1,715 @@ /* $NetBSD: nsdispatch.c,v 1.9 1999/01/25 00:16:17 lukem Exp $ */ /*- * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Luke Mewburn. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /*- * Copyright (c) 2003 Networks Associates Technology, Inc. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * Jacques A. Vidrine, Safeport Network Services, and Network * Associates Laboratories, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #define _NS_PRIVATE #include #include #include #include #include #include #include #include "un-namespace.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif enum _nss_constants { /* Number of elements allocated when we grow a vector */ ELEMSPERCHUNK = 8 }; /* * Global NSS data structures are mostly read-only, but we update * them when we read or re-read the nsswitch.conf. */ static pthread_rwlock_t nss_lock = PTHREAD_RWLOCK_INITIALIZER; /* * Runtime determination of whether we are dynamically linked or not. */ extern int _DYNAMIC __attribute__ ((weak)); #define is_dynamic() (&_DYNAMIC != NULL) /* * default sourcelist: `files' */ const ns_src __nsdefaultsrc[] = { { NSSRC_FILES, NS_SUCCESS }, { 0 }, }; /* Database, source mappings. */ static unsigned int _nsmapsize; static ns_dbt *_nsmap = NULL; /* NSS modules. */ static unsigned int _nsmodsize; static ns_mod *_nsmod; /* Placeholder for builtin modules' dlopen `handle'. */ static int __nss_builtin_handle; static void *nss_builtin_handle = &__nss_builtin_handle; +#ifdef NS_CACHING +/* + * Cache lookup cycle prevention function - if !NULL then no cache lookups + * will be made + */ +static void *nss_cache_cycle_prevention_func = NULL; +#endif + /* * Attempt to spew relatively uniform messages to syslog. */ #define nss_log(level, fmt, ...) \ syslog((level), "NSSWITCH(%s): " fmt, __func__, __VA_ARGS__) #define nss_log_simple(level, s) \ syslog((level), "NSSWITCH(%s): " s, __func__) /* * Dynamically growable arrays are used for lists of databases, sources, * and modules. The following `vector' interface is used to isolate the * common operations. */ typedef int (*vector_comparison)(const void *, const void *); typedef void (*vector_free_elem)(void *); static void vector_sort(void *, unsigned int, size_t, vector_comparison); static void vector_free(void *, unsigned int *, size_t, vector_free_elem); static void *vector_ref(unsigned int, void *, unsigned int, size_t); static void *vector_search(const void *, void *, unsigned int, size_t, vector_comparison); static void *vector_append(const void *, void *, unsigned int *, size_t); /* * Internal interfaces. */ static int string_compare(const void *, const void *); static int mtab_compare(const void *, const void *); static int nss_configure(void); static void ns_dbt_free(ns_dbt *); static void ns_mod_free(ns_mod *); static void ns_src_free(ns_src **, int); static void nss_load_builtin_modules(void); static void nss_load_module(const char *, nss_module_register_fn); static void nss_atexit(void); /* nsparser */ extern FILE *_nsyyin; /* * The vector operations */ static void vector_sort(void *vec, unsigned int count, size_t esize, vector_comparison comparison) { qsort(vec, count, esize, comparison); } static void * vector_search(const void *key, void *vec, unsigned int count, size_t esize, vector_comparison comparison) { return (bsearch(key, vec, count, esize, comparison)); } static void * vector_append(const void *elem, void *vec, unsigned int *count, size_t esize) { void *p; if ((*count % ELEMSPERCHUNK) == 0) { p = realloc(vec, (*count + ELEMSPERCHUNK) * esize); if (p == NULL) { nss_log_simple(LOG_ERR, "memory allocation failure"); return (vec); } vec = p; } memmove((void *)(((uintptr_t)vec) + (*count * esize)), elem, esize); (*count)++; return (vec); } static void * vector_ref(unsigned int i, void *vec, unsigned int count, size_t esize) { if (i < count) return (void *)((uintptr_t)vec + (i * esize)); else return (NULL); } #define VECTOR_FREE(v, c, s, f) \ do { vector_free(v, c, s, f); v = NULL; } while (0) static void vector_free(void *vec, unsigned int *count, size_t esize, vector_free_elem free_elem) { unsigned int i; void *elem; for (i = 0; i < *count; i++) { elem = vector_ref(i, vec, *count, esize); if (elem != NULL) free_elem(elem); } free(vec); *count = 0; } - - /* * Comparison functions for vector_search. */ static int string_compare(const void *a, const void *b) { return (strcasecmp(*(const char * const *)a, *(const char * const *)b)); } static int mtab_compare(const void *a, const void *b) { int cmp; cmp = strcmp(((const ns_mtab *)a)->name, ((const ns_mtab *)b)->name); if (cmp != 0) return (cmp); else return (strcmp(((const ns_mtab *)a)->database, ((const ns_mtab *)b)->database)); } - - /* * NSS nsmap management. */ void _nsdbtaddsrc(ns_dbt *dbt, const ns_src *src) { const ns_mod *modp; dbt->srclist = vector_append(src, dbt->srclist, &dbt->srclistsize, sizeof(*src)); modp = vector_search(&src->name, _nsmod, _nsmodsize, sizeof(*_nsmod), string_compare); if (modp == NULL) nss_load_module(src->name, NULL); } #ifdef _NSS_DEBUG void _nsdbtdump(const ns_dbt *dbt) { int i; printf("%s (%d source%s):", dbt->name, dbt->srclistsize, dbt->srclistsize == 1 ? "" : "s"); for (i = 0; i < (int)dbt->srclistsize; i++) { printf(" %s", dbt->srclist[i].name); if (!(dbt->srclist[i].flags & (NS_UNAVAIL|NS_NOTFOUND|NS_TRYAGAIN)) && (dbt->srclist[i].flags & NS_SUCCESS)) continue; printf(" ["); if (!(dbt->srclist[i].flags & NS_SUCCESS)) printf(" SUCCESS=continue"); if (dbt->srclist[i].flags & NS_UNAVAIL) printf(" UNAVAIL=return"); if (dbt->srclist[i].flags & NS_NOTFOUND) printf(" NOTFOUND=return"); if (dbt->srclist[i].flags & NS_TRYAGAIN) printf(" TRYAGAIN=return"); printf(" ]"); } printf("\n"); } #endif /* * The first time nsdispatch is called (during a process's lifetime, * or after nsswitch.conf has been updated), nss_configure will * prepare global data needed by NSS. */ static int nss_configure(void) { static pthread_mutex_t conf_lock = PTHREAD_MUTEX_INITIALIZER; static time_t confmod; struct stat statbuf; int result, isthreaded; const char *path; +#ifdef NS_CACHING + void *handle; +#endif result = 0; isthreaded = __isthreaded; #if defined(_NSS_DEBUG) && defined(_NSS_SHOOT_FOOT) /* NOTE WELL: THIS IS A SECURITY HOLE. This must only be built * for debugging purposes and MUST NEVER be used in production. */ path = getenv("NSSWITCH_CONF"); if (path == NULL) #endif path = _PATH_NS_CONF; if (stat(path, &statbuf) != 0) return (0); if (statbuf.st_mtime <= confmod) return (0); if (isthreaded) { result = _pthread_mutex_trylock(&conf_lock); if (result != 0) return (0); (void)_pthread_rwlock_unlock(&nss_lock); result = _pthread_rwlock_wrlock(&nss_lock); if (result != 0) goto fin2; } _nsyyin = fopen(path, "r"); if (_nsyyin == NULL) goto fin; VECTOR_FREE(_nsmap, &_nsmapsize, sizeof(*_nsmap), (vector_free_elem)ns_dbt_free); VECTOR_FREE(_nsmod, &_nsmodsize, sizeof(*_nsmod), (vector_free_elem)ns_mod_free); nss_load_builtin_modules(); _nsyyparse(); (void)fclose(_nsyyin); vector_sort(_nsmap, _nsmapsize, sizeof(*_nsmap), string_compare); if (confmod == 0) (void)atexit(nss_atexit); confmod = statbuf.st_mtime; + +#ifdef NS_CACHING + handle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL); + if (handle != NULL) { + nss_cache_cycle_prevention_func = dlsym(handle, + "_nss_cache_cycle_prevention_function"); + dlclose(handle); + } +#endif fin: if (isthreaded) { (void)_pthread_rwlock_unlock(&nss_lock); if (result == 0) result = _pthread_rwlock_rdlock(&nss_lock); } fin2: if (isthreaded) (void)_pthread_mutex_unlock(&conf_lock); return (result); } void _nsdbtput(const ns_dbt *dbt) { unsigned int i; ns_dbt *p; for (i = 0; i < _nsmapsize; i++) { p = vector_ref(i, _nsmap, _nsmapsize, sizeof(*_nsmap)); if (string_compare(&dbt->name, &p->name) == 0) { /* overwrite existing entry */ if (p->srclist != NULL) ns_src_free(&p->srclist, p->srclistsize); memmove(p, dbt, sizeof(*dbt)); return; } } _nsmap = vector_append(dbt, _nsmap, &_nsmapsize, sizeof(*_nsmap)); } static void ns_dbt_free(ns_dbt *dbt) { ns_src_free(&dbt->srclist, dbt->srclistsize); } static void ns_src_free(ns_src **src, int srclistsize) { int i; for (i = 0; i < srclistsize; i++) if ((*src)[i].name != NULL) /* This one was allocated by nslexer. You'll just * have to trust me. */ free((void *)((*src)[i].name)); free(*src); *src = NULL; } /* * NSS module management. */ /* The built-in NSS modules are all loaded at once. */ #define NSS_BACKEND(name, reg) \ ns_mtab *reg(unsigned int *, nss_module_unregister_fn *); #include "nss_backends.h" #undef NSS_BACKEND static void nss_load_builtin_modules(void) { #define NSS_BACKEND(name, reg) nss_load_module(#name, reg); #include "nss_backends.h" #undef NSS_BACKEND } /* Load a built-in or dynamically linked module. If the `reg_fn' * argument is non-NULL, assume a built-in module and use reg_fn to * register it. Otherwise, search for a dynamic NSS module. */ static void nss_load_module(const char *source, nss_module_register_fn reg_fn) { char buf[PATH_MAX]; ns_mod mod; nss_module_register_fn fn; memset(&mod, 0, sizeof(mod)); mod.name = strdup(source); if (mod.name == NULL) { nss_log_simple(LOG_ERR, "memory allocation failure"); return; } if (reg_fn != NULL) { /* The placeholder is required, as a NULL handle * represents an invalid module. */ mod.handle = nss_builtin_handle; fn = reg_fn; } else if (!is_dynamic()) goto fin; else { if (snprintf(buf, sizeof(buf), "nss_%s.so.%d", mod.name, NSS_MODULE_INTERFACE_VERSION) >= (int)sizeof(buf)) goto fin; mod.handle = dlopen(buf, RTLD_LOCAL|RTLD_LAZY); if (mod.handle == NULL) { #ifdef _NSS_DEBUG /* This gets pretty annoying since the built-in * sources aren't modules yet. */ nss_log(LOG_DEBUG, "%s, %s", mod.name, dlerror()); #endif goto fin; } fn = (nss_module_register_fn)dlfunc(mod.handle, "nss_module_register"); if (fn == NULL) { (void)dlclose(mod.handle); mod.handle = NULL; nss_log(LOG_ERR, "%s, %s", mod.name, dlerror()); goto fin; } } mod.mtab = fn(mod.name, &mod.mtabsize, &mod.unregister); if (mod.mtab == NULL || mod.mtabsize == 0) { if (mod.handle != nss_builtin_handle) (void)dlclose(mod.handle); mod.handle = NULL; nss_log(LOG_ERR, "%s, registration failed", mod.name); goto fin; } if (mod.mtabsize > 1) qsort(mod.mtab, mod.mtabsize, sizeof(mod.mtab[0]), mtab_compare); fin: _nsmod = vector_append(&mod, _nsmod, &_nsmodsize, sizeof(*_nsmod)); vector_sort(_nsmod, _nsmodsize, sizeof(*_nsmod), string_compare); } static void ns_mod_free(ns_mod *mod) { free(mod->name); if (mod->handle == NULL) return; if (mod->unregister != NULL) mod->unregister(mod->mtab, mod->mtabsize); if (mod->handle != nss_builtin_handle) (void)dlclose(mod->handle); } /* * Cleanup */ static void nss_atexit(void) { int isthreaded; isthreaded = __isthreaded; if (isthreaded) (void)_pthread_rwlock_wrlock(&nss_lock); VECTOR_FREE(_nsmap, &_nsmapsize, sizeof(*_nsmap), (vector_free_elem)ns_dbt_free); VECTOR_FREE(_nsmod, &_nsmodsize, sizeof(*_nsmod), (vector_free_elem)ns_mod_free); if (isthreaded) (void)_pthread_rwlock_unlock(&nss_lock); } /* * Finally, the actual implementation. */ static nss_method nss_method_lookup(const char *source, const char *database, const char *method, const ns_dtab disp_tab[], void **mdata) { ns_mod *mod; ns_mtab *match, key; int i; if (disp_tab != NULL) for (i = 0; disp_tab[i].src != NULL; i++) if (strcasecmp(source, disp_tab[i].src) == 0) { *mdata = disp_tab[i].mdata; return (disp_tab[i].method); } mod = vector_search(&source, _nsmod, _nsmodsize, sizeof(*_nsmod), string_compare); if (mod != NULL && mod->handle != NULL) { key.database = database; key.name = method; match = bsearch(&key, mod->mtab, mod->mtabsize, sizeof(mod->mtab[0]), mtab_compare); if (match != NULL) { *mdata = match->mdata; return (match->method); } } if (is_dynamic()) nss_log(LOG_DEBUG, "%s, %s, %s, not found", source, database, method); *mdata = NULL; return (NULL); } __weak_reference(_nsdispatch, nsdispatch); int _nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, const char *method_name, const ns_src defaults[], ...) { va_list ap; const ns_dbt *dbt; const ns_src *srclist; nss_method method; void *mdata; int isthreaded, serrno, i, result, srclistsize; +#ifdef NS_CACHING + nss_cache_data cache_data; + nss_cache_data *cache_data_p; + int cache_flag; +#endif + isthreaded = __isthreaded; serrno = errno; if (isthreaded) { result = _pthread_rwlock_rdlock(&nss_lock); if (result != 0) { result = NS_UNAVAIL; goto fin; } } result = nss_configure(); if (result != 0) { result = NS_UNAVAIL; goto fin; } dbt = vector_search(&database, _nsmap, _nsmapsize, sizeof(*_nsmap), string_compare); if (dbt != NULL) { srclist = dbt->srclist; srclistsize = dbt->srclistsize; } else { srclist = defaults; srclistsize = 0; while (srclist[srclistsize].name != NULL) srclistsize++; } + +#ifdef NS_CACHING + cache_data_p = NULL; + cache_flag = 0; +#endif for (i = 0; i < srclistsize; i++) { result = NS_NOTFOUND; method = nss_method_lookup(srclist[i].name, database, method_name, disp_tab, &mdata); + if (method != NULL) { +#ifdef NS_CACHING + if (strcmp(srclist[i].name, NSSRC_CACHE) == 0 && + nss_cache_cycle_prevention_func == NULL) { +#ifdef NS_STRICT_LIBC_EID_CHECKING + if (issetugid() != 0) + continue; +#endif + cache_flag = 1; + + memset(&cache_data, 0, sizeof(nss_cache_data)); + cache_data.info = (nss_cache_info const *)mdata; + cache_data_p = &cache_data; + + va_start(ap, defaults); + if (cache_data.info->id_func != NULL) + result = __nss_common_cache_read(retval, + cache_data_p, ap); + else if (cache_data.info->marshal_func != NULL) + result = __nss_mp_cache_read(retval, + cache_data_p, ap); + else + result = __nss_mp_cache_end(retval, + cache_data_p, ap); + va_end(ap); + } else { + cache_flag = 0; + va_start(ap, defaults); + result = method(retval, mdata, ap); + va_end(ap); + } +#else /* NS_CACHING */ va_start(ap, defaults); result = method(retval, mdata, ap); va_end(ap); +#endif /* NS_CACHING */ + if (result & (srclist[i].flags)) break; } } + +#ifdef NS_CACHING + if (cache_data_p != NULL && + (result & (NS_NOTFOUND | NS_SUCCESS)) && cache_flag == 0) { + va_start(ap, defaults); + if (result == NS_SUCCESS) { + if (cache_data.info->id_func != NULL) + __nss_common_cache_write(retval, cache_data_p, + ap); + else if (cache_data.info->marshal_func != NULL) + __nss_mp_cache_write(retval, cache_data_p, ap); + } else if (result == NS_NOTFOUND) { + if (cache_data.info->id_func == NULL) { + if (cache_data.info->marshal_func != NULL) + __nss_mp_cache_write_submit(retval, + cache_data_p, ap); + } else + __nss_common_cache_write_negative(cache_data_p); + } + va_end(ap); + } +#endif /* NS_CACHING */ + if (isthreaded) (void)_pthread_rwlock_unlock(&nss_lock); fin: errno = serrno; return (result); } diff --git a/lib/libc/rpc/getrpcent.c b/lib/libc/rpc/getrpcent.c index b20d80d3498e..2822c767ea8b 100644 --- a/lib/libc/rpc/getrpcent.c +++ b/lib/libc/rpc/getrpcent.c @@ -1,320 +1,1048 @@ /* $NetBSD: getrpcent.c,v 1.17 2000/01/22 22:19:17 mycroft Exp $ */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user or with the express written consent of * Sun Microsystems, Inc. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ #if defined(LIBC_SCCS) && !defined(lint) static char *sccsid = "@(#)getrpcent.c 1.14 91/03/11 Copyr 1984 Sun Micro"; #endif #include __FBSDID("$FreeBSD$"); /* * Copyright (c) 1984 by Sun Microsystems, Inc. */ -#include "namespace.h" +#include #include - -#include +#include #include - #include -#include +#include +#include +#include #include -#include #include - +#include +#include #include #ifdef YP #include #include #endif +#include +#include "namespace.h" +#include "reentrant.h" #include "un-namespace.h" #include "libc_private.h" +#include "nss_tls.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif -/* - * Internet version. - */ -static struct rpcdata { - FILE *rpcf; +#define RPCDB "/etc/rpc" + +/* nsswitch declarations */ +enum constants +{ + SETRPCENT = 1, + ENDRPCENT = 2, + RPCENT_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ + RPCENT_STORAGE_MAX = 1 << 20, /* 1 MByte */ +}; + +static const ns_src defaultsrc[] = { + { NSSRC_FILES, NS_SUCCESS }, +#ifdef YP + { NSSRC_NIS, NS_SUCCESS }, +#endif + { NULL, 0 } +}; + +/* files backend declarations */ +struct files_state { + FILE *fp; int stayopen; -#define MAXALIASES 35 - char *rpc_aliases[MAXALIASES]; - struct rpcent rpc; - char line[BUFSIZ+1]; -#ifdef YP - char *domain; +}; + +static int files_rpcent(void *, void *, va_list); +static int files_setrpcent(void *, void *, va_list); + +static void files_endstate(void *); +NSS_TLS_HANDLING(files); + +/* nis backend declarations */ +#ifdef YP +struct nis_state { + char domain[MAXHOSTNAMELEN]; char *current; int currentlen; + int stepping; + int no_name_map; +}; + +static int nis_rpcent(void *, void *, va_list); +static int nis_setrpcent(void *, void *, va_list); + +static void nis_endstate(void *); +NSS_TLS_HANDLING(nis); #endif -} *rpcdata; -static struct rpcent *interpret(char *val, size_t len); +/* get** wrappers for get**_r functions declarations */ +struct rpcent_state { + struct rpcent rpc; + char *buffer; + size_t bufsize; +}; +static void rpcent_endstate(void *); +NSS_TLS_HANDLING(rpcent); -#ifdef YP -static int __yp_nomap = 0; -#endif /* YP */ +union key { + const char *name; + int number; +}; -#define RPCDB "/etc/rpc" +static int wrap_getrpcbyname_r(union key, struct rpcent *, char *, + size_t, struct rpcent **); +static int wrap_getrpcbynumber_r(union key, struct rpcent *, char *, + size_t, struct rpcent **); +static int wrap_getrpcent_r(union key, struct rpcent *, char *, + size_t, struct rpcent **); +static struct rpcent *getrpc(int (*fn)(union key, struct rpcent *, char *, + size_t, struct rpcent **), union key); -static struct rpcdata *_rpcdata(void); +#ifdef NS_CACHING +static int rpc_id_func(char *, size_t *, va_list, void *); +static int rpc_marshal_func(char *, size_t *, void *, va_list, void *); +static int rpc_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif -static struct rpcdata * -_rpcdata() +static int +rpcent_unpack(char *p, struct rpcent *rpc, char **r_aliases, + size_t aliases_size, int *errnop) { - struct rpcdata *d = rpcdata; + char *cp, **q; + + assert(p != NULL); + + if (*p == '#') + return (-1); + cp = strpbrk(p, "#\n"); + if (cp == NULL) + return (-1); + *cp = '\0'; + cp = strpbrk(p, " \t"); + if (cp == NULL) + return (-1); + *cp++ = '\0'; + /* THIS STUFF IS INTERNET SPECIFIC */ + rpc->r_name = p; + while (*cp == ' ' || *cp == '\t') + cp++; + rpc->r_number = atoi(cp); + q = rpc->r_aliases = r_aliases; + cp = strpbrk(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < &(r_aliases[aliases_size - 1])) + *q++ = cp; + else { + *errnop = ERANGE; + return -1; + } - if (d == 0) { - d = (struct rpcdata *)calloc(1, sizeof (struct rpcdata)); - rpcdata = d; + cp = strpbrk(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; } - return (d); + *q = NULL; + return 0; } -struct rpcent * -getrpcbynumber(number) +/* files backend implementation */ +static void +files_endstate(void *p) +{ + FILE * f; + + if (p == NULL) + return; + + f = ((struct files_state *)p)->fp; + if (f != NULL) + fclose(f); + + free(p); +} + +static int +files_rpcent(void *retval, void *mdata, va_list ap) +{ + char *name; int number; + struct rpcent *rpc; + char *buffer; + size_t bufsize; + int *errnop; + + char *line; + size_t linesize; + char **aliases; + int aliases_size; + char **rp; + + struct files_state *st; + int rv; + int stayopen; + enum nss_lookup_type how; + + how = (enum nss_lookup_type)mdata; + switch (how) + { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + number = va_arg(ap, int); + break; + case nss_lt_all: + break; + default: + return (NS_NOTFOUND); + } + + rpc = va_arg(ap, struct rpcent *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + + *errnop = files_getstate(&st); + if (*errnop != 0) + return (NS_UNAVAIL); + + if (st->fp == NULL && (st->fp = fopen(RPCDB, "r")) == NULL) { + *errnop = errno; + return (NS_UNAVAIL); + } + + if (how == nss_lt_all) + stayopen = 1; + else { + rewind(st->fp); + stayopen = st->stayopen; + } + + do { + if ((line = fgetln(st->fp, &linesize)) == NULL) { + *errnop = errno; + rv = NS_RETURN; + break; + } + + if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) { + *errnop = ERANGE; + rv = NS_RETURN; + break; + } + + aliases = (char **)_ALIGN(&buffer[linesize+1]); + aliases_size = (buffer + bufsize - + (char *)aliases)/sizeof(char *); + if (aliases_size < 1) { + *errnop = ERANGE; + rv = NS_RETURN; + break; + } + + memcpy(buffer, line, linesize); + buffer[linesize] = '\0'; + + rv = rpcent_unpack(buffer, rpc, aliases, aliases_size, errnop); + if (rv != 0) { + if (*errnop == 0) { + rv = NS_NOTFOUND; + continue; + } + else { + rv = NS_RETURN; + break; + } + } + + switch (how) + { + case nss_lt_name: + if (strcmp(rpc->r_name, name) == 0) + goto done; + for (rp = rpc->r_aliases; *rp != NULL; rp++) { + if (strcmp(*rp, name) == 0) + goto done; + } + rv = NS_NOTFOUND; + continue; +done: + rv = NS_SUCCESS; + break; + case nss_lt_id: + rv = (rpc->r_number == number) ? NS_SUCCESS : + NS_NOTFOUND; + break; + case nss_lt_all: + rv = NS_SUCCESS; + break; + } + + } while (!(rv & NS_TERMINATE)); + + if (!stayopen && st->fp!=NULL) { + fclose(st->fp); + st->fp = NULL; + } + + if ((rv == NS_SUCCESS) && (retval != NULL)) + *((struct rpcent **)retval) = rpc; + + return (rv); +} + +static int +files_setrpcent(void *retval, void *mdata, va_list ap) { -#ifdef YP - int reason; - char adrstr[16]; -#endif - struct rpcent *p; - struct rpcdata *d = _rpcdata(); + struct files_state *st; + int rv; + int f; - if (d == 0) - return (0); -#ifdef YP - if (!__yp_nomap && _yp_check(&d->domain)) { - sprintf(adrstr, "%d", number); - reason = yp_match(d->domain, "rpc.bynumber", adrstr, strlen(adrstr), - &d->current, &d->currentlen); - switch(reason) { - case 0: - break; - case YPERR_MAP: - __yp_nomap = 1; - goto no_yp; - break; - default: - return(0); - break; - } - d->current[d->currentlen] = '\0'; - p = interpret(d->current, d->currentlen); - (void) free(d->current); - return p; - } -no_yp: -#endif /* YP */ - - setrpcent(0); - while ((p = getrpcent()) != NULL) { - if (p->r_number == number) + rv = files_getstate(&st); + if (rv != 0) + return (NS_UNAVAIL); + + switch ((enum constants)mdata) + { + case SETRPCENT: + f = va_arg(ap,int); + if (st->fp == NULL) + st->fp = fopen(RPCDB, "r"); + else + rewind(st->fp); + st->stayopen |= f; + break; + case ENDRPCENT: + if (st->fp != NULL) { + fclose(st->fp); + st->fp = NULL; + } + st->stayopen = 0; + break; + default: + break; + } + + return (NS_UNAVAIL); +} + +/* nis backend implementation */ +#ifdef YP +static void +nis_endstate(void *p) +{ + if (p == NULL) + return; + + free(((struct nis_state *)p)->current); + free(p); +} + +static int +nis_rpcent(void *retval, void *mdata, va_list ap) +{ + char *name; + int number; + struct rpcent *rpc; + char *buffer; + size_t bufsize; + int *errnop; + + char **rp; + char **aliases; + int aliases_size; + + char *lastkey; + char *resultbuf; + int resultbuflen; + char buf[YPMAXRECORD + 2]; + + struct nis_state *st; + int rv; + enum nss_lookup_type how; + int no_name_active; + + how = (enum nss_lookup_type)mdata; + switch (how) + { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + number = va_arg(ap, int); + break; + case nss_lt_all: + break; + default: + return (NS_NOTFOUND); + } + + rpc = va_arg(ap, struct rpcent *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + + *errnop = nis_getstate(&st); + if (*errnop != 0) + return (NS_UNAVAIL); + + if (st->domain[0] == '\0') { + if (getdomainname(st->domain, sizeof(st->domain)) != 0) { + *errnop = errno; + return (NS_UNAVAIL); + } + } + + no_name_active = 0; + do { + switch (how) + { + case nss_lt_name: + if (!st->no_name_map) + { + snprintf(buf, sizeof buf, "%s", name); + rv = yp_match(st->domain, "rpc.byname", buf, + strlen(buf), &resultbuf, &resultbuflen); + + switch (rv) { + case 0: + break; + case YPERR_MAP: + st->stepping = 0; + no_name_active = 1; + how = nss_lt_all; + + rv = NS_NOTFOUND; + continue; + default: + rv = NS_NOTFOUND; + goto fin; + } + } else { + st->stepping = 0; + no_name_active = 1; + how = nss_lt_all; + + rv = NS_NOTFOUND; + continue; + } + break; + case nss_lt_id: + snprintf(buf, sizeof buf, "%d", number); + if (yp_match(st->domain, "rpc.bynumber", buf, + strlen(buf), &resultbuf, &resultbuflen)) { + rv = NS_NOTFOUND; + goto fin; + } + break; + case nss_lt_all: + if (!st->stepping) { + rv = yp_first(st->domain, "rpc.bynumber", + &st->current, + &st->currentlen, &resultbuf, + &resultbuflen); + if (rv) { + rv = NS_NOTFOUND; + goto fin; + } + st->stepping = 1; + } else { + lastkey = st->current; + rv = yp_next(st->domain, "rpc.bynumber", + st->current, + st->currentlen, &st->current, + &st->currentlen, + &resultbuf, &resultbuflen); + free(lastkey); + if (rv) { + st->stepping = 0; + rv = NS_NOTFOUND; + goto fin; + } + } + break; + } + + /* we need a room for additional \n symbol */ + if (bufsize <= resultbuflen + 1 + _ALIGNBYTES + + sizeof(char *)) { + *errnop = ERANGE; + rv = NS_RETURN; break; + } + + aliases=(char **)_ALIGN(&buffer[resultbuflen+2]); + aliases_size = (buffer + bufsize - (char *)aliases) / + sizeof(char *); + if (aliases_size < 1) { + *errnop = ERANGE; + rv = NS_RETURN; + break; + } + + /* + * rpcent_unpack expects lines terminated with \n -- make it happy + */ + memcpy(buffer, resultbuf, resultbuflen); + buffer[resultbuflen] = '\n'; + buffer[resultbuflen+1] = '\0'; + free(resultbuf); + + if (rpcent_unpack(buffer, rpc, aliases, aliases_size, + errnop) != 0) { + if (*errnop == 0) + rv = NS_NOTFOUND; + else + rv = NS_RETURN; + } else { + if ((how == nss_lt_all) && (no_name_active != 0)) { + if (strcmp(rpc->r_name, name) == 0) + goto done; + for (rp = rpc->r_aliases; *rp != NULL; rp++) { + if (strcmp(*rp, name) == 0) + goto done; + } + rv = NS_NOTFOUND; + continue; +done: + rv = NS_SUCCESS; + } else + rv = NS_SUCCESS; + } + + } while (!(rv & NS_TERMINATE) && (how == nss_lt_all)); + +fin: + if ((rv == NS_SUCCESS) && (retval != NULL)) + *((struct rpcent **)retval) = rpc; + + return (rv); +} + +static int +nis_setrpcent(void *retval, void *mdata, va_list ap) +{ + struct nis_state *st; + int rv; + + rv = nis_getstate(&st); + if (rv != 0) + return (NS_UNAVAIL); + + switch ((enum constants)mdata) + { + case SETRPCENT: + case ENDRPCENT: + free(st->current); + st->current = NULL; + st->stepping = 0; + break; + default: + break; } - endrpcent(); - return (p); + + return (NS_UNAVAIL); } +#endif -struct rpcent * -getrpcbyname(name) +#ifdef NS_CACHING +static int +rpc_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) +{ char *name; + int rpc; + + size_t desired_size, size; + enum nss_lookup_type lookup_type; + int res = NS_UNAVAIL; + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + name = va_arg(ap, char *); + + size = strlen(name); + desired_size = sizeof(enum nss_lookup_type) + size + 1; + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + rpc = va_arg(ap, int); + + desired_size = sizeof(enum nss_lookup_type) + sizeof(int); + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), &rpc, + sizeof(int)); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + +static int +rpc_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, + void *cache_mdata) { - struct rpcent *rpc = NULL; - char **rp; + char *name; + int num; + struct rpcent *rpc; + char *orig_buf; + size_t orig_buf_size; - assert(name != NULL); + struct rpcent new_rpc; + size_t desired_size, size, aliases_size; + char *p; + char **alias; - setrpcent(0); - while ((rpc = getrpcent()) != NULL) { - if (strcmp(rpc->r_name, name) == 0) - goto done; - for (rp = rpc->r_aliases; *rp != NULL; rp++) { - if (strcmp(*rp, name) == 0) - goto done; + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + num = va_arg(ap, int); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + rpc = va_arg(ap, struct rpcent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + + desired_size = _ALIGNBYTES + sizeof(struct rpcent) + sizeof(char *); + if (rpc->r_name != NULL) + desired_size += strlen(rpc->r_name) + 1; + + if (rpc->r_aliases != NULL) { + aliases_size = 0; + for (alias = rpc->r_aliases; *alias; ++alias) { + desired_size += strlen(*alias) + 1; + ++aliases_size; } + + desired_size += _ALIGNBYTES + (aliases_size + 1) * + sizeof(char *); } -done: - endrpcent(); - return (rpc); + + if (*buffer_size < desired_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_rpc, rpc, sizeof(struct rpcent)); + + *buffer_size = desired_size; + memset(buffer, 0, desired_size); + p = buffer + sizeof(struct rpcent) + sizeof(char *); + memcpy(buffer + sizeof(struct rpcent), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_rpc.r_name != NULL) { + size = strlen(new_rpc.r_name); + memcpy(p, new_rpc.r_name, size); + new_rpc.r_name = p; + p += size + 1; + } + + if (new_rpc.r_aliases != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_rpc.r_aliases, sizeof(char *) * aliases_size); + new_rpc.r_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); + + for (alias = new_rpc.r_aliases; *alias; ++alias) { + size = strlen(*alias); + memcpy(p, *alias, size); + *alias = p; + p += size + 1; + } + } + + memcpy(buffer, &new_rpc, sizeof(struct rpcent)); + return (NS_SUCCESS); } -void -setrpcent(f) - int f; +static int +rpc_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, + void *cache_mdata) { - struct rpcdata *d = _rpcdata(); + char *name; + int num; + struct rpcent *rpc; + char *orig_buf; + size_t orig_buf_size; + int *ret_errno; - if (d == 0) - return; -#ifdef YP - if (!__yp_nomap && _yp_check(NULL)) { - if (d->current) - free(d->current); - d->current = NULL; - d->currentlen = 0; - return; - } - __yp_nomap = 0; -#endif /* YP */ - if (d->rpcf == NULL) - d->rpcf = fopen(RPCDB, "r"); + char *p; + char **alias; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + num = va_arg(ap, int); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + rpc = va_arg(ap, struct rpcent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + ret_errno = va_arg(ap, int *); + + if (orig_buf_size < + buffer_size - sizeof(struct rpcent) - sizeof(char *)) { + *ret_errno = ERANGE; + return (NS_RETURN); + } + + memcpy(rpc, buffer, sizeof(struct rpcent)); + memcpy(&p, buffer + sizeof(struct rpcent), sizeof(char *)); + + orig_buf = (char *)_ALIGN(orig_buf); + memcpy(orig_buf, buffer + sizeof(struct rpcent) + sizeof(char *) + + _ALIGN(p) - (size_t)p, + buffer_size - sizeof(struct rpcent) - sizeof(char *) - + _ALIGN(p) + (size_t)p); + p = (char *)_ALIGN(p); + + NS_APPLY_OFFSET(rpc->r_name, orig_buf, p, char *); + if (rpc->r_aliases != NULL) { + NS_APPLY_OFFSET(rpc->r_aliases, orig_buf, p, char **); + + for (alias = rpc->r_aliases ; *alias; ++alias) + NS_APPLY_OFFSET(*alias, orig_buf, p, char *); + } + + if (retval != NULL) + *((struct rpcent **)retval) = rpc; + + return (NS_SUCCESS); +} + +NSS_MP_CACHE_HANDLING(rpc); +#endif /* NS_CACHING */ + + +/* get**_r functions implementation */ +static int +getrpcbyname_r(const char *name, struct rpcent *rpc, char *buffer, + size_t bufsize, struct rpcent **result) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + rpc, (void *)nss_lt_name, + rpc_id_func, rpc_marshal_func, rpc_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_rpcent, (void *)nss_lt_name }, +#ifdef YP + { NSSRC_NIS, nis_rpcent, (void *)nss_lt_name }, +#endif +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_RPC, "getrpcbyname_r", defaultsrc, + name, rpc, buffer, bufsize, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); else - rewind(d->rpcf); - d->stayopen |= f; + return (ret_errno); } -void -endrpcent() +static int +getrpcbynumber_r(int number, struct rpcent *rpc, char *buffer, + size_t bufsize, struct rpcent **result) { - struct rpcdata *d = _rpcdata(); +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + rpc, (void *)nss_lt_id, + rpc_id_func, rpc_marshal_func, rpc_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_rpcent, (void *)nss_lt_id }, +#ifdef YP + { NSSRC_NIS, nis_rpcent, (void *)nss_lt_id }, +#endif +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_RPC, "getrpcbynumber_r", defaultsrc, + number, rpc, buffer, bufsize, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); +} + +static int +getrpcent_r(struct rpcent *rpc, char *buffer, size_t bufsize, + struct rpcent **result) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + rpc, (void *)nss_lt_all, + rpc_marshal_func, rpc_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_rpcent, (void *)nss_lt_all }, +#ifdef YP + { NSSRC_NIS, nis_rpcent, (void *)nss_lt_all }, +#endif +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_RPC, "getrpcent_r", defaultsrc, + rpc, buffer, bufsize, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); +} - if (d == 0) +/* get** wrappers for get**_r functions implementation */ +static void +rpcent_endstate(void *p) +{ + if (p == NULL) return; -#ifdef YP - if (!__yp_nomap && _yp_check(NULL)) { - if (d->current && !d->stayopen) - free(d->current); - d->current = NULL; - d->currentlen = 0; - return; - } - __yp_nomap = 0; -#endif /* YP */ - if (d->rpcf && !d->stayopen) { - fclose(d->rpcf); - d->rpcf = NULL; + + free(((struct rpcent_state *)p)->buffer); + free(p); +} + +static int +wrap_getrpcbyname_r(union key key, struct rpcent *rpc, char *buffer, + size_t bufsize, struct rpcent **res) +{ + return (getrpcbyname_r(key.name, rpc, buffer, bufsize, res)); +} + +static int +wrap_getrpcbynumber_r(union key key, struct rpcent *rpc, char *buffer, + size_t bufsize, struct rpcent **res) +{ + return (getrpcbynumber_r(key.number, rpc, buffer, bufsize, res)); +} + +static int +wrap_getrpcent_r(union key key __unused, struct rpcent *rpc, char *buffer, + size_t bufsize, struct rpcent **res) +{ + return (getrpcent_r(rpc, buffer, bufsize, res)); +} + +static struct rpcent * +getrpc(int (*fn)(union key, struct rpcent *, char *, size_t, struct rpcent **), + union key key) +{ + int rv; + struct rpcent *res; + struct rpcent_state * st; + + rv=rpcent_getstate(&st); + if (rv != 0) { + errno = rv; + return NULL; + } + + if (st->buffer == NULL) { + st->buffer = malloc(RPCENT_STORAGE_INITIAL); + if (st->buffer == NULL) + return (NULL); + st->bufsize = RPCENT_STORAGE_INITIAL; } + do { + rv = fn(key, &st->rpc, st->buffer, st->bufsize, &res); + if (res == NULL && rv == ERANGE) { + free(st->buffer); + if ((st->bufsize << 1) > RPCENT_STORAGE_MAX) { + st->buffer = NULL; + errno = ERANGE; + return (NULL); + } + st->bufsize <<= 1; + st->buffer = malloc(st->bufsize); + if (st->buffer == NULL) + return (NULL); + } + } while (res == NULL && rv == ERANGE); + if (rv != 0) + errno = rv; + + return (res); +} + +struct rpcent * +getrpcbyname(char *name) +{ + union key key; + + key.name = name; + + return (getrpc(wrap_getrpcbyname_r, key)); +} + +struct rpcent * +getrpcbynumber(int number) +{ + union key key; + + key.number = number; + + return (getrpc(wrap_getrpcbynumber_r, key)); } struct rpcent * getrpcent() { - struct rpcdata *d = _rpcdata(); -#ifdef YP - struct rpcent *hp; - int reason; - char *val = NULL; - int vallen; -#endif + union key key; - if (d == 0) - return(NULL); -#ifdef YP - if (!__yp_nomap && _yp_check(&d->domain)) { - if (d->current == NULL && d->currentlen == 0) { - reason = yp_first(d->domain, "rpc.bynumber", - &d->current, &d->currentlen, - &val, &vallen); - } else { - reason = yp_next(d->domain, "rpc.bynumber", - d->current, d->currentlen, - &d->current, &d->currentlen, - &val, &vallen); - } - switch(reason) { - case 0: - break; - case YPERR_MAP: - __yp_nomap = 1; - goto no_yp; - break; - default: - return(0); - break; - } - val[vallen] = '\0'; - hp = interpret(val, vallen); - (void) free(val); - return hp; - } -no_yp: -#endif /* YP */ - if (d->rpcf == NULL && (d->rpcf = fopen(RPCDB, "r")) == NULL) - return (NULL); - /* -1 so there is room to append a \n below */ - if (fgets(d->line, BUFSIZ - 1, d->rpcf) == NULL) - return (NULL); - return (interpret(d->line, strlen(d->line))); + key.number = 0; /* not used */ + + return (getrpc(wrap_getrpcent_r, key)); } -static struct rpcent * -interpret(val, len) - char *val; - size_t len; +void +setrpcent(int stayopen) { - struct rpcdata *d = _rpcdata(); - char *p; - char *cp, **q; +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + rpc, (void *)nss_lt_all, + NULL, NULL); +#endif - assert(val != NULL); + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_setrpcent, (void *)SETRPCENT }, +#ifdef YP + { NSSRC_NIS, nis_setrpcent, (void *)SETRPCENT }, +#endif +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; - if (d == 0) - return (0); - (void) strncpy(d->line, val, BUFSIZ); - d->line[BUFSIZ] = '\0'; - p = d->line; - p[len] = '\n'; - if (*p == '#') - return (getrpcent()); - cp = strpbrk(p, "#\n"); - if (cp == NULL) - return (getrpcent()); - *cp = '\0'; - cp = strpbrk(p, " \t"); - if (cp == NULL) - return (getrpcent()); - *cp++ = '\0'; - /* THIS STUFF IS INTERNET SPECIFIC */ - d->rpc.r_name = d->line; - while (*cp == ' ' || *cp == '\t') - cp++; - d->rpc.r_number = atoi(cp); - q = d->rpc.r_aliases = d->rpc_aliases; - cp = strpbrk(cp, " \t"); - if (cp != NULL) - *cp++ = '\0'; - while (cp && *cp) { - if (*cp == ' ' || *cp == '\t') { - cp++; - continue; - } - if (q < &(d->rpc_aliases[MAXALIASES - 1])) - *q++ = cp; - cp = strpbrk(cp, " \t"); - if (cp != NULL) - *cp++ = '\0'; - } - *q = NULL; - return (&d->rpc); + (void)nsdispatch(NULL, dtab, NSDB_RPC, "setrpcent", defaultsrc, + stayopen); } +void +endrpcent() +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + rpc, (void *)nss_lt_all, + NULL, NULL); +#endif + + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_setrpcent, (void *)ENDRPCENT }, +#ifdef YP + { NSSRC_NIS, nis_setrpcent, (void *)ENDRPCENT }, +#endif +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + + (void)nsdispatch(NULL, dtab, NSDB_SERVICES, "endrpcent", defaultsrc); +} diff --git a/share/mk/bsd.own.mk b/share/mk/bsd.own.mk index 9e052e12c01a..49c85011fe74 100644 --- a/share/mk/bsd.own.mk +++ b/share/mk/bsd.own.mk @@ -1,452 +1,454 @@ # $FreeBSD$ # # The include file set common variables for owner, # group, mode, and directories. Defaults are in brackets. # # # +++ variables +++ # # DESTDIR Change the tree where the file gets installed. [not set] # # DISTDIR Change the tree where the file for a distribution # gets installed (see /usr/src/release/Makefile). [not set] # # COMPRESS_CMD Program to compress documents. # Output is to stdout. [gzip -cn] # # COMPRESS_EXT File name extension of ${COMPRESS_CMD} command. [.gz] # # BINOWN Binary owner. [root] # # BINGRP Binary group. [wheel] # # BINMODE Binary mode. [555] # # NOBINMODE Mode for non-executable files. [444] # # LIBDIR Base path for libraries. [/usr/lib] # # LIBCOMPATDIR Base path for compat libraries. [/usr/lib/compat] # # LIBDATADIR Base path for misc. utility data files. [/usr/libdata] # # LINTLIBDIR Base path for lint libraries. [/usr/libdata/lint] # # SHLIBDIR Base path for shared libraries. [${LIBDIR}] # # LIBOWN Library owner. [${BINOWN}] # # LIBGRP Library group. [${BINGRP}] # # LIBMODE Library mode. [${NOBINMODE}] # # # KMODDIR Base path for loadable kernel modules # (see kld(4)). [/boot/kernel] # # KMODOWN KLD owner. [${BINOWN}] # # KMODGRP KLD group. [${BINGRP}] # # KMODMODE KLD mode. [${BINMODE}] # # # SHAREDIR Base path for architecture-independent ascii # text files. [/usr/share] # # SHAREOWN ASCII text file owner. [root] # # SHAREGRP ASCII text file group. [wheel] # # SHAREMODE ASCII text file mode. [${NOBINMODE}] # # # DOCDIR Base path for system documentation (e.g. PSD, USD, # handbook, FAQ etc.). [${SHAREDIR}/doc] # # DOCOWN Documentation owner. [${SHAREOWN}] # # DOCGRP Documentation group. [${SHAREGRP}] # # DOCMODE Documentation mode. [${NOBINMODE}] # # # INFODIR Base path for GNU's hypertext system # called Info (see info(1)). [${SHAREDIR}/info] # # INFOOWN Info owner. [${SHAREOWN}] # # INFOGRP Info group. [${SHAREGRP}] # # INFOMODE Info mode. [${NOBINMODE}] # # # MANDIR Base path for manual installation. [${SHAREDIR}/man/man] # # MANOWN Manual owner. [${SHAREOWN}] # # MANGRP Manual group. [${SHAREGRP}] # # MANMODE Manual mode. [${NOBINMODE}] # # # NLSDIR Base path for National Language Support files # installation. [${SHAREDIR}/nls] # # NLSOWN National Language Support files owner. [${SHAREOWN}] # # NLSGRP National Language Support files group. [${SHAREGRP}] # # NLSMODE National Language Support files mode. [${NOBINMODE}] # # INCLUDEDIR Base path for standard C include files [/usr/include] .if !target(____) ____: SRCCONF?= /etc/src.conf .if exists(${SRCCONF}) .include "${SRCCONF}" .endif # Binaries BINOWN?= root BINGRP?= wheel BINMODE?= 555 NOBINMODE?= 444 .if defined(MODULES_WITH_WORLD) KMODDIR?= /boot/modules .else KMODDIR?= /boot/kernel .endif KMODOWN?= ${BINOWN} KMODGRP?= ${BINGRP} KMODMODE?= ${BINMODE} LIBDIR?= /usr/lib LIBCOMPATDIR?= /usr/lib/compat LIBDATADIR?= /usr/libdata LINTLIBDIR?= /usr/libdata/lint SHLIBDIR?= ${LIBDIR} LIBOWN?= ${BINOWN} LIBGRP?= ${BINGRP} LIBMODE?= ${NOBINMODE} # Share files SHAREDIR?= /usr/share SHAREOWN?= root SHAREGRP?= wheel SHAREMODE?= ${NOBINMODE} MANDIR?= ${SHAREDIR}/man/man MANOWN?= ${SHAREOWN} MANGRP?= ${SHAREGRP} MANMODE?= ${NOBINMODE} DOCDIR?= ${SHAREDIR}/doc DOCOWN?= ${SHAREOWN} DOCGRP?= ${SHAREGRP} DOCMODE?= ${NOBINMODE} INFODIR?= ${SHAREDIR}/info INFOOWN?= ${SHAREOWN} INFOGRP?= ${SHAREGRP} INFOMODE?= ${NOBINMODE} NLSDIR?= ${SHAREDIR}/nls NLSOWN?= ${SHAREOWN} NLSGRP?= ${SHAREGRP} NLSMODE?= ${NOBINMODE} INCLUDEDIR?= /usr/include # Common variables .if !defined(DEBUG_FLAGS) STRIP?= -s .endif COMPRESS_CMD?= gzip -cn COMPRESS_EXT?= .gz # # Define MK_* variables (which are either "yes" or "no") for users # to set via WITH_*/WITHOUT_* in /etc/src.conf and override in the # make(1) environment. # These should be tested with `== "no"' or `!= "no"' in makefiles. # The NO_* variables should only be set by makefiles. # # # Supported NO_* options (if defined, MK_* will be forced to "no", # regardless of user's setting). # .for var in \ MAN \ PROFILE .if defined(NO_${var}) WITHOUT_${var}= .endif .endfor # # Compat NO_* options (same as above, except their use is deprecated). # .if !defined(BURN_BRIDGES) .for var in \ ACPI \ ATM \ AUDIT \ AUTHPF \ BIND \ BIND_DNSSEC \ BIND_ETC \ BIND_LIBS_LWRES \ BIND_MTREE \ BIND_NAMED \ BIND_UTILS \ BLUETOOTH \ BOOT \ CALENDAR \ CPP \ CRYPT \ CVS \ CXX \ DICT \ DYNAMICROOT \ EXAMPLES \ FORTH \ FORTRAN \ FP_LIBC \ GAMES \ GCOV \ GDB \ GNU \ GPIB \ GROFF \ HTML \ I4B \ INET6 \ INFO \ IPFILTER \ IPX \ KERBEROS \ LIB32 \ LIBC_R \ LIBPTHREAD \ LIBTHR \ LOCALES \ LPR \ MAILWRAPPER \ NETCAT \ NIS \ NLS \ NLS_CATALOGS \ + NS_CACHING \ OBJC \ OPENSSH \ OPENSSL \ PAM \ PCVT \ PF \ RCMDS \ RCS \ RESCUE \ SENDMAIL \ SETUID_LOGIN \ SHAREDOCS \ SYSCONS \ TCSH \ TOOLCHAIN \ USB \ WPA_SUPPLICANT_EAPOL .if defined(NO_${var}) #.warning NO_${var} is deprecated in favour of WITHOUT_${var}= WITHOUT_${var}= .endif .endfor .endif # !defined(BURN_BRIDGES) # # Older-style variables that enabled behaviour when set. # .if defined(YES_HESIOD) WITH_HESIOD= .endif .if defined(MAKE_IDEA) WITH_IDEA= .endif # # MK_* options which default to "yes". # .for var in \ ACPI \ ATM \ AUDIT \ AUTHPF \ BIND \ BIND_DNSSEC \ BIND_ETC \ BIND_LIBS_LWRES \ BIND_MTREE \ BIND_NAMED \ BIND_UTILS \ BLUETOOTH \ BOOT \ CALENDAR \ CPP \ CRYPT \ CVS \ CXX \ DICT \ DYNAMICROOT \ EXAMPLES \ FORTH \ FORTRAN \ FP_LIBC \ GAMES \ GCOV \ GDB \ GNU \ GPIB \ GROFF \ HTML \ I4B \ INET6 \ INFO \ IPFILTER \ IPX \ KERBEROS \ LIB32 \ LIBC_R \ LIBPTHREAD \ LIBTHR \ LOCALES \ LPR \ MAILWRAPPER \ MAN \ NCP \ NETCAT \ NIS \ NLS \ NLS_CATALOGS \ + NS_CACHING \ OBJC \ OPENSSH \ OPENSSL \ PAM \ PCVT \ PF \ PROFILE \ RCMDS \ RCS \ RESCUE \ SENDMAIL \ SETUID_LOGIN \ SHAREDOCS \ SYSCONS \ TCSH \ TOOLCHAIN \ USB \ WPA_SUPPLICANT_EAPOL .if defined(WITH_${var}) && defined(WITHOUT_${var}) .error WITH_${var} and WITHOUT_${var} can't both be set. .endif .if defined(MK_${var}) .error MK_${var} can't be set by a user. .endif .if defined(WITHOUT_${var}) MK_${var}:= no .else MK_${var}:= yes .endif .endfor # # MK_* options which default to "no". # .for var in \ BIND_LIBS \ HESIOD \ IDEA .if defined(WITH_${var}) && defined(WITHOUT_${var}) .error WITH_${var} and WITHOUT_${var} can't both be set. .endif .if defined(MK_${var}) .error MK_${var} can't be set by a user. .endif .if defined(WITH_${var}) MK_${var}:= yes .else MK_${var}:= no .endif .endfor # # Force some options off if their dependencies are off. # .if ${MK_BIND} == "no" MK_BIND_DNSSEC:= no MK_BIND_ETC:= no MK_BIND_LIBS:= no MK_BIND_LIBS_LWRES:= no MK_BIND_MTREE:= no MK_BIND_NAMED:= no MK_BIND_UTILS:= no .endif .if ${MK_BIND_MTREE} == "no" MK_BIND_ETC:= no .endif .if ${MK_CRYPT} == "no" MK_OPENSSL:= no MK_OPENSSH:= no MK_KERBEROS:= no .endif .if ${MK_IPX} == "no" MK_NCP:= no .endif .if ${MK_OPENSSL} == "no" MK_OPENSSH:= no MK_KERBEROS:= no .endif .if ${MK_PF} == "no" MK_AUTHPF:= no .endif .if ${MK_TOOLCHAIN} == "no" MK_GDB:= no .endif # # Set defaults for the MK_*_SUPPORT variables. # # # MK_*_SUPPORT options which default to "yes" unless their corresponding # MK_* variable is set to "no". # .for var in \ GNU \ INET6 \ IPX \ KERBEROS \ PAM .if defined(WITH_${var}_SUPPORT) && defined(WITHOUT_${var}_SUPPORT) .error WITH_${var}_SUPPORT and WITHOUT_${var}_SUPPORT can't both be set. .endif .if defined(MK_${var}_SUPPORT) .error MK_${var}_SUPPORT can't be set by a user. .endif .if defined(WITHOUT_${var}_SUPPORT) || ${MK_${var}} == "no" MK_${var}_SUPPORT:= no .else MK_${var}_SUPPORT:= yes .endif .endfor .endif # !target(____) diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 0b2d93ef7d99..524f9c2dab2b 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -1,376 +1,381 @@ # From: @(#)Makefile 5.20 (Berkeley) 6/12/93 # $FreeBSD$ .include # XXX MISSING: mkproto SUBDIR= ac \ accton \ ${_acpi} \ adduser \ amd \ ancontrol \ ${_apm} \ ${_apmd} \ ${_arlcontrol} \ arp \ ${_audit} \ ${_auditd} \ ${_auditreduce} \ ${_asf} \ ${_atm} \ ${_authpf} \ ${_bluetooth} \ ${_boot0cfg} \ ${_boot98cfg} \ bootparamd \ bsnmpd \ ${_btxld} \ burncd \ + ${_cached} \ cdcontrol \ chkgrp \ chown \ chroot \ ckdist \ config \ cron \ crunch \ ctm \ daemon \ dconschat \ devinfo \ digictl \ diskinfo \ ${_dnssec-keygen} \ ${_dnssec-signzone} \ ${_editmap} \ edquota \ ${_eeprom} \ ${_elf2exe} \ extattr \ extattrctl \ faithd \ fdcontrol \ fdformat \ fdread \ fdwrite \ flowctl \ fwcontrol \ getfmac \ getpmac \ gstat \ ${_i4b} \ ifmcstat \ inetd \ iostat \ ip6addrctl \ ipfwpcap \ ${_IPXrouted} \ jail \ jexec \ jls \ kbdcontrol \ kbdmap \ ${_keyserv} \ ${_kgmon} \ ${_kgzip} \ kldxref \ lastlogin \ ${_lpr} \ ${_lptcontrol} \ lmcconfig \ ${_mailstats} \ mailwrapper \ ${_makemap} \ manctl \ memcontrol \ mergemaster \ mixer \ ${_mld6query} \ mlxcontrol \ mountd \ ${_mount_nwfs} \ mount_portalfs \ ${_mount_smbfs} \ moused \ ${_mptable} \ mrouted \ mtest \ mtree \ ${_named} \ ${_named-checkconf} \ ${_named-checkzone} \ ${_named.reload} \ ${_ndiscvt} \ ndp \ newsyslog \ nfsd \ ngctl \ nghook \ nologin \ ntp \ ${_ofwdump} \ ${_pccard} \ pciconf \ ${_pcvt} \ periodic \ pkg_install \ pmccontrol \ pmcstat \ ${_pnpinfo} \ portsnap \ powerd \ ppp \ ${_pppctl} \ pppd \ pppstats \ ${_praliases} \ ${_praudit} \ procctl \ pstat \ pw \ pwd_mkdb \ quot \ quotaon \ rarpd \ raycontrol \ repquota \ ${_rip6query} \ rmt \ ${_rndc} \ ${_rndc-confgen} \ ${_route6d} \ rpcbind \ rpc.lockd \ rpc.statd \ rpc.umntall \ ${_rpc.yppasswdd} \ ${_rpc.ypupdated} \ ${_rpc.ypxfrd} \ rrenumd \ rtadvd \ rtprio \ rtsold \ rwhod \ sa \ ${_sendmail} \ setfmac \ setpmac \ ${_sicontrol} \ sliplogin \ slstat \ smbmsg \ snapinfo \ ${_spkrtest} \ spray \ ${_sysinstall} \ syslogd \ tcpdchk \ tcpdmatch \ tcpdrop \ tcpdump \ timed \ traceroute \ ${_traceroute6} \ trpt \ tzsetup \ ugidfw \ ${_usbdevs} \ vidcontrol \ vipw \ vnconfig \ watch \ watchdogd \ wicontrol \ ${_wlconfig} \ wpa \ ${_ypbind} \ ${_yp_mkdb} \ ${_yppoll} \ ${_yppush} \ ${_ypserv} \ ${_ypset} \ zic \ ${_zzz} .if ${MACHINE_ARCH} != "arm" _sysinstall= sysinstall .endif .if ${MK_ATM} != "no" _atm= atm .endif .if ${MK_AUDIT} != "no" _audit= audit _auditd= auditd _auditreduce= auditreduce _praudit= praudit .endif .if ${MK_BIND_DNSSEC} != "no" && ${MK_OPENSSL} != "no" _dnssec-keygen= dnssec-keygen _dnssec-signzone= dnssec-signzone .endif .if ${MK_BIND_NAMED} != "no" _named= named _named-checkconf= named-checkconf _named-checkzone= named-checkzone _named.reload= named.reload _rndc= rndc _rndc-confgen= rndc-confgen .endif .if ${MK_BLUETOOTH} != "no" _bluetooth= bluetooth .endif +.if ${MK_NS_CACHING} != "no" +_cached= cached +.endif + .if ${MK_OPENSSL} != "no" _keyserv= keyserv .endif .if ${MK_INET6} != "no" _mld6query= mld6query _rip6query= rip6query _route6d= route6d _traceroute6= traceroute6 .endif .if ${MK_IPX} != "no" _IPXrouted= IPXrouted .endif .if ${MK_NIS} != "no" _rpc.yppasswdd= rpc.yppasswdd _rpc.ypupdated= rpc.ypupdated _rpc.ypxfrd= rpc.ypxfrd _ypbind= ypbind _yp_mkdb= yp_mkdb _yppoll= yppoll _yppush= yppush _ypserv= ypserv _ypset= ypset .endif .if ${MK_AUTHPF} != "no" _authpf= authpf .endif .if ${MK_LPR} != "no" _lpr= lpr .endif .if ${MK_SENDMAIL} != "no" _editmap= editmap _mailstats= mailstats _makemap= makemap _praliases= praliases _sendmail= sendmail .endif .if ${MK_USB} != "no" _usbdevs= usbdevs .endif .if ${MACHINE_ARCH} == "arm" _kgmon= kgmon .endif .if ${MACHINE_ARCH} == "i386" _apm= apm _apmd= apmd _asf= asf _btxld= btxld .if ${MK_I4B} != "no" _i4b= i4b .endif _kgmon= kgmon _kgzip= kgzip _lptcontrol= lptcontrol .if ${MK_NCP} != "no" _mount_nwfs= mount_nwfs .endif _mount_smbfs= mount_smbfs _mptable= mptable _ndiscvt= ndiscvt _pccard= pccard _pnpinfo= pnpinfo .if ${MK_LIBPTHREAD} != "no" _pppctl= pppctl .endif _sicontrol= sicontrol _spkrtest= spkrtest _zzz= zzz .if ${MACHINE} == "i386" .if ${MK_ACPI} != "no" _acpi= acpi .endif _arlcontrol= arlcontrol _boot0cfg= boot0cfg .if ${MK_PCVT} != "no" _pcvt= pcvt .endif _wlconfig= wlconfig .elif ${MACHINE} == "pc98" _boot98cfg= boot98cfg .endif .endif .if ${MACHINE_ARCH} == "alpha" _elf2exe= elf2exe _pnpinfo= pnpinfo .if ${MK_LIBTHR} != "no" _pppctl= pppctl .endif .endif # kgzip: builds, but missing support files # mptable: broken (not 64 bit clean) # pnpinfo: crashes (not really useful anyway) .if ${MACHINE_ARCH} == "amd64" .if ${MK_ACPI} != "no" _acpi= acpi .endif _asf= asf _boot0cfg= boot0cfg _btxld= btxld _kgmon= kgmon _lptcontrol= lptcontrol .if ${MK_NCP} != "no" _mount_nwfs= mount_nwfs .endif _mount_smbfs= mount_smbfs _mptable= mptable _ndiscvt= ndiscvt .if ${MK_LIBPTHREAD} != "no" _pppctl= pppctl .endif _sicontrol= sicontrol _spkrtest= spkrtest _zzz= zzz .endif .if ${MACHINE_ARCH} == "ia64" .if ${MK_ACPI} != "no" _acpi= acpi .endif _kgmon= kgmon .if ${MK_LIBPTHREAD} != "no" _pppctl= pppctl .endif _zzz= zzz .endif .if ${MACHINE_ARCH} == "powerpc" _mount_smbfs= mount_smbfs .endif .if ${MACHINE_ARCH} == "sparc64" _eeprom= eeprom _ofwdump= ofwdump .if ${MK_LIBTHR} != "no" _pppctl= pppctl .endif .endif .include diff --git a/usr.sbin/cached/Makefile b/usr.sbin/cached/Makefile new file mode 100644 index 000000000000..54783416da87 --- /dev/null +++ b/usr.sbin/cached/Makefile @@ -0,0 +1,19 @@ +# $FreeBSD$ + +PROG=cached +PROGNAME=cached +MAN=cached.conf.5 cached.8 + +WARNS?=2 +SRCS= agent.c cached.c cachedcli.c cachelib.c cacheplcs.c debug.c log.c \ + config.c query.c mp_ws_query.c mp_rs_query.c singletons.c protocol.c \ + parser.c +CFLAGS+= -DCONFIG_PATH="\"${PREFIX}/etc/cached.conf\"" +DPADD+=${LIBM} ${LIBPTHREAD} ${LIBUTIL} +LDADD+=${LIBM} ${LIBPTHREAD} ${LIBUTIL} +LDFLAGS+= -Xlinker --export-dynamic + +.PATH: ${.CURDIR}/agents +.include "agents/Makefile.inc" +.include "../Makefile.inc" +.include diff --git a/usr.sbin/cached/agent.c b/usr.sbin/cached/agent.c new file mode 100644 index 000000000000..2d58ef1b48c8 --- /dev/null +++ b/usr.sbin/cached/agent.c @@ -0,0 +1,127 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "agent.h" +#include "debug.h" + +static int +agent_cmp_func(const void *a1, const void *a2) +{ + struct agent const *ap1 = *((struct agent const **)a1); + struct agent const *ap2 = *((struct agent const **)a2); + int res; + + res = strcmp(ap1->name, ap2->name); + if (res == 0) { + if (ap1->type == ap2->type) + res = 0; + else if (ap1->type < ap2->type) + res = -1; + else + res = 1; + } + + return (res); +} + +struct agent_table * +init_agent_table() +{ + struct agent_table *retval; + + TRACE_IN(init_agent_table); + retval = (struct agent_table *)malloc(sizeof(struct agent_table)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct agent_table)); + + TRACE_OUT(init_agent_table); + return (retval); +} + +void +register_agent(struct agent_table *at, struct agent *a) +{ + struct agent **new_agents; + size_t new_agents_num; + + TRACE_IN(register_agent); + assert(at != NULL); + assert(a != NULL); + new_agents_num = at->agents_num + 1; + new_agents = (struct agent **)malloc(sizeof(struct agent *) * + new_agents_num); + assert(new_agents != NULL); + memcpy(new_agents, at->agents, at->agents_num * sizeof(struct agent *)); + new_agents[new_agents_num - 1] = a; + qsort(new_agents, new_agents_num, sizeof(struct agent *), + agent_cmp_func); + + free(at->agents); + at->agents = new_agents; + at->agents_num = new_agents_num; + TRACE_OUT(register_agent); +} + +struct agent * +find_agent(struct agent_table *at, const char *name, enum agent_type type) +{ + struct agent **res; + struct agent model, *model_p; + + TRACE_IN(find_agent); + model.name = (char *)name; + model.type = type; + model_p = &model; + res = bsearch(&model_p, at->agents, at->agents_num, + sizeof(struct agent *), agent_cmp_func); + + TRACE_OUT(find_agent); + return ( res == NULL ? NULL : *res); +} + +void +destroy_agent_table(struct agent_table *at) +{ + size_t i; + + TRACE_IN(destroy_agent_table); + assert(at != NULL); + for (i = 0; i < at->agents_num; ++i) { + free(at->agents[i]->name); + free(at->agents[i]); + } + + free(at->agents); + free(at); + TRACE_OUT(destroy_agent_table); +} diff --git a/usr.sbin/cached/agent.h b/usr.sbin/cached/agent.h new file mode 100644 index 000000000000..5dc368d49612 --- /dev/null +++ b/usr.sbin/cached/agent.h @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_AGENT_H__ +#define __CACHED_AGENT_H__ + +/* + * Agents are used to perform the actual lookups from the caching daemon. + * There are two types of daemons: for common requests and for multipart + * requests. + * All agents are stored in the agents table, which is the singleton. + */ + +enum agent_type { + COMMON_AGENT = 0, + MULTIPART_AGENT = 1 +}; + +struct agent { + char *name; + enum agent_type type; +}; + +struct common_agent { + struct agent parent; + int (*lookup_func)(const char *, size_t, char **, size_t *); +}; + +struct multipart_agent { + struct agent parent; + void *(*mp_init_func)(); + int (*mp_lookup_func)(char **, size_t *, void *); + void (*mp_destroy_func)(void *); +}; + +struct agent_table { + struct agent **agents; + size_t agents_num; +}; + +extern struct agent_table *init_agent_table(); +extern void register_agent(struct agent_table *, struct agent *); +extern struct agent *find_agent(struct agent_table *, const char *, + enum agent_type); +extern void destroy_agent_table(struct agent_table *); + +#endif diff --git a/usr.sbin/cached/agents/Makefile.inc b/usr.sbin/cached/agents/Makefile.inc new file mode 100644 index 000000000000..1be32e1d986f --- /dev/null +++ b/usr.sbin/cached/agents/Makefile.inc @@ -0,0 +1,3 @@ +# $FreeBSD$ + +SRCS += passwd.c group.c services.c diff --git a/usr.sbin/cached/agents/group.c b/usr.sbin/cached/agents/group.c new file mode 100644 index 000000000000..b9190be44849 --- /dev/null +++ b/usr.sbin/cached/agents/group.c @@ -0,0 +1,262 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include "../debug.h" +#include "passwd.h" + +static int group_marshal_func(struct group *, char *, size_t *); +static int group_lookup_func(const char *, size_t, char **, size_t *); +static void *group_mp_init_func(); +static int group_mp_lookup_func(char **, size_t *, void *); +static void group_mp_destroy_func(void *); + +static int +group_marshal_func(struct group *grp, char *buffer, size_t *buffer_size) +{ + struct group new_grp; + size_t desired_size, size, mem_size; + char *p, **mem; + + TRACE_IN(group_marshal_func); + desired_size = ALIGNBYTES + sizeof(struct group) + sizeof(char *); + + if (grp->gr_name != NULL) + desired_size += strlen(grp->gr_name) + 1; + if (grp->gr_passwd != NULL) + desired_size += strlen(grp->gr_passwd) + 1; + + if (grp->gr_mem != NULL) { + mem_size = 0; + for (mem = grp->gr_mem; *mem; ++mem) { + desired_size += strlen(*mem) + 1; + ++mem_size; + } + + desired_size += ALIGNBYTES + (mem_size + 1) * sizeof(char *); + } + + if ((desired_size > *buffer_size) || (buffer == NULL)) { + *buffer_size = desired_size; + TRACE_OUT(group_marshal_func); + return (NS_RETURN); + } + + memcpy(&new_grp, grp, sizeof(struct group)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct group) + sizeof(char *); + memcpy(buffer + sizeof(struct group), &p, sizeof(char *)); + p = (char *)ALIGN(p); + + if (new_grp.gr_name != NULL) { + size = strlen(new_grp.gr_name); + memcpy(p, new_grp.gr_name, size); + new_grp.gr_name = p; + p += size + 1; + } + + if (new_grp.gr_passwd != NULL) { + size = strlen(new_grp.gr_passwd); + memcpy(p, new_grp.gr_passwd, size); + new_grp.gr_passwd = p; + p += size + 1; + } + + if (new_grp.gr_mem != NULL) { + p = (char *)ALIGN(p); + memcpy(p, new_grp.gr_mem, sizeof(char *) * mem_size); + new_grp.gr_mem = (char **)p; + p += sizeof(char *) * (mem_size + 1); + + for (mem = new_grp.gr_mem; *mem; ++mem) { + size = strlen(*mem); + memcpy(p, *mem, size); + *mem = p; + p += size + 1; + } + } + + memcpy(buffer, &new_grp, sizeof(struct group)); + TRACE_OUT(group_marshal_func); + return (NS_SUCCESS); +} + +static int +group_lookup_func(const char *key, size_t key_size, char **buffer, + size_t *buffer_size) +{ + enum nss_lookup_type lookup_type; + char *name; + size_t size; + gid_t gid; + + struct group *result; + + TRACE_IN(group_lookup_func); + assert(buffer != NULL); + assert(buffer_size != NULL); + + if (key_size < sizeof(enum nss_lookup_type)) { + TRACE_OUT(group_lookup_func); + return (NS_UNAVAIL); + } + memcpy(&lookup_type, key, sizeof(enum nss_lookup_type)); + + switch (lookup_type) { + case nss_lt_name: + size = key_size - sizeof(enum nss_lookup_type) + 1; + name = (char *)malloc(size); + assert(name != NULL); + memset(name, 0, size); + memcpy(name, key + sizeof(enum nss_lookup_type), size - 1); + break; + case nss_lt_id: + if (key_size < sizeof(enum nss_lookup_type) + + sizeof(gid_t)) { + TRACE_OUT(passwd_lookup_func); + return (NS_UNAVAIL); + } + + memcpy(&gid, key + sizeof(enum nss_lookup_type), sizeof(gid_t)); + break; + default: + TRACE_OUT(group_lookup_func); + return (NS_UNAVAIL); + } + + switch (lookup_type) { + case nss_lt_name: + TRACE_STR(name); + result = getgrnam(name); + free(name); + break; + case nss_lt_id: + result = getgrgid(gid); + break; + default: + /* SHOULD NOT BE REACHED */ + break; + } + + if (result != NULL) { + group_marshal_func(result, NULL, buffer_size); + *buffer = (char *)malloc(*buffer_size); + assert(*buffer != NULL); + group_marshal_func(result, *buffer, buffer_size); + } + + TRACE_OUT(group_lookup_func); + return (result == NULL ? NS_NOTFOUND : NS_SUCCESS); +} + +static void * +group_mp_init_func() +{ + TRACE_IN(group_mp_init_func); + setgrent(); + TRACE_OUT(group_mp_init_func); + + return (NULL); +} + +static int +group_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata) +{ + struct group *result; + + TRACE_IN(group_mp_lookup_func); + result = getgrent(); + if (result != NULL) { + group_marshal_func(result, NULL, buffer_size); + *buffer = (char *)malloc(*buffer_size); + assert(*buffer != NULL); + group_marshal_func(result, *buffer, buffer_size); + } + + TRACE_OUT(group_mp_lookup_func); + return (result == NULL ? NS_NOTFOUND : NS_SUCCESS); +} + +static void +group_mp_destroy_func(void *mdata) +{ + TRACE_IN(group_mp_destroy_func); + TRACE_OUT(group_mp_destroy_func); +} + +struct agent * +init_group_agent() +{ + struct common_agent *retval; + + TRACE_IN(init_group_agent); + retval = (struct common_agent *)malloc(sizeof(struct common_agent)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct common_agent)); + + retval->parent.name = strdup("group"); + assert(retval->parent.name != NULL); + + retval->parent.type = COMMON_AGENT; + retval->lookup_func = group_lookup_func; + + TRACE_OUT(init_group_agent); + return ((struct agent *)retval); +} + +struct agent * +init_group_mp_agent() +{ + struct multipart_agent *retval; + + TRACE_IN(init_group_mp_agent); + retval = (struct multipart_agent *)malloc( + sizeof(struct multipart_agent)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct multipart_agent)); + + retval->parent.name = strdup("group"); + retval->parent.type = MULTIPART_AGENT; + retval->mp_init_func = group_mp_init_func; + retval->mp_lookup_func = group_mp_lookup_func; + retval->mp_destroy_func = group_mp_destroy_func; + assert(retval->parent.name != NULL); + + TRACE_OUT(init_group_mp_agent); + return ((struct agent *)retval); +} diff --git a/usr.sbin/cached/agents/group.h b/usr.sbin/cached/agents/group.h new file mode 100644 index 000000000000..e6c73973f1d2 --- /dev/null +++ b/usr.sbin/cached/agents/group.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include "../agent.h" + +extern struct agent *init_group_agent(); +extern struct agent *init_group_mp_agent(); diff --git a/usr.sbin/cached/agents/passwd.c b/usr.sbin/cached/agents/passwd.c new file mode 100644 index 000000000000..50c55babc633 --- /dev/null +++ b/usr.sbin/cached/agents/passwd.c @@ -0,0 +1,269 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include "../debug.h" +#include "passwd.h" + +static int passwd_marshal_func(struct passwd *, char *, size_t *); +static int passwd_lookup_func(const char *, size_t, char **, size_t *); +static void *passwd_mp_init_func(); +static int passwd_mp_lookup_func(char **, size_t *, void *); +static void passwd_mp_destroy_func(void *mdata); + +static int +passwd_marshal_func(struct passwd *pwd, char *buffer, size_t *buffer_size) +{ + char *p; + struct passwd new_pwd; + size_t desired_size, size; + + TRACE_IN(passwd_marshal_func); + desired_size = sizeof(struct passwd) + sizeof(char *) + + strlen(pwd->pw_name) + 1; + if (pwd->pw_passwd != NULL) + desired_size += strlen(pwd->pw_passwd) + 1; + if (pwd->pw_class != NULL) + desired_size += strlen(pwd->pw_class) + 1; + if (pwd->pw_gecos != NULL) + desired_size += strlen(pwd->pw_gecos) + 1; + if (pwd->pw_dir != NULL) + desired_size += strlen(pwd->pw_dir) + 1; + if (pwd->pw_shell != NULL) + desired_size += strlen(pwd->pw_shell) + 1; + + if ((*buffer_size < desired_size) || (buffer == NULL)) { + *buffer_size = desired_size; + TRACE_OUT(passwd_marshal_func); + return (NS_RETURN); + } + + memcpy(&new_pwd, pwd, sizeof(struct passwd)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct passwd) + sizeof(char *); + memcpy(buffer + sizeof(struct passwd), &p, sizeof(char *)); + + if (new_pwd.pw_name != NULL) { + size = strlen(new_pwd.pw_name); + memcpy(p, new_pwd.pw_name, size); + new_pwd.pw_name = p; + p += size + 1; + } + + if (new_pwd.pw_passwd != NULL) { + size = strlen(new_pwd.pw_passwd); + memcpy(p, new_pwd.pw_passwd, size); + new_pwd.pw_passwd = p; + p += size + 1; + } + + if (new_pwd.pw_class != NULL) { + size = strlen(new_pwd.pw_class); + memcpy(p, new_pwd.pw_class, size); + new_pwd.pw_class = p; + p += size + 1; + } + + if (new_pwd.pw_gecos != NULL) { + size = strlen(new_pwd.pw_gecos); + memcpy(p, new_pwd.pw_gecos, size); + new_pwd.pw_gecos = p; + p += size + 1; + } + + if (new_pwd.pw_dir != NULL) { + size = strlen(new_pwd.pw_dir); + memcpy(p, new_pwd.pw_dir, size); + new_pwd.pw_dir = p; + p += size + 1; + } + + if (new_pwd.pw_shell != NULL) { + size = strlen(new_pwd.pw_shell); + memcpy(p, new_pwd.pw_shell, size); + new_pwd.pw_shell = p; + p += size + 1; + } + + memcpy(buffer, &new_pwd, sizeof(struct passwd)); + TRACE_OUT(passwd_marshal_func); + return (NS_SUCCESS); +} + +static int +passwd_lookup_func(const char *key, size_t key_size, char **buffer, + size_t *buffer_size) +{ + enum nss_lookup_type lookup_type; + char *login; + size_t size; + uid_t uid; + + struct passwd *result; + + TRACE_IN(passwd_lookup_func); + assert(buffer != NULL); + assert(buffer_size != NULL); + + if (key_size < sizeof(enum nss_lookup_type)) { + TRACE_OUT(passwd_lookup_func); + return (NS_UNAVAIL); + } + memcpy(&lookup_type, key, sizeof(enum nss_lookup_type)); + + switch (lookup_type) { + case nss_lt_name: + size = key_size - sizeof(enum nss_lookup_type) + 1; + login = (char *)malloc(size); + assert(login != NULL); + memset(login, 0, size); + memcpy(login, key + sizeof(enum nss_lookup_type), size - 1); + break; + case nss_lt_id: + if (key_size < sizeof(enum nss_lookup_type) + + sizeof(uid_t)) { + TRACE_OUT(passwd_lookup_func); + return (NS_UNAVAIL); + } + + memcpy(&uid, key + sizeof(enum nss_lookup_type), sizeof(uid_t)); + break; + default: + TRACE_OUT(passwd_lookup_func); + return (NS_UNAVAIL); + } + + switch (lookup_type) { + case nss_lt_name: + result = getpwnam(login); + free(login); + break; + case nss_lt_id: + result = getpwuid(uid); + break; + default: + /* SHOULD NOT BE REACHED */ + break; + } + + if (result != NULL) { + passwd_marshal_func(result, NULL, buffer_size); + *buffer = (char *)malloc(*buffer_size); + assert(*buffer != NULL); + passwd_marshal_func(result, *buffer, buffer_size); + } + + TRACE_OUT(passwd_lookup_func); + return (result == NULL ? NS_NOTFOUND : NS_SUCCESS); +} + +static void * +passwd_mp_init_func() +{ + TRACE_IN(passwd_mp_init_func); + setpwent(); + TRACE_OUT(passwd_mp_init_func); + + return (NULL); +} + +static int +passwd_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata) +{ + struct passwd *result; + + TRACE_IN(passwd_mp_lookup_func); + result = getpwent(); + if (result != NULL) { + passwd_marshal_func(result, NULL, buffer_size); + *buffer = (char *)malloc(*buffer_size); + assert(*buffer != NULL); + passwd_marshal_func(result, *buffer, buffer_size); + } + + TRACE_OUT(passwd_mp_lookup_func); + return (result == NULL ? NS_NOTFOUND : NS_SUCCESS); +} + +static void +passwd_mp_destroy_func(void *mdata) +{ + TRACE_IN(passwd_mp_destroy_func); + TRACE_OUT(passwd_mp_destroy_func); +} + +struct agent * +init_passwd_agent() +{ + struct common_agent *retval; + + TRACE_IN(init_passwd_agent); + retval = (struct common_agent *)malloc(sizeof(struct common_agent)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct common_agent)); + + retval->parent.name = strdup("passwd"); + assert(retval->parent.name != NULL); + + retval->parent.type = COMMON_AGENT; + retval->lookup_func = passwd_lookup_func; + + TRACE_OUT(init_passwd_agent); + return ((struct agent *)retval); +} + +struct agent * +init_passwd_mp_agent() +{ + struct multipart_agent *retval; + + TRACE_IN(init_passwd_mp_agent); + retval = (struct multipart_agent *)malloc( + sizeof(struct multipart_agent)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct multipart_agent)); + + retval->parent.name = strdup("passwd"); + retval->parent.type = MULTIPART_AGENT; + retval->mp_init_func = passwd_mp_init_func; + retval->mp_lookup_func = passwd_mp_lookup_func; + retval->mp_destroy_func = passwd_mp_destroy_func; + assert(retval->parent.name != NULL); + + TRACE_OUT(init_passwd_mp_agent); + return ((struct agent *)retval); +} diff --git a/usr.sbin/cached/agents/passwd.h b/usr.sbin/cached/agents/passwd.h new file mode 100644 index 000000000000..956a50d78008 --- /dev/null +++ b/usr.sbin/cached/agents/passwd.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include "../agent.h" + +extern struct agent *init_passwd_agent(); +extern struct agent *init_passwd_mp_agent(); diff --git a/usr.sbin/cached/agents/services.c b/usr.sbin/cached/agents/services.c new file mode 100644 index 000000000000..36833960f892 --- /dev/null +++ b/usr.sbin/cached/agents/services.c @@ -0,0 +1,286 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include "../debug.h" +#include "services.h" + +static int services_marshal_func(struct servent *, char *, size_t *); +static int services_lookup_func(const char *, size_t, char **, size_t *); +static void *services_mp_init_func(); +static int services_mp_lookup_func(char **, size_t *, void *); +static void services_mp_destroy_func(void *); + +static int +services_marshal_func(struct servent *serv, char *buffer, size_t *buffer_size) +{ + struct servent new_serv; + size_t desired_size; + char **alias; + char *p; + size_t size; + size_t aliases_size; + + TRACE_IN(services_marshal_func); + desired_size = ALIGNBYTES + sizeof(struct servent) + sizeof(char *); + if (serv->s_name != NULL) + desired_size += strlen(serv->s_name) + 1; + if (serv->s_proto != NULL) + desired_size += strlen(serv->s_proto) + 1; + + aliases_size = 0; + if (serv->s_aliases != NULL) { + for (alias = serv->s_aliases; *alias; ++alias) { + desired_size += strlen(*alias) + 1; + ++aliases_size; + } + + desired_size += ALIGNBYTES + sizeof(char *) * + (aliases_size + 1); + } + + if ((*buffer_size < desired_size) || (buffer == NULL)) { + *buffer_size = desired_size; + TRACE_OUT(services_marshal_func); + return (NS_RETURN); + } + + memcpy(&new_serv, serv, sizeof(struct servent)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct servent) + sizeof(char *); + memcpy(buffer + sizeof(struct servent), &p, sizeof(char *)); + p = (char *)ALIGN(p); + + if (new_serv.s_name != NULL) { + size = strlen(new_serv.s_name); + memcpy(p, new_serv.s_name, size); + new_serv.s_name = p; + p += size + 1; + } + + if (new_serv.s_proto != NULL) { + size = strlen(new_serv.s_proto); + memcpy(p, new_serv.s_proto, size); + new_serv.s_proto = p; + p += size + 1; + } + + if (new_serv.s_aliases != NULL) { + p = (char *)ALIGN(p); + memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size); + new_serv.s_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); + + for (alias = new_serv.s_aliases; *alias; ++alias) { + size = strlen(*alias); + memcpy(p, *alias, size); + *alias = p; + p += size + 1; + } + } + + memcpy(buffer, &new_serv, sizeof(struct servent)); + TRACE_OUT(services_marshal_func); + return (NS_SUCCESS); +} + +static int +services_lookup_func(const char *key, size_t key_size, char **buffer, + size_t *buffer_size) +{ + enum nss_lookup_type lookup_type; + char *name = NULL; + char *proto = NULL; + size_t size, size2; + int port; + + struct servent *result; + + TRACE_IN(services_lookup_func); + + assert(buffer != NULL); + assert(buffer_size != NULL); + + if (key_size < sizeof(enum nss_lookup_type)) { + TRACE_OUT(passwd_lookup_func); + return (NS_UNAVAIL); + } + memcpy(&lookup_type, key, sizeof(enum nss_lookup_type)); + + switch (lookup_type) { + case nss_lt_name: + size = key_size - sizeof(enum nss_lookup_type) + 1; + name = (char *)malloc(size); + assert(name != NULL); + memset(name, 0, size); + memcpy(name, key + sizeof(enum nss_lookup_type), size - 1); + + size2 = strlen(name) + 1; + if (size2 < size) { + proto = strchr(name, '\0'); + if (strrchr(name, '\0') > proto) + ++proto ; + else + proto = NULL; + } + break; + case nss_lt_id: + if (key_size < sizeof(enum nss_lookup_type) + + sizeof(int)) { + TRACE_OUT(passwd_lookup_func); + return (NS_UNAVAIL); + } + + memcpy(&port, key + sizeof(enum nss_lookup_type), + sizeof(int)); + + size = key_size - sizeof(enum nss_lookup_type) + sizeof(int); + if (size > 0) { + proto = (char *)malloc(size + 1); + assert(proto != NULL); + memset(proto, size + 1, 0); + memcpy(proto, key + sizeof(enum nss_lookup_type) + + sizeof(int), size); + } + break; + default: + TRACE_OUT(passwd_lookup_func); + return (NS_UNAVAIL); + } + + switch (lookup_type) { + case nss_lt_name: + result = getservbyname(name, proto); + free(name); + break; + case nss_lt_id: + result = getservbyport(port, proto); + free(proto); + break; + default: + /* SHOULD NOT BE REACHED */ + break; + } + + if (result != NULL) { + services_marshal_func(result, NULL, buffer_size); + *buffer = (char *)malloc(*buffer_size); + assert(*buffer != NULL); + services_marshal_func(result, *buffer, buffer_size); + } + + TRACE_OUT(services_lookup_func); + return (result == NULL ? NS_NOTFOUND : NS_SUCCESS); +} + +static void * +services_mp_init_func() +{ + TRACE_IN(services_mp_init_func); + setservent(0); + TRACE_OUT(services_mp_init_func); + + return (NULL); +} + +static int +services_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata) +{ + struct servent *result; + + TRACE_IN(services_mp_lookup_func); + result = getservent(); + if (result != NULL) { + services_marshal_func(result, NULL, buffer_size); + *buffer = (char *)malloc(*buffer_size); + assert(*buffer != NULL); + services_marshal_func(result, *buffer, buffer_size); + } + + TRACE_OUT(services_mp_lookup_func); + return (result == NULL ? NS_NOTFOUND : NS_SUCCESS); +} + +static void +services_mp_destroy_func(void *mdata) +{ + TRACE_IN(services_mp_destroy_func); + TRACE_OUT(services_mp_destroy_func); +} + +struct agent * +init_services_agent() +{ + struct common_agent *retval; + TRACE_IN(init_services_agent); + + retval = (struct common_agent *)malloc(sizeof(struct common_agent)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct common_agent)); + + retval->parent.name = strdup("services"); + assert(retval->parent.name != NULL); + + retval->parent.type = COMMON_AGENT; + retval->lookup_func = services_lookup_func; + + TRACE_OUT(init_services_agent); + return ((struct agent *)retval); +} + +struct agent * +init_services_mp_agent() +{ + struct multipart_agent *retval; + + TRACE_IN(init_services_mp_agent); + retval = (struct multipart_agent *)malloc( + sizeof(struct multipart_agent)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct multipart_agent)); + + retval->parent.name = strdup("services"); + retval->parent.type = MULTIPART_AGENT; + retval->mp_init_func = services_mp_init_func; + retval->mp_lookup_func = services_mp_lookup_func; + retval->mp_destroy_func = services_mp_destroy_func; + assert(retval->parent.name != NULL); + + TRACE_OUT(init_services_mp_agent); + return ((struct agent *)retval); +} diff --git a/usr.sbin/cached/agents/services.h b/usr.sbin/cached/agents/services.h new file mode 100644 index 000000000000..0b77c87a9979 --- /dev/null +++ b/usr.sbin/cached/agents/services.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include "../agent.h" + +extern struct agent *init_services_agent(); +extern struct agent *init_services_mp_agent(); diff --git a/usr.sbin/cached/cached.8 b/usr.sbin/cached/cached.8 new file mode 100644 index 000000000000..5a8693ac453a --- /dev/null +++ b/usr.sbin/cached/cached.8 @@ -0,0 +1,148 @@ +.\" Copyright (c) 2005 Michael Bushkov +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" + +.Dd October 20, 2005 +.Dt CACHED 8 +.Os +.Sh NAME +.Nm cached +.Nd caching server daemon +.Sh SYNOPSIS +.Nm +.Op Fl nst +.Op Fl i Ar cachename +.Op Fl I Ar cachename +.Sh DESCRIPTION +.Nm +- is the system caching daemon. +It can cache almost all types of data and is basically intended to be used +with +.Pa nsswitch +subsystem. +The cache is actually per-user. It means that each user can work only with the +cached data, that were cached by him and can't poison the cache of other users. +.Nm +supports 2 types of caching: +.Bl -tag -width Pair +.It Sy Type +.Sy Description +.It Common caching +Each cached element is the the key+value pair. +This type of caching supports policies, which are applied, when maximum +number of cached elements is exceeded. +3 policies are available: +.Pa FIFO +(first in - first out), +.Pa LRU +(least recently used) and +.Pa LFU +(least frequently used). +This type of caching is used with +.Fn getXXXbyname +- like functions. +.It Multipart caching +Each cached element is the part of the elements sequence. +This type of caching is intended to be used with +.Fn getXXXent +- like functions. +.El +.Pp +.Nm +is able not only to cache elements, but to perform the actual nsswitch +lookups by itself. To enable this feature use +.Pa perform-actual-lookups +parameter in the +.Xr cached.conf 5 +.Pp +.Nm +recognizes the following runtime options: +.Bl -tag -width indent +.It Fl n +Do not daemonize. +.Nm +doesn't fork and doesn't disconnect itself from the terminal. +.It Fl s +Single-threaded mode. +Forces using only one thread for all processing purposes (it overrides +the +.Pa threads +parameter in the +.Xr cached.conf 5 +file). +.It Fl t +Trace mode. +All trace messages would be written to the stdout. +This mode is usually used with +.Fl n +and +.Fl s +flags for debugging purposes. +.It Fl i Ar cachename +Invalidates personal cache. When specified, +.Nm +acts as the administration tool. It asks the already +running +.Nm +to invalidate the specified part of the cache of the +calling user. For example, you may want sometimes +to invalidate your +.Pa hosts +cache. You can specify +.Pa all +as the +.Pa cachename +to invalidate your personal cache as a whole. You +can't use this option for the cachename, for which +the +.Pa perform-actual-lookups +option is enabled. +.It Fl I Ar cachename +Invalidates the cache for every user. When specified, +.Nm +acts as the administration tool. It asks the already +running +.Nm +to invalidate the specified part of the cache for +every user. You can specify +.Pa all +as the +.Pa cachename +to invalidate the whole cache. Only root can use this +option. +.El +.Sh FILES +.Xr cached.conf 5 +.Sh SEE ALSO +.Xr cached.conf 5 +.Xr nsswitch.conf 5 +.Xr nsdispatch 3 +.Sh "AUTHORS" +Michael Bushkov +.Aq bushman@rsu.ru +.Sh "BUGS" +To report bugs or suggestions please mail me +.Aq bushman@rsu.ru diff --git a/usr.sbin/cached/cached.c b/usr.sbin/cached/cached.c new file mode 100644 index 000000000000..3219d22a7e1a --- /dev/null +++ b/usr.sbin/cached/cached.c @@ -0,0 +1,884 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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 thereg + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "agents/passwd.h" +#include "agents/group.h" +#include "agents/services.h" +#include "cachedcli.h" +#include "cachelib.h" +#include "config.h" +#include "debug.h" +#include "log.h" +#include "parser.h" +#include "query.h" +#include "singletons.h" + +#ifndef CONFIG_PATH +#define CONFIG_PATH "/etc/cached.conf" +#endif +#define DEFAULT_CONFIG_PATH "cached.conf" + +#define MAX_SOCKET_IO_SIZE 4096 + +struct processing_thread_args { + cache the_cache; + struct configuration *the_configuration; + struct runtime_env *the_runtime_env; +}; + +static void accept_connection(struct kevent *, struct runtime_env *, + struct configuration *); +static void destroy_cache_(cache); +static void destroy_runtime_env(struct runtime_env *); +static cache init_cache_(struct configuration *); +static struct runtime_env *init_runtime_env(struct configuration *); +static void print_version_info(void); +static void processing_loop(cache, struct runtime_env *, + struct configuration *); +static void process_socket_event(struct kevent *, struct runtime_env *, + struct configuration *); +static void process_timer_event(struct kevent *, struct runtime_env *, + struct configuration *); +static void *processing_thread(void *); +static void usage(void); + +void get_time_func(struct timeval *); + +static void +print_version_info(void) +{ + TRACE_IN(print_version_info); + printf("cached v0.2 (20 Oct 2005)\nwas developed during SoC 2005\n"); + TRACE_OUT(print_version_info); +} + +static void +usage(void) +{ + fprintf(stderr,"usage: cached [-nstiId]\n"); + exit(1); +} + +static cache +init_cache_(struct configuration *config) +{ + struct cache_params params; + cache retval; + + struct configuration_entry *config_entry; + size_t size, i; + int res; + + TRACE_IN(init_cache_); + + memset(¶ms, 0, sizeof(struct cache_params)); + params.get_time_func = get_time_func; + retval = init_cache(¶ms); + + size = configuration_get_entries_size(config); + for (i = 0; i < size; ++i) { + config_entry = configuration_get_entry(config, i); + /* + * We should register common entries now - multipart entries + * would be registered automatically during the queries. + */ + res = register_cache_entry(retval, (struct cache_entry_params *) + &config_entry->positive_cache_params); + config_entry->positive_cache_entry = find_cache_entry(retval, + config_entry->positive_cache_params.entry_name); + assert(config_entry->positive_cache_entry != + INVALID_CACHE_ENTRY); + + res = register_cache_entry(retval, (struct cache_entry_params *) + &config_entry->negative_cache_params); + config_entry->negative_cache_entry = find_cache_entry(retval, + config_entry->negative_cache_params.entry_name); + assert(config_entry->negative_cache_entry != + INVALID_CACHE_ENTRY); + } + + LOG_MSG_2("cache", "cache was successfully initialized"); + TRACE_OUT(init_cache_); + return (retval); +} + +static void +destroy_cache_(cache the_cache) +{ + TRACE_IN(destroy_cache_); + destroy_cache(the_cache); + TRACE_OUT(destroy_cache_); +} + +/* + * Socket and kqueues are prepared here. We have one global queue for both + * socket and timers events. + */ +static struct runtime_env * +init_runtime_env(struct configuration *config) +{ + int serv_addr_len; + struct sockaddr_un serv_addr; + + struct kevent eventlist; + struct timespec timeout; + + struct runtime_env *retval; + + TRACE_IN(init_runtime_env); + retval = (struct runtime_env *)malloc(sizeof(struct runtime_env)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct runtime_env)); + + retval->sockfd = socket(PF_LOCAL, SOCK_STREAM, 0); + + if (config->force_unlink == 1) + unlink(config->socket_path); + + memset(&serv_addr, 0, sizeof(struct sockaddr_un)); + serv_addr.sun_family = PF_LOCAL; + strncpy(serv_addr.sun_path, config->socket_path, + sizeof(serv_addr.sun_path)); + serv_addr_len = sizeof(serv_addr.sun_family) + + strlen(serv_addr.sun_path) + 1; + + if (bind(retval->sockfd, (struct sockaddr *)&serv_addr, + serv_addr_len) == -1) { + close(retval->sockfd); + free(retval); + + LOG_ERR_2("runtime environment", "can't bind socket to path: " + "%s", config->socket_path); + TRACE_OUT(init_runtime_env); + return (NULL); + } + LOG_MSG_2("runtime environment", "using socket %s", + config->socket_path); + + /* + * Here we're marking socket as non-blocking and setting its backlog + * to the maximum value + */ + chmod(config->socket_path, config->socket_mode); + listen(retval->sockfd, -1); + fcntl(retval->sockfd, F_SETFL, O_NONBLOCK); + + retval->queue = kqueue(); + assert(retval->queue != -1); + + EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD | EV_ONESHOT, + 0, 0, 0); + memset(&timeout, 0, sizeof(struct timespec)); + kevent(retval->queue, &eventlist, 1, NULL, 0, &timeout); + + LOG_MSG_2("runtime environment", "successfully initialized"); + TRACE_OUT(init_runtime_env); + return (retval); +} + +static void +destroy_runtime_env(struct runtime_env *env) +{ + TRACE_IN(destroy_runtime_env); + close(env->queue); + close(env->sockfd); + free(env); + TRACE_OUT(destroy_runtime_env); +} + +static void +accept_connection(struct kevent *event_data, struct runtime_env *env, + struct configuration *config) +{ + struct kevent eventlist[2]; + struct timespec timeout; + struct query_state *qstate; + + int fd; + int res; + + uid_t euid; + gid_t egid; + + TRACE_IN(accept_connection); + fd = accept(event_data->ident, NULL, NULL); + if (fd == -1) { + LOG_ERR_2("accept_connection", "error %d during accept()", + errno); + TRACE_OUT(accept_connection); + return; + } + + if (getpeereid(fd, &euid, &egid) != 0) { + LOG_ERR_2("accept_connection", "error %d during getpeereid()", + errno); + TRACE_OUT(accept_connection); + return; + } + + qstate = init_query_state(fd, sizeof(int), euid, egid); + if (qstate == NULL) { + LOG_ERR_2("accept_connection", "can't init query_state"); + TRACE_OUT(accept_connection); + return; + } + + memset(&timeout, 0, sizeof(struct timespec)); + EV_SET(&eventlist[0], fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, + 0, qstate->timeout.tv_sec * 1000, qstate); + EV_SET(&eventlist[1], fd, EVFILT_READ, EV_ADD | EV_ONESHOT, + NOTE_LOWAT, qstate->kevent_watermark, qstate); + res = kevent(env->queue, eventlist, 2, NULL, 0, &timeout); + if (res < 0) + LOG_ERR_2("accept_connection", "kevent error"); + + TRACE_OUT(accept_connection); +} + +static void +process_socket_event(struct kevent *event_data, struct runtime_env *env, + struct configuration *config) +{ + struct kevent eventlist[2]; + struct timeval query_timeout; + struct timespec kevent_timeout; + int nevents; + int eof_res, res; + ssize_t io_res; + struct query_state *qstate; + + TRACE_IN(process_socket_event); + eof_res = event_data->flags & EV_EOF ? 1 : 0; + res = 0; + + memset(&kevent_timeout, 0, sizeof(struct timespec)); + EV_SET(&eventlist[0], event_data->ident, EVFILT_TIMER, EV_DELETE, + 0, 0, NULL); + nevents = kevent(env->queue, eventlist, 1, NULL, 0, &kevent_timeout); + if (nevents == -1) { + if (errno == ENOENT) { + /* the timer is already handling this event */ + TRACE_OUT(process_socket_event); + return; + } else { + /* some other error happened */ + LOG_ERR_2("process_socket_event", "kevent error, errno" + " is %d", errno); + TRACE_OUT(process_socket_event); + return; + } + } + qstate = (struct query_state *)event_data->udata; + + /* + * If the buffer that is to be send/received is too large, + * we send it implicitly, by using query_io_buffer_read and + * query_io_buffer_write functions in the query_state. These functions + * use the temporary buffer, which is later send/received in parts. + * The code below implements buffer splitting/mergind for send/receive + * operations. It also does the actual socket IO operations. + */ + if (((qstate->use_alternate_io == 0) && + (qstate->kevent_watermark <= event_data->data)) || + ((qstate->use_alternate_io != 0) && + (qstate->io_buffer_watermark <= event_data->data))) { + if (qstate->use_alternate_io != 0) { + switch (qstate->io_buffer_filter) { + case EVFILT_READ: + io_res = query_socket_read(qstate, + qstate->io_buffer_p, + qstate->io_buffer_watermark); + if (io_res < 0) { + qstate->use_alternate_io = 0; + qstate->process_func = NULL; + } else { + qstate->io_buffer_p += io_res; + if (qstate->io_buffer_p == + qstate->io_buffer + + qstate->io_buffer_size) { + qstate->io_buffer_p = + qstate->io_buffer; + qstate->use_alternate_io = 0; + } + } + break; + default: + break; + } + } + + if (qstate->use_alternate_io == 0) { + do { + res = qstate->process_func(qstate); + } while ((qstate->kevent_watermark == 0) && + (qstate->process_func != NULL) && + (res == 0)); + + if (res != 0) + qstate->process_func = NULL; + } + + if ((qstate->use_alternate_io != 0) && + (qstate->io_buffer_filter == EVFILT_WRITE)) { + io_res = query_socket_write(qstate, qstate->io_buffer_p, + qstate->io_buffer_watermark); + if (io_res < 0) { + qstate->use_alternate_io = 0; + qstate->process_func = NULL; + } else + qstate->io_buffer_p += io_res; + } + } else { + /* assuming that socket was closed */ + qstate->process_func = NULL; + qstate->use_alternate_io = 0; + } + + if (((qstate->process_func == NULL) && + (qstate->use_alternate_io == 0)) || + (eof_res != 0) || (res != 0)) { + destroy_query_state(qstate); + close(event_data->ident); + TRACE_OUT(process_socket_event); + return; + } + + /* updating the query_state lifetime variable */ + get_time_func(&query_timeout); + query_timeout.tv_usec = 0; + query_timeout.tv_sec -= qstate->creation_time.tv_sec; + if (query_timeout.tv_sec > qstate->timeout.tv_sec) + query_timeout.tv_sec = 0; + else + query_timeout.tv_sec = qstate->timeout.tv_sec - + query_timeout.tv_sec; + + if ((qstate->use_alternate_io != 0) && (qstate->io_buffer_p == + qstate->io_buffer + qstate->io_buffer_size)) + qstate->use_alternate_io = 0; + + if (qstate->use_alternate_io == 0) { + /* + * If we must send/receive the large block of data, + * we should prepare the query_state's io_XXX fields. + * We should also substitute its write_func and read_func + * with the query_io_buffer_write and query_io_buffer_read, + * which will allow us to implicitly send/receive this large + * buffer later (in the subsequent calls to the + * process_socket_event). + */ + if (qstate->kevent_watermark > MAX_SOCKET_IO_SIZE) { + if (qstate->io_buffer != NULL) + free(qstate->io_buffer); + + qstate->io_buffer = (char *)malloc( + qstate->kevent_watermark); + assert(qstate->io_buffer != NULL); + memset(qstate->io_buffer, 0, qstate->kevent_watermark); + + qstate->io_buffer_p = qstate->io_buffer; + qstate->io_buffer_size = qstate->kevent_watermark; + qstate->io_buffer_filter = qstate->kevent_filter; + + qstate->write_func = query_io_buffer_write; + qstate->read_func = query_io_buffer_read; + + if (qstate->kevent_filter == EVFILT_READ) + qstate->use_alternate_io = 1; + + qstate->io_buffer_watermark = MAX_SOCKET_IO_SIZE; + EV_SET(&eventlist[1], event_data->ident, + qstate->kevent_filter, EV_ADD | EV_ONESHOT, + NOTE_LOWAT, MAX_SOCKET_IO_SIZE, qstate); + } else { + EV_SET(&eventlist[1], event_data->ident, + qstate->kevent_filter, EV_ADD | EV_ONESHOT, + NOTE_LOWAT, qstate->kevent_watermark, qstate); + } + } else { + if (qstate->io_buffer + qstate->io_buffer_size - + qstate->io_buffer_p < + MAX_SOCKET_IO_SIZE) { + qstate->io_buffer_watermark = qstate->io_buffer + + qstate->io_buffer_size - qstate->io_buffer_p; + EV_SET(&eventlist[1], event_data->ident, + qstate->io_buffer_filter, + EV_ADD | EV_ONESHOT, NOTE_LOWAT, + qstate->io_buffer_watermark, + qstate); + } else { + qstate->io_buffer_watermark = MAX_SOCKET_IO_SIZE; + EV_SET(&eventlist[1], event_data->ident, + qstate->io_buffer_filter, EV_ADD | EV_ONESHOT, + NOTE_LOWAT, MAX_SOCKET_IO_SIZE, qstate); + } + } + EV_SET(&eventlist[0], event_data->ident, EVFILT_TIMER, + EV_ADD | EV_ONESHOT, 0, query_timeout.tv_sec * 1000, qstate); + kevent(env->queue, eventlist, 2, NULL, 0, &kevent_timeout); + + TRACE_OUT(process_socket_event); +} + +/* + * This routine is called if timer event has been signaled in the kqueue. It + * just closes the socket and destroys the query_state. + */ +static void +process_timer_event(struct kevent *event_data, struct runtime_env *env, + struct configuration *config) +{ + struct query_state *qstate; + + TRACE_IN(process_timer_event); + qstate = (struct query_state *)event_data->udata; + destroy_query_state(qstate); + close(event_data->ident); + TRACE_OUT(process_timer_event); +} + +/* + * Processing loop is the basic processing routine, that forms a body of each + * procssing thread + */ +static void +processing_loop(cache the_cache, struct runtime_env *env, + struct configuration *config) +{ + struct timespec timeout; + const int eventlist_size = 1; + struct kevent eventlist[eventlist_size]; + int nevents, i; + + TRACE_MSG("=> processing_loop"); + memset(&timeout, 0, sizeof(struct timespec)); + memset(&eventlist, 0, sizeof(struct kevent) * eventlist_size); + + for (;;) { + nevents = kevent(env->queue, NULL, 0, eventlist, + eventlist_size, NULL); + /* + * we can only receive 1 event on success + */ + if (nevents == 1) { + struct kevent *event_data; + event_data = &eventlist[0]; + + if (event_data->ident == env->sockfd) { + for (i = 0; i < event_data->data; ++i) + accept_connection(event_data, env, config); + + EV_SET(eventlist, s_runtime_env->sockfd, + EVFILT_READ, EV_ADD | EV_ONESHOT, + 0, 0, 0); + memset(&timeout, 0, + sizeof(struct timespec)); + kevent(s_runtime_env->queue, eventlist, + 1, NULL, 0, &timeout); + + } else { + switch (event_data->filter) { + case EVFILT_READ: + case EVFILT_WRITE: + process_socket_event(event_data, + env, config); + break; + case EVFILT_TIMER: + process_timer_event(event_data, + env, config); + break; + default: + break; + } + } + } else { + /* this branch shouldn't be currently executed */ + } + } + + TRACE_MSG("<= processing_loop"); +} + +/* + * Wrapper above the processing loop function. It sets the thread signal mask + * to avoid SIGPIPE signals (which can happen if the client works incorrectly). + */ +static void * +processing_thread(void *data) +{ + struct processing_thread_args *args; + sigset_t new; + + TRACE_MSG("=> processing_thread"); + args = (struct processing_thread_args *)data; + + sigemptyset(&new); + sigaddset(&new, SIGPIPE); + if (pthread_sigmask(SIG_BLOCK, &new, NULL) != 0) + LOG_ERR_1("processing thread", + "thread can't block the SIGPIPE signal"); + + processing_loop(args->the_cache, args->the_runtime_env, + args->the_configuration); + free(args); + TRACE_MSG("<= processing_thread"); + + return (NULL); +} + +void +get_time_func(struct timeval *time) +{ + struct timespec res; + memset(&res, 0, sizeof(struct timespec)); + clock_gettime(CLOCK_MONOTONIC, &res); + + time->tv_sec = res.tv_sec; + time->tv_usec = 0; +} + +/* + * The idea of _nss_cache_cycle_prevention_function is that nsdispatch will + * search for this symbol in the executable. This symbol is the attribute of + * the caching daemon. So, if it exists, nsdispatch won't try to connect to + * the caching daemon and will just ignore the 'cache' source in the + * nsswitch.conf. This method helps to avoid cycles and organize + * self-performing requests. + */ +void +_nss_cache_cycle_prevention_function(void) +{ +} + +int +main(int argc, char *argv[]) +{ + struct processing_thread_args *thread_args; + pthread_t *threads; + + struct pidfh *pidfile; + pid_t pid; + + char const *config_file; + char const *error_str; + int error_line; + int i, res; + + int trace_mode_enabled; + int force_single_threaded; + int do_not_daemonize; + int clear_user_cache_entries, clear_all_cache_entries; + char *user_config_entry_name, *global_config_entry_name; + int show_statistics; + int daemon_mode, interactive_mode; + + + /* by default all debug messages are omitted */ + TRACE_OFF(); + + /* startup output */ + print_version_info(); + + /* parsing command line arguments */ + trace_mode_enabled = 0; + force_single_threaded = 0; + do_not_daemonize = 0; + clear_user_cache_entries = 0; + clear_all_cache_entries = 0; + show_statistics = 0; + user_config_entry_name = NULL; + global_config_entry_name = NULL; + while ((res = getopt(argc, argv, "nstdi:I:")) != -1) { + switch (res) { + case 'n': + do_not_daemonize = 1; + break; + case 's': + force_single_threaded = 1; + break; + case 't': + trace_mode_enabled = 1; + break; + case 'i': + clear_user_cache_entries = 1; + if (optarg != NULL) + if (strcmp(optarg, "all") != 0) + user_config_entry_name = strdup(optarg); + break; + case 'I': + clear_all_cache_entries = 1; + if (optarg != NULL) + if (strcmp(optarg, "all") != 0) + global_config_entry_name = + strdup(optarg); + break; + case 'd': + show_statistics = 1; + break; + case '?': + default: + usage(); + /* NOT REACHED */ + } + } + + daemon_mode = do_not_daemonize | force_single_threaded | + trace_mode_enabled; + interactive_mode = clear_user_cache_entries | clear_all_cache_entries | + show_statistics; + + if ((daemon_mode != 0) && (interactive_mode != 0)) { + LOG_ERR_1("main", "daemon mode and interactive_mode arguments " + "can't be used together"); + usage(); + } + + if (interactive_mode != 0) { + FILE *pidfin = fopen(DEFAULT_PIDFILE_PATH, "r"); + char pidbuf[256]; + + struct cached_connection_params connection_params; + cached_connection connection; + + int result; + + if (pidfin == NULL) + errx(EXIT_FAILURE, "There is no daemon running."); + + memset(pidbuf, 0, sizeof(pidbuf)); + fread(pidbuf, sizeof(pidbuf) - 1, 1, pidfin); + fclose(pidfin); + + if (ferror(pidfin) != 0) + errx(EXIT_FAILURE, "Can't read from pidfile."); + + if (sscanf(pidbuf, "%d", &pid) != 1) + errx(EXIT_FAILURE, "Invalid pidfile."); + LOG_MSG_1("main", "daemon PID is %d", pid); + + + memset(&connection_params, 0, + sizeof(struct cached_connection_params)); + connection_params.socket_path = DEFAULT_SOCKET_PATH; + connection = open_cached_connection__(&connection_params); + if (connection == INVALID_CACHED_CONNECTION) + errx(EXIT_FAILURE, "Can't connect to the daemon."); + + if (clear_user_cache_entries != 0) { + result = cached_transform__(connection, + user_config_entry_name, TT_USER); + if (result != 0) + LOG_MSG_1("main", + "user cache transformation failed"); + else + LOG_MSG_1("main", + "user cache_transformation " + "succeeded"); + } + + if (clear_all_cache_entries != 0) { + if (geteuid() != 0) + errx(EXIT_FAILURE, "Only root can initiate " + "global cache transformation."); + + result = cached_transform__(connection, + global_config_entry_name, TT_ALL); + if (result != 0) + LOG_MSG_1("main", + "global cache transformation " + "failed"); + else + LOG_MSG_1("main", + "global cache transformation " + "succeeded"); + } + + close_cached_connection__(connection); + + free(user_config_entry_name); + free(global_config_entry_name); + return (EXIT_SUCCESS); + } + + pidfile = pidfile_open(DEFAULT_PIDFILE_PATH, 0644, &pid); + if (pidfile == NULL) { + if (errno == EEXIST) + errx(EXIT_FAILURE, "Daemon already running, pid: %d.", + pid); + warn("Cannot open or create pidfile"); + } + + if (trace_mode_enabled == 1) + TRACE_ON(); + + /* blocking the main thread from receiving SIGPIPE signal */ + sigblock(sigmask(SIGPIPE)); + + /* daemonization */ + if (do_not_daemonize == 0) { + res = daemon(0, trace_mode_enabled == 0 ? 0 : 1); + if (res != 0) { + LOG_ERR_1("main", "can't daemonize myself: %s", + strerror(errno)); + pidfile_remove(pidfile); + goto fin; + } else + LOG_MSG_1("main", "successfully daemonized"); + } + + pidfile_write(pidfile); + + s_agent_table = init_agent_table(); + register_agent(s_agent_table, init_passwd_agent()); + register_agent(s_agent_table, init_passwd_mp_agent()); + register_agent(s_agent_table, init_group_agent()); + register_agent(s_agent_table, init_group_mp_agent()); + register_agent(s_agent_table, init_services_agent()); + register_agent(s_agent_table, init_services_mp_agent()); + LOG_MSG_1("main", "request agents registered successfully"); + + /* + * Hosts agent can't work properly until we have access to the + * appropriate dtab structures, which are used in nsdispatch + * calls + * + register_agent(s_agent_table, init_hosts_agent()); + */ + + /* configuration initialization */ + s_configuration = init_configuration(); + fill_configuration_defaults(s_configuration); + + error_str = NULL; + error_line = 0; + config_file = CONFIG_PATH; + + res = parse_config_file(s_configuration, config_file, &error_str, + &error_line); + if ((res != 0) && (error_str == NULL)) { + config_file = DEFAULT_CONFIG_PATH; + res = parse_config_file(s_configuration, config_file, + &error_str, &error_line); + } + + if (res != 0) { + if (error_str != NULL) { + LOG_ERR_1("main", "error in configuration file(%s, %d): %s\n", + config_file, error_line, error_str); + } else { + LOG_ERR_1("main", "no configuration file found " + "- was looking for %s and %s", + CONFIG_PATH, DEFAULT_CONFIG_PATH); + } + destroy_configuration(s_configuration); + return (-1); + } + + if (force_single_threaded == 1) + s_configuration->threads_num = 1; + + /* cache initialization */ + s_cache = init_cache_(s_configuration); + if (s_cache == NULL) { + LOG_ERR_1("main", "can't initialize the cache"); + destroy_configuration(s_configuration); + return (-1); + } + + /* runtime environment initialization */ + s_runtime_env = init_runtime_env(s_configuration); + if (s_runtime_env == NULL) { + LOG_ERR_1("main", "can't initialize the runtime environment"); + destroy_configuration(s_configuration); + destroy_cache_(s_cache); + return (-1); + } + + if (s_configuration->threads_num > 1) { + threads = (pthread_t *)malloc(sizeof(pthread_t) * + s_configuration->threads_num); + memset(threads, 0, sizeof(pthread_t) * + s_configuration->threads_num); + for (i = 0; i < s_configuration->threads_num; ++i) { + thread_args = (struct processing_thread_args *)malloc( + sizeof(struct processing_thread_args)); + thread_args->the_cache = s_cache; + thread_args->the_runtime_env = s_runtime_env; + thread_args->the_configuration = s_configuration; + + LOG_MSG_1("main", "thread #%d was successfully created", + i); + pthread_create(&threads[i], NULL, processing_thread, + thread_args); + + thread_args = NULL; + } + + for (i = 0; i < s_configuration->threads_num; ++i) + pthread_join(threads[i], NULL); + } else { + LOG_MSG_1("main", "working in single-threaded mode"); + processing_loop(s_cache, s_runtime_env, s_configuration); + } + +fin: + /* runtime environment destruction */ + destroy_runtime_env(s_runtime_env); + + /* cache destruction */ + destroy_cache_(s_cache); + + /* configuration destruction */ + destroy_configuration(s_configuration); + + /* agents table destruction */ + destroy_agent_table(s_agent_table); + + pidfile_remove(pidfile); + return (EXIT_SUCCESS); +} diff --git a/usr.sbin/cached/cached.conf.5 b/usr.sbin/cached/cached.conf.5 new file mode 100644 index 000000000000..de6d43a978d0 --- /dev/null +++ b/usr.sbin/cached/cached.conf.5 @@ -0,0 +1,102 @@ +.\" Copyright (c) 2005 Michael Bushkov +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" + +.Dd August 29, 2005 +.Dt CACHED.CONF 5 +.Os +.Sh NAME +.Nm cached.conf +.Nd "caching daemon configuration file manual page" +.Sh "DESCRIPTION" +.Nm +is used by the +.Xr cached 8 +daemon and is read on its startup. Its syntax is mostly similar to the +nscd.conf syntax in Linux and Solaris. It has some differences, though - +see them below. +.Pp +Each line specifies either an attribute and a value, or an attribute, +a cachename and a value. Usual cachenames are passwd, groups, hosts, +services, protocols and rpc. You can also use any other cachename +(for example, if some third-party application uses nsswitch). +.Pp +.Bl -tag -width Pair +.It Sy threads [value] +Number of threads, which would listen for connections and process requests. The +minimum is 1. The default value is 8. +.It Sy enable-cache [cachename] [yes|no] +Enables or disables the cache for specified cachename. +.It Sy positive-time-to-live [cachename] [value] +Sets the TTL (time-to-live) for the specified cache in seconds. Larger values +can increase system's performance, but they also can affect the cache +coherence. The default value is 3600. +.It Sy positive-policy [cachename] [fifo|lru|lfu] +The policy that is applied to erase some of the cache elements, when the +size limit of the given cachename is exceeded. Possible policies are: +fifo (first-in-first-out), lru (least-recently-used), +lfu (least-frequently-used). The default policy is +.It Sy negative-time-to-live [cachename] [value] +The TTL of the negative cached elements in seconds. The larger values can +significantly increase system performance in some environments +(when dealing with files with UIDs, which are not in system databases, +for example). This number should be kept low to avoid the cache +coherence problems. The default value is 60. +.It Sy negative-policy [cachename] [fifo|lru|lfu] +The same as the positive-policy, but this one is applied to the negative +elements of the given cachename. The default policy is +.It Sy suggested-size [cachename] [value] +This is the internal hash table size. The value should be a prime number +for optimum performance. You should only change this value when the number +of cached elements is significantly (in 5-10 times) greater then the default +hash table size (255). +.It Sy keep-hot-count [cachename] [value] +The size limit of the cache with the given cachename. When it is exceeded, +the policy will be applied. The default value is 2048. +.It Sy perform-actual-lookups [cachename] [yes|no] +If enabled, the +.Xr cached 8 +doesn't simply receive and cache the NSS-requests results, but performs +all the lookups by itself and only returns the responses. If this feature is +enabled, then for the given cachename +.Xr cached 8 +will act similarly to the NSCD. +.Pp +.Pa NOTE: +this feature is currently experimental - it supports only passwd, groups and +services cachenames. +.Sh "NOTES" +You can use +.Sq # +symbol at the beginning of the line for comments. +.Sh "SEE ALSO" +.Xr cached 8 +.Sh "AUTHORS" +Michael Bushkov +.Aq bushman@rsu.ru +.Sh "BUGS" +To report bugs or suggestions please mail me +.Aq bushman@rsu.ru diff --git a/usr.sbin/cached/cachedcli.c b/usr.sbin/cached/cachedcli.c new file mode 100644 index 000000000000..c78c875bc1ff --- /dev/null +++ b/usr.sbin/cached/cachedcli.c @@ -0,0 +1,284 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "cachedcli.h" +#include "protocol.h" + +#define DEFAULT_CACHED_IO_TIMEOUT 4 + +static int safe_write(struct cached_connection_ *, const void *, size_t); +static int safe_read(struct cached_connection_ *, void *, size_t); +static int send_credentials(struct cached_connection_ *, int); + +static int +safe_write(struct cached_connection_ *connection, const void *data, + size_t data_size) +{ + struct kevent eventlist; + int nevents; + size_t result; + ssize_t s_result; + struct timespec timeout; + + if (data_size == 0) + return (0); + + timeout.tv_sec = DEFAULT_CACHED_IO_TIMEOUT; + timeout.tv_nsec = 0; + result = 0; + do { + nevents = kevent(connection->write_queue, NULL, 0, &eventlist, + 1, &timeout); + if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) { + s_result = write(connection->sockfd, data + result, + eventlist.data < data_size - result ? + eventlist.data : data_size - result); + if (s_result == -1) + return (-1); + else + result += s_result; + + if (eventlist.flags & EV_EOF) + return (result < data_size ? -1 : 0); + } else + return (-1); + } while (result < data_size); + + return (0); +} + +static int +safe_read(struct cached_connection_ *connection, void *data, size_t data_size) +{ + struct kevent eventlist; + size_t result; + ssize_t s_result; + struct timespec timeout; + int nevents; + + if (data_size == 0) + return (0); + + timeout.tv_sec = DEFAULT_CACHED_IO_TIMEOUT; + timeout.tv_nsec = 0; + result = 0; + do { + nevents = kevent(connection->read_queue, NULL, 0, &eventlist, 1, + &timeout); + if ((nevents == 1) && (eventlist.filter == EVFILT_READ)) { + s_result = read(connection->sockfd, data + result, + eventlist.data <= data_size - result ? eventlist.data : + data_size - result); + if (s_result == -1) + return (-1); + else + result += s_result; + + if (eventlist.flags & EV_EOF) + return (result < data_size ? -1 : 0); + } else + return (-1); + } while (result < data_size); + + return (0); +} + +static int +send_credentials(struct cached_connection_ *connection, int type) +{ + struct kevent eventlist; + int nevents; + ssize_t result; + int res; + + struct msghdr cred_hdr; + struct iovec iov; + + struct { + struct cmsghdr hdr; + struct cmsgcred creds; + } cmsg; + + TRACE_IN(send_credentials); + memset(&cmsg, 0, sizeof(cmsg)); + cmsg.hdr.cmsg_len = sizeof(cmsg); + cmsg.hdr.cmsg_level = SOL_SOCKET; + cmsg.hdr.cmsg_type = SCM_CREDS; + + memset(&cred_hdr, 0, sizeof(struct msghdr)); + cred_hdr.msg_iov = &iov; + cred_hdr.msg_iovlen = 1; + cred_hdr.msg_control = &cmsg; + cred_hdr.msg_controllen = sizeof(cmsg); + + iov.iov_base = &type; + iov.iov_len = sizeof(int); + + EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, + NOTE_LOWAT, sizeof(int), NULL); + res = kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); + + nevents = kevent(connection->write_queue, NULL, 0, &eventlist, 1, NULL); + if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) { + result = (sendmsg(connection->sockfd, &cred_hdr, 0) == -1) ? -1 + : 0; + EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, + 0, 0, NULL); + kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); + TRACE_OUT(send_credentials); + return (result); + } else { + TRACE_OUT(send_credentials); + return (-1); + } +} + +struct cached_connection_ * +open_cached_connection__(struct cached_connection_params const *params) +{ + struct cached_connection_ *retval; + struct kevent eventlist; + struct sockaddr_un client_address; + int client_address_len, client_socket; + int res; + + TRACE_IN(open_cached_connection); + assert(params != NULL); + + client_socket = socket(PF_LOCAL, SOCK_STREAM, 0); + client_address.sun_family = PF_LOCAL; + strncpy(client_address.sun_path, params->socket_path, + sizeof(client_address.sun_path)); + client_address_len = sizeof(client_address.sun_family) + + strlen(client_address.sun_path) + 1; + + res = connect(client_socket, (struct sockaddr *)&client_address, + client_address_len); + if (res == -1) { + close(client_socket); + TRACE_OUT(open_cached_connection); + return (NULL); + } + fcntl(client_socket, F_SETFL, O_NONBLOCK); + + retval = malloc(sizeof(struct cached_connection_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cached_connection_)); + + retval->sockfd = client_socket; + + retval->write_queue = kqueue(); + assert(retval->write_queue != -1); + + EV_SET(&eventlist, retval->sockfd, EVFILT_WRITE, EV_ADD, + 0, 0, NULL); + res = kevent(retval->write_queue, &eventlist, 1, NULL, 0, NULL); + + retval->read_queue = kqueue(); + assert(retval->read_queue != -1); + + EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD, + 0, 0, NULL); + res = kevent(retval->read_queue, &eventlist, 1, NULL, 0, NULL); + + TRACE_OUT(open_cached_connection); + return (retval); +} + +void +close_cached_connection__(struct cached_connection_ *connection) +{ + + TRACE_IN(close_cached_connection); + assert(connection != NULL); + + close(connection->sockfd); + close(connection->read_queue); + close(connection->write_queue); + free(connection); + TRACE_OUT(close_cached_connection); +} + +int +cached_transform__(struct cached_connection_ *connection, + const char *entry_name, int transformation_type) +{ + size_t name_size; + int error_code; + int result; + + TRACE_IN(cached_transform); + + error_code = -1; + result = 0; + result = send_credentials(connection, CET_TRANSFORM_REQUEST); + if (result != 0) + goto fin; + + if (entry_name != NULL) + name_size = strlen(entry_name); + else + name_size = 0; + + result = safe_write(connection, &name_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, &transformation_type, sizeof(int)); + if (result != 0) + goto fin; + + if (entry_name != NULL) { + result = safe_write(connection, entry_name, name_size); + if (result != 0) + goto fin; + } + + result = safe_read(connection, &error_code, sizeof(int)); + if (result != 0) + error_code = -1; + +fin: + TRACE_OUT(cached_transform); + return (error_code); +} diff --git a/usr.sbin/cached/cachedcli.h b/usr.sbin/cached/cachedcli.h new file mode 100644 index 000000000000..58d9ccc0ceef --- /dev/null +++ b/usr.sbin/cached/cachedcli.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 2004 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_CACHEDCLI_H__ +#define __CACHED_CACHEDCLI_H__ + +struct cached_connection_params { + char *socket_path; + struct timeval timeout; +}; + +struct cached_connection_ { + int sockfd; + int read_queue; + int write_queue; +}; + +/* simple abstractions for not to write "struct" every time */ +typedef struct cached_connection_ *cached_connection; +typedef struct cached_connection_ *cached_mp_write_session; +typedef struct cached_connection_ *cached_mp_read_session; + +#define INVALID_CACHED_CONNECTION (NULL) + +/* initialization/destruction routines */ +extern cached_connection open_cached_connection__( + struct cached_connection_params const *); +extern void close_cached_connection__(cached_connection); + +extern int cached_transform__(cached_connection, const char *, int); + +#endif diff --git a/usr.sbin/cached/cachelib.c b/usr.sbin/cached/cachelib.c new file mode 100644 index 000000000000..4f771cc864c3 --- /dev/null +++ b/usr.sbin/cached/cachelib.c @@ -0,0 +1,1234 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include "cachelib.h" +#include "debug.h" + +#define INITIAL_ENTRIES_CAPACITY 32 +#define ENTRIES_CAPACITY_STEP 32 + +#define STRING_SIMPLE_HASH_BODY(in_var, var, a, M) \ + for ((var) = 0; *(in_var) != '\0'; ++(in_var)) \ + (var) = ((a)*(var) + *(in_var)) % (M) + +#define STRING_SIMPLE_MP2_HASH_BODY(in_var, var, a, M) \ + for ((var) = 0; *(in_var) != 0; ++(in_var)) \ + (var) = ((a)*(var) + *(in_var)) & (M - 1) + +static int cache_elemsize_common_continue_func(struct cache_common_entry_ *, + struct cache_policy_item_ *); +static int cache_lifetime_common_continue_func(struct cache_common_entry_ *, + struct cache_policy_item_ *); +static void clear_cache_entry(struct cache_entry_ *); +static void destroy_cache_entry(struct cache_entry_ *); +static void destroy_cache_mp_read_session(struct cache_mp_read_session_ *); +static void destroy_cache_mp_write_session(struct cache_mp_write_session_ *); +static int entries_bsearch_cmp_func(const void *, const void *); +static int entries_qsort_cmp_func(const void *, const void *); +static struct cache_entry_ ** find_cache_entry_p(struct cache_ *, + const char *); +static void flush_cache_entry(struct cache_entry_ *); +static void flush_cache_policy(struct cache_common_entry_ *, + struct cache_policy_ *, struct cache_policy_ *, + int (*)(struct cache_common_entry_ *, + struct cache_policy_item_ *)); +static int ht_items_cmp_func(const void *, const void *); +static int ht_items_fixed_size_left_cmp_func(const void *, const void *); +static hashtable_index_t ht_item_hash_func(const void *, size_t); + +/* + * Hashing and comparing routines, that are used with the hash tables + */ +static int +ht_items_cmp_func(const void *p1, const void *p2) +{ + struct cache_ht_item_data_ *hp1, *hp2; + size_t min_size; + int result; + + hp1 = (struct cache_ht_item_data_ *)p1; + hp2 = (struct cache_ht_item_data_ *)p2; + + assert(hp1->key != NULL); + assert(hp2->key != NULL); + + if (hp1->key_size != hp2->key_size) { + min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size : + hp2->key_size; + result = memcmp(hp1->key, hp2->key, min_size); + + if (result == 0) + return ((hp1->key_size < hp2->key_size) ? -1 : 1); + else + return (result); + } else + return (memcmp(hp1->key, hp2->key, hp1->key_size)); +} + +static int +ht_items_fixed_size_left_cmp_func(const void *p1, const void *p2) +{ + struct cache_ht_item_data_ *hp1, *hp2; + size_t min_size; + int result; + + hp1 = (struct cache_ht_item_data_ *)p1; + hp2 = (struct cache_ht_item_data_ *)p2; + + assert(hp1->key != NULL); + assert(hp2->key != NULL); + + if (hp1->key_size != hp2->key_size) { + min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size : + hp2->key_size; + result = memcmp(hp1->key, hp2->key, min_size); + + if (result == 0) + if (min_size == hp1->key_size) + return (0); + else + return ((hp1->key_size < hp2->key_size) ? -1 : 1); + else + return (result); + } else + return (memcmp(hp1->key, hp2->key, hp1->key_size)); +} + +static hashtable_index_t +ht_item_hash_func(const void *p, size_t cache_entries_size) +{ + struct cache_ht_item_data_ *hp; + size_t i; + + hashtable_index_t retval; + + hp = (struct cache_ht_item_data_ *)p; + assert(hp->key != NULL); + + retval = 0; + for (i = 0; i < hp->key_size; ++i) + retval = (127 * retval + (unsigned char)hp->key[i]) % + cache_entries_size; + + return retval; +} + +HASHTABLE_GENERATE(cache_ht_, cache_ht_item_, struct cache_ht_item_data_, data, + ht_item_hash_func, ht_items_cmp_func); + +/* + * Routines to sort and search the entries by name + */ +static int +entries_bsearch_cmp_func(const void *key, const void *ent) +{ + + assert(key != NULL); + assert(ent != NULL); + + return (strcmp((char const *)key, + (*(struct cache_entry_ const **)ent)->name)); +} + +static int +entries_qsort_cmp_func(const void *e1, const void *e2) +{ + + assert(e1 != NULL); + assert(e2 != NULL); + + return (strcmp((*(struct cache_entry_ const **)e1)->name, + (*(struct cache_entry_ const **)e2)->name)); +} + +static struct cache_entry_ ** +find_cache_entry_p(struct cache_ *the_cache, const char *entry_name) +{ + + return ((struct cache_entry_ **)(bsearch(entry_name, the_cache->entries, + the_cache->entries_size, sizeof(struct cache_entry_ *), + entries_bsearch_cmp_func))); +} + +static void +destroy_cache_mp_write_session(struct cache_mp_write_session_ *ws) +{ + + struct cache_mp_data_item_ *data_item; + + TRACE_IN(destroy_cache_mp_write_session); + assert(ws != NULL); + while (!TAILQ_EMPTY(&ws->items)) { + data_item = TAILQ_FIRST(&ws->items); + TAILQ_REMOVE(&ws->items, data_item, entries); + free(data_item->value); + free(data_item); + } + + free(ws); + TRACE_OUT(destroy_cache_mp_write_session); +} + +static void +destroy_cache_mp_read_session(struct cache_mp_read_session_ *rs) +{ + + TRACE_IN(destroy_cache_mp_read_session); + assert(rs != NULL); + free(rs); + TRACE_OUT(destroy_cache_mp_read_session); +} + +static void +destroy_cache_entry(struct cache_entry_ *entry) +{ + struct cache_common_entry_ *common_entry; + struct cache_mp_entry_ *mp_entry; + struct cache_mp_read_session_ *rs; + struct cache_mp_write_session_ *ws; + struct cache_ht_item_ *ht_item; + struct cache_ht_item_data_ *ht_item_data; + + TRACE_IN(destroy_cache_entry); + assert(entry != NULL); + + if (entry->params->entry_type == CET_COMMON) { + common_entry = (struct cache_common_entry_ *)entry; + + HASHTABLE_FOREACH(&(common_entry->items), ht_item) { + HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data) + { + free(ht_item_data->key); + free(ht_item_data->value); + } + HASHTABLE_ENTRY_CLEAR(ht_item, data); + } + + HASHTABLE_DESTROY(&(common_entry->items), data); + + /* FIFO policy is always first */ + destroy_cache_fifo_policy(common_entry->policies[0]); + switch (common_entry->common_params.policy) { + case CPT_LRU: + destroy_cache_lru_policy(common_entry->policies[1]); + break; + case CPT_LFU: + destroy_cache_lfu_policy(common_entry->policies[1]); + break; + default: + break; + } + free(common_entry->policies); + } else { + mp_entry = (struct cache_mp_entry_ *)entry; + + while (!TAILQ_EMPTY(&mp_entry->ws_head)) { + ws = TAILQ_FIRST(&mp_entry->ws_head); + TAILQ_REMOVE(&mp_entry->ws_head, ws, entries); + destroy_cache_mp_write_session(ws); + } + + while (!TAILQ_EMPTY(&mp_entry->rs_head)) { + rs = TAILQ_FIRST(&mp_entry->rs_head); + TAILQ_REMOVE(&mp_entry->rs_head, rs, entries); + destroy_cache_mp_read_session(rs); + } + + if (mp_entry->completed_write_session != NULL) + destroy_cache_mp_write_session( + mp_entry->completed_write_session); + + if (mp_entry->pending_write_session != NULL) + destroy_cache_mp_write_session( + mp_entry->pending_write_session); + } + + free(entry->name); + free(entry); + TRACE_OUT(destroy_cache_entry); +} + +static void +clear_cache_entry(struct cache_entry_ *entry) +{ + struct cache_mp_entry_ *mp_entry; + struct cache_common_entry_ *common_entry; + struct cache_ht_item_ *ht_item; + struct cache_ht_item_data_ *ht_item_data; + struct cache_policy_ *policy; + struct cache_policy_item_ *item, *next_item; + size_t entry_size; + int i; + + if (entry->params->entry_type == CET_COMMON) { + common_entry = (struct cache_common_entry_ *)entry; + + entry_size = 0; + HASHTABLE_FOREACH(&(common_entry->items), ht_item) { + HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data) + { + free(ht_item_data->key); + free(ht_item_data->value); + } + entry_size += HASHTABLE_ENTRY_SIZE(ht_item, data); + HASHTABLE_ENTRY_CLEAR(ht_item, data); + } + + common_entry->items_size -= entry_size; + for (i = 0; i < common_entry->policies_size; ++i) { + policy = common_entry->policies[i]; + + next_item = NULL; + item = policy->get_first_item_func(policy); + while (item != NULL) { + next_item = policy->get_next_item_func(policy, + item); + policy->remove_item_func(policy, item); + policy->destroy_item_func(item); + item = next_item; + } + } + } else { + mp_entry = (struct cache_mp_entry_ *)entry; + + if (mp_entry->rs_size == 0) { + if (mp_entry->completed_write_session != NULL) { + destroy_cache_mp_write_session( + mp_entry->completed_write_session); + mp_entry->completed_write_session = NULL; + } + + memset(&mp_entry->creation_time, 0, + sizeof(struct timeval)); + memset(&mp_entry->last_request_time, 0, + sizeof(struct timeval)); + } + } +} + +/* + * When passed to the flush_cache_policy, ensures that all old elements are + * deleted. + */ +static int +cache_lifetime_common_continue_func(struct cache_common_entry_ *entry, + struct cache_policy_item_ *item) +{ + + return ((item->last_request_time.tv_sec - item->creation_time.tv_sec > + entry->common_params.max_lifetime.tv_sec) ? 1: 0); +} + +/* + * When passed to the flush_cache_policy, ensures that all elements, that + * exceed the size limit, are deleted. + */ +static int +cache_elemsize_common_continue_func(struct cache_common_entry_ *entry, + struct cache_policy_item_ *item) +{ + + return ((entry->items_size > entry->common_params.satisf_elemsize) ? 1 + : 0); +} + +/* + * Removes the elements from the cache entry, while the continue_func returns 1. + */ +static void +flush_cache_policy(struct cache_common_entry_ *entry, + struct cache_policy_ *policy, + struct cache_policy_ *connected_policy, + int (*continue_func)(struct cache_common_entry_ *, + struct cache_policy_item_ *)) +{ + struct cache_policy_item_ *item, *next_item, *connected_item; + struct cache_ht_item_ *ht_item; + struct cache_ht_item_data_ *ht_item_data, ht_key; + hashtable_index_t hash; + + assert(policy != NULL); + + next_item = NULL; + item = policy->get_first_item_func(policy); + while ((item != NULL) && (continue_func(entry, item) == 1)) { + next_item = policy->get_next_item_func(policy, item); + + connected_item = item->connected_item; + policy->remove_item_func(policy, item); + + memset(&ht_key, 0, sizeof(struct cache_ht_item_data_)); + ht_key.key = item->key; + ht_key.key_size = item->key_size; + + hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &entry->items, + &ht_key); + assert(hash >= 0); + assert(hash < HASHTABLE_ENTRIES_COUNT(&entry->items)); + + ht_item = HASHTABLE_GET_ENTRY(&(entry->items), hash); + ht_item_data = HASHTABLE_ENTRY_FIND(cache_ht_, ht_item, + &ht_key); + assert(ht_item_data != NULL); + free(ht_item_data->key); + free(ht_item_data->value); + HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item, ht_item_data); + --entry->items_size; + + policy->destroy_item_func(item); + + if (connected_item != NULL) { + connected_policy->remove_item_func(connected_policy, + connected_item); + connected_policy->destroy_item_func(connected_item); + } + + item = next_item; + } +} + +static void +flush_cache_entry(struct cache_entry_ *entry) +{ + struct cache_mp_entry_ *mp_entry; + struct cache_common_entry_ *common_entry; + struct cache_policy_ *policy, *connected_policy; + + connected_policy = NULL; + if (entry->params->entry_type == CET_COMMON) { + common_entry = (struct cache_common_entry_ *)entry; + if ((common_entry->common_params.max_lifetime.tv_sec != 0) || + (common_entry->common_params.max_lifetime.tv_usec != 0)) { + + policy = common_entry->policies[0]; + if (common_entry->policies_size > 1) + connected_policy = common_entry->policies[1]; + + flush_cache_policy(common_entry, policy, + connected_policy, + cache_lifetime_common_continue_func); + } + + + if ((common_entry->common_params.max_elemsize != 0) && + common_entry->items_size > + common_entry->common_params.max_elemsize) { + + if (common_entry->policies_size > 1) { + policy = common_entry->policies[1]; + connected_policy = common_entry->policies[0]; + } else { + policy = common_entry->policies[0]; + connected_policy = NULL; + } + + flush_cache_policy(common_entry, policy, + connected_policy, + cache_elemsize_common_continue_func); + } + } else { + mp_entry = (struct cache_mp_entry_ *)entry; + + if ((mp_entry->mp_params.max_lifetime.tv_sec != 0) + || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) { + + if (mp_entry->last_request_time.tv_sec - + mp_entry->last_request_time.tv_sec > + mp_entry->mp_params.max_lifetime.tv_sec) + clear_cache_entry(entry); + } + } +} + +struct cache_ * +init_cache(struct cache_params const *params) +{ + struct cache_ *retval; + + TRACE_IN(init_cache); + assert(params != NULL); + + retval = (struct cache_ *)malloc(sizeof(struct cache_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cache_)); + + assert(params != NULL); + memcpy(&retval->params, params, sizeof(struct cache_params)); + + retval->entries = (struct cache_entry_ **)malloc( + sizeof(struct cache_entry_ *) * INITIAL_ENTRIES_CAPACITY); + assert(retval->entries != NULL); + memset(retval->entries, 0, sizeof(sizeof(struct cache_entry_ *) + * INITIAL_ENTRIES_CAPACITY)); + + retval->entries_capacity = INITIAL_ENTRIES_CAPACITY; + retval->entries_size = 0; + + TRACE_OUT(init_cache); + return (retval); +} + +void +destroy_cache(struct cache_ *the_cache) +{ + + TRACE_IN(destroy_cache); + assert(the_cache != NULL); + + if (the_cache->entries != NULL) { + size_t i; + for (i = 0; i < the_cache->entries_size; ++i) + destroy_cache_entry(the_cache->entries[i]); + + free(the_cache->entries); + } + + free(the_cache); + TRACE_OUT(destroy_cache); +} + +int +register_cache_entry(struct cache_ *the_cache, + struct cache_entry_params const *params) +{ + int policies_size; + size_t entry_name_size; + struct cache_common_entry_ *new_common_entry; + struct cache_mp_entry_ *new_mp_entry; + + TRACE_IN(register_cache_entry); + assert(the_cache != NULL); + + if (find_cache_entry(the_cache, params->entry_name) != NULL) { + TRACE_OUT(register_cache_entry); + return (-1); + } + + if (the_cache->entries_size == the_cache->entries_capacity) { + struct cache_entry_ **new_entries; + size_t new_capacity; + + new_capacity = the_cache->entries_capacity + + ENTRIES_CAPACITY_STEP; + new_entries = (struct cache_entry_ **)malloc( + sizeof(struct cache_entry_ *) * new_capacity); + assert(new_entries != NULL); + + memset(new_entries, 0, sizeof(struct cache_entry_ *) * + new_capacity); + memcpy(new_entries, the_cache->entries, + sizeof(struct cache_entry_ *) + * the_cache->entries_size); + + free(the_cache->entries); + the_cache->entries = new_entries; + } + + entry_name_size = strlen(params->entry_name); + switch (params->entry_type) + { + case CET_COMMON: + new_common_entry = (struct cache_common_entry_ *)malloc( + sizeof(struct cache_common_entry_)); + assert(new_common_entry != NULL); + memset(new_common_entry, 0, sizeof(struct cache_common_entry_)); + + memcpy(&new_common_entry->common_params, params, + sizeof(struct common_cache_entry_params)); + new_common_entry->params = + (struct cache_entry_params *)&new_common_entry->common_params; + + new_common_entry->common_params.entry_name = (char *)malloc( + entry_name_size+1); + assert(new_common_entry->common_params.entry_name != NULL); + memset(new_common_entry->common_params.entry_name, 0, + entry_name_size + 1); + strncpy(new_common_entry->common_params.entry_name, + params->entry_name, entry_name_size); + new_common_entry->name = + new_common_entry->common_params.entry_name; + + HASHTABLE_INIT(&(new_common_entry->items), + struct cache_ht_item_data_, data, + new_common_entry->common_params.cache_entries_size); + + if (new_common_entry->common_params.policy == CPT_FIFO) + policies_size = 1; + else + policies_size = 2; + + new_common_entry->policies = (struct cache_policy_ **)malloc( + sizeof(struct cache_policy_ *) * policies_size); + assert(new_common_entry->policies != NULL); + memset(new_common_entry->policies, 0, + sizeof(struct cache_policy_ *) * policies_size); + + new_common_entry->policies_size = policies_size; + new_common_entry->policies[0] = init_cache_fifo_policy(); + + if (policies_size > 1) { + switch (new_common_entry->common_params.policy) { + case CPT_LRU: + new_common_entry->policies[1] = + init_cache_lru_policy(); + break; + case CPT_LFU: + new_common_entry->policies[1] = + init_cache_lfu_policy(); + break; + default: + break; + } + } + + new_common_entry->get_time_func = + the_cache->params.get_time_func; + the_cache->entries[the_cache->entries_size++] = + (struct cache_entry_ *)new_common_entry; + break; + case CET_MULTIPART: + new_mp_entry = (struct cache_mp_entry_ *)malloc( + sizeof(struct cache_mp_entry_)); + assert(new_mp_entry != NULL); + memset(new_mp_entry, 0, sizeof(struct cache_mp_entry_)); + + memcpy(&new_mp_entry->mp_params, params, + sizeof(struct mp_cache_entry_params)); + new_mp_entry->params = + (struct cache_entry_params *)&new_mp_entry->mp_params; + + new_mp_entry->mp_params.entry_name = (char *)malloc( + entry_name_size+1); + assert(new_mp_entry->mp_params.entry_name != NULL); + memset(new_mp_entry->mp_params.entry_name, 0, + entry_name_size + 1); + strncpy(new_mp_entry->mp_params.entry_name, params->entry_name, + entry_name_size); + new_mp_entry->name = new_mp_entry->mp_params.entry_name; + + TAILQ_INIT(&new_mp_entry->ws_head); + TAILQ_INIT(&new_mp_entry->rs_head); + + new_mp_entry->get_time_func = the_cache->params.get_time_func; + the_cache->entries[the_cache->entries_size++] = + (struct cache_entry_ *)new_mp_entry; + break; + } + + + qsort(the_cache->entries, the_cache->entries_size, + sizeof(struct cache_entry_ *), entries_qsort_cmp_func); + + TRACE_OUT(register_cache_entry); + return (0); +} + +int +unregister_cache_entry(struct cache_ *the_cache, const char *entry_name) +{ + struct cache_entry_ **del_ent; + + TRACE_IN(unregister_cache_entry); + assert(the_cache != NULL); + + del_ent = find_cache_entry_p(the_cache, entry_name); + if (del_ent != NULL) { + destroy_cache_entry(*del_ent); + --the_cache->entries_size; + + memmove(del_ent, del_ent + 1, + (&(the_cache->entries[--the_cache->entries_size]) - + del_ent) * sizeof(struct cache_entry_ *)); + + TRACE_OUT(unregister_cache_entry); + return (0); + } else { + TRACE_OUT(unregister_cache_entry); + return (-1); + } +} + +struct cache_entry_ * +find_cache_entry(struct cache_ *the_cache, const char *entry_name) +{ + struct cache_entry_ **result; + + TRACE_IN(find_cache_entry); + result = find_cache_entry_p(the_cache, entry_name); + + if (result == NULL) { + TRACE_OUT(find_cache_entry); + return (NULL); + } else { + TRACE_OUT(find_cache_entry); + return (*result); + } +} + +/* + * Tries to read the element with the specified key from the cache. If the + * value_size is too small, it will be filled with the proper number, and + * the user will need to call cache_read again with the value buffer, that + * is large enough. + * Function returns 0 on success, -1 on error, and -2 if the value_size is too + * small. + */ +int +cache_read(struct cache_entry_ *entry, const char *key, size_t key_size, + char *value, size_t *value_size) +{ + struct cache_common_entry_ *common_entry; + struct cache_ht_item_data_ item_data, *find_res; + struct cache_ht_item_ *item; + hashtable_index_t hash; + struct cache_policy_item_ *connected_item; + + TRACE_IN(cache_read); + assert(entry != NULL); + assert(key != NULL); + assert(value_size != NULL); + assert(entry->params->entry_type == CET_COMMON); + + common_entry = (struct cache_common_entry_ *)entry; + + memset(&item_data, 0, sizeof(struct cache_ht_item_data_)); + /* can't avoid the cast here */ + item_data.key = (char *)key; + item_data.key_size = key_size; + + hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items, + &item_data); + assert(hash >= 0); + assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items)); + + item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash); + find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data); + if (find_res == NULL) { + TRACE_OUT(cache_read); + return (-1); + } + + if ((common_entry->common_params.max_lifetime.tv_sec != 0) || + (common_entry->common_params.max_lifetime.tv_usec != 0)) { + + if (find_res->fifo_policy_item->last_request_time.tv_sec - + find_res->fifo_policy_item->creation_time.tv_sec > + common_entry->common_params.max_lifetime.tv_sec) { + + free(find_res->key); + free(find_res->value); + + connected_item = + find_res->fifo_policy_item->connected_item; + if (connected_item != NULL) { + common_entry->policies[1]->remove_item_func( + common_entry->policies[1], + connected_item); + common_entry->policies[1]->destroy_item_func( + connected_item); + } + + common_entry->policies[0]->remove_item_func( + common_entry->policies[0], + find_res->fifo_policy_item); + common_entry->policies[0]->destroy_item_func( + find_res->fifo_policy_item); + + HASHTABLE_ENTRY_REMOVE(cache_ht_, item, find_res); + --common_entry->items_size; + } + } + + if ((*value_size < find_res->value_size) || (value == NULL)) { + *value_size = find_res->value_size; + TRACE_OUT(cache_read); + return (-2); + } + + *value_size = find_res->value_size; + memcpy(value, find_res->value, find_res->value_size); + + ++find_res->fifo_policy_item->request_count; + common_entry->get_time_func( + &find_res->fifo_policy_item->last_request_time); + common_entry->policies[0]->update_item_func(common_entry->policies[0], + find_res->fifo_policy_item); + + if (find_res->fifo_policy_item->connected_item != NULL) { + connected_item = find_res->fifo_policy_item->connected_item; + memcpy(&connected_item->last_request_time, + &find_res->fifo_policy_item->last_request_time, + sizeof(struct timeval)); + connected_item->request_count = + find_res->fifo_policy_item->request_count; + + common_entry->policies[1]->update_item_func( + common_entry->policies[1], connected_item); + } + + TRACE_OUT(cache_read); + return (0); +} + +/* + * Writes the value with the specified key into the cache entry. + * Functions returns 0 on success, and -1 on error. + */ +int +cache_write(struct cache_entry_ *entry, const char *key, size_t key_size, + char const *value, size_t value_size) +{ + struct cache_common_entry_ *common_entry; + struct cache_ht_item_data_ item_data, *find_res; + struct cache_ht_item_ *item; + hashtable_index_t hash; + + struct cache_policy_ *policy, *connected_policy; + struct cache_policy_item_ *policy_item; + struct cache_policy_item_ *connected_policy_item; + + TRACE_IN(cache_write); + assert(entry != NULL); + assert(key != NULL); + assert(value != NULL); + assert(entry->params->entry_type == CET_COMMON); + + common_entry = (struct cache_common_entry_ *)entry; + + memset(&item_data, 0, sizeof(struct cache_ht_item_data_)); + /* can't avoid the cast here */ + item_data.key = (char *)key; + item_data.key_size = key_size; + + hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items, + &item_data); + assert(hash >= 0); + assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items)); + + item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash); + find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data); + if (find_res != NULL) { + TRACE_OUT(cache_write); + return (-1); + } + + item_data.key = (char *)malloc(key_size); + memcpy(item_data.key, key, key_size); + + item_data.value = (char *)malloc(value_size); + assert(item_data.value != NULL); + + memcpy(item_data.value, value, value_size); + item_data.value_size = value_size; + + policy_item = common_entry->policies[0]->create_item_func(); + policy_item->key = item_data.key; + policy_item->key_size = item_data.key_size; + common_entry->get_time_func(&policy_item->creation_time); + + if (common_entry->policies_size > 1) { + connected_policy_item = + common_entry->policies[1]->create_item_func(); + memcpy(&connected_policy_item->creation_time, + &policy_item->creation_time, + sizeof(struct timeval)); + connected_policy_item->key = policy_item->key; + connected_policy_item->key_size = policy_item->key_size; + + connected_policy_item->connected_item = policy_item; + policy_item->connected_item = connected_policy_item; + } + + item_data.fifo_policy_item = policy_item; + + common_entry->policies[0]->add_item_func(common_entry->policies[0], + policy_item); + if (common_entry->policies_size > 1) + common_entry->policies[1]->add_item_func( + common_entry->policies[1], connected_policy_item); + + HASHTABLE_ENTRY_STORE(cache_ht_, item, &item_data); + ++common_entry->items_size; + + if ((common_entry->common_params.max_elemsize != 0) && + (common_entry->items_size > + common_entry->common_params.max_elemsize)) { + if (common_entry->policies_size > 1) { + policy = common_entry->policies[1]; + connected_policy = common_entry->policies[0]; + } else { + policy = common_entry->policies[0]; + connected_policy = NULL; + } + + flush_cache_policy(common_entry, policy, connected_policy, + cache_elemsize_common_continue_func); + } + + TRACE_OUT(cache_write); + return (0); +} + +/* + * Initializes the write session for the specified multipart entry. This + * session then should be filled with data either committed or abandoned by + * using close_cache_mp_write_session or abandon_cache_mp_write_session + * respectively. + * Returns NULL on errors (when there are too many opened write sessions for + * the entry). + */ +struct cache_mp_write_session_ * +open_cache_mp_write_session(struct cache_entry_ *entry) +{ + struct cache_mp_entry_ *mp_entry; + struct cache_mp_write_session_ *retval; + + TRACE_IN(open_cache_mp_write_session); + assert(entry != NULL); + assert(entry->params->entry_type == CET_MULTIPART); + mp_entry = (struct cache_mp_entry_ *)entry; + + if ((mp_entry->mp_params.max_sessions > 0) && + (mp_entry->ws_size == mp_entry->mp_params.max_sessions)) { + TRACE_OUT(open_cache_mp_write_session); + return (NULL); + } + + retval = (struct cache_mp_write_session_ *)malloc( + sizeof(struct cache_mp_write_session_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cache_mp_write_session_)); + + TAILQ_INIT(&retval->items); + retval->parent_entry = mp_entry; + + TAILQ_INSERT_HEAD(&mp_entry->ws_head, retval, entries); + ++mp_entry->ws_size; + + TRACE_OUT(open_cache_mp_write_session); + return (retval); +} + +/* + * Writes data to the specified session. Return 0 on success and -1 on errors + * (when write session size limit is exceeded). + */ +int +cache_mp_write(struct cache_mp_write_session_ *ws, char *data, + size_t data_size) +{ + struct cache_mp_data_item_ *new_item; + + TRACE_IN(cache_mp_write); + assert(ws != NULL); + assert(ws->parent_entry != NULL); + assert(ws->parent_entry->params->entry_type == CET_MULTIPART); + + if ((ws->parent_entry->mp_params.max_elemsize > 0) && + (ws->parent_entry->mp_params.max_elemsize == ws->items_size)) { + TRACE_OUT(cache_mp_write); + return (-1); + } + + new_item = (struct cache_mp_data_item_ *)malloc( + sizeof(struct cache_mp_data_item_)); + assert(new_item != NULL); + memset(new_item, 0, sizeof(struct cache_mp_data_item_)); + + new_item->value = (char *)malloc(data_size); + assert(new_item->value != NULL); + memcpy(new_item->value, data, data_size); + new_item->value_size = data_size; + + TAILQ_INSERT_TAIL(&ws->items, new_item, entries); + ++ws->items_size; + + TRACE_OUT(cache_mp_write); + return (0); +} + +/* + * Abandons the write session and frees all the connected resources. + */ +void +abandon_cache_mp_write_session(struct cache_mp_write_session_ *ws) +{ + + TRACE_IN(abandon_cache_mp_write_session); + assert(ws != NULL); + assert(ws->parent_entry != NULL); + assert(ws->parent_entry->params->entry_type == CET_MULTIPART); + + TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries); + --ws->parent_entry->ws_size; + + destroy_cache_mp_write_session(ws); + TRACE_OUT(abandon_cache_mp_write_session); +} + +/* + * Commits the session to the entry, for which it was created. + */ +void +close_cache_mp_write_session(struct cache_mp_write_session_ *ws) +{ + + TRACE_IN(close_cache_mp_write_session); + assert(ws != NULL); + assert(ws->parent_entry != NULL); + assert(ws->parent_entry->params->entry_type == CET_MULTIPART); + + TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries); + --ws->parent_entry->ws_size; + + if (ws->parent_entry->completed_write_session == NULL) { + /* + * If there is no completed session yet, this will be the one + */ + ws->parent_entry->get_time_func( + &ws->parent_entry->creation_time); + ws->parent_entry->completed_write_session = ws; + } else { + /* + * If there is a completed session, then we'll save our session + * as a pending session. If there is already a pending session, + * it would be destroyed. + */ + if (ws->parent_entry->pending_write_session != NULL) + destroy_cache_mp_write_session( + ws->parent_entry->pending_write_session); + + ws->parent_entry->pending_write_session = ws; + } + TRACE_OUT(close_cache_mp_write_session); +} + +/* + * Opens read session for the specified entry. Returns NULL on errors (when + * there are no data in the entry, or the data are obsolete). + */ +struct cache_mp_read_session_ * +open_cache_mp_read_session(struct cache_entry_ *entry) +{ + struct cache_mp_entry_ *mp_entry; + struct cache_mp_read_session_ *retval; + + TRACE_IN(open_cache_mp_read_session); + assert(entry != NULL); + assert(entry->params->entry_type == CET_MULTIPART); + mp_entry = (struct cache_mp_entry_ *)entry; + + if (mp_entry->completed_write_session == NULL) { + TRACE_OUT(open_cache_mp_read_session); + return (NULL); + } + + if ((mp_entry->mp_params.max_lifetime.tv_sec != 0) + || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) { + if (mp_entry->last_request_time.tv_sec - + mp_entry->last_request_time.tv_sec > + mp_entry->mp_params.max_lifetime.tv_sec) { + flush_cache_entry(entry); + TRACE_OUT(open_cache_mp_read_session); + return (NULL); + } + } + + retval = (struct cache_mp_read_session_ *)malloc( + sizeof(struct cache_mp_read_session_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cache_mp_read_session_)); + + retval->parent_entry = mp_entry; + retval->current_item = TAILQ_FIRST( + &mp_entry->completed_write_session->items); + + TAILQ_INSERT_HEAD(&mp_entry->rs_head, retval, entries); + ++mp_entry->rs_size; + + mp_entry->get_time_func(&mp_entry->last_request_time); + TRACE_OUT(open_cache_mp_read_session); + return (retval); +} + +/* + * Reads the data from the read session - step by step. + * Returns 0 on success, -1 on error (when there are no more data), and -2 if + * the data_size is too small. In the last case, data_size would be filled + * the proper value. + */ +int +cache_mp_read(struct cache_mp_read_session_ *rs, char *data, size_t *data_size) +{ + + TRACE_IN(cache_mp_read); + assert(rs != NULL); + + if (rs->current_item == NULL) { + TRACE_OUT(cache_mp_read); + return (-1); + } + + if (rs->current_item->value_size > *data_size) { + *data_size = rs->current_item->value_size; + if (data == NULL) { + TRACE_OUT(cache_mp_read); + return (0); + } + + TRACE_OUT(cache_mp_read); + return (-2); + } + + *data_size = rs->current_item->value_size; + memcpy(data, rs->current_item->value, rs->current_item->value_size); + rs->current_item = TAILQ_NEXT(rs->current_item, entries); + + TRACE_OUT(cache_mp_read); + return (0); +} + +/* + * Closes the read session. If there are no more read sessions and there is + * a pending write session, it will be committed and old + * completed_write_session will be destroyed. + */ +void +close_cache_mp_read_session(struct cache_mp_read_session_ *rs) +{ + + TRACE_IN(close_cache_mp_read_session); + assert(rs != NULL); + assert(rs->parent_entry != NULL); + + TAILQ_REMOVE(&rs->parent_entry->rs_head, rs, entries); + --rs->parent_entry->rs_size; + + if ((rs->parent_entry->rs_size == 0) && + (rs->parent_entry->pending_write_session != NULL)) { + destroy_cache_mp_write_session( + rs->parent_entry->completed_write_session); + rs->parent_entry->completed_write_session = + rs->parent_entry->pending_write_session; + rs->parent_entry->pending_write_session = NULL; + } + + destroy_cache_mp_read_session(rs); + TRACE_OUT(close_cache_mp_read_session); +} + +int +transform_cache_entry(struct cache_entry_ *entry, + enum cache_transformation_t transformation) +{ + + TRACE_IN(transform_cache_entry); + switch (transformation) { + case CTT_CLEAR: + clear_cache_entry(entry); + TRACE_OUT(transform_cache_entry); + return (0); + case CTT_FLUSH: + flush_cache_entry(entry); + TRACE_OUT(transform_cache_entry); + return (0); + default: + TRACE_OUT(transform_cache_entry); + return (-1); + } +} + +int +transform_cache_entry_part(struct cache_entry_ *entry, + enum cache_transformation_t transformation, const char *key_part, + size_t key_part_size, enum part_position_t part_position) +{ + struct cache_common_entry_ *common_entry; + struct cache_ht_item_ *ht_item; + struct cache_ht_item_data_ *ht_item_data, ht_key; + + struct cache_policy_item_ *item, *connected_item; + + TRACE_IN(transform_cache_entry_part); + if (entry->params->entry_type != CET_COMMON) { + TRACE_OUT(transform_cache_entry_part); + return (-1); + } + + if (transformation != CTT_CLEAR) { + TRACE_OUT(transform_cache_entry_part); + return (-1); + } + + memset(&ht_key, 0, sizeof(struct cache_ht_item_data_)); + ht_key.key = (char *)key_part; /* can't avoid casting here */ + ht_key.key_size = key_part_size; + + common_entry = (struct cache_common_entry_ *)entry; + HASHTABLE_FOREACH(&(common_entry->items), ht_item) { + do { + ht_item_data = HASHTABLE_ENTRY_FIND_SPECIAL(cache_ht_, + ht_item, &ht_key, + ht_items_fixed_size_left_cmp_func); + + if (ht_item_data != NULL) { + item = ht_item_data->fifo_policy_item; + connected_item = item->connected_item; + + common_entry->policies[0]->remove_item_func( + common_entry->policies[0], + item); + + free(ht_item_data->key); + free(ht_item_data->value); + HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item, + ht_item_data); + --common_entry->items_size; + + common_entry->policies[0]->destroy_item_func( + item); + if (common_entry->policies_size == 2) { + common_entry->policies[1]->remove_item_func( + common_entry->policies[1], + connected_item); + common_entry->policies[1]->destroy_item_func( + connected_item); + } + } + } while (ht_item_data != NULL); + } + + TRACE_OUT(transform_cache_entry_part); + return (0); +} diff --git a/usr.sbin/cached/cachelib.h b/usr.sbin/cached/cachelib.h new file mode 100644 index 000000000000..d67e83063870 --- /dev/null +++ b/usr.sbin/cached/cachelib.h @@ -0,0 +1,281 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_CACHELIB_H__ +#define __CACHED_CACHELIB_H__ + +#include +#include +#include +#include "hashtable.h" +#include "cacheplcs.h" + +enum cache_entry_t { + CET_COMMON = 0, /* cache item is atomic */ + CET_MULTIPART /* cache item is formed part by part */ +}; + +enum cache_transformation_t { + CTT_FLUSH = 0, /* flush the cache - delete all obsolete items */ + CTT_CLEAR = 1 /* delete all items in the cache */ +}; + +/* cache deletion policy type enum */ +enum cache_policy_t { + CPT_FIFO = 0, /* first-in first-out */ + CPT_LRU = 1, /* least recently used */ + CPT_LFU = 2 /* least frequently used */ +}; + +/* multipart sessions can be used for reading and writing */ +enum cache_mp_session_t { + CMPT_READ_SESSION, + CMPT_WRITE_SESSION +}; + +/* + * When doing partial transformations of entries (which are applied for + * elements with keys, that contain specified buffer in its left or + * right part), this enum will show the needed position of the key part. + */ +enum part_position_t { + KPPT_LEFT, + KPPT_RIGHT +}; + +/* num_levels attribute is obsolete, i think - user can always emulate it + * by using one entry. + * get_time_func is needed to have the clocks-independent counter + */ +struct cache_params +{ + void (*get_time_func)(struct timeval *); +}; + +/* + * base structure - normal_cache_entry_params and multipart_cache_entry_params + * are "inherited" from it + */ +struct cache_entry_params +{ + enum cache_entry_t entry_type; + char *entry_name; +}; + +/* params, used for most entries */ +struct common_cache_entry_params +{ + /* inherited fields */ + enum cache_entry_t entry_type; + + /* unique fields */ + char *entry_name; + size_t cache_entries_size; + + size_t max_elemsize; /* if 0 then no check is made */ + size_t satisf_elemsize; /* if entry size is exceeded, + * this number of elements will be left, + * others will be deleted */ + struct timeval max_lifetime; /* if 0 then no check is made */ + enum cache_policy_t policy; /* policy used for transformations */ +}; + +/* params, used for multipart entries */ +struct mp_cache_entry_params +{ + /* inherited fields */ + enum cache_entry_t entry_type; + char *entry_name; + + /* unique fields */ + size_t max_elemsize; /* if 0 then no check is made */ + size_t max_sessions; /* maximum number of active sessions */ + + struct timeval max_lifetime; /* maximum elements lifetime */ +}; + +struct cache_ht_item_data_ +{ + /* key is the bytes sequence only - not the null-terminated string */ + char *key; + size_t key_size; + + char *value; + size_t value_size; + + struct cache_policy_item_ *fifo_policy_item; +}; + +struct cache_ht_item_ +{ + HASHTABLE_ENTRY_HEAD(ht_item_, struct cache_ht_item_data_) data; +}; + +struct cache_entry_ +{ + char *name; + struct cache_entry_params *params; +}; + +struct cache_common_entry_ +{ + char *name; + struct cache_entry_params *params; + + struct common_cache_entry_params common_params; + + HASHTABLE_HEAD(cache_ht_, cache_ht_item_) items; + size_t items_size; + + /* + * Entry always has the FIFO policy, that is used to eliminate old + * elements (the ones, with lifetime more than max_lifetime). Besides, + * user can specify another policy to be applied, when there are too + * many elements in the entry. So policies_size can be 1 or 2. + */ + struct cache_policy_ **policies; + size_t policies_size; + + void (*get_time_func)(struct timeval *); +}; + +struct cache_mp_data_item_ { + char *value; + size_t value_size; + + TAILQ_ENTRY(cache_mp_data_item_) entries; +}; + +struct cache_mp_write_session_ +{ + struct cache_mp_entry_ *parent_entry; + + /* + * All items are accumulated in this queue. When the session is + * committed, they all will be copied to the multipart entry. + */ + TAILQ_HEAD(cache_mp_data_item_head, cache_mp_data_item_) items; + size_t items_size; + + TAILQ_ENTRY(cache_mp_write_session_) entries; +}; + +struct cache_mp_read_session_ +{ + struct cache_mp_entry_ *parent_entry; + struct cache_mp_data_item_ *current_item; + + TAILQ_ENTRY(cache_mp_read_session_) entries; +}; + +struct cache_mp_entry_ +{ + char *name; + struct cache_entry_params *params; + + struct mp_cache_entry_params mp_params; + + /* All opened write sessions */ + TAILQ_HEAD(write_sessions_head, cache_mp_write_session_) ws_head; + size_t ws_size; + + /* All opened read sessions */ + TAILQ_HEAD(read_sessions_head, cache_mp_read_session_) rs_head; + size_t rs_size; + + /* + * completed_write_session is the committed write sessions. All read + * sessions use data from it. If the completed_write_session is out of + * date, but still in use by some of the read sessions, the newly + * committed write session is stored in the pending_write_session. + * In such a case, completed_write_session will be substituted with + * pending_write_session as soon as it won't be used by any of + * the read sessions. + */ + struct cache_mp_write_session_ *completed_write_session; + struct cache_mp_write_session_ *pending_write_session; + struct timeval creation_time; + struct timeval last_request_time; + + void (*get_time_func)(struct timeval *); +}; + +struct cache_ +{ + struct cache_params params; + + struct cache_entry_ **entries; + size_t entries_capacity; + size_t entries_size; +}; + +/* simple abstractions - for not to write "struct" every time */ +typedef struct cache_ *cache; +typedef struct cache_entry_ *cache_entry; +typedef struct cache_mp_write_session_ *cache_mp_write_session; +typedef struct cache_mp_read_session_ *cache_mp_read_session; + +#define INVALID_CACHE (NULL) +#define INVALID_CACHE_ENTRY (NULL) +#define INVALID_CACHE_MP_WRITE_SESSION (NULL) +#define INVALID_CACHE_MP_READ_SESSION (NULL) + +/* + * NOTE: all cache operations are thread-unsafe. You must ensure thread-safety + * externally, by yourself. + */ + +/* cache initialization/destruction routines */ +extern cache init_cache(struct cache_params const *); +extern void destroy_cache(cache); + +/* cache entries manipulation routines */ +extern int register_cache_entry(cache, struct cache_entry_params const *); +extern int unregister_cache_entry(cache, const char *); +extern cache_entry find_cache_entry(cache, const char *); + +/* read/write operations used on common entries */ +extern int cache_read(cache_entry, const char *, size_t, char *, size_t *); +extern int cache_write(cache_entry, const char *, size_t, char const *, size_t); + +/* read/write operations used on multipart entries */ +extern cache_mp_write_session open_cache_mp_write_session(cache_entry); +extern int cache_mp_write(cache_mp_write_session, char *, size_t); +extern void abandon_cache_mp_write_session(cache_mp_write_session); +extern void close_cache_mp_write_session(cache_mp_write_session); + +extern cache_mp_read_session open_cache_mp_read_session(cache_entry); +extern int cache_mp_read(cache_mp_read_session, char *, size_t *); +extern void close_cache_mp_read_session(cache_mp_read_session); + +/* transformation routines */ +extern int transform_cache_entry(cache_entry, enum cache_transformation_t); +extern int transform_cache_entry_part(cache_entry, enum cache_transformation_t, + const char *, size_t, enum part_position_t); + +#endif diff --git a/usr.sbin/cached/cacheplcs.c b/usr.sbin/cached/cacheplcs.c new file mode 100644 index 000000000000..a7ee38de4801 --- /dev/null +++ b/usr.sbin/cached/cacheplcs.c @@ -0,0 +1,590 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "cacheplcs.h" +#include "debug.h" + +static void cache_fifo_policy_update_item(struct cache_policy_ *, + struct cache_policy_item_ *); +static void cache_lfu_policy_add_item(struct cache_policy_ *, + struct cache_policy_item_ *); +static struct cache_policy_item_ * cache_lfu_policy_create_item(void); +static void cache_lfu_policy_destroy_item(struct cache_policy_item_ *); +static struct cache_policy_item_ *cache_lfu_policy_get_first_item( + struct cache_policy_ *); +static struct cache_policy_item_ *cache_lfu_policy_get_last_item( + struct cache_policy_ *); +static struct cache_policy_item_ *cache_lfu_policy_get_next_item( + struct cache_policy_ *, struct cache_policy_item_ *); +static struct cache_policy_item_ *cache_lfu_policy_get_prev_item( + struct cache_policy_ *, struct cache_policy_item_ *); +static void cache_lfu_policy_remove_item(struct cache_policy_ *, + struct cache_policy_item_ *); +static void cache_lfu_policy_update_item(struct cache_policy_ *, + struct cache_policy_item_ *); +static void cache_lru_policy_update_item(struct cache_policy_ *, + struct cache_policy_item_ *); +static void cache_queue_policy_add_item(struct cache_policy_ *, + struct cache_policy_item_ *); +static struct cache_policy_item_ * cache_queue_policy_create_item(); +static void cache_queue_policy_destroy_item(struct cache_policy_item_ *); +static struct cache_policy_item_ *cache_queue_policy_get_first_item( + struct cache_policy_ *); +static struct cache_policy_item_ *cache_queue_policy_get_last_item( + struct cache_policy_ *); +static struct cache_policy_item_ *cache_queue_policy_get_next_item( + struct cache_policy_ *, struct cache_policy_item_ *); +static struct cache_policy_item_ *cache_queue_policy_get_prev_item( + struct cache_policy_ *, struct cache_policy_item_ *); +static void cache_queue_policy_remove_item(struct cache_policy_ *, + struct cache_policy_item_ *); +static void destroy_cache_queue_policy(struct cache_queue_policy_ *); +static struct cache_queue_policy_ *init_cache_queue_policy(void); + +/* + * All cache_queue_policy_XXX functions below will be used to fill + * the cache_queue_policy structure. They implement the most functionality of + * LRU and FIFO policies. LRU and FIFO policies are actually the + * cache_queue_policy_ with cache_update_item function changed. + */ +static struct cache_policy_item_ * +cache_queue_policy_create_item() +{ + struct cache_queue_policy_item_ *retval; + + TRACE_IN(cache_queue_policy_create_item); + retval = (struct cache_queue_policy_item_ *)malloc( + sizeof(struct cache_queue_policy_item_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cache_queue_policy_item_)); + + TRACE_OUT(cache_queue_policy_create_item); + return ((struct cache_policy_item_ *)retval); +} + +static void +cache_queue_policy_destroy_item(struct cache_policy_item_ *item) +{ + + TRACE_IN(cache_queue_policy_destroy_item); + assert(item != NULL); + free(item); + TRACE_OUT(cache_queue_policy_destroy_item); +} + +static void +cache_queue_policy_add_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_queue_policy_ *queue_policy; + struct cache_queue_policy_item_ *queue_item; + + TRACE_IN(cache_queue_policy_add_item); + queue_policy = (struct cache_queue_policy_ *)policy; + queue_item = (struct cache_queue_policy_item_ *)item; + TAILQ_INSERT_TAIL(&queue_policy->head, queue_item, entries); + TRACE_OUT(cache_queue_policy_add_item); +} + +static void +cache_queue_policy_remove_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_queue_policy_ *queue_policy; + struct cache_queue_policy_item_ *queue_item; + + TRACE_IN(cache_queue_policy_remove_item); + queue_policy = (struct cache_queue_policy_ *)policy; + queue_item = (struct cache_queue_policy_item_ *)item; + TAILQ_REMOVE(&queue_policy->head, queue_item, entries); + TRACE_OUT(cache_queue_policy_remove_item); +} + +static struct cache_policy_item_ * +cache_queue_policy_get_first_item(struct cache_policy_ *policy) +{ + struct cache_queue_policy_ *queue_policy; + + TRACE_IN(cache_queue_policy_get_first_item); + queue_policy = (struct cache_queue_policy_ *)policy; + TRACE_OUT(cache_queue_policy_get_first_item); + return ((struct cache_policy_item_ *)TAILQ_FIRST(&queue_policy->head)); +} + +static struct cache_policy_item_ * +cache_queue_policy_get_last_item(struct cache_policy_ *policy) +{ + struct cache_queue_policy_ *queue_policy; + + TRACE_IN(cache_queue_policy_get_last_item); + queue_policy = (struct cache_queue_policy_ *)policy; + TRACE_OUT(cache_queue_policy_get_last_item); + return ((struct cache_policy_item_ *)TAILQ_LAST(&queue_policy->head, + cache_queue_policy_head_)); +} + +static struct cache_policy_item_ * +cache_queue_policy_get_next_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_queue_policy_ *queue_policy; + struct cache_queue_policy_item_ *queue_item; + + TRACE_IN(cache_queue_policy_get_next_item); + queue_policy = (struct cache_queue_policy_ *)policy; + queue_item = (struct cache_queue_policy_item_ *)item; + + TRACE_OUT(cache_queue_policy_get_next_item); + return ((struct cache_policy_item_ *)TAILQ_NEXT(queue_item, entries)); +} + +static struct cache_policy_item_ * +cache_queue_policy_get_prev_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_queue_policy_ *queue_policy; + struct cache_queue_policy_item_ *queue_item; + + TRACE_IN(cache_queue_policy_get_prev_item); + queue_policy = (struct cache_queue_policy_ *)policy; + queue_item = (struct cache_queue_policy_item_ *)item; + + TRACE_OUT(cache_queue_policy_get_prev_item); + return ((struct cache_policy_item_ *)TAILQ_PREV(queue_item, + cache_queue_policy_head_, entries)); +} + +/* + * Initializes cache_queue_policy_ by filling the structure with the functions + * pointers, defined above + */ +static struct cache_queue_policy_ * +init_cache_queue_policy(void) +{ + struct cache_queue_policy_ *retval; + + TRACE_IN(init_cache_queue_policy); + retval = (struct cache_queue_policy_ *)malloc( + sizeof(struct cache_queue_policy_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cache_queue_policy_)); + + retval->parent_data.create_item_func = cache_queue_policy_create_item; + retval->parent_data.destroy_item_func = cache_queue_policy_destroy_item; + + retval->parent_data.add_item_func = cache_queue_policy_add_item; + retval->parent_data.remove_item_func = cache_queue_policy_remove_item; + + retval->parent_data.get_first_item_func = + cache_queue_policy_get_first_item; + retval->parent_data.get_last_item_func = + cache_queue_policy_get_last_item; + retval->parent_data.get_next_item_func = + cache_queue_policy_get_next_item; + retval->parent_data.get_prev_item_func = + cache_queue_policy_get_prev_item; + + TAILQ_INIT(&retval->head); + TRACE_OUT(init_cache_queue_policy); + return (retval); +} + +static void +destroy_cache_queue_policy(struct cache_queue_policy_ *queue_policy) +{ + struct cache_queue_policy_item_ *queue_item; + + TRACE_IN(destroy_cache_queue_policy); + while (!TAILQ_EMPTY(&queue_policy->head)) { + queue_item = TAILQ_FIRST(&queue_policy->head); + TAILQ_REMOVE(&queue_policy->head, queue_item, entries); + cache_queue_policy_destroy_item( + (struct cache_policy_item_ *)queue_item); + } + free(queue_policy); + TRACE_OUT(destroy_cache_queue_policy); +} + +/* + * Makes cache_queue_policy_ behave like FIFO policy - we don't do anything, + * when the cache element is updated. So it always stays in its initial + * position in the queue - that is exactly the FIFO functionality. + */ +static void +cache_fifo_policy_update_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + + TRACE_IN(cache_fifo_policy_update_item); + /* policy and item arguments are ignored */ + TRACE_OUT(cache_fifo_policy_update_item); +} + +struct cache_policy_ * +init_cache_fifo_policy() +{ + struct cache_queue_policy_ *retval; + + TRACE_IN(init_cache_fifo_policy); + retval = init_cache_queue_policy(); + retval->parent_data.update_item_func = cache_fifo_policy_update_item; + + TRACE_OUT(init_cache_fifo_policy); + return ((struct cache_policy_ *)retval); +} + +void +destroy_cache_fifo_policy(struct cache_policy_ *policy) +{ + struct cache_queue_policy_ *queue_policy; + + TRACE_IN(destroy_cache_fifo_policy); + queue_policy = (struct cache_queue_policy_ *)policy; + destroy_cache_queue_policy(queue_policy); + TRACE_OUT(destroy_cache_fifo_policy); +} + +/* + * Makes cache_queue_policy_ behave like LRU policy. On each update, cache + * element is moved to the end of the queue - so it would be deleted in last + * turn. That is exactly the LRU policy functionality. + */ +static void +cache_lru_policy_update_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_queue_policy_ *queue_policy; + struct cache_queue_policy_item_ *queue_item; + + TRACE_IN(cache_lru_policy_update_item); + queue_policy = (struct cache_queue_policy_ *)policy; + queue_item = (struct cache_queue_policy_item_ *)item; + + TAILQ_REMOVE(&queue_policy->head, queue_item, entries); + TAILQ_INSERT_TAIL(&queue_policy->head, queue_item, entries); + TRACE_OUT(cache_lru_policy_update_item); +} + +struct cache_policy_ * +init_cache_lru_policy() +{ + struct cache_queue_policy_ *retval; + + TRACE_IN(init_cache_lru_policy); + retval = init_cache_queue_policy(); + retval->parent_data.update_item_func = cache_lru_policy_update_item; + + TRACE_OUT(init_cache_lru_policy); + return ((struct cache_policy_ *)retval); +} + +void +destroy_cache_lru_policy(struct cache_policy_ *policy) +{ + struct cache_queue_policy_ *queue_policy; + + TRACE_IN(destroy_cache_lru_policy); + queue_policy = (struct cache_queue_policy_ *)policy; + destroy_cache_queue_policy(queue_policy); + TRACE_OUT(destroy_cache_lru_policy); +} + +/* + * LFU (least frequently used) policy implementation differs much from the + * LRU and FIFO (both based on cache_queue_policy_). Almost all cache_policy_ + * functions are implemented specifically for this policy. The idea of this + * policy is to represent frequency (real number) as the integer number and + * use it as the index in the array. Each array's element is + * the list of elements. For example, if we have the 100-elements + * array for this policy, the elements with frequency 0.1 (calls per-second) + * would be in 10th element of the array. + */ +static struct cache_policy_item_ * +cache_lfu_policy_create_item(void) +{ + struct cache_lfu_policy_item_ *retval; + + TRACE_IN(cache_lfu_policy_create_item); + retval = (struct cache_lfu_policy_item_ *)malloc( + sizeof(struct cache_lfu_policy_item_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cache_lfu_policy_item_)); + + TRACE_OUT(cache_lfu_policy_create_item); + return ((struct cache_policy_item_ *)retval); +} + +static void +cache_lfu_policy_destroy_item(struct cache_policy_item_ *item) +{ + + TRACE_IN(cache_lfu_policy_destroy_item); + assert(item != NULL); + free(item); + TRACE_OUT(cache_lfu_policy_destroy_item); +} + +/* + * When placed in the LFU policy queue for the first time, the maximum + * frequency is assigned to the element + */ +static void +cache_lfu_policy_add_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_lfu_policy_ *lfu_policy; + struct cache_lfu_policy_item_ *lfu_item; + + TRACE_IN(cache_lfu_policy_add_item); + lfu_policy = (struct cache_lfu_policy_ *)policy; + lfu_item = (struct cache_lfu_policy_item_ *)item; + + lfu_item->frequency = CACHELIB_MAX_FREQUENCY - 1; + TAILQ_INSERT_HEAD(&(lfu_policy->groups[CACHELIB_MAX_FREQUENCY - 1]), + lfu_item, entries); + TRACE_OUT(cache_lfu_policy_add_item); +} + +/* + * On each update the frequency of the element is recalculated and, if it + * changed, the element would be moved to the another place in the array. + */ +static void +cache_lfu_policy_update_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_lfu_policy_ *lfu_policy; + struct cache_lfu_policy_item_ *lfu_item; + int index; + + TRACE_IN(cache_lfu_policy_update_item); + lfu_policy = (struct cache_lfu_policy_ *)policy; + lfu_item = (struct cache_lfu_policy_item_ *)item; + + /* + * We calculate the square of the request_count to avoid grouping of + * all elements at the start of the array (for example, if array size is + * 100 and most of its elements has frequency below the 0.01, they + * all would be grouped in the first array's position). Other + * techniques should be used here later to ensure, that elements are + * equally distributed in the array and not grouped in its beginning. + */ + if (lfu_item->parent_data.last_request_time.tv_sec != + lfu_item->parent_data.creation_time.tv_sec) { + index = ((double)lfu_item->parent_data.request_count * + (double)lfu_item->parent_data.request_count / + (lfu_item->parent_data.last_request_time.tv_sec - + lfu_item->parent_data.creation_time.tv_sec + 1)) * + CACHELIB_MAX_FREQUENCY; + if (index >= CACHELIB_MAX_FREQUENCY) + index = CACHELIB_MAX_FREQUENCY - 1; + } else + index = CACHELIB_MAX_FREQUENCY - 1; + + TAILQ_REMOVE(&(lfu_policy->groups[lfu_item->frequency]), lfu_item, + entries); + lfu_item->frequency = index; + TAILQ_INSERT_HEAD(&(lfu_policy->groups[index]), lfu_item, entries); + + TRACE_OUT(cache_lfu_policy_update_item); +} + +static void +cache_lfu_policy_remove_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_lfu_policy_ *lfu_policy; + struct cache_lfu_policy_item_ *lfu_item; + + TRACE_IN(cache_lfu_policy_remove_item); + lfu_policy = (struct cache_lfu_policy_ *)policy; + lfu_item = (struct cache_lfu_policy_item_ *)item; + + TAILQ_REMOVE(&(lfu_policy->groups[lfu_item->frequency]), lfu_item, + entries); + TRACE_OUT(cache_lfu_policy_remove_item); +} + +static struct cache_policy_item_ * +cache_lfu_policy_get_first_item(struct cache_policy_ *policy) +{ + struct cache_lfu_policy_ *lfu_policy; + struct cache_lfu_policy_item_ *lfu_item; + int i; + + TRACE_IN(cache_lfu_policy_get_first_item); + lfu_item = NULL; + lfu_policy = (struct cache_lfu_policy_ *)policy; + for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i) + if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) { + lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i])); + break; + } + + TRACE_OUT(cache_lfu_policy_get_first_item); + return ((struct cache_policy_item_ *)lfu_item); +} + +static struct cache_policy_item_ * +cache_lfu_policy_get_last_item(struct cache_policy_ *policy) +{ + struct cache_lfu_policy_ *lfu_policy; + struct cache_lfu_policy_item_ *lfu_item; + int i; + + TRACE_IN(cache_lfu_policy_get_last_item); + lfu_item = NULL; + lfu_policy = (struct cache_lfu_policy_ *)policy; + for (i = CACHELIB_MAX_FREQUENCY - 1; i >= 0; --i) + if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) { + lfu_item = TAILQ_LAST(&(lfu_policy->groups[i]), + cache_lfu_policy_group_); + break; + } + + TRACE_OUT(cache_lfu_policy_get_last_item); + return ((struct cache_policy_item_ *)lfu_item); +} + +static struct cache_policy_item_ * +cache_lfu_policy_get_next_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_lfu_policy_ *lfu_policy; + struct cache_lfu_policy_item_ *lfu_item; + int i; + + TRACE_IN(cache_lfu_policy_get_next_item); + lfu_policy = (struct cache_lfu_policy_ *)policy; + lfu_item = TAILQ_NEXT((struct cache_lfu_policy_item_ *)item, entries); + if (lfu_item == NULL) + { + for (i = ((struct cache_lfu_policy_item_ *)item)->frequency + 1; + i < CACHELIB_MAX_FREQUENCY; ++i) { + if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) { + lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i])); + break; + } + } + } + + TRACE_OUT(cache_lfu_policy_get_next_item); + return ((struct cache_policy_item_ *)lfu_item); +} + +static struct cache_policy_item_ * +cache_lfu_policy_get_prev_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_lfu_policy_ *lfu_policy; + struct cache_lfu_policy_item_ *lfu_item; + int i; + + TRACE_IN(cache_lfu_policy_get_prev_item); + lfu_policy = (struct cache_lfu_policy_ *)policy; + lfu_item = TAILQ_PREV((struct cache_lfu_policy_item_ *)item, + cache_lfu_policy_group_, entries); + if (lfu_item == NULL) + { + for (i = ((struct cache_lfu_policy_item_ *)item)->frequency - 1; + i >= 0; --i) + if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) { + lfu_item = TAILQ_LAST(&(lfu_policy->groups[i]), + cache_lfu_policy_group_); + break; + } + } + + TRACE_OUT(cache_lfu_policy_get_prev_item); + return ((struct cache_policy_item_ *)lfu_item); +} + +/* + * Initializes the cache_policy_ structure by filling it with appropriate + * functions pointers + */ +struct cache_policy_ * +init_cache_lfu_policy() +{ + int i; + struct cache_lfu_policy_ *retval; + + TRACE_IN(init_cache_lfu_policy); + retval = (struct cache_lfu_policy_ *)malloc( + sizeof(struct cache_lfu_policy_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cache_lfu_policy_)); + + retval->parent_data.create_item_func = cache_lfu_policy_create_item; + retval->parent_data.destroy_item_func = cache_lfu_policy_destroy_item; + + retval->parent_data.add_item_func = cache_lfu_policy_add_item; + retval->parent_data.update_item_func = cache_lfu_policy_update_item; + retval->parent_data.remove_item_func = cache_lfu_policy_remove_item; + + retval->parent_data.get_first_item_func = + cache_lfu_policy_get_first_item; + retval->parent_data.get_last_item_func = + cache_lfu_policy_get_last_item; + retval->parent_data.get_next_item_func = + cache_lfu_policy_get_next_item; + retval->parent_data.get_prev_item_func = + cache_lfu_policy_get_prev_item; + + for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i) + TAILQ_INIT(&(retval->groups[i])); + + TRACE_OUT(init_cache_lfu_policy); + return ((struct cache_policy_ *)retval); +} + +void +destroy_cache_lfu_policy(struct cache_policy_ *policy) +{ + int i; + struct cache_lfu_policy_ *lfu_policy; + struct cache_lfu_policy_item_ *lfu_item; + + TRACE_IN(destroy_cache_lfu_policy); + lfu_policy = (struct cache_lfu_policy_ *)policy; + for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i) { + while (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) { + lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i])); + TAILQ_REMOVE(&(lfu_policy->groups[i]), lfu_item, + entries); + cache_lfu_policy_destroy_item( + (struct cache_policy_item_ *)lfu_item); + } + } + free(policy); + TRACE_OUT(destroy_cache_lfu_policy); +} diff --git a/usr.sbin/cached/cacheplcs.h b/usr.sbin/cached/cacheplcs.h new file mode 100644 index 000000000000..36997f57a931 --- /dev/null +++ b/usr.sbin/cached/cacheplcs.h @@ -0,0 +1,137 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_CACHEPLCS_H__ +#define __CACHED_CACHEPLCS_H__ + +#include +#include +#include + +/* common policy definitions */ +#define CACHELIB_MAX_FREQUENCY 100 + +/* + * cache_policy_item_ represents some abstract cache element in the policy + * queue. connected_item pointers to the corresponding cache_policy_item_ in + * another policy queue. + */ +struct cache_policy_item_ +{ + char *key; + size_t key_size; + + size_t request_count; + struct timeval last_request_time; + struct timeval creation_time; + + struct cache_policy_item_ *connected_item; +}; + +/* + * cache_policy_ represents an abstract policy queue. It can be customized by + * setting appropriate function pointers + */ +struct cache_policy_ +{ + struct cache_policy_item_* (*create_item_func)(); + void (*destroy_item_func)(struct cache_policy_item_ *); + + void (*add_item_func)(struct cache_policy_ *, + struct cache_policy_item_ *); + void (*remove_item_func)(struct cache_policy_ *, + struct cache_policy_item_ *); + void (*update_item_func)(struct cache_policy_ *, + struct cache_policy_item_ *); + + struct cache_policy_item_ *(*get_first_item_func)( + struct cache_policy_ *); + struct cache_policy_item_ *(*get_last_item_func)( + struct cache_policy_ *); + struct cache_policy_item_ *(*get_next_item_func)( + struct cache_policy_ *, struct cache_policy_item_ *); + struct cache_policy_item_ *(*get_prev_item_func)( + struct cache_policy_ *, struct cache_policy_item_ *); +}; + +/* + * LFU cache policy item "inherited" from cache_policy_item_ structure + */ +struct cache_lfu_policy_item_ +{ + struct cache_policy_item_ parent_data; + int frequency; + + TAILQ_ENTRY(cache_lfu_policy_item_) entries; +}; + +TAILQ_HEAD(cache_lfu_policy_group_, cache_lfu_policy_item_); + +/* + * LFU policy queue "inherited" from cache_policy_. + */ +struct cache_lfu_policy_ +{ + struct cache_policy_ parent_data; + struct cache_lfu_policy_group_ groups[CACHELIB_MAX_FREQUENCY]; +}; + +/* + * LRU and FIFO policies item "inherited" from cache_policy_item_ + */ +struct cache_queue_policy_item_ +{ + struct cache_policy_item_ parent_data; + TAILQ_ENTRY(cache_queue_policy_item_) entries; +}; + +/* + * LRU and FIFO policies "inherited" from cache_policy_ + */ +struct cache_queue_policy_ +{ + struct cache_policy_ parent_data; + TAILQ_HEAD(cache_queue_policy_head_, cache_queue_policy_item_) head; +}; + +typedef struct cache_queue_policy_ cache_fifo_policy_; +typedef struct cache_queue_policy_ cache_lru_policy_; + +/* fifo policy routines */ +extern struct cache_policy_ *init_cache_fifo_policy(); +extern void destroy_cache_fifo_policy(struct cache_policy_ *); + +/* lru policy routines */ +extern struct cache_policy_ *init_cache_lru_policy(); +extern void destroy_cache_lru_policy(struct cache_policy_ *); + +/* lfu policy routines */ +extern struct cache_policy_ *init_cache_lfu_policy(); +extern void destroy_cache_lfu_policy(struct cache_policy_ *); + +#endif diff --git a/usr.sbin/cached/config.c b/usr.sbin/cached/config.c new file mode 100644 index 000000000000..bc3eb49eeb71 --- /dev/null +++ b/usr.sbin/cached/config.c @@ -0,0 +1,588 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include "config.h" +#include "debug.h" +#include "log.h" + +/* + * Default entries, which always exist in the configuration + */ +const char *c_default_entries[6] = { + NSDB_PASSWD, + NSDB_GROUP, + NSDB_HOSTS, + NSDB_SERVICES, + NSDB_PROTOCOLS, + NSDB_RPC + }; + +static int configuration_entry_cmp(const void *, const void *); +static int configuration_entry_sort_cmp(const void *, const void *); +static int configuration_entry_cache_mp_sort_cmp(const void *, const void *); +static int configuration_entry_cache_mp_cmp(const void *, const void *); +static int configuration_entry_cache_mp_part_cmp(const void *, const void *); +static struct configuration_entry *create_configuration_entry(const char *, + struct timeval const *, struct timeval const *, + struct common_cache_entry_params const *, + struct common_cache_entry_params const *, + struct mp_cache_entry_params const *); + +static int +configuration_entry_sort_cmp(const void *e1, const void *e2) +{ + return (strcmp((*((struct configuration_entry **)e1))->name, + (*((struct configuration_entry **)e2))->name + )); +} + +static int +configuration_entry_cmp(const void *e1, const void *e2) +{ + return (strcmp((const char *)e1, + (*((struct configuration_entry **)e2))->name + )); +} + +static int +configuration_entry_cache_mp_sort_cmp(const void *e1, const void *e2) +{ + return (strcmp((*((cache_entry *)e1))->params->entry_name, + (*((cache_entry *)e2))->params->entry_name + )); +} + +static int +configuration_entry_cache_mp_cmp(const void *e1, const void *e2) +{ + return (strcmp((const char *)e1, + (*((cache_entry *)e2))->params->entry_name + )); +} + +static int +configuration_entry_cache_mp_part_cmp(const void *e1, const void *e2) +{ + return (strncmp((const char *)e1, + (*((cache_entry *)e2))->params->entry_name, + strlen((const char *)e1) + )); +} + +static struct configuration_entry * +create_configuration_entry(const char *name, + struct timeval const *common_timeout, + struct timeval const *mp_timeout, + struct common_cache_entry_params const *positive_params, + struct common_cache_entry_params const *negative_params, + struct mp_cache_entry_params const *mp_params) +{ + struct configuration_entry *retval; + size_t size; + int res; + + TRACE_IN(create_configuration_entry); + assert(name != NULL); + assert(positive_params != NULL); + assert(negative_params != NULL); + assert(mp_params != NULL); + + retval = (struct configuration_entry *)malloc( + sizeof(struct configuration_entry)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct configuration_entry)); + + res = pthread_mutex_init(&retval->positive_cache_lock, NULL); + if (res != 0) { + free(retval); + LOG_ERR_2("create_configuration_entry", + "can't create positive cache lock"); + TRACE_OUT(create_configuration_entry); + return (NULL); + } + + res = pthread_mutex_init(&retval->negative_cache_lock, NULL); + if (res != 0) { + pthread_mutex_destroy(&retval->positive_cache_lock); + free(retval); + LOG_ERR_2("create_configuration_entry", + "can't create negative cache lock"); + TRACE_OUT(create_configuration_entry); + return (NULL); + } + + res = pthread_mutex_init(&retval->mp_cache_lock, NULL); + if (res != 0) { + pthread_mutex_destroy(&retval->positive_cache_lock); + pthread_mutex_destroy(&retval->negative_cache_lock); + free(retval); + LOG_ERR_2("create_configuration_entry", + "can't create negative cache lock"); + TRACE_OUT(create_configuration_entry); + return (NULL); + } + + memcpy(&retval->positive_cache_params, positive_params, + sizeof(struct common_cache_entry_params)); + memcpy(&retval->negative_cache_params, negative_params, + sizeof(struct common_cache_entry_params)); + memcpy(&retval->mp_cache_params, mp_params, + sizeof(struct mp_cache_entry_params)); + + size = strlen(name); + retval->name = (char *)malloc(size + 1); + assert(retval->name != NULL); + memset(retval->name, 0, size + 1); + memcpy(retval->name, name, size); + + memcpy(&retval->common_query_timeout, common_timeout, + sizeof(struct timeval)); + memcpy(&retval->mp_query_timeout, mp_timeout, + sizeof(struct timeval)); + + asprintf(&retval->positive_cache_params.entry_name, "%s+", name); + assert(retval->positive_cache_params.entry_name != NULL); + + asprintf(&retval->negative_cache_params.entry_name, "%s-", name); + assert(retval->negative_cache_params.entry_name != NULL); + + asprintf(&retval->mp_cache_params.entry_name, "%s*", name); + assert(retval->mp_cache_params.entry_name != NULL); + + TRACE_OUT(create_configuration_entry); + return (retval); +} + +/* + * Creates configuration entry and fills it with default values + */ +struct configuration_entry * +create_def_configuration_entry(const char *name) +{ + struct common_cache_entry_params positive_params, negative_params; + struct mp_cache_entry_params mp_params; + struct timeval default_common_timeout, default_mp_timeout; + + struct configuration_entry *res = NULL; + + TRACE_IN(create_def_configuration_entry); + memset(&positive_params, 0, + sizeof(struct common_cache_entry_params)); + positive_params.entry_type = CET_COMMON; + positive_params.cache_entries_size = DEFAULT_CACHE_HT_SIZE; + positive_params.max_elemsize = DEFAULT_POSITIVE_ELEMENTS_SIZE; + positive_params.satisf_elemsize = DEFAULT_POSITIVE_ELEMENTS_SIZE / 2; + positive_params.max_lifetime.tv_sec = DEFAULT_POSITIVE_LIFETIME; + positive_params.policy = CPT_LRU; + + memcpy(&negative_params, &positive_params, + sizeof(struct common_cache_entry_params)); + negative_params.max_elemsize = DEFAULT_NEGATIVE_ELEMENTS_SIZE; + negative_params.satisf_elemsize = DEFAULT_NEGATIVE_ELEMENTS_SIZE / 2; + negative_params.max_lifetime.tv_sec = DEFAULT_NEGATIVE_LIFETIME; + negative_params.policy = CPT_FIFO; + + memset(&default_common_timeout, 0, sizeof(struct timeval)); + default_common_timeout.tv_sec = DEFAULT_COMMON_ENTRY_TIMEOUT; + + memset(&default_mp_timeout, 0, sizeof(struct timeval)); + default_mp_timeout.tv_sec = DEFAULT_MP_ENTRY_TIMEOUT; + + memset(&mp_params, 0, + sizeof(struct mp_cache_entry_params)); + mp_params.entry_type = CET_MULTIPART; + mp_params.max_elemsize = DEFAULT_MULTIPART_ELEMENTS_SIZE; + mp_params.max_sessions = DEFAULT_MULITPART_SESSIONS_SIZE; + mp_params.max_lifetime.tv_sec = DEFAULT_MULITPART_LIFETIME; + + res = create_configuration_entry(name, &default_common_timeout, + &default_mp_timeout, &positive_params, &negative_params, + &mp_params); + + TRACE_OUT(create_def_configuration_entry); + return (res); +} + +void +destroy_configuration_entry(struct configuration_entry *entry) +{ + TRACE_IN(destroy_configuration_entry); + assert(entry != NULL); + pthread_mutex_destroy(&entry->positive_cache_lock); + pthread_mutex_destroy(&entry->negative_cache_lock); + pthread_mutex_destroy(&entry->mp_cache_lock); + free(entry->name); + free(entry->positive_cache_params.entry_name); + free(entry->negative_cache_params.entry_name); + free(entry->mp_cache_params.entry_name); + free(entry->mp_cache_entries); + free(entry); + TRACE_OUT(destroy_configuration_entry); +} + +int +add_configuration_entry(struct configuration *config, + struct configuration_entry *entry) +{ + TRACE_IN(add_configuration_entry); + assert(entry != NULL); + assert(entry->name != NULL); + if (configuration_find_entry(config, entry->name) != NULL) { + TRACE_OUT(add_configuration_entry); + return (-1); + } + + if (config->entries_size == config->entries_capacity) { + struct configuration_entry **new_entries; + + config->entries_capacity *= 2; + new_entries = (struct configuration_entry **)malloc( + sizeof(struct configuration_entry *) * + config->entries_capacity); + assert(new_entries != NULL); + memset(new_entries, 0, sizeof(struct configuration_entry *) * + config->entries_capacity); + memcpy(new_entries, config->entries, + sizeof(struct configuration_entry *) * + config->entries_size); + + free(config->entries); + config->entries = new_entries; + } + + config->entries[config->entries_size++] = entry; + qsort(config->entries, config->entries_size, + sizeof(struct configuration_entry *), + configuration_entry_sort_cmp); + + TRACE_OUT(add_configuration_entry); + return (0); +} + +size_t +configuration_get_entries_size(struct configuration *config) +{ + TRACE_IN(configuration_get_entries_size); + assert(config != NULL); + TRACE_OUT(configuration_get_entries_size); + return (config->entries_size); +} + +struct configuration_entry * +configuration_get_entry(struct configuration *config, size_t index) +{ + TRACE_IN(configuration_get_entry); + assert(config != NULL); + assert(index < config->entries_size); + TRACE_OUT(configuration_get_entry); + return (config->entries[index]); +} + +struct configuration_entry * +configuration_find_entry(struct configuration *config, + const char *name) +{ + struct configuration_entry **retval; + + TRACE_IN(configuration_find_entry); + + retval = bsearch(name, config->entries, config->entries_size, + sizeof(struct configuration_entry *), configuration_entry_cmp); + TRACE_OUT(configuration_find_entry); + + return ((retval != NULL) ? *retval : NULL); +} + +/* + * All multipart cache entries are stored in the configuration_entry in the + * sorted array (sorted by names). The 3 functions below manage this array. + */ + +int +configuration_entry_add_mp_cache_entry(struct configuration_entry *config_entry, + cache_entry c_entry) +{ + cache_entry *new_mp_entries, *old_mp_entries; + + TRACE_IN(configuration_entry_add_mp_cache_entry); + ++config_entry->mp_cache_entries_size; + new_mp_entries = (cache_entry *)malloc(sizeof(cache_entry) * + config_entry->mp_cache_entries_size); + assert(new_mp_entries != NULL); + new_mp_entries[0] = c_entry; + + if (config_entry->mp_cache_entries_size - 1 > 0) { + memcpy(new_mp_entries + 1, + config_entry->mp_cache_entries, + (config_entry->mp_cache_entries_size - 1) * + sizeof(cache_entry)); + } + + old_mp_entries = config_entry->mp_cache_entries; + config_entry->mp_cache_entries = new_mp_entries; + free(old_mp_entries); + + qsort(config_entry->mp_cache_entries, + config_entry->mp_cache_entries_size, + sizeof(cache_entry), + configuration_entry_cache_mp_sort_cmp); + + TRACE_OUT(configuration_entry_add_mp_cache_entry); + return (0); +} + +cache_entry +configuration_entry_find_mp_cache_entry( + struct configuration_entry *config_entry, const char *mp_name) +{ + cache_entry *result; + + TRACE_IN(configuration_entry_find_mp_cache_entry); + result = bsearch(mp_name, config_entry->mp_cache_entries, + config_entry->mp_cache_entries_size, + sizeof(cache_entry), configuration_entry_cache_mp_cmp); + + if (result == NULL) { + TRACE_OUT(configuration_entry_find_mp_cache_entry); + return (NULL); + } else { + TRACE_OUT(configuration_entry_find_mp_cache_entry); + return (*result); + } +} + +/* + * Searches for all multipart entries with names starting with mp_name. + * Needed for cache flushing. + */ +int +configuration_entry_find_mp_cache_entries( + struct configuration_entry *config_entry, const char *mp_name, + cache_entry **start, cache_entry **finish) +{ + cache_entry *result; + + TRACE_IN(configuration_entry_find_mp_cache_entries); + result = bsearch(mp_name, config_entry->mp_cache_entries, + config_entry->mp_cache_entries_size, + sizeof(cache_entry), configuration_entry_cache_mp_part_cmp); + + if (result == NULL) { + TRACE_OUT(configuration_entry_find_mp_cache_entries); + return (-1); + } + + *start = result; + *finish = result + 1; + + while (*start != config_entry->mp_cache_entries) { + if (configuration_entry_cache_mp_part_cmp(mp_name, *start - 1) == 0) + *start = *start - 1; + else + break; + } + + while (*finish != config_entry->mp_cache_entries + + config_entry->mp_cache_entries_size) { + + if (configuration_entry_cache_mp_part_cmp( + mp_name, *finish) == 0) + *finish = *finish + 1; + else + break; + } + + TRACE_OUT(configuration_entry_find_mp_cache_entries); + return (0); +} + +/* + * Configuration entry uses rwlock to handle access to its fields. + */ +void +configuration_lock_rdlock(struct configuration *config) +{ + TRACE_IN(configuration_lock_rdlock); + pthread_rwlock_rdlock(&config->rwlock); + TRACE_OUT(configuration_lock_rdlock); +} + +void +configuration_lock_wrlock(struct configuration *config) +{ + TRACE_IN(configuration_lock_wrlock); + pthread_rwlock_wrlock(&config->rwlock); + TRACE_OUT(configuration_lock_wrlock); +} + +void +configuration_unlock(struct configuration *config) +{ + TRACE_IN(configuration_unlock); + pthread_rwlock_unlock(&config->rwlock); + TRACE_OUT(configuration_unlock); +} + +/* + * Configuration entry uses 3 mutexes to handle cache operations. They are + * acquired by configuration_lock_entry and configuration_unlock_entry + * functions. + */ +void +configuration_lock_entry(struct configuration_entry *entry, + enum config_entry_lock_type lock_type) +{ + TRACE_IN(configuration_lock_entry); + assert(entry != NULL); + + switch (lock_type) { + case CELT_POSITIVE: + pthread_mutex_lock(&entry->positive_cache_lock); + break; + case CELT_NEGATIVE: + pthread_mutex_lock(&entry->negative_cache_lock); + break; + case CELT_MULTIPART: + pthread_mutex_lock(&entry->mp_cache_lock); + break; + default: + /* should be unreachable */ + break; + } + TRACE_OUT(configuration_lock_entry); +} + +void +configuration_unlock_entry(struct configuration_entry *entry, + enum config_entry_lock_type lock_type) +{ + TRACE_IN(configuration_unlock_entry); + assert(entry != NULL); + + switch (lock_type) { + case CELT_POSITIVE: + pthread_mutex_unlock(&entry->positive_cache_lock); + break; + case CELT_NEGATIVE: + pthread_mutex_unlock(&entry->negative_cache_lock); + break; + case CELT_MULTIPART: + pthread_mutex_unlock(&entry->mp_cache_lock); + break; + default: + /* should be unreachable */ + break; + } + TRACE_OUT(configuration_unlock_entry); +} + +struct configuration * +init_configuration(void) +{ + struct configuration *retval; + + TRACE_IN(init_configuration); + retval = (struct configuration *)malloc(sizeof(struct configuration)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct configuration)); + + retval->entries_capacity = INITIAL_ENTRIES_CAPACITY; + retval->entries = (struct configuration_entry **)malloc( + sizeof(struct configuration_entry *) * + retval->entries_capacity); + assert(retval->entries != NULL); + memset(retval->entries, 0, sizeof(struct configuration_entry *) * + retval->entries_capacity); + + pthread_rwlock_init(&retval->rwlock, NULL); + + TRACE_OUT(init_configuration); + return (retval); +} + +void +fill_configuration_defaults(struct configuration *config) +{ + size_t len, i; + + TRACE_IN(fill_configuration_defaults); + assert(config != NULL); + + if (config->socket_path != NULL) + free(config->socket_path); + + len = strlen(DEFAULT_SOCKET_PATH); + config->socket_path = (char *)malloc(len + 1); + assert(config->socket_path != NULL); + memset(config->socket_path, 0, len + 1); + memcpy(config->socket_path, DEFAULT_SOCKET_PATH, len); + + len = strlen(DEFAULT_PIDFILE_PATH); + config->pidfile_path = (char *)malloc(len + 1); + assert(config->pidfile_path != NULL); + memset(config->pidfile_path, 0, len + 1); + memcpy(config->pidfile_path, DEFAULT_PIDFILE_PATH, len); + + config->socket_mode = S_IFSOCK | S_IRUSR | S_IWUSR | + S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + config->force_unlink = 1; + + config->query_timeout = DEFAULT_QUERY_TIMEOUT; + config->threads_num = DEFAULT_THREADS_NUM; + + for (i = 0; i < config->entries_size; ++i) + destroy_configuration_entry(config->entries[i]); + config->entries_size = 0; + + TRACE_OUT(fill_configuration_defaults); +} + +void +destroy_configuration(struct configuration *config) +{ + int i; + TRACE_IN(destroy_configuration); + assert(config != NULL); + free(config->pidfile_path); + free(config->socket_path); + + for (i = 0; i < config->entries_size; ++i) + destroy_configuration_entry(config->entries[i]); + free(config->entries); + + pthread_rwlock_destroy(&config->rwlock); + free(config); + TRACE_OUT(destroy_configuration); +} diff --git a/usr.sbin/cached/config.h b/usr.sbin/cached/config.h new file mode 100644 index 000000000000..b54dc9bfbacf --- /dev/null +++ b/usr.sbin/cached/config.h @@ -0,0 +1,156 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_CONFIG_H__ +#define __CACHED_CONFIG_H__ + +#include +#include +#include +#include +#include +#include "cachelib.h" + +#define DEFAULT_QUERY_TIMEOUT 8 +#define DEFAULT_THREADS_NUM 8 + +#define DEFAULT_COMMON_ENTRY_TIMEOUT 10 +#define DEFAULT_MP_ENTRY_TIMEOUT 60 +#define DEFAULT_CACHE_HT_SIZE 257 + +#define INITIAL_ENTRIES_CAPACITY 8 +#define DEFAULT_SOCKET_PATH "/var/run/cached" +#define DEFAULT_PIDFILE_PATH "/var/run/cached.pid" + +#define DEFAULT_POSITIVE_ELEMENTS_SIZE (2048) +#define DEFAULT_POSITIVE_LIFETIME (3600) + +#define DEFAULT_NEGATIVE_ELEMENTS_SIZE (2048) +#define DEFAULT_NEGATIVE_LIFETIME (60) + +#define DEFAULT_MULTIPART_ELEMENTS_SIZE (1024 * 8) +#define DEFAULT_MULITPART_SESSIONS_SIZE (1024) +#define DEFAULT_MULITPART_LIFETIME (3600) + +extern const char *c_default_entries[6]; + +/* + * Configuration entry represents the details of each cache entry in the + * config file (i.e. passwd or group). Its purpose also is to acquire locks + * of three different types (for usual read/write caching, for multipart + * caching and for caching of the negative results) for that cache entry. + */ +struct configuration_entry { + struct common_cache_entry_params positive_cache_params; + struct common_cache_entry_params negative_cache_params; + struct mp_cache_entry_params mp_cache_params; + + /* + * configuration_entry holds pointers for all actual cache_entries, + * which are used for it. There is one for positive caching, one for + * for negative caching, and several (one per each euid/egid) for + * multipart caching. + */ + cache_entry positive_cache_entry; + cache_entry negative_cache_entry; + + cache_entry *mp_cache_entries; + size_t mp_cache_entries_size; + + struct timeval common_query_timeout; + struct timeval mp_query_timeout; + + char *name; + pthread_mutex_t positive_cache_lock; + pthread_mutex_t negative_cache_lock; + pthread_mutex_t mp_cache_lock; + + int perform_actual_lookups; + int enabled; +}; + +/* + * Contains global configuration options and array of all configuration entries + */ +struct configuration { + char *pidfile_path; + char *socket_path; + + struct configuration_entry **entries; + size_t entries_capacity; + size_t entries_size; + + pthread_rwlock_t rwlock; + + mode_t socket_mode; + int force_unlink; + int query_timeout; + + int threads_num; +}; + +enum config_entry_lock_type { + CELT_POSITIVE, + CELT_NEGATIVE, + CELT_MULTIPART +}; + +extern struct configuration *init_configuration(void); +extern void destroy_configuration(struct configuration *); +extern void fill_configuration_defaults(struct configuration *); + +extern int add_configuration_entry(struct configuration *, + struct configuration_entry *); +extern struct configuration_entry *create_def_configuration_entry( + const char *); +extern void destroy_configuration_entry(struct configuration_entry *); +extern size_t configuration_get_entries_size(struct configuration *); +extern struct configuration_entry *configuration_get_entry( + struct configuration *, size_t); +extern struct configuration_entry *configuration_find_entry( + struct configuration *, const char *); + +extern int configuration_entry_add_mp_cache_entry(struct configuration_entry *, + cache_entry); +extern cache_entry configuration_entry_find_mp_cache_entry( + struct configuration_entry *, + const char *); +extern int configuration_entry_find_mp_cache_entries( + struct configuration_entry *, const char *, cache_entry **, + cache_entry **); + +extern void configuration_lock_rdlock(struct configuration *config); +extern void configuration_lock_wrlock(struct configuration *config); +extern void configuration_unlock(struct configuration *config); + +extern void configuration_lock_entry(struct configuration_entry *, + enum config_entry_lock_type); +extern void configuration_unlock_entry(struct configuration_entry *, + enum config_entry_lock_type); + +#endif diff --git a/usr.sbin/cached/debug.c b/usr.sbin/cached/debug.c new file mode 100644 index 000000000000..420c517c1159 --- /dev/null +++ b/usr.sbin/cached/debug.c @@ -0,0 +1,149 @@ +/*- + * Copyright (c) 2004 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "debug.h" + +static int trace_level = 0; +static int trace_level_bk = 0; + +void +__trace_in(const char *s, const char *f, int l) +{ + int i; + if (trace_level < TRACE_WANTED) + { + for (i = 0; i < trace_level; ++i) + printf("\t"); + + printf("=> %s\n", s); + } + + ++trace_level; +} + +void +__trace_point(const char *f, int l) +{ + int i; + + if (trace_level < TRACE_WANTED) + { + for (i = 0; i < trace_level - 1; ++i) + printf("\t"); + + printf("= %s: %d\n", f, l); + } +} + +void +__trace_msg(const char *msg, const char *f, int l) +{ + int i; + + if (trace_level < TRACE_WANTED) + { + for (i = 0; i < trace_level - 1; ++i) + printf("\t"); + + printf("= MSG %s, %s: %d\n", msg, f, l); + } +} + +void +__trace_ptr(const char *desc, const void *p, const char *f, int l) +{ + int i; + + if (trace_level < TRACE_WANTED) + { + for (i = 0; i < trace_level - 1; ++i) + printf("\t"); + + printf("= PTR %s: %p, %s: %d\n", desc, p, f, l); + } +} + +void +__trace_int(const char *desc, int i, const char *f, int l) +{ + int j; + + if (trace_level < TRACE_WANTED) + { + for (j = 0; j < trace_level - 1; ++j) + printf("\t"); + + printf("= INT %s: %i, %s: %d\n",desc, i, f, l); + } +} + +void +__trace_str(const char *desc, const char *s, const char *f, int l) +{ + int i; + + if (trace_level < TRACE_WANTED) + { + for (i = 0; i < trace_level - 1; ++i) + printf("\t"); + + printf("= STR %s: '%s', %s: %d\n", desc, s, f, l); + } +} + +void +__trace_out(const char *s, const char *f, int l) +{ + int i; + + --trace_level; + if (trace_level < TRACE_WANTED) + { + for (i = 0; i < trace_level; ++i) + printf("\t"); + + printf("<= %s\n", s); + } +} + +void +__trace_on() +{ + trace_level = trace_level_bk; + trace_level_bk = 0; +} + +void +__trace_off() +{ + trace_level_bk = trace_level; + trace_level = 1024; +} diff --git a/usr.sbin/cached/debug.h b/usr.sbin/cached/debug.h new file mode 100644 index 000000000000..320e10f30a1b --- /dev/null +++ b/usr.sbin/cached/debug.h @@ -0,0 +1,67 @@ +/*- + * Copyright (c) 2004 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_DEBUG_H__ +#define __CACHED_DEBUG_H__ + +#define TRACE_WANTED 32 + +/* #ifndef NDEBUG */ +#if 0 +#define TRACE_IN(x) __trace_in(#x, __FILE__, __LINE__) +#define TRACE_POINT() __trace_point(__FILE__, __LINE__) +#define TRACE_MSG(x) __trace_msg(x, __FILE__, __LINE__) +#define TRACE_PTR(p) __trace_ptr(#p, p, __FILE__, __LINE__) +#define TRACE_INT(i) __trace_int(#i, i, __FILE__, __LINE__) +#define TRACE_STR(s) __trace_str(#s, s, __FILE__, __LINE__) +#define TRACE_OUT(x) __trace_out(#x, __FILE__, __LINE__) +#define TRACE_ON() __trace_on() +#define TRACE_OFF() __trace_off() +#else +#define TRACE_IN(x) +#define TRACE_POINT() +#define TRACE_MSG(x) +#define TRACE_PTR(p) +#define TRACE_INT(i) +#define TRACE_STR(s) +#define TRACE_OUT(x) +#define TRACE_ON() +#define TRACE_OFF() +#endif + +extern void __trace_in(const char *, const char *, int); +extern void __trace_point(const char *, int); +extern void __trace_msg(const char *, const char *, int); +extern void __trace_ptr(const char *, const void *, const char *, int); +extern void __trace_int(const char *, int, const char *, int); +extern void __trace_str(const char *, const char *, const char *, int); +extern void __trace_out(const char *, const char *, int); +extern void __trace_on(); +extern void __trace_off(); + +#endif diff --git a/usr.sbin/cached/hashtable.h b/usr.sbin/cached/hashtable.h new file mode 100644 index 000000000000..86dad9f92b34 --- /dev/null +++ b/usr.sbin/cached/hashtable.h @@ -0,0 +1,218 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHELIB_HASHTABLE_H__ +#define __CACHELIB_HASHTABLE_H__ + +#include +#include + +#define HASHTABLE_INITIAL_ENTRIES_CAPACITY 8 +typedef int hashtable_index_t; + +/* + * This file contains queue.h-like macro definitions for hash tables. + * Hash table is organized as an array of the specified size of the user + * defined (with HASTABLE_ENTRY_HEAD) structures. Each hash table + * entry (user defined structure) stores its elements in the sorted array. + * You can place elements into the hash table, retrieve elements with + * specified key, traverse through all elements, and delete them. + * New elements are placed into the hash table by using the compare and + * hashing functions, provided by the user. + */ + +/* + * Defines the hash table entry structure, that uses specified type of + * elements. + */ +#define HASHTABLE_ENTRY_HEAD(name, type) struct name { \ + type *values; \ + size_t capacity; \ + size_t size; \ +} + +/* + * Defines the hash table structure, which uses the specified type of entries. + * The only restriction for entries is that is that they should have the field, + * defined with HASHTABLE_ENTRY_HEAD macro. + */ +#define HASHTABLE_HEAD(name, entry) struct name { \ + struct entry *entries; \ + size_t entries_size; \ +} + +#define HASHTABLE_ENTRIES_COUNT(table) ((table)->entries_size) + +/* + * Unlike most of queue.h data types, hash tables can not be initialized + * statically - so there is no HASHTABLE_HEAD_INITIALIZED macro. + */ +#define HASHTABLE_INIT(table, type, field, _entries_size) \ + do { \ + hashtable_index_t var; \ + (table)->entries = (void *)malloc( \ + sizeof(*(table)->entries) * (_entries_size)); \ + memset((table)->entries, 0, \ + sizeof(*(table)->entries) * (_entries_size)); \ + (table)->entries_size = (_entries_size); \ + for (var = 0; var < HASHTABLE_ENTRIES_COUNT(table); ++var) {\ + (table)->entries[var].field.capacity = \ + HASHTABLE_INITIAL_ENTRIES_CAPACITY; \ + (table)->entries[var].field.size = 0; \ + (table)->entries[var].field.values = (type *)malloc(\ + sizeof(type) * \ + HASHTABLE_INITIAL_ENTRIES_CAPACITY); \ + assert((table)->entries[var].field.values != NULL);\ + } \ + } while (0) + +/* + * All initialized hashtables should be destroyed with this macro. + */ +#define HASHTABLE_DESTROY(table, field) \ + do { \ + hashtable_index_t var; \ + for (var = 0; var < HASHTABLE_ENTRIES_COUNT(table); ++var) {\ + free((table)->entries[var].field.values); \ + } \ + } while (0) + +#define HASHTABLE_GET_ENTRY(table, hash) (&((table)->entries[hash])) + +/* + * Traverses through all hash table entries + */ +#define HASHTABLE_FOREACH(table, var) \ + for ((var) = &((table)->entries[0]); \ + (var) < &((table)->entries[HASHTABLE_ENTRIES_COUNT(table)]);\ + ++(var)) + +/* + * Traverses through all elements of the specified hash table entry + */ +#define HASHTABLE_ENTRY_FOREACH(entry, field, var) \ + for ((var) = &((entry)->field.values[0]); \ + (var) < &((entry)->field.values[(entry)->field.size]); \ + ++(var)) + +#define HASHTABLE_ENTRY_CLEAR(entry, field) \ + ((entry)->field.size = 0) + +#define HASHTABLE_ENTRY_SIZE(entry, field) \ + ((entry)->field.size) + +#define HASHTABLE_ENTRY_CAPACITY(entry, field) \ + ((entry)->field.capacity) + +#define HASHTABLE_ENTRY_CAPACITY_INCREASE(entry, field, type) \ + (entry)->field.capacity *= 2; \ + (entry)->field.values = (type *)realloc((entry)->field.values, \ + (entry)->field.capacity * sizeof(type)); + +#define HASHTABLE_ENTRY_CAPACITY_DECREASE(entry, field, type) \ + (entry)->field.capacity /= 2; \ + (entry)->field.values = (type *)realloc((entry)->field.values, \ + (entry)->field.capacity * sizeof(type)); + +/* + * Generates prototypes for the hash table functions + */ +#define HASHTABLE_PROTOTYPE(name, entry_, type) \ +hashtable_index_t name##_CALCULATE_HASH(struct name *, type *); \ +void name##_ENTRY_STORE(struct entry_*, type *); \ +type *name##_ENTRY_FIND(struct entry_*, type *); \ +type *name##_ENTRY_FIND_SPECIAL(struct entry_ *, type *, \ + int (*) (const void *, const void *)); \ +void name##_ENTRY_REMOVE(struct entry_*, type *); + +/* + * Generates implementations of the hash table functions + */ +#define HASHTABLE_GENERATE(name, entry_, type, field, HASH, CMP) \ +hashtable_index_t name##_CALCULATE_HASH(struct name *table, type *data) \ +{ \ + \ + return HASH(data, table->entries_size); \ +} \ + \ +void name##_ENTRY_STORE(struct entry_ *the_entry, type *data) \ +{ \ + \ + if (the_entry->field.size == the_entry->field.capacity) \ + HASHTABLE_ENTRY_CAPACITY_INCREASE(the_entry, field, type);\ + \ + memcpy(&(the_entry->field.values[the_entry->field.size++]), \ + data, \ + sizeof(type)); \ + qsort(the_entry->field.values, the_entry->field.size, \ + sizeof(type), CMP); \ +} \ + \ +type *name##_ENTRY_FIND(struct entry_ *the_entry, type *key) \ +{ \ + \ + return ((type *)bsearch(key, the_entry->field.values, \ + the_entry->field.size, sizeof(type), CMP)); \ +} \ + \ +type *name##_ENTRY_FIND_SPECIAL(struct entry_ *the_entry, type *key, \ + int (*compar) (const void *, const void *)) \ +{ \ + return ((type *)bsearch(key, the_entry->field.values, \ + the_entry->field.size, sizeof(type), compar)); \ +} \ + \ +void name##_ENTRY_REMOVE(struct entry_ *the_entry, type *del_elm) \ +{ \ + \ + memmove(del_elm, del_elm + 1, \ + (&the_entry->field.values[--the_entry->field.size] - del_elm) *\ + sizeof(type)); \ +} + +/* + * Macro definitions below wrap the functions, generaed with + * HASHTABLE_GENERATE macro. You should use them and avoid using generated + * functions directly. + */ +#define HASHTABLE_CALCULATE_HASH(name, table, data) \ + (name##_CALCULATE_HASH((table), data)) + +#define HASHTABLE_ENTRY_STORE(name, entry, data) \ + name##_ENTRY_STORE((entry), data) + +#define HASHTABLE_ENTRY_FIND(name, entry, key) \ + (name##_ENTRY_FIND((entry), (key))) + +#define HASHTABLE_ENTRY_FIND_SPECIAL(name, entry, key, cmp) \ + (name##_ENTRY_FIND_SPECIAL((entry), (key), (cmp))) + +#define HASHTABLE_ENTRY_REMOVE(name, entry, del_elm) \ + name##_ENTRY_REMOVE((entry), (del_elm)) + +#endif diff --git a/usr.sbin/cached/log.c b/usr.sbin/cached/log.c new file mode 100644 index 000000000000..053930a233fe --- /dev/null +++ b/usr.sbin/cached/log.c @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include "log.h" + +void +__log_msg(int level, const char *sender, const char *message, ...) +{ + va_list ap; + char *fmessage; + + fmessage = NULL; + va_start(ap, message); + vasprintf(&fmessage, message, ap); + va_end(ap); + assert(fmessage != NULL); + + printf("M%d from %s: %s\n", level, sender, fmessage); +#ifndef NO_SYSLOG + if (level == 0) + syslog(LOG_INFO, "cached message (from %s): %s", sender, + fmessage); +#endif + free(fmessage); +} + +void +__log_err(int level, const char *sender, const char *error, ...) +{ + va_list ap; + char *ferror; + + ferror = NULL; + va_start(ap, error); + vasprintf(&ferror, error, ap); + va_end(ap); + assert(ferror != NULL); + + printf("E%d from %s: %s\n", level, sender, ferror); + +#ifndef NO_SYSLOG + if (level == 0) + syslog(LOG_ERR, "cached error (from %s): %s", sender, ferror); +#endif + free(ferror); +} diff --git a/usr.sbin/cached/log.h b/usr.sbin/cached/log.h new file mode 100644 index 000000000000..8d665a492752 --- /dev/null +++ b/usr.sbin/cached/log.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_LOG_H__ +#define __CACHED_LOG_H__ + +#define LOG_MSG_1(sender, msg, ...) __log_msg(1, sender, msg, ##__VA_ARGS__) +#define LOG_MSG_2(sender, msg, ...) __log_msg(2, sender, msg, ##__VA_ARGS__) +#define LOG_MSG_3(sender, msg, ...) __log_msg(3, sedner, msg, ##__VA_ARGS__) + +#define LOG_ERR_1(sender, err, ...) __log_err(1, sender, err, ##__VA_ARGS__) +#define LOG_ERR_2(sender, err, ...) __log_err(2, sender, err, ##__VA_ARGS__) +#define LOG_ERR_3(sender, err, ...) __log_err(3, sender, err, ##__VA_ARGS__) + +extern void __log_msg(int, const char *, const char *, ...); +extern void __log_err(int, const char *, const char *, ...); + +#endif diff --git a/usr.sbin/cached/mp_rs_query.c b/usr.sbin/cached/mp_rs_query.c new file mode 100644 index 000000000000..ae0f3bbbe98e --- /dev/null +++ b/usr.sbin/cached/mp_rs_query.c @@ -0,0 +1,537 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cachelib.h" +#include "config.h" +#include "debug.h" +#include "log.h" +#include "query.h" +#include "mp_rs_query.h" +#include "mp_ws_query.h" +#include "singletons.h" + +static int on_mp_read_session_close_notification(struct query_state *); +static void on_mp_read_session_destroy(struct query_state *); +static int on_mp_read_session_mapper(struct query_state *); +/* int on_mp_read_session_request_read1(struct query_state *); */ +static int on_mp_read_session_request_read2(struct query_state *); +static int on_mp_read_session_request_process(struct query_state *); +static int on_mp_read_session_response_write1(struct query_state *); +static int on_mp_read_session_read_request_process(struct query_state *); +static int on_mp_read_session_read_response_write1(struct query_state *); +static int on_mp_read_session_read_response_write2(struct query_state *); + +/* + * This function is used as the query_state's destroy_func to make the + * proper cleanup in case of errors. + */ +static void +on_mp_read_session_destroy(struct query_state *qstate) +{ + TRACE_IN(on_mp_read_session_destroy); + finalize_comm_element(&qstate->request); + finalize_comm_element(&qstate->response); + + if (qstate->mdata != NULL) { + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + close_cache_mp_read_session( + (cache_mp_read_session)qstate->mdata); + configuration_unlock_entry(qstate->config_entry, + CELT_MULTIPART); + } + TRACE_OUT(on_mp_read_session_destroy); +} + +/* + * The functions below are used to process multipart read session initiation + * requests. + * - on_mp_read_session_request_read1 and on_mp_read_session_request_read2 read + * the request itself + * - on_mp_read_session_request_process processes it + * - on_mp_read_session_response_write1 sends the response + */ +int +on_mp_read_session_request_read1(struct query_state *qstate) +{ + struct cache_mp_read_session_request *c_mp_rs_request; + ssize_t result; + + TRACE_IN(on_mp_read_session_request_read1); + if (qstate->kevent_watermark == 0) + qstate->kevent_watermark = sizeof(size_t); + else { + init_comm_element(&qstate->request, + CET_MP_READ_SESSION_REQUEST); + c_mp_rs_request = get_cache_mp_read_session_request( + &qstate->request); + + result = qstate->read_func(qstate, + &c_mp_rs_request->entry_length, sizeof(size_t)); + + if (result != sizeof(size_t)) { + TRACE_OUT(on_mp_read_session_request_read1); + return (-1); + } + + if (BUFSIZE_INVALID(c_mp_rs_request->entry_length)) { + TRACE_OUT(on_mp_read_session_request_read1); + return (-1); + } + + c_mp_rs_request->entry = (char *)malloc( + c_mp_rs_request->entry_length + 1); + assert(c_mp_rs_request->entry != NULL); + memset(c_mp_rs_request->entry, 0, + c_mp_rs_request->entry_length + 1); + + qstate->kevent_watermark = c_mp_rs_request->entry_length; + qstate->process_func = on_mp_read_session_request_read2; + } + TRACE_OUT(on_mp_read_session_request_read1); + return (0); +} + +static int +on_mp_read_session_request_read2(struct query_state *qstate) +{ + struct cache_mp_read_session_request *c_mp_rs_request; + ssize_t result; + + TRACE_IN(on_mp_read_session_request_read2); + c_mp_rs_request = get_cache_mp_read_session_request(&qstate->request); + + result = qstate->read_func(qstate, c_mp_rs_request->entry, + c_mp_rs_request->entry_length); + + if (result != qstate->kevent_watermark) { + LOG_ERR_3("on_mp_read_session_request_read2", + "read failed"); + TRACE_OUT(on_mp_read_session_request_read2); + return (-1); + } + + qstate->kevent_watermark = 0; + qstate->process_func = on_mp_read_session_request_process; + TRACE_OUT(on_mp_read_session_request_read2); + return (0); +} + +static int +on_mp_read_session_request_process(struct query_state *qstate) +{ + struct cache_mp_read_session_request *c_mp_rs_request; + struct cache_mp_read_session_response *c_mp_rs_response; + cache_mp_read_session rs; + cache_entry c_entry; + char *dec_cache_entry_name; + + char *buffer; + size_t buffer_size; + cache_mp_write_session ws; + struct agent *lookup_agent; + struct multipart_agent *mp_agent; + void *mdata; + int res; + + TRACE_IN(on_mp_read_session_request_process); + init_comm_element(&qstate->response, CET_MP_READ_SESSION_RESPONSE); + c_mp_rs_response = get_cache_mp_read_session_response( + &qstate->response); + c_mp_rs_request = get_cache_mp_read_session_request(&qstate->request); + + qstate->config_entry = configuration_find_entry( + s_configuration, c_mp_rs_request->entry); + if (qstate->config_entry == NULL) { + c_mp_rs_response->error_code = ENOENT; + + LOG_ERR_2("read_session_request", + "can't find configuration entry '%s'." + " aborting request", c_mp_rs_request->entry); + goto fin; + } + + if (qstate->config_entry->enabled == 0) { + c_mp_rs_response->error_code = EACCES; + + LOG_ERR_2("read_session_request", + "configuration entry '%s' is disabled", + c_mp_rs_request->entry); + goto fin; + } + + if (qstate->config_entry->perform_actual_lookups != 0) + dec_cache_entry_name = strdup( + qstate->config_entry->mp_cache_params.entry_name); + else { +#ifdef NS_CACHED_EID_CHECKING + if (check_query_eids(qstate) != 0) { + c_mp_rs_response->error_code = EPERM; + goto fin; + } +#endif + + asprintf(&dec_cache_entry_name, "%s%s", qstate->eid_str, + qstate->config_entry->mp_cache_params.entry_name); + } + + assert(dec_cache_entry_name != NULL); + + configuration_lock_rdlock(s_configuration); + c_entry = find_cache_entry(s_cache, dec_cache_entry_name); + configuration_unlock(s_configuration); + + if ((c_entry == INVALID_CACHE) && + (qstate->config_entry->perform_actual_lookups != 0)) + c_entry = register_new_mp_cache_entry(qstate, + dec_cache_entry_name); + + free(dec_cache_entry_name); + + if (c_entry != INVALID_CACHE_ENTRY) { + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + rs = open_cache_mp_read_session(c_entry); + configuration_unlock_entry(qstate->config_entry, + CELT_MULTIPART); + + if ((rs == INVALID_CACHE_MP_READ_SESSION) && + (qstate->config_entry->perform_actual_lookups != 0)) { + lookup_agent = find_agent(s_agent_table, + c_mp_rs_request->entry, MULTIPART_AGENT); + + if ((lookup_agent != NULL) && + (lookup_agent->type == MULTIPART_AGENT)) { + mp_agent = (struct multipart_agent *) + lookup_agent; + mdata = mp_agent->mp_init_func(); + + /* + * Multipart agents read the whole snapshot + * of the data at one time. + */ + configuration_lock_entry(qstate->config_entry, + CELT_MULTIPART); + ws = open_cache_mp_write_session(c_entry); + configuration_unlock_entry(qstate->config_entry, + CELT_MULTIPART); + if (ws != NULL) { + do { + buffer = NULL; + res = mp_agent->mp_lookup_func(&buffer, + &buffer_size, + mdata); + + if ((res & NS_TERMINATE) && + (buffer != NULL)) { + configuration_lock_entry( + qstate->config_entry, + CELT_MULTIPART); + if (cache_mp_write(ws, buffer, + buffer_size) != 0) { + abandon_cache_mp_write_session(ws); + ws = NULL; + } + configuration_unlock_entry( + qstate->config_entry, + CELT_MULTIPART); + + free(buffer); + buffer = NULL; + } else { + configuration_lock_entry( + qstate->config_entry, + CELT_MULTIPART); + close_cache_mp_write_session(ws); + configuration_unlock_entry( + qstate->config_entry, + CELT_MULTIPART); + + free(buffer); + buffer = NULL; + } + } while ((res & NS_TERMINATE) && + (ws != NULL)); + } + + configuration_lock_entry(qstate->config_entry, + CELT_MULTIPART); + rs = open_cache_mp_read_session(c_entry); + configuration_unlock_entry(qstate->config_entry, + CELT_MULTIPART); + } + } + + if (rs == INVALID_CACHE_MP_READ_SESSION) + c_mp_rs_response->error_code = -1; + else { + qstate->mdata = rs; + qstate->destroy_func = on_mp_read_session_destroy; + + configuration_lock_entry(qstate->config_entry, + CELT_MULTIPART); + if ((qstate->config_entry->mp_query_timeout.tv_sec != 0) || + (qstate->config_entry->mp_query_timeout.tv_usec != 0)) + memcpy(&qstate->timeout, + &qstate->config_entry->mp_query_timeout, + sizeof(struct timeval)); + configuration_unlock_entry(qstate->config_entry, + CELT_MULTIPART); + } + } else + c_mp_rs_response->error_code = -1; + +fin: + qstate->process_func = on_mp_read_session_response_write1; + qstate->kevent_watermark = sizeof(int); + qstate->kevent_filter = EVFILT_WRITE; + + TRACE_OUT(on_mp_read_session_request_process); + return (0); +} + +static int +on_mp_read_session_response_write1(struct query_state *qstate) +{ + struct cache_mp_read_session_response *c_mp_rs_response; + ssize_t result; + + TRACE_IN(on_mp_read_session_response_write1); + c_mp_rs_response = get_cache_mp_read_session_response( + &qstate->response); + result = qstate->write_func(qstate, &c_mp_rs_response->error_code, + sizeof(int)); + + if (result != sizeof(int)) { + LOG_ERR_3("on_mp_read_session_response_write1", + "write failed"); + TRACE_OUT(on_mp_read_session_response_write1); + return (-1); + } + + if (c_mp_rs_response->error_code == 0) { + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_mp_read_session_mapper; + qstate->kevent_filter = EVFILT_READ; + } else { + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + } + TRACE_OUT(on_mp_read_session_response_write1); + return (0); +} + +/* + * Mapper function is used to avoid multiple connections for each session + * write or read requests. After processing the request, it does not close + * the connection, but waits for the next request. + */ +static int +on_mp_read_session_mapper(struct query_state *qstate) +{ + ssize_t result; + int elem_type; + + TRACE_IN(on_mp_read_session_mapper); + if (qstate->kevent_watermark == 0) { + qstate->kevent_watermark = sizeof(int); + } else { + result = qstate->read_func(qstate, &elem_type, sizeof(int)); + if (result != sizeof(int)) { + LOG_ERR_3("on_mp_read_session_mapper", + "read failed"); + TRACE_OUT(on_mp_read_session_mapper); + return (-1); + } + + switch (elem_type) { + case CET_MP_READ_SESSION_READ_REQUEST: + qstate->kevent_watermark = 0; + qstate->process_func = + on_mp_read_session_read_request_process; + break; + case CET_MP_READ_SESSION_CLOSE_NOTIFICATION: + qstate->kevent_watermark = 0; + qstate->process_func = + on_mp_read_session_close_notification; + break; + default: + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + LOG_ERR_3("on_mp_read_session_mapper", + "unknown element type"); + TRACE_OUT(on_mp_read_session_mapper); + return (-1); + } + } + TRACE_OUT(on_mp_read_session_mapper); + return (0); +} + +/* + * The functions below are used to process multipart read sessions read + * requests. User doesn't have to pass any kind of data, besides the + * request identificator itself. So we don't need any XXX_read functions and + * start with the XXX_process function. + * - on_mp_read_session_read_request_process processes it + * - on_mp_read_session_read_response_write1 and + * on_mp_read_session_read_response_write2 sends the response + */ +static int +on_mp_read_session_read_request_process(struct query_state *qstate) +{ + struct cache_mp_read_session_read_response *read_response; + + TRACE_IN(on_mp_read_session_response_process); + init_comm_element(&qstate->response, CET_MP_READ_SESSION_READ_RESPONSE); + read_response = get_cache_mp_read_session_read_response( + &qstate->response); + + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + read_response->error_code = cache_mp_read( + (cache_mp_read_session)qstate->mdata, NULL, + &read_response->data_size); + + if (read_response->error_code == 0) { + read_response->data = (char *)malloc(read_response->data_size); + assert(read_response != NULL); + read_response->error_code = cache_mp_read( + (cache_mp_read_session)qstate->mdata, + read_response->data, + &read_response->data_size); + } + configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART); + + if (read_response->error_code == 0) + qstate->kevent_watermark = sizeof(size_t) + sizeof(int); + else + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_mp_read_session_read_response_write1; + qstate->kevent_filter = EVFILT_WRITE; + + TRACE_OUT(on_mp_read_session_response_process); + return (0); +} + +static int +on_mp_read_session_read_response_write1(struct query_state *qstate) +{ + struct cache_mp_read_session_read_response *read_response; + ssize_t result; + + TRACE_IN(on_mp_read_session_read_response_write1); + read_response = get_cache_mp_read_session_read_response( + &qstate->response); + + result = qstate->write_func(qstate, &read_response->error_code, + sizeof(int)); + if (read_response->error_code == 0) { + result += qstate->write_func(qstate, &read_response->data_size, + sizeof(size_t)); + if (result != qstate->kevent_watermark) { + TRACE_OUT(on_mp_read_session_read_response_write1); + LOG_ERR_3("on_mp_read_session_read_response_write1", + "write failed"); + return (-1); + } + + qstate->kevent_watermark = read_response->data_size; + qstate->process_func = on_mp_read_session_read_response_write2; + } else { + if (result != qstate->kevent_watermark) { + LOG_ERR_3("on_mp_read_session_read_response_write1", + "write failed"); + TRACE_OUT(on_mp_read_session_read_response_write1); + return (-1); + } + + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + } + + TRACE_OUT(on_mp_read_session_read_response_write1); + return (0); +} + +static int +on_mp_read_session_read_response_write2(struct query_state *qstate) +{ + struct cache_mp_read_session_read_response *read_response; + ssize_t result; + + TRACE_IN(on_mp_read_session_read_response_write2); + read_response = get_cache_mp_read_session_read_response( + &qstate->response); + result = qstate->write_func(qstate, read_response->data, + read_response->data_size); + if (result != qstate->kevent_watermark) { + LOG_ERR_3("on_mp_read_session_read_response_write2", + "write failed"); + TRACE_OUT(on_mp_read_session_read_response_write2); + return (-1); + } + + finalize_comm_element(&qstate->request); + finalize_comm_element(&qstate->response); + + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_mp_read_session_mapper; + qstate->kevent_filter = EVFILT_READ; + + TRACE_OUT(on_mp_read_session_read_response_write2); + return (0); +} + +/* + * Handles session close notification by calling close_cache_mp_read_session + * function. + */ +static int +on_mp_read_session_close_notification(struct query_state *qstate) +{ + + TRACE_IN(on_mp_read_session_close_notification); + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + close_cache_mp_read_session((cache_mp_read_session)qstate->mdata); + configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART); + qstate->mdata = NULL; + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + TRACE_OUT(on_mp_read_session_close_notification); + return (0); +} diff --git a/usr.sbin/cached/mp_rs_query.h b/usr.sbin/cached/mp_rs_query.h new file mode 100644 index 000000000000..f468afae25ac --- /dev/null +++ b/usr.sbin/cached/mp_rs_query.h @@ -0,0 +1,34 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_MP_RS_QUERY_H__ +#define __CACHED_MP_RS_QUERY_H__ + +extern int on_mp_read_session_request_read1(struct query_state *); + +#endif diff --git a/usr.sbin/cached/mp_ws_query.c b/usr.sbin/cached/mp_ws_query.c new file mode 100644 index 000000000000..d7aeb495d6fb --- /dev/null +++ b/usr.sbin/cached/mp_ws_query.c @@ -0,0 +1,548 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cachelib.h" +#include "config.h" +#include "debug.h" +#include "log.h" +#include "query.h" +#include "mp_ws_query.h" +#include "singletons.h" + +static int on_mp_write_session_abandon_notification(struct query_state *); +static int on_mp_write_session_close_notification(struct query_state *); +static void on_mp_write_session_destroy(struct query_state *); +static int on_mp_write_session_mapper(struct query_state *); +/* int on_mp_write_session_request_read1(struct query_state *); */ +static int on_mp_write_session_request_read2(struct query_state *); +static int on_mp_write_session_request_process(struct query_state *); +static int on_mp_write_session_response_write1(struct query_state *); +static int on_mp_write_session_write_request_read1(struct query_state *); +static int on_mp_write_session_write_request_read2(struct query_state *); +static int on_mp_write_session_write_request_process(struct query_state *); +static int on_mp_write_session_write_response_write1(struct query_state *); + +/* + * This function is used as the query_state's destroy_func to make the + * proper cleanup in case of errors. + */ +static void +on_mp_write_session_destroy(struct query_state *qstate) +{ + + TRACE_IN(on_mp_write_session_destroy); + finalize_comm_element(&qstate->request); + finalize_comm_element(&qstate->response); + + if (qstate->mdata != NULL) { + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + abandon_cache_mp_write_session( + (cache_mp_write_session)qstate->mdata); + configuration_unlock_entry(qstate->config_entry, + CELT_MULTIPART); + } + TRACE_OUT(on_mp_write_session_destroy); +} + +/* + * The functions below are used to process multipart write session initiation + * requests. + * - on_mp_write_session_request_read1 and on_mp_write_session_request_read2 + * read the request itself + * - on_mp_write_session_request_process processes it + * - on_mp_write_session_response_write1 sends the response + */ +int +on_mp_write_session_request_read1(struct query_state *qstate) +{ + struct cache_mp_write_session_request *c_mp_ws_request; + ssize_t result; + + TRACE_IN(on_mp_write_session_request_read1); + if (qstate->kevent_watermark == 0) + qstate->kevent_watermark = sizeof(size_t); + else { + init_comm_element(&qstate->request, + CET_MP_WRITE_SESSION_REQUEST); + c_mp_ws_request = get_cache_mp_write_session_request( + &qstate->request); + + result = qstate->read_func(qstate, + &c_mp_ws_request->entry_length, sizeof(size_t)); + + if (result != sizeof(size_t)) { + LOG_ERR_3("on_mp_write_session_request_read1", + "read failed"); + TRACE_OUT(on_mp_write_session_request_read1); + return (-1); + } + + if (BUFSIZE_INVALID(c_mp_ws_request->entry_length)) { + LOG_ERR_3("on_mp_write_session_request_read1", + "invalid entry_length value"); + TRACE_OUT(on_mp_write_session_request_read1); + return (-1); + } + + c_mp_ws_request->entry = (char *)malloc( + c_mp_ws_request->entry_length + 1); + assert(c_mp_ws_request->entry != NULL); + memset(c_mp_ws_request->entry, 0, + c_mp_ws_request->entry_length + 1); + + qstate->kevent_watermark = c_mp_ws_request->entry_length; + qstate->process_func = on_mp_write_session_request_read2; + } + TRACE_OUT(on_mp_write_session_request_read1); + return (0); +} + +static int +on_mp_write_session_request_read2(struct query_state *qstate) +{ + struct cache_mp_write_session_request *c_mp_ws_request; + ssize_t result; + + TRACE_IN(on_mp_write_session_request_read2); + c_mp_ws_request = get_cache_mp_write_session_request(&qstate->request); + + result = qstate->read_func(qstate, c_mp_ws_request->entry, + c_mp_ws_request->entry_length); + + if (result != qstate->kevent_watermark) { + LOG_ERR_3("on_mp_write_session_request_read2", + "read failed"); + TRACE_OUT(on_mp_write_session_request_read2); + return (-1); + } + + qstate->kevent_watermark = 0; + qstate->process_func = on_mp_write_session_request_process; + + TRACE_OUT(on_mp_write_session_request_read2); + return (0); +} + +static int +on_mp_write_session_request_process(struct query_state *qstate) +{ + struct cache_mp_write_session_request *c_mp_ws_request; + struct cache_mp_write_session_response *c_mp_ws_response; + cache_mp_write_session ws; + cache_entry c_entry; + char *dec_cache_entry_name; + + TRACE_IN(on_mp_write_session_request_process); + init_comm_element(&qstate->response, CET_MP_WRITE_SESSION_RESPONSE); + c_mp_ws_response = get_cache_mp_write_session_response( + &qstate->response); + c_mp_ws_request = get_cache_mp_write_session_request(&qstate->request); + + qstate->config_entry = configuration_find_entry( + s_configuration, c_mp_ws_request->entry); + if (qstate->config_entry == NULL) { + c_mp_ws_response->error_code = ENOENT; + + LOG_ERR_2("write_session_request", + "can't find configuration entry '%s'. " + "aborting request", c_mp_ws_request->entry); + goto fin; + } + + if (qstate->config_entry->enabled == 0) { + c_mp_ws_response->error_code = EACCES; + + LOG_ERR_2("write_session_request", + "configuration entry '%s' is disabled", + c_mp_ws_request->entry); + goto fin; + } + + if (qstate->config_entry->perform_actual_lookups != 0) { + c_mp_ws_response->error_code = EOPNOTSUPP; + + LOG_ERR_2("write_session_request", + "entry '%s' performs lookups by itself: " + "can't write to it", c_mp_ws_request->entry); + goto fin; + } else { +#ifdef NS_CACHED_EID_CHECKING + if (check_query_eids(qstate) != 0) { + c_mp_ws_response->error_code = EPERM; + goto fin; + } +#endif + } + + /* + * All multipart entries are separated by their name decorations. + * For one configuration entry there will be a lot of multipart + * cache entries - each with its own decorated name. + */ + asprintf(&dec_cache_entry_name, "%s%s", qstate->eid_str, + qstate->config_entry->mp_cache_params.entry_name); + assert(dec_cache_entry_name != NULL); + + configuration_lock_rdlock(s_configuration); + c_entry = find_cache_entry(s_cache, + dec_cache_entry_name); + configuration_unlock(s_configuration); + + if (c_entry == INVALID_CACHE_ENTRY) + c_entry = register_new_mp_cache_entry(qstate, + dec_cache_entry_name); + + free(dec_cache_entry_name); + + assert(c_entry != NULL); + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + ws = open_cache_mp_write_session(c_entry); + if (ws == INVALID_CACHE_MP_WRITE_SESSION) + c_mp_ws_response->error_code = -1; + else { + qstate->mdata = ws; + qstate->destroy_func = on_mp_write_session_destroy; + + if ((qstate->config_entry->mp_query_timeout.tv_sec != 0) || + (qstate->config_entry->mp_query_timeout.tv_usec != 0)) + memcpy(&qstate->timeout, + &qstate->config_entry->mp_query_timeout, + sizeof(struct timeval)); + } + configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART); + +fin: + qstate->process_func = on_mp_write_session_response_write1; + qstate->kevent_watermark = sizeof(int); + qstate->kevent_filter = EVFILT_WRITE; + + TRACE_OUT(on_mp_write_session_request_process); + return (0); +} + +static int +on_mp_write_session_response_write1(struct query_state *qstate) +{ + struct cache_mp_write_session_response *c_mp_ws_response; + ssize_t result; + + TRACE_IN(on_mp_write_session_response_write1); + c_mp_ws_response = get_cache_mp_write_session_response( + &qstate->response); + result = qstate->write_func(qstate, &c_mp_ws_response->error_code, + sizeof(int)); + if (result != sizeof(int)) { + LOG_ERR_3("on_mp_write_session_response_write1", + "write failed"); + TRACE_OUT(on_mp_write_session_response_write1); + return (-1); + } + + if (c_mp_ws_response->error_code == 0) { + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_mp_write_session_mapper; + qstate->kevent_filter = EVFILT_READ; + } else { + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + } + TRACE_OUT(on_mp_write_session_response_write1); + return (0); +} + +/* + * Mapper function is used to avoid multiple connections for each session + * write or read requests. After processing the request, it does not close + * the connection, but waits for the next request. + */ +static int +on_mp_write_session_mapper(struct query_state *qstate) +{ + ssize_t result; + int elem_type; + + TRACE_IN(on_mp_write_session_mapper); + if (qstate->kevent_watermark == 0) { + qstate->kevent_watermark = sizeof(int); + } else { + result = qstate->read_func(qstate, &elem_type, sizeof(int)); + if (result != sizeof(int)) { + LOG_ERR_3("on_mp_write_session_mapper", + "read failed"); + TRACE_OUT(on_mp_write_session_mapper); + return (-1); + } + + switch (elem_type) { + case CET_MP_WRITE_SESSION_WRITE_REQUEST: + qstate->kevent_watermark = sizeof(size_t); + qstate->process_func = + on_mp_write_session_write_request_read1; + break; + case CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION: + qstate->kevent_watermark = 0; + qstate->process_func = + on_mp_write_session_abandon_notification; + break; + case CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION: + qstate->kevent_watermark = 0; + qstate->process_func = + on_mp_write_session_close_notification; + break; + default: + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + LOG_ERR_2("on_mp_write_session_mapper", + "unknown element type"); + TRACE_OUT(on_mp_write_session_mapper); + return (-1); + } + } + TRACE_OUT(on_mp_write_session_mapper); + return (0); +} + +/* + * The functions below are used to process multipart write sessions write + * requests. + * - on_mp_write_session_write_request_read1 and + * on_mp_write_session_write_request_read2 read the request itself + * - on_mp_write_session_write_request_process processes it + * - on_mp_write_session_write_response_write1 sends the response + */ +static int +on_mp_write_session_write_request_read1(struct query_state *qstate) +{ + struct cache_mp_write_session_write_request *write_request; + ssize_t result; + + TRACE_IN(on_mp_write_session_write_request_read1); + init_comm_element(&qstate->request, + CET_MP_WRITE_SESSION_WRITE_REQUEST); + write_request = get_cache_mp_write_session_write_request( + &qstate->request); + + result = qstate->read_func(qstate, &write_request->data_size, + sizeof(size_t)); + + if (result != sizeof(size_t)) { + LOG_ERR_3("on_mp_write_session_write_request_read1", + "read failed"); + TRACE_OUT(on_mp_write_session_write_request_read1); + return (-1); + } + + if (BUFSIZE_INVALID(write_request->data_size)) { + LOG_ERR_3("on_mp_write_session_write_request_read1", + "invalid data_size value"); + TRACE_OUT(on_mp_write_session_write_request_read1); + return (-1); + } + + write_request->data = (char *)malloc(write_request->data_size); + assert(write_request->data != NULL); + memset(write_request->data, 0, write_request->data_size); + + qstate->kevent_watermark = write_request->data_size; + qstate->process_func = on_mp_write_session_write_request_read2; + TRACE_OUT(on_mp_write_session_write_request_read1); + return (0); +} + +static int +on_mp_write_session_write_request_read2(struct query_state *qstate) +{ + struct cache_mp_write_session_write_request *write_request; + ssize_t result; + + TRACE_IN(on_mp_write_session_write_request_read2); + write_request = get_cache_mp_write_session_write_request( + &qstate->request); + + result = qstate->read_func(qstate, write_request->data, + write_request->data_size); + + if (result != qstate->kevent_watermark) { + LOG_ERR_3("on_mp_write_session_write_request_read2", + "read failed"); + TRACE_OUT(on_mp_write_session_write_request_read2); + return (-1); + } + + qstate->kevent_watermark = 0; + qstate->process_func = on_mp_write_session_write_request_process; + TRACE_OUT(on_mp_write_session_write_request_read2); + return (0); +} + +static int +on_mp_write_session_write_request_process(struct query_state *qstate) +{ + struct cache_mp_write_session_write_request *write_request; + struct cache_mp_write_session_write_response *write_response; + + TRACE_IN(on_mp_write_session_write_request_process); + init_comm_element(&qstate->response, + CET_MP_WRITE_SESSION_WRITE_RESPONSE); + write_response = get_cache_mp_write_session_write_response( + &qstate->response); + write_request = get_cache_mp_write_session_write_request( + &qstate->request); + + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + write_response->error_code = cache_mp_write( + (cache_mp_write_session)qstate->mdata, + write_request->data, + write_request->data_size); + configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART); + + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_mp_write_session_write_response_write1; + qstate->kevent_filter = EVFILT_WRITE; + + TRACE_OUT(on_mp_write_session_write_request_process); + return (0); +} + +static int +on_mp_write_session_write_response_write1(struct query_state *qstate) +{ + struct cache_mp_write_session_write_response *write_response; + ssize_t result; + + TRACE_IN(on_mp_write_session_write_response_write1); + write_response = get_cache_mp_write_session_write_response( + &qstate->response); + result = qstate->write_func(qstate, &write_response->error_code, + sizeof(int)); + if (result != sizeof(int)) { + LOG_ERR_3("on_mp_write_session_write_response_write1", + "write failed"); + TRACE_OUT(on_mp_write_session_write_response_write1); + return (-1); + } + + if (write_response->error_code == 0) { + finalize_comm_element(&qstate->request); + finalize_comm_element(&qstate->response); + + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_mp_write_session_mapper; + qstate->kevent_filter = EVFILT_READ; + } else { + qstate->kevent_watermark = 0; + qstate->process_func = 0; + } + + TRACE_OUT(on_mp_write_session_write_response_write1); + return (0); +} + +/* + * Handles abandon notifications. Destroys the session by calling the + * abandon_cache_mp_write_session. + */ +static int +on_mp_write_session_abandon_notification(struct query_state *qstate) +{ + TRACE_IN(on_mp_write_session_abandon_notification); + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + abandon_cache_mp_write_session((cache_mp_write_session)qstate->mdata); + configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART); + qstate->mdata = INVALID_CACHE_MP_WRITE_SESSION; + + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + TRACE_OUT(on_mp_write_session_abandon_notification); + return (0); +} + +/* + * Handles close notifications. Commits the session by calling + * the close_cache_mp_write_session. + */ +static int +on_mp_write_session_close_notification(struct query_state *qstate) +{ + TRACE_IN(on_mp_write_session_close_notification); + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + close_cache_mp_write_session((cache_mp_write_session)qstate->mdata); + configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART); + qstate->mdata = INVALID_CACHE_MP_WRITE_SESSION; + + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + TRACE_OUT(on_mp_write_session_close_notification); + return (0); +} + +cache_entry register_new_mp_cache_entry(struct query_state *qstate, + const char *dec_cache_entry_name) +{ + cache_entry c_entry; + char *en_bkp; + + TRACE_IN(register_new_mp_cache_entry); + c_entry = INVALID_CACHE_ENTRY; + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + + configuration_lock_wrlock(s_configuration); + en_bkp = qstate->config_entry->mp_cache_params.entry_name; + qstate->config_entry->mp_cache_params.entry_name = + (char *)dec_cache_entry_name; + register_cache_entry(s_cache, (struct cache_entry_params *) + &qstate->config_entry->mp_cache_params); + qstate->config_entry->mp_cache_params.entry_name = en_bkp; + configuration_unlock(s_configuration); + + configuration_lock_rdlock(s_configuration); + c_entry = find_cache_entry(s_cache, + dec_cache_entry_name); + configuration_unlock(s_configuration); + + configuration_entry_add_mp_cache_entry(qstate->config_entry, + c_entry); + + configuration_unlock_entry(qstate->config_entry, + CELT_MULTIPART); + + TRACE_OUT(register_new_mp_cache_entry); + return (c_entry); +} diff --git a/usr.sbin/cached/mp_ws_query.h b/usr.sbin/cached/mp_ws_query.h new file mode 100644 index 000000000000..ba776656b833 --- /dev/null +++ b/usr.sbin/cached/mp_ws_query.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_MP_WS_QUERY_H__ +#define __CACHED_MP_WS_QUERY_H__ + +extern int on_mp_write_session_request_read1(struct query_state *); +extern cache_entry register_new_mp_cache_entry(struct query_state *, + const char *); + +#endif diff --git a/usr.sbin/cached/parser.c b/usr.sbin/cached/parser.c new file mode 100644 index 000000000000..b877efa6cf44 --- /dev/null +++ b/usr.sbin/cached/parser.c @@ -0,0 +1,474 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "config.h" +#include "debug.h" +#include "log.h" +#include "parser.h" + +static void enable_cache(struct configuration *,const char *, int); +static struct configuration_entry *find_create_entry(struct configuration *, + const char *); +static int get_number(const char *, int, int); +static enum cache_policy_t get_policy(const char *); +static int get_yesno(const char *); +static int check_cachename(const char *); +static void check_files(struct configuration *, const char *, int); +static void set_keep_hot_count(struct configuration *, const char *, int); +static void set_negative_policy(struct configuration *, const char *, + enum cache_policy_t); +static void set_negative_time_to_live(struct configuration *, + const char *, int); +static void set_positive_policy(struct configuration *, const char *, + enum cache_policy_t); +static void set_perform_actual_lookups(struct configuration *, const char *, + int); +static void set_positive_time_to_live(struct configuration *, + const char *, int); +static void set_suggested_size(struct configuration *, const char *, + int size); +static void set_threads_num(struct configuration *, int); +static int strbreak(char *, char **, int); + +static int +strbreak(char *str, char **fields, int fields_size) +{ + char *c = str; + int i, num; + + TRACE_IN(strbreak); + num = 0; + for (i = 0; + ((*fields = + strsep(i < fields_size ? &c : NULL, "\n\t ")) != NULL); + ++i) + if ((*(*fields)) != '\0') { + ++fields; + ++num; + } + + TRACE_OUT(strbreak); + return (num); +} + +/* + * Tries to find the configuration entry with the specified name. If search + * fails, the new entry with the default parameters will be created. + */ +static struct configuration_entry * +find_create_entry(struct configuration *config, + const char *entry_name) +{ + struct configuration_entry *entry = NULL; + int res; + + TRACE_IN(find_create_entry); + entry = configuration_find_entry(config, entry_name); + if (entry == NULL) { + entry = create_def_configuration_entry(entry_name); + assert( entry != NULL); + res = add_configuration_entry(config, entry); + assert(res == 0); + } + + TRACE_OUT(find_create_entry); + return (entry); +} + +/* + * The vast majority of the functions below corresponds to the particular + * keywords in the configuration file. + */ +static void +enable_cache(struct configuration *config, const char *entry_name, int flag) +{ + struct configuration_entry *entry; + + TRACE_IN(enable_cache); + entry = find_create_entry(config, entry_name); + entry->enabled = flag; + TRACE_OUT(enable_cache); +} + +static void +set_positive_time_to_live(struct configuration *config, + const char *entry_name, int ttl) +{ + struct configuration_entry *entry; + struct timeval lifetime; + + TRACE_IN(set_positive_time_to_live); + assert(ttl >= 0); + assert(entry_name != NULL); + memset(&lifetime, 0, sizeof(struct timeval)); + lifetime.tv_sec = ttl; + + entry = find_create_entry(config, entry_name); + memcpy(&entry->positive_cache_params.max_lifetime, + &lifetime, sizeof(struct timeval)); + memcpy(&entry->mp_cache_params.max_lifetime, + &lifetime, sizeof(struct timeval)); + + TRACE_OUT(set_positive_time_to_live); +} + +static void +set_negative_time_to_live(struct configuration *config, + const char *entry_name, int nttl) +{ + struct configuration_entry *entry; + struct timeval lifetime; + + TRACE_IN(set_negative_time_to_live); + assert(nttl > 0); + assert(entry_name != NULL); + memset(&lifetime, 0, sizeof(struct timeval)); + lifetime.tv_sec = nttl; + + entry = find_create_entry(config, entry_name); + assert(entry != NULL); + memcpy(&entry->negative_cache_params.max_lifetime, + &lifetime, sizeof(struct timeval)); + + TRACE_OUT(set_negative_time_to_live); +} + +/* + * Hot count is actually the elements size limit. + */ +static void +set_keep_hot_count(struct configuration *config, + const char *entry_name, int count) +{ + struct configuration_entry *entry; + + TRACE_IN(set_keep_hot_count); + assert(count >= 0); + assert(entry_name != NULL); + + entry = find_create_entry(config, entry_name); + assert(entry != NULL); + entry->positive_cache_params.max_elemsize = count; + + entry = find_create_entry(config, entry_name); + assert(entry != NULL); + entry->negative_cache_params.max_elemsize = count; + + TRACE_OUT(set_keep_hot_count); +} + +static void +set_positive_policy(struct configuration *config, + const char *entry_name, enum cache_policy_t policy) +{ + struct configuration_entry *entry; + + TRACE_IN(set_positive_policy); + assert(entry_name != NULL); + + entry = find_create_entry(config, entry_name); + assert(entry != NULL); + entry->positive_cache_params.policy = policy; + + TRACE_OUT(set_positive_policy); +} + +static void +set_negative_policy(struct configuration *config, + const char *entry_name, enum cache_policy_t policy) +{ + struct configuration_entry *entry; + + TRACE_IN(set_negative_policy); + assert(entry_name != NULL); + + entry = find_create_entry(config, entry_name); + assert(entry != NULL); + entry->negative_cache_params.policy = policy; + + TRACE_OUT(set_negative_policy); +} + +static void +set_perform_actual_lookups(struct configuration *config, + const char *entry_name, int flag) +{ + struct configuration_entry *entry; + + TRACE_IN(set_perform_actual_lookups); + assert(entry_name != NULL); + + entry = find_create_entry(config, entry_name); + assert(entry != NULL); + entry->perform_actual_lookups = flag; + + TRACE_OUT(set_perform_actual_lookups); +} + +static void +set_suggested_size(struct configuration *config, + const char *entry_name, int size) +{ + struct configuration_entry *entry; + + TRACE_IN(set_suggested_size); + assert(config != NULL); + assert(entry_name != NULL); + assert(size > 0); + + entry = find_create_entry(config, entry_name); + assert(entry != NULL); + entry->positive_cache_params.cache_entries_size = size; + entry->negative_cache_params.cache_entries_size = size; + + TRACE_OUT(set_suggested_size); +} + +static void +check_files(struct configuration *config, const char *entry_name, int flag) +{ + + TRACE_IN(check_files); + assert(entry_name != NULL); + TRACE_OUT(check_files); +} + +static int +get_yesno(const char *str) +{ + + if (strcmp(str, "yes") == 0) + return (1); + else if (strcmp(str, "no") == 0) + return (0); + else + return (-1); +} + +static int +get_number(const char *str, int low, int max) +{ + + char *end = NULL; + int res = 0; + + if (str[0] == '\0') + return (-1); + + res = strtol(str, &end, 10); + if (*end != '\0') + return (-1); + else + if (((res >= low) || (low == -1)) && + ((res <= max) || (max == -1))) + return (res); + else + return (-2); +} + +static enum cache_policy_t +get_policy(const char *str) +{ + + if (strcmp(str, "fifo") == 0) + return (CPT_FIFO); + else if (strcmp(str, "lru") == 0) + return (CPT_LRU); + else if (strcmp(str, "lfu") == 0) + return (CPT_LFU); + + return (-1); +} + +static int +check_cachename(const char *str) +{ + + assert(str != NULL); + return ((strlen(str) > 0) ? 0 : -1); +} + +static void +set_threads_num(struct configuration *config, int value) +{ + + assert(config != NULL); + config->threads_num = value; +} + +/* + * The main configuration routine. Its implementation is hugely inspired by the + * the same routine implementation in Solaris NSCD. + */ +int +parse_config_file(struct configuration *config, + const char *fname, char const **error_str, int *error_line) +{ + FILE *fin; + char buffer[255]; + char *fields[128]; + int field_count, line_num, value; + int res; + + TRACE_IN(parse_config_file); + assert(config != NULL); + assert(fname != NULL); + + fin = fopen(fname, "r"); + if (fin == NULL) { + TRACE_OUT(parse_config_file); + return (-1); + } + + res = 0; + line_num = 0; + memset(buffer, 0, sizeof(buffer)); + while ((res == 0) && (fgets(buffer, sizeof(buffer) - 1, fin) != NULL)) { + field_count = strbreak(buffer, fields, sizeof(fields)); + ++line_num; + + if (field_count == 0) + continue; + + switch (fields[0][0]) { + case '#': + case '\0': + continue; + case 'e': + if ((field_count == 3) && + (strcmp(fields[0], "enable-cache") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_yesno(fields[2])) != -1)) { + enable_cache(config, fields[1], value); + continue; + } + break; + case 'd': + if ((field_count == 2) && + (strcmp(fields[0], "debug-level") == 0) && + ((value = get_number(fields[1], 0, 10)) != -1)) { + continue; + } + break; + case 'p': + if ((field_count == 3) && + (strcmp(fields[0], "positive-time-to-live") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_number(fields[2], 0, -1)) != -1)) { + set_positive_time_to_live(config, + fields[1], value); + continue; + } else if ((field_count == 3) && + (strcmp(fields[0], "positive-policy") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_policy(fields[2])) != -1)) { + set_positive_policy(config, fields[1], value); + continue; + } else if ((field_count == 3) && + (strcmp(fields[0], "perform-actual-lookups") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_yesno(fields[2])) != -1)) { + set_perform_actual_lookups(config, fields[1], + value); + continue; + } + break; + case 'n': + if ((field_count == 3) && + (strcmp(fields[0], "negative-time-to-live") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_number(fields[2], 0, -1)) != -1)) { + set_negative_time_to_live(config, + fields[1], value); + continue; + } else if ((field_count == 3) && + (strcmp(fields[0], "negative-policy") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_policy(fields[2])) != -1)) { + set_negative_policy(config, + fields[1], value); + continue; + } + break; + case 's': + if ((field_count == 3) && + (strcmp(fields[0], "suggested-size") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_number(fields[2], 1, -1)) != -1)) { + set_suggested_size(config, fields[1], value); + continue; + } + break; + case 't': + if ((field_count == 2) && + (strcmp(fields[0], "threads") == 0) && + ((value = get_number(fields[1], 1, -1)) != -1)) { + set_threads_num(config, value); + continue; + } + break; + case 'k': + if ((field_count == 3) && + (strcmp(fields[0], "keep-hot-count") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_number(fields[2], 0, -1)) != -1)) { + set_keep_hot_count(config, + fields[1], value); + continue; + } + break; + case 'c': + if ((field_count == 3) && + (strcmp(fields[0], "check-files") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_yesno(fields[2])) != -1)) { + check_files(config, + fields[1], value); + continue; + } + break; + default: + break; + } + + LOG_ERR_2("config file parser", "error in file " + "%s on line %d", fname, line_num); + *error_str = "syntax error"; + *error_line = line_num; + res = -1; + } + fclose(fin); + + TRACE_OUT(parse_config_file); + return (res); +} diff --git a/usr.sbin/cached/parser.h b/usr.sbin/cached/parser.h new file mode 100644 index 000000000000..54cc898873cc --- /dev/null +++ b/usr.sbin/cached/parser.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_PARSER_H__ +#define __CACHED_PARSER_H__ + +extern int parse_config_file(struct configuration *, + const char *, char const **, int *); + +#endif diff --git a/usr.sbin/cached/protocol.c b/usr.sbin/cached/protocol.c new file mode 100644 index 000000000000..08cea9230995 --- /dev/null +++ b/usr.sbin/cached/protocol.c @@ -0,0 +1,550 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "debug.h" +#include "log.h" +#include "protocol.h" + +/* + * Initializes the comm_element with any given type of data + */ +void +init_comm_element(struct comm_element *element, enum comm_element_t type) +{ + + TRACE_IN(init_comm_element); + memset(element, 0, sizeof(struct comm_element)); + + switch (type) { + case CET_WRITE_REQUEST: + init_cache_write_request(&element->c_write_request); + break; + case CET_WRITE_RESPONSE: + init_cache_write_response(&element->c_write_response); + break; + case CET_READ_REQUEST: + init_cache_read_request(&element->c_read_request); + break; + case CET_READ_RESPONSE: + init_cache_read_response(&element->c_read_response); + break; + case CET_TRANSFORM_REQUEST: + init_cache_transform_request(&element->c_transform_request); + break; + case CET_TRANSFORM_RESPONSE: + init_cache_transform_response(&element->c_transform_response); + break; + case CET_MP_WRITE_SESSION_REQUEST: + init_cache_mp_write_session_request(&element->c_mp_ws_request); + break; + case CET_MP_WRITE_SESSION_RESPONSE: + init_cache_mp_write_session_response(&element->c_mp_ws_response); + break; + case CET_MP_WRITE_SESSION_WRITE_REQUEST: + init_cache_mp_write_session_write_request( + &element->c_mp_ws_write_request); + break; + case CET_MP_WRITE_SESSION_WRITE_RESPONSE: + init_cache_mp_write_session_write_response( + &element->c_mp_ws_write_response); + break; + case CET_MP_READ_SESSION_REQUEST: + init_cache_mp_read_session_request(&element->c_mp_rs_request); + break; + case CET_MP_READ_SESSION_RESPONSE: + init_cache_mp_read_session_response(&element->c_mp_rs_response); + break; + case CET_MP_READ_SESSION_READ_RESPONSE: + init_cache_mp_read_session_read_response( + &element->c_mp_rs_read_response); + break; + case CET_UNDEFINED: + break; + default: + LOG_ERR_2("init_comm_element", "invalid communication element"); + TRACE_OUT(init_comm_element); + return; + } + + element->type = type; + TRACE_OUT(init_comm_element); +} + +void +finalize_comm_element(struct comm_element *element) +{ + + TRACE_IN(finalize_comm_element); + switch (element->type) { + case CET_WRITE_REQUEST: + finalize_cache_write_request(&element->c_write_request); + break; + case CET_WRITE_RESPONSE: + finalize_cache_write_response(&element->c_write_response); + break; + case CET_READ_REQUEST: + finalize_cache_read_request(&element->c_read_request); + break; + case CET_READ_RESPONSE: + finalize_cache_read_response(&element->c_read_response); + break; + case CET_TRANSFORM_REQUEST: + finalize_cache_transform_request(&element->c_transform_request); + break; + case CET_TRANSFORM_RESPONSE: + finalize_cache_transform_response( + &element->c_transform_response); + break; + case CET_MP_WRITE_SESSION_REQUEST: + finalize_cache_mp_write_session_request( + &element->c_mp_ws_request); + break; + case CET_MP_WRITE_SESSION_RESPONSE: + finalize_cache_mp_write_session_response( + &element->c_mp_ws_response); + break; + case CET_MP_WRITE_SESSION_WRITE_REQUEST: + finalize_cache_mp_write_session_write_request( + &element->c_mp_ws_write_request); + break; + case CET_MP_WRITE_SESSION_WRITE_RESPONSE: + finalize_cache_mp_write_session_write_response( + &element->c_mp_ws_write_response); + break; + case CET_MP_READ_SESSION_REQUEST: + finalize_cache_mp_read_session_request( + &element->c_mp_rs_request); + break; + case CET_MP_READ_SESSION_RESPONSE: + finalize_cache_mp_read_session_response( + &element->c_mp_rs_response); + break; + case CET_MP_READ_SESSION_READ_RESPONSE: + finalize_cache_mp_read_session_read_response( + &element->c_mp_rs_read_response); + break; + case CET_UNDEFINED: + break; + default: + break; + } + + element->type = CET_UNDEFINED; + TRACE_OUT(finalize_comm_element); +} + +void +init_cache_write_request(struct cache_write_request *write_request) +{ + + TRACE_IN(init_cache_write_request); + memset(write_request, 0, sizeof(struct cache_write_request)); + TRACE_OUT(init_cache_write_request); +} + +void +finalize_cache_write_request(struct cache_write_request *write_request) +{ + + TRACE_IN(finalize_cache_write_request); + free(write_request->entry); + free(write_request->cache_key); + free(write_request->data); + TRACE_OUT(finalize_cache_write_request); +} + +struct cache_write_request * +get_cache_write_request(struct comm_element *element) +{ + + TRACE_IN(get_cache_write_request); + assert(element->type == CET_WRITE_REQUEST); + TRACE_OUT(get_cache_write_request); + return (&element->c_write_request); +} + +void +init_cache_write_response(struct cache_write_response *write_response) +{ + + TRACE_IN(init_cache_write_response); + memset(write_response, 0, sizeof(struct cache_write_response)); + TRACE_OUT(init_cache_write_response); +} + +void +finalize_cache_write_response(struct cache_write_response *write_response) +{ + + TRACE_IN(finalize_cache_write_response); + TRACE_OUT(finalize_cache_write_response); +} + +struct cache_write_response * +get_cache_write_response(struct comm_element *element) +{ + + TRACE_IN(get_cache_write_response); + assert(element->type == CET_WRITE_RESPONSE); + TRACE_OUT(get_cache_write_response); + return (&element->c_write_response); +} + +void +init_cache_read_request(struct cache_read_request *read_request) +{ + + TRACE_IN(init_cache_read_request); + memset(read_request, 0, sizeof(struct cache_read_request)); + TRACE_OUT(init_cache_read_request); +} + +void +finalize_cache_read_request(struct cache_read_request *read_request) +{ + + TRACE_IN(finalize_cache_read_request); + free(read_request->entry); + free(read_request->cache_key); + TRACE_OUT(finalize_cache_read_request); +} + +struct cache_read_request * +get_cache_read_request(struct comm_element *element) +{ + + TRACE_IN(get_cache_read_request); + assert(element->type == CET_READ_REQUEST); + TRACE_OUT(get_cache_read_request); + return (&element->c_read_request); +} + +void +init_cache_read_response(struct cache_read_response *read_response) +{ + + TRACE_IN(init_cache_read_response); + memset(read_response, 0, sizeof(struct cache_read_response)); + TRACE_OUT(init_cache_read_response); +} + +void +finalize_cache_read_response(struct cache_read_response *read_response) +{ + + TRACE_IN(finalize_cache_read_response); + free(read_response->data); + TRACE_OUT(finalize_cache_read_response); +} + +struct cache_read_response * +get_cache_read_response(struct comm_element *element) +{ + + TRACE_IN(get_cache_read_response); + assert(element->type == CET_READ_RESPONSE); + TRACE_OUT(get_cache_read_response); + return (&element->c_read_response); +} + +void +init_cache_transform_request(struct cache_transform_request *transform_request) +{ + + TRACE_IN(init_cache_transform_request); + memset(transform_request, 0, sizeof(struct cache_transform_request)); + TRACE_OUT(init_cache_transform_request); +} + +void +finalize_cache_transform_request( + struct cache_transform_request *transform_request) +{ + + TRACE_IN(finalize_cache_transform_request); + free(transform_request->entry); + TRACE_OUT(finalize_cache_transform_request); +} + +struct cache_transform_request * +get_cache_transform_request(struct comm_element *element) +{ + + TRACE_IN(get_cache_transform_request); + assert(element->type == CET_TRANSFORM_REQUEST); + TRACE_OUT(get_cache_transform_request); + return (&element->c_transform_request); +} + +void +init_cache_transform_response( + struct cache_transform_response *transform_response) +{ + + TRACE_IN(init_cache_transform_request); + memset(transform_response, 0, sizeof(struct cache_transform_response)); + TRACE_OUT(init_cache_transform_request); +} + +void +finalize_cache_transform_response( + struct cache_transform_response *transform_response) +{ + + TRACE_IN(finalize_cache_transform_response); + TRACE_OUT(finalize_cache_transform_response); +} + +struct cache_transform_response * +get_cache_transform_response(struct comm_element *element) +{ + + TRACE_IN(get_cache_transform_response); + assert(element->type == CET_TRANSFORM_RESPONSE); + TRACE_OUT(get_cache_transform_response); + return (&element->c_transform_response); +} + + +void +init_cache_mp_write_session_request( + struct cache_mp_write_session_request *mp_ws_request) +{ + + TRACE_IN(init_cache_mp_write_session_request); + memset(mp_ws_request, 0, + sizeof(struct cache_mp_write_session_request)); + TRACE_OUT(init_cache_mp_write_session_request); +} + +void +finalize_cache_mp_write_session_request( + struct cache_mp_write_session_request *mp_ws_request) +{ + + TRACE_IN(finalize_cache_mp_write_session_request); + free(mp_ws_request->entry); + TRACE_OUT(finalize_cache_mp_write_session_request); +} + +struct cache_mp_write_session_request * +get_cache_mp_write_session_request(struct comm_element *element) +{ + + TRACE_IN(get_cache_mp_write_session_request); + assert(element->type == CET_MP_WRITE_SESSION_REQUEST); + TRACE_OUT(get_cache_mp_write_session_request); + return (&element->c_mp_ws_request); +} + +void +init_cache_mp_write_session_response( + struct cache_mp_write_session_response *mp_ws_response) +{ + + TRACE_IN(init_cache_mp_write_session_response); + memset(mp_ws_response, 0, + sizeof(struct cache_mp_write_session_response)); + TRACE_OUT(init_cache_mp_write_session_response); +} + +void +finalize_cache_mp_write_session_response( + struct cache_mp_write_session_response *mp_ws_response) +{ + + TRACE_IN(finalize_cache_mp_write_session_response); + TRACE_OUT(finalize_cache_mp_write_session_response); +} + +struct cache_mp_write_session_response * +get_cache_mp_write_session_response(struct comm_element *element) +{ + + TRACE_IN(get_cache_mp_write_session_response); + assert(element->type == CET_MP_WRITE_SESSION_RESPONSE); + TRACE_OUT(get_cache_mp_write_session_response); + return (&element->c_mp_ws_response); +} + +void +init_cache_mp_write_session_write_request( + struct cache_mp_write_session_write_request *mp_ws_write_request) +{ + + TRACE_IN(init_cache_mp_write_session_write_request); + memset(mp_ws_write_request, 0, + sizeof(struct cache_mp_write_session_write_request)); + TRACE_OUT(init_cache_mp_write_session_write_response); +} + +void +finalize_cache_mp_write_session_write_request( + struct cache_mp_write_session_write_request *mp_ws_write_request) +{ + + TRACE_IN(finalize_cache_mp_write_session_write_request); + free(mp_ws_write_request->data); + TRACE_OUT(finalize_cache_mp_write_session_write_request); +} + +struct cache_mp_write_session_write_request * +get_cache_mp_write_session_write_request(struct comm_element *element) +{ + + TRACE_IN(get_cache_mp_write_session_write_request); + assert(element->type == CET_MP_WRITE_SESSION_WRITE_REQUEST); + TRACE_OUT(get_cache_mp_write_session_write_request); + return (&element->c_mp_ws_write_request); +} + +void +init_cache_mp_write_session_write_response( + struct cache_mp_write_session_write_response *mp_ws_write_response) +{ + + TRACE_IN(init_cache_mp_write_session_write_response); + memset(mp_ws_write_response, 0, + sizeof(struct cache_mp_write_session_write_response)); + TRACE_OUT(init_cache_mp_write_session_write_response); +} + +void +finalize_cache_mp_write_session_write_response( + struct cache_mp_write_session_write_response *mp_ws_write_response) +{ + + TRACE_IN(finalize_cache_mp_write_session_write_response); + TRACE_OUT(finalize_cache_mp_write_session_write_response); +} + +struct cache_mp_write_session_write_response * +get_cache_mp_write_session_write_response(struct comm_element *element) +{ + + TRACE_IN(get_cache_mp_write_session_write_response); + assert(element->type == CET_MP_WRITE_SESSION_WRITE_RESPONSE); + TRACE_OUT(get_cache_mp_write_session_write_response); + return (&element->c_mp_ws_write_response); +} + +void +init_cache_mp_read_session_request( + struct cache_mp_read_session_request *mp_rs_request) +{ + + TRACE_IN(init_cache_mp_read_session_request); + memset(mp_rs_request, 0, sizeof(struct cache_mp_read_session_request)); + TRACE_OUT(init_cache_mp_read_session_request); +} + +void +finalize_cache_mp_read_session_request( + struct cache_mp_read_session_request *mp_rs_request) +{ + + TRACE_IN(finalize_cache_mp_read_session_request); + free(mp_rs_request->entry); + TRACE_OUT(finalize_cache_mp_read_session_request); +} + +struct cache_mp_read_session_request * +get_cache_mp_read_session_request(struct comm_element *element) +{ + + TRACE_IN(get_cache_mp_read_session_request); + assert(element->type == CET_MP_READ_SESSION_REQUEST); + TRACE_OUT(get_cache_mp_read_session_request); + return (&element->c_mp_rs_request); +} + +void +init_cache_mp_read_session_response( + struct cache_mp_read_session_response *mp_rs_response) +{ + + TRACE_IN(init_cache_mp_read_session_response); + memset(mp_rs_response, 0, + sizeof(struct cache_mp_read_session_response)); + TRACE_OUT(init_cache_mp_read_session_response); +} + +void +finalize_cache_mp_read_session_response( + struct cache_mp_read_session_response *mp_rs_response) +{ + + TRACE_IN(finalize_cache_mp_read_session_response); + TRACE_OUT(finalize_cache_mp_read_session_response); +} + +struct cache_mp_read_session_response * +get_cache_mp_read_session_response(struct comm_element *element) +{ + + TRACE_IN(get_cache_mp_read_session_response); + assert(element->type == CET_MP_READ_SESSION_RESPONSE); + TRACE_OUT(get_cache_mp_read_session_response); + return (&element->c_mp_rs_response); +} + +void +init_cache_mp_read_session_read_response( + struct cache_mp_read_session_read_response *mp_ws_read_response) +{ + + TRACE_IN(init_cache_mp_read_session_read_response); + memset(mp_ws_read_response, 0, + sizeof(struct cache_mp_read_session_read_response)); + TRACE_OUT(init_cache_mp_read_session_read_response); +} + +void +finalize_cache_mp_read_session_read_response( + struct cache_mp_read_session_read_response *mp_rs_read_response) +{ + + TRACE_IN(finalize_cache_mp_read_session_read_response); + free(mp_rs_read_response->data); + TRACE_OUT(finalize_cache_mp_read_session_read_response); +} + +struct cache_mp_read_session_read_response * +get_cache_mp_read_session_read_response(struct comm_element *element) +{ + + TRACE_IN(get_cache_mp_read_session_read_response); + assert(element->type == CET_MP_READ_SESSION_READ_RESPONSE); + TRACE_OUT(get_cache_mp_read_session_read_response); + return (&element->c_mp_rs_read_response); +} diff --git a/usr.sbin/cached/protocol.h b/usr.sbin/cached/protocol.h new file mode 100644 index 000000000000..7fadbfc4c2fb --- /dev/null +++ b/usr.sbin/cached/protocol.h @@ -0,0 +1,265 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_PROTOCOL_H__ +#define __CACHED_PROTOCOL_H__ + +#include + +/* maximum buffer size to receive - larger buffers are not allowed */ +#define MAX_BUFFER_SIZE (1 << 20) + +/* buffer size correctness checking routine */ +#define BUFSIZE_CORRECT(x) (((x) > 0) && ((x) < MAX_BUFFER_SIZE)) +#define BUFSIZE_INVALID(x) (!BUFSIZE_CORRECT(x)) + +/* structures below represent the data that are sent/received by the daemon */ +struct cache_write_request +{ + char *entry; + char *cache_key; + char *data; + + size_t entry_length; + size_t cache_key_size; + size_t data_size; +}; + +struct cache_write_response +{ + int error_code; +}; + +struct cache_read_request +{ + char *entry; + char *cache_key; + + size_t entry_length; + size_t cache_key_size; +}; + +struct cache_read_response +{ + char *data; // ignored if error_code is not 0 + size_t data_size; // ignored if error_code is not 0 + + int error_code; +}; + +enum transformation_type { + TT_USER = 0, // tranform only the entries of the caller + TT_ALL = 1 // transform all entries +}; + +struct cache_transform_request +{ + char *entry; // ignored if entry_length is 0 + size_t entry_length; + + int transformation_type; +}; + +struct cache_transform_response +{ + int error_code; +}; + +struct cache_mp_write_session_request { + char *entry; + size_t entry_length; +}; + +struct cache_mp_write_session_response { + int error_code; +}; + +struct cache_mp_write_session_write_request { + char *data; + size_t data_size; +}; + +struct cache_mp_write_session_write_response { + int error_code; +}; + +struct cache_mp_read_session_request { + char *entry; + size_t entry_length; +}; + +struct cache_mp_read_session_response { + int error_code; +}; + +struct cache_mp_read_session_read_response { + char *data; + size_t data_size; + + int error_code; +}; + + +enum comm_element_t { + CET_UNDEFINED = 0, + CET_WRITE_REQUEST = 1, + CET_WRITE_RESPONSE = 2, + CET_READ_REQUEST = 3, + CET_READ_RESPONSE = 4, + CET_TRANSFORM_REQUEST = 5, + CET_TRANSFORM_RESPONSE = 6, + CET_MP_WRITE_SESSION_REQUEST = 7, + CET_MP_WRITE_SESSION_RESPONSE = 8, + CET_MP_WRITE_SESSION_WRITE_REQUEST = 9, + CET_MP_WRITE_SESSION_WRITE_RESPONSE = 10, + CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION = 11, + CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION = 12, + CET_MP_READ_SESSION_REQUEST = 13, + CET_MP_READ_SESSION_RESPONSE = 14, + CET_MP_READ_SESSION_READ_REQUEST = 15, + CET_MP_READ_SESSION_READ_RESPONSE = 16, + CET_MP_READ_SESSION_CLOSE_NOTIFICATION = 17, + CET_MAX = 18 +}; + +/* + * The comm_element is used as the holder of any known (defined above) data + * type that is to be sent/received. + */ +struct comm_element +{ + union { + struct cache_write_request c_write_request; + struct cache_write_response c_write_response; + struct cache_read_request c_read_request; + struct cache_read_response c_read_response; + struct cache_transform_request c_transform_request; + struct cache_transform_response c_transform_response; + + struct cache_mp_write_session_request c_mp_ws_request; + struct cache_mp_write_session_response c_mp_ws_response; + struct cache_mp_write_session_write_request c_mp_ws_write_request; + struct cache_mp_write_session_write_response c_mp_ws_write_response; + + struct cache_mp_read_session_request c_mp_rs_request; + struct cache_mp_read_session_response c_mp_rs_response; + struct cache_mp_read_session_read_response c_mp_rs_read_response; + }; + enum comm_element_t type; +}; + +extern void init_comm_element(struct comm_element *, enum comm_element_t type); +extern void finalize_comm_element(struct comm_element *); + +/* + * For each type of data, there is three functions (init/finalize/get), that + * used with comm_element structure + */ +extern void init_cache_write_request(struct cache_write_request *); +extern void finalize_cache_write_request(struct cache_write_request *); +extern struct cache_write_request *get_cache_write_request( + struct comm_element *); + +extern void init_cache_write_response(struct cache_write_response *); +extern void finalize_cache_write_response(struct cache_write_response *); +extern struct cache_write_response *get_cache_write_response( + struct comm_element *); + +extern void init_cache_read_request(struct cache_read_request *); +extern void finalize_cache_read_request(struct cache_read_request *); +extern struct cache_read_request *get_cache_read_request( + struct comm_element *); + +extern void init_cache_read_response(struct cache_read_response *); +extern void finalize_cache_read_response(struct cache_read_response *); +extern struct cache_read_response *get_cache_read_response( + struct comm_element *); + +extern void init_cache_transform_request(struct cache_transform_request *); +extern void finalize_cache_transform_request(struct cache_transform_request *); +extern struct cache_transform_request *get_cache_transform_request( + struct comm_element *); + +extern void init_cache_transform_response(struct cache_transform_response *); +extern void finalize_cache_transform_response( + struct cache_transform_response *); +extern struct cache_transform_response *get_cache_transform_response( + struct comm_element *); + +extern void init_cache_mp_write_session_request( + struct cache_mp_write_session_request *); +extern void finalize_cache_mp_write_session_request( + struct cache_mp_write_session_request *); +extern struct cache_mp_write_session_request * + get_cache_mp_write_session_request( + struct comm_element *); + +extern void init_cache_mp_write_session_response( + struct cache_mp_write_session_response *); +extern void finalize_cache_mp_write_session_response( + struct cache_mp_write_session_response *); +extern struct cache_mp_write_session_response * + get_cache_mp_write_session_response(struct comm_element *); + +extern void init_cache_mp_write_session_write_request( + struct cache_mp_write_session_write_request *); +extern void finalize_cache_mp_write_session_write_request( + struct cache_mp_write_session_write_request *); +extern struct cache_mp_write_session_write_request * + get_cache_mp_write_session_write_request(struct comm_element *); + +extern void init_cache_mp_write_session_write_response( + struct cache_mp_write_session_write_response *); +extern void finalize_cache_mp_write_session_write_response( + struct cache_mp_write_session_write_response *); +extern struct cache_mp_write_session_write_response * + get_cache_mp_write_session_write_response(struct comm_element *); + +extern void init_cache_mp_read_session_request( + struct cache_mp_read_session_request *); +extern void finalize_cache_mp_read_session_request( + struct cache_mp_read_session_request *); +extern struct cache_mp_read_session_request *get_cache_mp_read_session_request( + struct comm_element *); + +extern void init_cache_mp_read_session_response( + struct cache_mp_read_session_response *); +extern void finalize_cache_mp_read_session_response( + struct cache_mp_read_session_response *); +extern struct cache_mp_read_session_response * + get_cache_mp_read_session_response( + struct comm_element *); + +extern void init_cache_mp_read_session_read_response( + struct cache_mp_read_session_read_response *); +extern void finalize_cache_mp_read_session_read_response( + struct cache_mp_read_session_read_response *); +extern struct cache_mp_read_session_read_response * + get_cache_mp_read_session_read_response(struct comm_element *); + +#endif diff --git a/usr.sbin/cached/query.c b/usr.sbin/cached/query.c new file mode 100644 index 000000000000..28882c3d01df --- /dev/null +++ b/usr.sbin/cached/query.c @@ -0,0 +1,1278 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "debug.h" +#include "query.h" +#include "log.h" +#include "mp_ws_query.h" +#include "mp_rs_query.h" +#include "singletons.h" + +static const char negative_data[1] = { 0 }; + +extern void get_time_func(struct timeval *); + +static void clear_config_entry(struct configuration_entry *); +static void clear_config_entry_part(struct configuration_entry *, + const char *, size_t); + +static int on_query_startup(struct query_state *); +static void on_query_destroy(struct query_state *); + +static int on_read_request_read1(struct query_state *); +static int on_read_request_read2(struct query_state *); +static int on_read_request_process(struct query_state *); +static int on_read_response_write1(struct query_state *); +static int on_read_response_write2(struct query_state *); + +static int on_rw_mapper(struct query_state *); + +static int on_transform_request_read1(struct query_state *); +static int on_transform_request_read2(struct query_state *); +static int on_transform_request_process(struct query_state *); +static int on_transform_response_write1(struct query_state *); + +static int on_write_request_read1(struct query_state *); +static int on_write_request_read2(struct query_state *); +static int on_negative_write_request_process(struct query_state *); +static int on_write_request_process(struct query_state *); +static int on_write_response_write1(struct query_state *); + +/* + * Clears the specified configuration entry (clears the cache for positive and + * and negative entries) and also for all multipart entries. + */ +static void +clear_config_entry(struct configuration_entry *config_entry) +{ + size_t i; + + TRACE_IN(clear_config_entry); + configuration_lock_entry(config_entry, CELT_POSITIVE); + if (config_entry->positive_cache_entry != NULL) + transform_cache_entry( + config_entry->positive_cache_entry, + CTT_CLEAR); + configuration_unlock_entry(config_entry, CELT_POSITIVE); + + configuration_lock_entry(config_entry, CELT_NEGATIVE); + if (config_entry->negative_cache_entry != NULL) + transform_cache_entry( + config_entry->negative_cache_entry, + CTT_CLEAR); + configuration_unlock_entry(config_entry, CELT_NEGATIVE); + + configuration_lock_entry(config_entry, CELT_MULTIPART); + for (i = 0; i < config_entry->mp_cache_entries_size; ++i) + transform_cache_entry( + config_entry->mp_cache_entries[i], + CTT_CLEAR); + configuration_unlock_entry(config_entry, CELT_MULTIPART); + + TRACE_OUT(clear_config_entry); +} + +/* + * Clears the specified configuration entry by deleting only the elements, + * that are owned by the user with specified eid_str. + */ +static void +clear_config_entry_part(struct configuration_entry *config_entry, + const char *eid_str, size_t eid_str_length) +{ + cache_entry *start, *finish, *mp_entry; + TRACE_IN(clear_config_entry_part); + configuration_lock_entry(config_entry, CELT_POSITIVE); + if (config_entry->positive_cache_entry != NULL) + transform_cache_entry_part( + config_entry->positive_cache_entry, + CTT_CLEAR, eid_str, eid_str_length, KPPT_LEFT); + configuration_unlock_entry(config_entry, CELT_POSITIVE); + + configuration_lock_entry(config_entry, CELT_NEGATIVE); + if (config_entry->negative_cache_entry != NULL) + transform_cache_entry_part( + config_entry->negative_cache_entry, + CTT_CLEAR, eid_str, eid_str_length, KPPT_LEFT); + configuration_unlock_entry(config_entry, CELT_NEGATIVE); + + configuration_lock_entry(config_entry, CELT_MULTIPART); + if (configuration_entry_find_mp_cache_entries(config_entry, + eid_str, &start, &finish) == 0) { + for (mp_entry = start; mp_entry != finish; ++mp_entry) + transform_cache_entry(*mp_entry, CTT_CLEAR); + } + configuration_unlock_entry(config_entry, CELT_MULTIPART); + + TRACE_OUT(clear_config_entry_part); +} + +/* + * This function is assigned to the query_state structue on its creation. + * It's main purpose is to receive credentials from the client. + */ +static int +on_query_startup(struct query_state *qstate) +{ + struct msghdr cred_hdr; + struct iovec iov; + int elem_type; + + struct { + struct cmsghdr hdr; + struct cmsgcred creds; + } cmsg; + + TRACE_IN(on_query_startup); + assert(qstate != NULL); + + memset(&cred_hdr, 0, sizeof(struct msghdr)); + cred_hdr.msg_iov = &iov; + cred_hdr.msg_iovlen = 1; + cred_hdr.msg_control = &cmsg; + cred_hdr.msg_controllen = sizeof(cmsg); + + memset(&iov, 0, sizeof(struct iovec)); + iov.iov_base = &elem_type; + iov.iov_len = sizeof(int); + + if (recvmsg(qstate->sockfd, &cred_hdr, 0) == -1) { + TRACE_OUT(on_query_startup); + return (-1); + } + + if (cmsg.hdr.cmsg_len != sizeof cmsg + || cmsg.hdr.cmsg_level != SOL_SOCKET + || cmsg.hdr.cmsg_type != SCM_CREDS) { + TRACE_OUT(on_query_startup); + return (-1); + } + + qstate->uid = cmsg.creds.cmcred_uid; + qstate->gid = cmsg.creds.cmcred_gid; + +#if defined(NS_CACHED_EID_CHECKING) || defined(NS_STRICT_CACHED_EID_CHECKING) +/* + * This check is probably a bit redundant - per-user cache is always separated + * by the euid/egid pair + */ + if (check_query_eids(qstate) != 0) { +#ifdef NS_STRICT_CACHED_EID_CHECKING + TRACE_OUT(on_query_startup); + return (-1); +#else + if ((elem_type != CET_READ_REQUEST) && + (elem_type != CET_MP_READ_SESSION_REQUEST) && + (elem_type != CET_WRITE_REQUEST) && + (elem_type != CET_MP_WRITE_SESSION_REQUEST)) { + TRACE_OUT(on_query_startup); + return (-1); + } +#endif + } +#endif + + switch (elem_type) { + case CET_WRITE_REQUEST: + qstate->process_func = on_write_request_read1; + break; + case CET_READ_REQUEST: + qstate->process_func = on_read_request_read1; + break; + case CET_TRANSFORM_REQUEST: + qstate->process_func = on_transform_request_read1; + break; + case CET_MP_WRITE_SESSION_REQUEST: + qstate->process_func = on_mp_write_session_request_read1; + break; + case CET_MP_READ_SESSION_REQUEST: + qstate->process_func = on_mp_read_session_request_read1; + break; + default: + TRACE_OUT(on_query_startup); + return (-1); + } + + qstate->kevent_watermark = 0; + TRACE_OUT(on_query_startup); + return (0); +} + +/* + * on_rw_mapper is used to process multiple read/write requests during + * one connection session. It's never called in the beginning (on query_state + * creation) as it does not process the multipart requests and does not + * receive credentials + */ +static int +on_rw_mapper(struct query_state *qstate) +{ + ssize_t result; + int elem_type; + + TRACE_IN(on_rw_mapper); + if (qstate->kevent_watermark == 0) { + qstate->kevent_watermark = sizeof(int); + } else { + result = qstate->read_func(qstate, &elem_type, sizeof(int)); + if (result != sizeof(int)) { + TRACE_OUT(on_rw_mapper); + return (-1); + } + + switch (elem_type) { + case CET_WRITE_REQUEST: + qstate->kevent_watermark = sizeof(size_t); + qstate->process_func = on_write_request_read1; + break; + case CET_READ_REQUEST: + qstate->kevent_watermark = sizeof(size_t); + qstate->process_func = on_read_request_read1; + break; + default: + TRACE_OUT(on_rw_mapper); + return (-1); + break; + } + } + TRACE_OUT(on_rw_mapper); + return (0); +} + +/* + * The default query_destroy function + */ +static void +on_query_destroy(struct query_state *qstate) +{ + + TRACE_IN(on_query_destroy); + finalize_comm_element(&qstate->response); + finalize_comm_element(&qstate->request); + TRACE_OUT(on_query_destroy); +} + +/* + * The functions below are used to process write requests. + * - on_write_request_read1 and on_write_request_read2 read the request itself + * - on_write_request_process processes it (if the client requests to + * cache the negative result, the on_negative_write_request_process is used) + * - on_write_response_write1 sends the response + */ +static int +on_write_request_read1(struct query_state *qstate) +{ + struct cache_write_request *write_request; + ssize_t result; + + TRACE_IN(on_write_request_read1); + if (qstate->kevent_watermark == 0) + qstate->kevent_watermark = sizeof(size_t) * 3; + else { + init_comm_element(&qstate->request, CET_WRITE_REQUEST); + write_request = get_cache_write_request(&qstate->request); + + result = qstate->read_func(qstate, &write_request->entry_length, + sizeof(size_t)); + result += qstate->read_func(qstate, + &write_request->cache_key_size, sizeof(size_t)); + result += qstate->read_func(qstate, + &write_request->data_size, sizeof(size_t)); + + if (result != sizeof(size_t) * 3) { + TRACE_OUT(on_write_request_read1); + return (-1); + } + + if (BUFSIZE_INVALID(write_request->entry_length) || + BUFSIZE_INVALID(write_request->cache_key_size) || + (BUFSIZE_INVALID(write_request->data_size) && + (write_request->data_size != 0))) { + TRACE_OUT(on_write_request_read1); + return (-1); + } + + write_request->entry = (char *)malloc( + write_request->entry_length + 1); + assert(write_request->entry != NULL); + memset(write_request->entry, 0, + write_request->entry_length + 1); + + write_request->cache_key = (char *)malloc( + write_request->cache_key_size + + qstate->eid_str_length); + assert(write_request->cache_key != NULL); + memcpy(write_request->cache_key, qstate->eid_str, + qstate->eid_str_length); + memset(write_request->cache_key + qstate->eid_str_length, 0, + write_request->cache_key_size); + + if (write_request->data_size != 0) { + write_request->data = (char *)malloc( + write_request->data_size); + assert(write_request->data != NULL); + memset(write_request->data, 0, + write_request->data_size); + } + + qstate->kevent_watermark = write_request->entry_length + + write_request->cache_key_size + + write_request->data_size; + qstate->process_func = on_write_request_read2; + } + + TRACE_OUT(on_write_request_read1); + return (0); +} + +static int +on_write_request_read2(struct query_state *qstate) +{ + struct cache_write_request *write_request; + ssize_t result; + + TRACE_IN(on_write_request_read2); + write_request = get_cache_write_request(&qstate->request); + + result = qstate->read_func(qstate, write_request->entry, + write_request->entry_length); + result += qstate->read_func(qstate, write_request->cache_key + + qstate->eid_str_length, write_request->cache_key_size); + if (write_request->data_size != 0) + result += qstate->read_func(qstate, write_request->data, + write_request->data_size); + + if (result != qstate->kevent_watermark) { + TRACE_OUT(on_write_request_read2); + return (-1); + } + write_request->cache_key_size += qstate->eid_str_length; + + qstate->kevent_watermark = 0; + if (write_request->data_size != 0) + qstate->process_func = on_write_request_process; + else + qstate->process_func = on_negative_write_request_process; + TRACE_OUT(on_write_request_read2); + return (0); +} + +static int +on_write_request_process(struct query_state *qstate) +{ + struct cache_write_request *write_request; + struct cache_write_response *write_response; + cache_entry c_entry; + + TRACE_IN(on_write_request_process); + init_comm_element(&qstate->response, CET_WRITE_RESPONSE); + write_response = get_cache_write_response(&qstate->response); + write_request = get_cache_write_request(&qstate->request); + + qstate->config_entry = configuration_find_entry( + s_configuration, write_request->entry); + + if (qstate->config_entry == NULL) { + write_response->error_code = ENOENT; + + LOG_ERR_2("write_request", "can't find configuration" + " entry '%s'. aborting request", write_request->entry); + goto fin; + } + + if (qstate->config_entry->enabled == 0) { + write_response->error_code = EACCES; + + LOG_ERR_2("write_request", + "configuration entry '%s' is disabled", + write_request->entry); + goto fin; + } + + if (qstate->config_entry->perform_actual_lookups != 0) { + write_response->error_code = EOPNOTSUPP; + + LOG_ERR_2("write_request", + "entry '%s' performs lookups by itself: " + "can't write to it", write_request->entry); + goto fin; + } + + configuration_lock_rdlock(s_configuration); + c_entry = find_cache_entry(s_cache, + qstate->config_entry->positive_cache_params.entry_name); + configuration_unlock(s_configuration); + if (c_entry != NULL) { + configuration_lock_entry(qstate->config_entry, CELT_POSITIVE); + qstate->config_entry->positive_cache_entry = c_entry; + write_response->error_code = cache_write(c_entry, + write_request->cache_key, + write_request->cache_key_size, + write_request->data, + write_request->data_size); + configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE); + + if ((qstate->config_entry->common_query_timeout.tv_sec != 0) || + (qstate->config_entry->common_query_timeout.tv_usec != 0)) + memcpy(&qstate->timeout, + &qstate->config_entry->common_query_timeout, + sizeof(struct timeval)); + + } else + write_response->error_code = -1; + +fin: + qstate->kevent_filter = EVFILT_WRITE; + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_write_response_write1; + + TRACE_OUT(on_write_request_process); + return (0); +} + +static int +on_negative_write_request_process(struct query_state *qstate) +{ + struct cache_write_request *write_request; + struct cache_write_response *write_response; + cache_entry c_entry; + + TRACE_IN(on_negative_write_request_process); + init_comm_element(&qstate->response, CET_WRITE_RESPONSE); + write_response = get_cache_write_response(&qstate->response); + write_request = get_cache_write_request(&qstate->request); + + qstate->config_entry = configuration_find_entry ( + s_configuration, write_request->entry); + + if (qstate->config_entry == NULL) { + write_response->error_code = ENOENT; + + LOG_ERR_2("negative_write_request", + "can't find configuration" + " entry '%s'. aborting request", write_request->entry); + goto fin; + } + + if (qstate->config_entry->enabled == 0) { + write_response->error_code = EACCES; + + LOG_ERR_2("negative_write_request", + "configuration entry '%s' is disabled", + write_request->entry); + goto fin; + } + + if (qstate->config_entry->perform_actual_lookups != 0) { + write_response->error_code = EOPNOTSUPP; + + LOG_ERR_2("negative_write_request", + "entry '%s' performs lookups by itself: " + "can't write to it", write_request->entry); + goto fin; + } else { +#ifdef NS_CACHED_EID_CHECKING + if (check_query_eids(qstate) != 0) { + write_response->error_code = EPERM; + goto fin; + } +#endif + } + + configuration_lock_rdlock(s_configuration); + c_entry = find_cache_entry(s_cache, + qstate->config_entry->negative_cache_params.entry_name); + configuration_unlock(s_configuration); + if (c_entry != NULL) { + configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE); + qstate->config_entry->negative_cache_entry = c_entry; + write_response->error_code = cache_write(c_entry, + write_request->cache_key, + write_request->cache_key_size, + negative_data, + sizeof(negative_data)); + configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE); + + if ((qstate->config_entry->common_query_timeout.tv_sec != 0) || + (qstate->config_entry->common_query_timeout.tv_usec != 0)) + memcpy(&qstate->timeout, + &qstate->config_entry->common_query_timeout, + sizeof(struct timeval)); + } else + write_response->error_code = -1; + +fin: + qstate->kevent_filter = EVFILT_WRITE; + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_write_response_write1; + + TRACE_OUT(on_negative_write_request_process); + return (0); +} + +static int +on_write_response_write1(struct query_state *qstate) +{ + struct cache_write_response *write_response; + ssize_t result; + + TRACE_IN(on_write_response_write1); + write_response = get_cache_write_response(&qstate->response); + result = qstate->write_func(qstate, &write_response->error_code, + sizeof(int)); + if (result != sizeof(int)) { + TRACE_OUT(on_write_response_write1); + return (-1); + } + + finalize_comm_element(&qstate->request); + finalize_comm_element(&qstate->response); + + qstate->kevent_watermark = sizeof(int); + qstate->kevent_filter = EVFILT_READ; + qstate->process_func = on_rw_mapper; + + TRACE_OUT(on_write_response_write1); + return (0); +} + +/* + * The functions below are used to process read requests. + * - on_read_request_read1 and on_read_request_read2 read the request itself + * - on_read_request_process processes it + * - on_read_response_write1 and on_read_response_write2 send the response + */ +static int +on_read_request_read1(struct query_state *qstate) +{ + struct cache_read_request *read_request; + ssize_t result; + + TRACE_IN(on_read_request_read1); + if (qstate->kevent_watermark == 0) + qstate->kevent_watermark = sizeof(size_t) * 2; + else { + init_comm_element(&qstate->request, CET_READ_REQUEST); + read_request = get_cache_read_request(&qstate->request); + + result = qstate->read_func(qstate, + &read_request->entry_length, sizeof(size_t)); + result += qstate->read_func(qstate, + &read_request->cache_key_size, sizeof(size_t)); + + if (result != sizeof(size_t) * 2) { + TRACE_OUT(on_read_request_read1); + return (-1); + } + + if (BUFSIZE_INVALID(read_request->entry_length) || + BUFSIZE_INVALID(read_request->cache_key_size)) { + TRACE_OUT(on_read_request_read1); + return (-1); + } + + read_request->entry = (char *)malloc( + read_request->entry_length + 1); + assert(read_request->entry != NULL); + memset(read_request->entry, 0, read_request->entry_length + 1); + + read_request->cache_key = (char *)malloc( + read_request->cache_key_size + + qstate->eid_str_length); + assert(read_request->cache_key != NULL); + memcpy(read_request->cache_key, qstate->eid_str, + qstate->eid_str_length); + memset(read_request->cache_key + qstate->eid_str_length, 0, + read_request->cache_key_size); + + qstate->kevent_watermark = read_request->entry_length + + read_request->cache_key_size; + qstate->process_func = on_read_request_read2; + } + + TRACE_OUT(on_read_request_read1); + return (0); +} + +static int +on_read_request_read2(struct query_state *qstate) +{ + struct cache_read_request *read_request; + ssize_t result; + + TRACE_IN(on_read_request_read2); + read_request = get_cache_read_request(&qstate->request); + + result = qstate->read_func(qstate, read_request->entry, + read_request->entry_length); + result += qstate->read_func(qstate, + read_request->cache_key + qstate->eid_str_length, + read_request->cache_key_size); + + if (result != qstate->kevent_watermark) { + TRACE_OUT(on_read_request_read2); + return (-1); + } + read_request->cache_key_size += qstate->eid_str_length; + + qstate->kevent_watermark = 0; + qstate->process_func = on_read_request_process; + + TRACE_OUT(on_read_request_read2); + return (0); +} + +static int +on_read_request_process(struct query_state *qstate) +{ + struct cache_read_request *read_request; + struct cache_read_response *read_response; + cache_entry c_entry, neg_c_entry; + + struct agent *lookup_agent; + struct common_agent *c_agent; + int res; + + TRACE_IN(on_read_request_process); + init_comm_element(&qstate->response, CET_READ_RESPONSE); + read_response = get_cache_read_response(&qstate->response); + read_request = get_cache_read_request(&qstate->request); + + qstate->config_entry = configuration_find_entry( + s_configuration, read_request->entry); + if (qstate->config_entry == NULL) { + read_response->error_code = ENOENT; + + LOG_ERR_2("read_request", + "can't find configuration " + "entry '%s'. aborting request", read_request->entry); + goto fin; + } + + if (qstate->config_entry->enabled == 0) { + read_response->error_code = EACCES; + + LOG_ERR_2("read_request", + "configuration entry '%s' is disabled", + read_request->entry); + goto fin; + } + + /* + * if we perform lookups by ourselves, then we don't need to separate + * cache entries by euid and egid + */ + if (qstate->config_entry->perform_actual_lookups != 0) + memset(read_request->cache_key, 0, qstate->eid_str_length); + else { +#ifdef NS_CACHED_EID_CHECKING + if (check_query_eids(qstate) != 0) { + /* if the lookup is not self-performing, we check for clients euid/egid */ + read_response->error_code = EPERM; + goto fin; + } +#endif + } + + configuration_lock_rdlock(s_configuration); + c_entry = find_cache_entry(s_cache, + qstate->config_entry->positive_cache_params.entry_name); + neg_c_entry = find_cache_entry(s_cache, + qstate->config_entry->negative_cache_params.entry_name); + configuration_unlock(s_configuration); + if ((c_entry != NULL) && (neg_c_entry != NULL)) { + configuration_lock_entry(qstate->config_entry, CELT_POSITIVE); + qstate->config_entry->positive_cache_entry = c_entry; + read_response->error_code = cache_read(c_entry, + read_request->cache_key, + read_request->cache_key_size, NULL, + &read_response->data_size); + + if (read_response->error_code == -2) { + read_response->data = (char *)malloc( + read_response->data_size); + assert(read_response != NULL); + read_response->error_code = cache_read(c_entry, + read_request->cache_key, + read_request->cache_key_size, + read_response->data, + &read_response->data_size); + } + configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE); + + configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE); + qstate->config_entry->negative_cache_entry = neg_c_entry; + if (read_response->error_code == -1) { + read_response->error_code = cache_read(neg_c_entry, + read_request->cache_key, + read_request->cache_key_size, NULL, + &read_response->data_size); + + if (read_response->error_code == -2) { + read_response->error_code = 0; + read_response->data = NULL; + read_response->data_size = 0; + } + } + configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE); + + if ((read_response->error_code == -1) && + (qstate->config_entry->perform_actual_lookups != 0)) { + free(read_response->data); + read_response->data = NULL; + read_response->data_size = 0; + + lookup_agent = find_agent(s_agent_table, + read_request->entry, COMMON_AGENT); + + if ((lookup_agent != NULL) && + (lookup_agent->type == COMMON_AGENT)) { + c_agent = (struct common_agent *)lookup_agent; + res = c_agent->lookup_func( + read_request->cache_key + + qstate->eid_str_length, + read_request->cache_key_size - + qstate->eid_str_length, + &read_response->data, + &read_response->data_size); + + if (res == NS_SUCCESS) { + read_response->error_code = 0; + configuration_lock_entry( + qstate->config_entry, + CELT_POSITIVE); + cache_write(c_entry, + read_request->cache_key, + read_request->cache_key_size, + read_response->data, + read_response->data_size); + configuration_unlock_entry( + qstate->config_entry, + CELT_POSITIVE); + } else if ((res == NS_NOTFOUND) || + (res == NS_RETURN)) { + configuration_lock_entry( + qstate->config_entry, + CELT_NEGATIVE); + cache_write(neg_c_entry, + read_request->cache_key, + read_request->cache_key_size, + negative_data, + sizeof(negative_data)); + configuration_unlock_entry( + qstate->config_entry, + CELT_NEGATIVE); + + read_response->error_code = 0; + read_response->data = NULL; + read_response->data_size = 0; + } + } + } + + if ((qstate->config_entry->common_query_timeout.tv_sec != 0) || + (qstate->config_entry->common_query_timeout.tv_usec != 0)) + memcpy(&qstate->timeout, + &qstate->config_entry->common_query_timeout, + sizeof(struct timeval)); + } else + read_response->error_code = -1; + +fin: + qstate->kevent_filter = EVFILT_WRITE; + if (read_response->error_code == 0) + qstate->kevent_watermark = sizeof(int) + sizeof(size_t); + else + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_read_response_write1; + + TRACE_OUT(on_read_request_process); + return (0); +} + +static int +on_read_response_write1(struct query_state *qstate) +{ + struct cache_read_response *read_response; + ssize_t result; + + TRACE_IN(on_read_response_write1); + read_response = get_cache_read_response(&qstate->response); + + result = qstate->write_func(qstate, &read_response->error_code, + sizeof(int)); + + if (read_response->error_code == 0) { + result += qstate->write_func(qstate, &read_response->data_size, + sizeof(size_t)); + if (result != qstate->kevent_watermark) { + TRACE_OUT(on_read_response_write1); + return (-1); + } + + qstate->kevent_watermark = read_response->data_size; + qstate->process_func = on_read_response_write2; + } else { + if (result != qstate->kevent_watermark) { + TRACE_OUT(on_read_response_write1); + return (-1); + } + + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + } + + TRACE_OUT(on_read_response_write1); + return (0); +} + +static int +on_read_response_write2(struct query_state *qstate) +{ + struct cache_read_response *read_response; + ssize_t result; + + TRACE_IN(on_read_response_write2); + read_response = get_cache_read_response(&qstate->response); + if (read_response->data_size > 0) { + result = qstate->write_func(qstate, read_response->data, + read_response->data_size); + if (result != qstate->kevent_watermark) { + TRACE_OUT(on_read_response_write2); + return (-1); + } + } + + finalize_comm_element(&qstate->request); + finalize_comm_element(&qstate->response); + + qstate->kevent_watermark = sizeof(int); + qstate->kevent_filter = EVFILT_READ; + qstate->process_func = on_rw_mapper; + TRACE_OUT(on_read_response_write2); + return (0); +} + +/* + * The functions below are used to process write requests. + * - on_transform_request_read1 and on_transform_request_read2 read the + * request itself + * - on_transform_request_process processes it + * - on_transform_response_write1 sends the response + */ +static int +on_transform_request_read1(struct query_state *qstate) +{ + struct cache_transform_request *transform_request; + ssize_t result; + + TRACE_IN(on_transform_request_read1); + if (qstate->kevent_watermark == 0) + qstate->kevent_watermark = sizeof(size_t) + sizeof(int); + else { + init_comm_element(&qstate->request, CET_TRANSFORM_REQUEST); + transform_request = + get_cache_transform_request(&qstate->request); + + result = qstate->read_func(qstate, + &transform_request->entry_length, sizeof(size_t)); + result += qstate->read_func(qstate, + &transform_request->transformation_type, sizeof(int)); + + if (result != sizeof(size_t) + sizeof(int)) { + TRACE_OUT(on_transform_request_read1); + return (-1); + } + + if ((transform_request->transformation_type != TT_USER) && + (transform_request->transformation_type != TT_ALL)) { + TRACE_OUT(on_transform_request_read1); + return (-1); + } + + if (transform_request->entry_length != 0) { + if (BUFSIZE_INVALID(transform_request->entry_length)) { + TRACE_OUT(on_transform_request_read1); + return (-1); + } + + transform_request->entry = (char *)malloc( + transform_request->entry_length + 1); + assert(transform_request->entry != NULL); + memset(transform_request->entry, 0, + transform_request->entry_length + 1); + + qstate->process_func = on_transform_request_read2; + } else + qstate->process_func = on_transform_request_process; + + qstate->kevent_watermark = transform_request->entry_length; + } + + TRACE_OUT(on_transform_request_read1); + return (0); +} + +static int +on_transform_request_read2(struct query_state *qstate) +{ + struct cache_transform_request *transform_request; + ssize_t result; + + TRACE_IN(on_transform_request_read2); + transform_request = get_cache_transform_request(&qstate->request); + + result = qstate->read_func(qstate, transform_request->entry, + transform_request->entry_length); + + if (result != qstate->kevent_watermark) { + TRACE_OUT(on_transform_request_read2); + return (-1); + } + + qstate->kevent_watermark = 0; + qstate->process_func = on_transform_request_process; + + TRACE_OUT(on_transform_request_read2); + return (0); +} + +static int +on_transform_request_process(struct query_state *qstate) +{ + struct cache_transform_request *transform_request; + struct cache_transform_response *transform_response; + struct configuration_entry *config_entry; + size_t i, size; + + TRACE_IN(on_transform_request_process); + init_comm_element(&qstate->response, CET_TRANSFORM_RESPONSE); + transform_response = get_cache_transform_response(&qstate->response); + transform_request = get_cache_transform_request(&qstate->request); + + switch (transform_request->transformation_type) { + case TT_USER: + if (transform_request->entry == NULL) { + size = configuration_get_entries_size(s_configuration); + for (i = 0; i < size; ++i) { + config_entry = configuration_get_entry( + s_configuration, i); + + if (config_entry->perform_actual_lookups == 0) + clear_config_entry_part(config_entry, + qstate->eid_str, qstate->eid_str_length); + } + } else { + qstate->config_entry = configuration_find_entry( + s_configuration, transform_request->entry); + + if (qstate->config_entry == NULL) { + LOG_ERR_2("transform_request", + "can't find configuration" + " entry '%s'. aborting request", + transform_request->entry); + transform_response->error_code = -1; + goto fin; + } + + if (qstate->config_entry->perform_actual_lookups != 0) { + LOG_ERR_2("transform_request", + "can't transform the cache entry %s" + ", because it ised for actual lookups", + transform_request->entry); + transform_response->error_code = -1; + goto fin; + } + + clear_config_entry_part(qstate->config_entry, + qstate->eid_str, qstate->eid_str_length); + } + break; + case TT_ALL: + if (qstate->euid != 0) + transform_response->error_code = -1; + else { + if (transform_request->entry == NULL) { + size = configuration_get_entries_size( + s_configuration); + for (i = 0; i < size; ++i) { + clear_config_entry( + configuration_get_entry( + s_configuration, i)); + } + } else { + qstate->config_entry = configuration_find_entry( + s_configuration, + transform_request->entry); + + if (qstate->config_entry == NULL) { + LOG_ERR_2("transform_request", + "can't find configuration" + " entry '%s'. aborting request", + transform_request->entry); + transform_response->error_code = -1; + goto fin; + } + + clear_config_entry(qstate->config_entry); + } + } + break; + default: + transform_response->error_code = -1; + } + +fin: + qstate->kevent_watermark = 0; + qstate->process_func = on_transform_response_write1; + TRACE_OUT(on_transform_request_process); + return (0); +} + +static int +on_transform_response_write1(struct query_state *qstate) +{ + struct cache_transform_response *transform_response; + ssize_t result; + + TRACE_IN(on_transform_response_write1); + transform_response = get_cache_transform_response(&qstate->response); + result = qstate->write_func(qstate, &transform_response->error_code, + sizeof(int)); + if (result != sizeof(int)) { + TRACE_OUT(on_transform_response_write1); + return (-1); + } + + finalize_comm_element(&qstate->request); + finalize_comm_element(&qstate->response); + + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + TRACE_OUT(on_transform_response_write1); + return (0); +} + +/* + * Checks if the client's euid and egid do not differ from its uid and gid. + * Returns 0 on success. + */ +int +check_query_eids(struct query_state *qstate) +{ + + return ((qstate->uid != qstate->euid) || (qstate->gid != qstate->egid) ? -1 : 0); +} + +/* + * Uses the qstate fields to process an "alternate" read - when the buffer is + * too large to be received during one socket read operation + */ +ssize_t +query_io_buffer_read(struct query_state *qstate, void *buf, size_t nbytes) +{ + ssize_t result; + + TRACE_IN(query_io_buffer_read); + if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL)) + return (-1); + + if (nbytes < qstate->io_buffer + qstate->io_buffer_size - + qstate->io_buffer_p) + result = nbytes; + else + result = qstate->io_buffer + qstate->io_buffer_size - + qstate->io_buffer_p; + + memcpy(buf, qstate->io_buffer_p, result); + qstate->io_buffer_p += result; + + if (qstate->io_buffer_p == qstate->io_buffer + qstate->io_buffer_size) { + free(qstate->io_buffer); + qstate->io_buffer = NULL; + + qstate->write_func = query_socket_write; + qstate->read_func = query_socket_read; + } + + TRACE_OUT(query_io_buffer_read); + return (result); +} + +/* + * Uses the qstate fields to process an "alternate" write - when the buffer is + * too large to be sent during one socket write operation + */ +ssize_t +query_io_buffer_write(struct query_state *qstate, const void *buf, + size_t nbytes) +{ + ssize_t result; + + TRACE_IN(query_io_buffer_write); + if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL)) + return (-1); + + if (nbytes < qstate->io_buffer + qstate->io_buffer_size - + qstate->io_buffer_p) + result = nbytes; + else + result = qstate->io_buffer + qstate->io_buffer_size - + qstate->io_buffer_p; + + memcpy(qstate->io_buffer_p, buf, result); + qstate->io_buffer_p += result; + + if (qstate->io_buffer_p == qstate->io_buffer + qstate->io_buffer_size) { + qstate->use_alternate_io = 1; + qstate->io_buffer_p = qstate->io_buffer; + + qstate->write_func = query_socket_write; + qstate->read_func = query_socket_read; + } + + TRACE_OUT(query_io_buffer_write); + return (result); +} + +/* + * The default "read" function, which reads data directly from socket + */ +ssize_t +query_socket_read(struct query_state *qstate, void *buf, size_t nbytes) +{ + ssize_t result; + + TRACE_IN(query_socket_read); + if (qstate->socket_failed != 0) { + TRACE_OUT(query_socket_read); + return (-1); + } + + result = read(qstate->sockfd, buf, nbytes); + if ((result == -1) || (result < nbytes)) + qstate->socket_failed = 1; + + TRACE_OUT(query_socket_read); + return (result); +} + +/* + * The default "write" function, which writes data directly to socket + */ +ssize_t +query_socket_write(struct query_state *qstate, const void *buf, size_t nbytes) +{ + ssize_t result; + + TRACE_IN(query_socket_write); + if (qstate->socket_failed != 0) { + TRACE_OUT(query_socket_write); + return (-1); + } + + result = write(qstate->sockfd, buf, nbytes); + if ((result == -1) || (result < nbytes)) + qstate->socket_failed = 1; + + TRACE_OUT(query_socket_write); + return (result); +} + +/* + * Initializes the query_state structure by filling it with the default values. + */ +struct query_state * +init_query_state(int sockfd, size_t kevent_watermark, uid_t euid, gid_t egid) +{ + struct query_state *retval; + + TRACE_IN(init_query_state); + retval = (struct query_state *)malloc(sizeof(struct query_state)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct query_state)); + + retval->sockfd = sockfd; + retval->kevent_filter = EVFILT_READ; + retval->kevent_watermark = kevent_watermark; + + retval->euid = euid; + retval->egid = egid; + retval->uid = retval->gid = -1; + + if (asprintf(&retval->eid_str, "%d_%d_", retval->euid, + retval->egid) == -1) { + free(retval); + return (NULL); + } + retval->eid_str_length = strlen(retval->eid_str); + + init_comm_element(&retval->request, CET_UNDEFINED); + init_comm_element(&retval->response, CET_UNDEFINED); + retval->process_func = on_query_startup; + retval->destroy_func = on_query_destroy; + + retval->write_func = query_socket_write; + retval->read_func = query_socket_read; + + get_time_func(&retval->creation_time); + memcpy(&retval->timeout, &s_configuration->query_timeout, + sizeof(struct timeval)); + + TRACE_OUT(init_query_state); + return (retval); +} + +void +destroy_query_state(struct query_state *qstate) +{ + + TRACE_IN(destroy_query_state); + if (qstate->eid_str != NULL) + free(qstate->eid_str); + + if (qstate->io_buffer != NULL) + free(qstate->io_buffer); + + qstate->destroy_func(qstate); + free(qstate); + TRACE_OUT(destroy_query_state); +} diff --git a/usr.sbin/cached/query.h b/usr.sbin/cached/query.h new file mode 100644 index 000000000000..5c4e918ef9f4 --- /dev/null +++ b/usr.sbin/cached/query.h @@ -0,0 +1,110 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_QUERY_H__ +#define __CACHED_QUERY_H__ + +#include +#include +#include +#include "cachelib.h" +#include "config.h" +#include "protocol.h" + +struct query_state; +struct configuration; +struct configuration_entry; + +typedef int (*query_process_func)(struct query_state *); +typedef void (*query_destroy_func)(struct query_state *); +typedef ssize_t (*query_read_func)(struct query_state *, void *, size_t); +typedef ssize_t (*query_write_func)(struct query_state *, const void *, + size_t); + +/* + * The query state structure contains the information to process all types of + * requests and to send all types of responses. + */ +struct query_state { + struct timeval creation_time; + struct timeval timeout; + + struct comm_element request; + struct comm_element response; + struct configuration_entry *config_entry; + void *mdata; + + query_process_func process_func; /* called on each event */ + query_destroy_func destroy_func; /* called on destroy */ + + /* + * By substituting these functions we can opaquely send and received + * very large buffers + */ + query_write_func write_func; /* data write function */ + query_read_func read_func; /* data read function */ + + char *eid_str; /* the user-identifying string (euid_egid_) */ + size_t eid_str_length; + + uid_t euid; /* euid of the caller, received via getpeereid */ + uid_t uid; /* uid of the caller, received via credentials */ + gid_t egid; /* egid of the caller, received via getpeereid */ + gid_t gid; /* gid of the caller received via credentials */ + + size_t io_buffer_size; + size_t io_buffer_watermark; + size_t kevent_watermark; /* bytes to be sent/received */ + int sockfd; /* the unix socket to read/write */ + int kevent_filter; /* EVFILT_READ or EVFILT_WRITE */ + int socket_failed; /* set to 1 if the socket doesn't work correctly */ + + /* + * These fields are used to opaquely proceed sending/receiving of + * the large buffers + */ + char *io_buffer; + char *io_buffer_p; + int io_buffer_filter; + int use_alternate_io; +}; + +extern int check_query_eids(struct query_state *); + +extern ssize_t query_io_buffer_read(struct query_state *, void *, size_t); +extern ssize_t query_io_buffer_write(struct query_state *, const void *, + size_t); + +extern ssize_t query_socket_read(struct query_state *, void *, size_t); +extern ssize_t query_socket_write(struct query_state *, const void *, + size_t); + +extern struct query_state *init_query_state(int, size_t, uid_t, gid_t); +extern void destroy_query_state(struct query_state *); + +#endif diff --git a/usr.sbin/cached/singletons.c b/usr.sbin/cached/singletons.c new file mode 100644 index 000000000000..669d12be2abd --- /dev/null +++ b/usr.sbin/cached/singletons.c @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "singletons.h" + +struct configuration *s_configuration = NULL; +cache s_cache = INVALID_CACHE; +struct runtime_env *s_runtime_env = NULL; +struct agent_table *s_agent_table = NULL; diff --git a/usr.sbin/cached/singletons.h b/usr.sbin/cached/singletons.h new file mode 100644 index 000000000000..918519c15cb8 --- /dev/null +++ b/usr.sbin/cached/singletons.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_SINGLETONS_H__ +#define __CACHED_SINGLETONS_H__ + +#include "cachelib.h" +#include "config.h" +#include "agent.h" + +struct runtime_env { + int queue; + int sockfd; + int finished; /* for future use */ +}; + +extern struct configuration *s_configuration; +extern cache s_cache; +extern struct runtime_env *s_runtime_env; +extern struct agent_table *s_agent_table; + +#endif diff --git a/usr.sbin/nscd/Makefile b/usr.sbin/nscd/Makefile new file mode 100644 index 000000000000..54783416da87 --- /dev/null +++ b/usr.sbin/nscd/Makefile @@ -0,0 +1,19 @@ +# $FreeBSD$ + +PROG=cached +PROGNAME=cached +MAN=cached.conf.5 cached.8 + +WARNS?=2 +SRCS= agent.c cached.c cachedcli.c cachelib.c cacheplcs.c debug.c log.c \ + config.c query.c mp_ws_query.c mp_rs_query.c singletons.c protocol.c \ + parser.c +CFLAGS+= -DCONFIG_PATH="\"${PREFIX}/etc/cached.conf\"" +DPADD+=${LIBM} ${LIBPTHREAD} ${LIBUTIL} +LDADD+=${LIBM} ${LIBPTHREAD} ${LIBUTIL} +LDFLAGS+= -Xlinker --export-dynamic + +.PATH: ${.CURDIR}/agents +.include "agents/Makefile.inc" +.include "../Makefile.inc" +.include diff --git a/usr.sbin/nscd/agent.c b/usr.sbin/nscd/agent.c new file mode 100644 index 000000000000..2d58ef1b48c8 --- /dev/null +++ b/usr.sbin/nscd/agent.c @@ -0,0 +1,127 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "agent.h" +#include "debug.h" + +static int +agent_cmp_func(const void *a1, const void *a2) +{ + struct agent const *ap1 = *((struct agent const **)a1); + struct agent const *ap2 = *((struct agent const **)a2); + int res; + + res = strcmp(ap1->name, ap2->name); + if (res == 0) { + if (ap1->type == ap2->type) + res = 0; + else if (ap1->type < ap2->type) + res = -1; + else + res = 1; + } + + return (res); +} + +struct agent_table * +init_agent_table() +{ + struct agent_table *retval; + + TRACE_IN(init_agent_table); + retval = (struct agent_table *)malloc(sizeof(struct agent_table)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct agent_table)); + + TRACE_OUT(init_agent_table); + return (retval); +} + +void +register_agent(struct agent_table *at, struct agent *a) +{ + struct agent **new_agents; + size_t new_agents_num; + + TRACE_IN(register_agent); + assert(at != NULL); + assert(a != NULL); + new_agents_num = at->agents_num + 1; + new_agents = (struct agent **)malloc(sizeof(struct agent *) * + new_agents_num); + assert(new_agents != NULL); + memcpy(new_agents, at->agents, at->agents_num * sizeof(struct agent *)); + new_agents[new_agents_num - 1] = a; + qsort(new_agents, new_agents_num, sizeof(struct agent *), + agent_cmp_func); + + free(at->agents); + at->agents = new_agents; + at->agents_num = new_agents_num; + TRACE_OUT(register_agent); +} + +struct agent * +find_agent(struct agent_table *at, const char *name, enum agent_type type) +{ + struct agent **res; + struct agent model, *model_p; + + TRACE_IN(find_agent); + model.name = (char *)name; + model.type = type; + model_p = &model; + res = bsearch(&model_p, at->agents, at->agents_num, + sizeof(struct agent *), agent_cmp_func); + + TRACE_OUT(find_agent); + return ( res == NULL ? NULL : *res); +} + +void +destroy_agent_table(struct agent_table *at) +{ + size_t i; + + TRACE_IN(destroy_agent_table); + assert(at != NULL); + for (i = 0; i < at->agents_num; ++i) { + free(at->agents[i]->name); + free(at->agents[i]); + } + + free(at->agents); + free(at); + TRACE_OUT(destroy_agent_table); +} diff --git a/usr.sbin/nscd/agent.h b/usr.sbin/nscd/agent.h new file mode 100644 index 000000000000..5dc368d49612 --- /dev/null +++ b/usr.sbin/nscd/agent.h @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_AGENT_H__ +#define __CACHED_AGENT_H__ + +/* + * Agents are used to perform the actual lookups from the caching daemon. + * There are two types of daemons: for common requests and for multipart + * requests. + * All agents are stored in the agents table, which is the singleton. + */ + +enum agent_type { + COMMON_AGENT = 0, + MULTIPART_AGENT = 1 +}; + +struct agent { + char *name; + enum agent_type type; +}; + +struct common_agent { + struct agent parent; + int (*lookup_func)(const char *, size_t, char **, size_t *); +}; + +struct multipart_agent { + struct agent parent; + void *(*mp_init_func)(); + int (*mp_lookup_func)(char **, size_t *, void *); + void (*mp_destroy_func)(void *); +}; + +struct agent_table { + struct agent **agents; + size_t agents_num; +}; + +extern struct agent_table *init_agent_table(); +extern void register_agent(struct agent_table *, struct agent *); +extern struct agent *find_agent(struct agent_table *, const char *, + enum agent_type); +extern void destroy_agent_table(struct agent_table *); + +#endif diff --git a/usr.sbin/nscd/agents/Makefile.inc b/usr.sbin/nscd/agents/Makefile.inc new file mode 100644 index 000000000000..1be32e1d986f --- /dev/null +++ b/usr.sbin/nscd/agents/Makefile.inc @@ -0,0 +1,3 @@ +# $FreeBSD$ + +SRCS += passwd.c group.c services.c diff --git a/usr.sbin/nscd/agents/group.c b/usr.sbin/nscd/agents/group.c new file mode 100644 index 000000000000..b9190be44849 --- /dev/null +++ b/usr.sbin/nscd/agents/group.c @@ -0,0 +1,262 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include "../debug.h" +#include "passwd.h" + +static int group_marshal_func(struct group *, char *, size_t *); +static int group_lookup_func(const char *, size_t, char **, size_t *); +static void *group_mp_init_func(); +static int group_mp_lookup_func(char **, size_t *, void *); +static void group_mp_destroy_func(void *); + +static int +group_marshal_func(struct group *grp, char *buffer, size_t *buffer_size) +{ + struct group new_grp; + size_t desired_size, size, mem_size; + char *p, **mem; + + TRACE_IN(group_marshal_func); + desired_size = ALIGNBYTES + sizeof(struct group) + sizeof(char *); + + if (grp->gr_name != NULL) + desired_size += strlen(grp->gr_name) + 1; + if (grp->gr_passwd != NULL) + desired_size += strlen(grp->gr_passwd) + 1; + + if (grp->gr_mem != NULL) { + mem_size = 0; + for (mem = grp->gr_mem; *mem; ++mem) { + desired_size += strlen(*mem) + 1; + ++mem_size; + } + + desired_size += ALIGNBYTES + (mem_size + 1) * sizeof(char *); + } + + if ((desired_size > *buffer_size) || (buffer == NULL)) { + *buffer_size = desired_size; + TRACE_OUT(group_marshal_func); + return (NS_RETURN); + } + + memcpy(&new_grp, grp, sizeof(struct group)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct group) + sizeof(char *); + memcpy(buffer + sizeof(struct group), &p, sizeof(char *)); + p = (char *)ALIGN(p); + + if (new_grp.gr_name != NULL) { + size = strlen(new_grp.gr_name); + memcpy(p, new_grp.gr_name, size); + new_grp.gr_name = p; + p += size + 1; + } + + if (new_grp.gr_passwd != NULL) { + size = strlen(new_grp.gr_passwd); + memcpy(p, new_grp.gr_passwd, size); + new_grp.gr_passwd = p; + p += size + 1; + } + + if (new_grp.gr_mem != NULL) { + p = (char *)ALIGN(p); + memcpy(p, new_grp.gr_mem, sizeof(char *) * mem_size); + new_grp.gr_mem = (char **)p; + p += sizeof(char *) * (mem_size + 1); + + for (mem = new_grp.gr_mem; *mem; ++mem) { + size = strlen(*mem); + memcpy(p, *mem, size); + *mem = p; + p += size + 1; + } + } + + memcpy(buffer, &new_grp, sizeof(struct group)); + TRACE_OUT(group_marshal_func); + return (NS_SUCCESS); +} + +static int +group_lookup_func(const char *key, size_t key_size, char **buffer, + size_t *buffer_size) +{ + enum nss_lookup_type lookup_type; + char *name; + size_t size; + gid_t gid; + + struct group *result; + + TRACE_IN(group_lookup_func); + assert(buffer != NULL); + assert(buffer_size != NULL); + + if (key_size < sizeof(enum nss_lookup_type)) { + TRACE_OUT(group_lookup_func); + return (NS_UNAVAIL); + } + memcpy(&lookup_type, key, sizeof(enum nss_lookup_type)); + + switch (lookup_type) { + case nss_lt_name: + size = key_size - sizeof(enum nss_lookup_type) + 1; + name = (char *)malloc(size); + assert(name != NULL); + memset(name, 0, size); + memcpy(name, key + sizeof(enum nss_lookup_type), size - 1); + break; + case nss_lt_id: + if (key_size < sizeof(enum nss_lookup_type) + + sizeof(gid_t)) { + TRACE_OUT(passwd_lookup_func); + return (NS_UNAVAIL); + } + + memcpy(&gid, key + sizeof(enum nss_lookup_type), sizeof(gid_t)); + break; + default: + TRACE_OUT(group_lookup_func); + return (NS_UNAVAIL); + } + + switch (lookup_type) { + case nss_lt_name: + TRACE_STR(name); + result = getgrnam(name); + free(name); + break; + case nss_lt_id: + result = getgrgid(gid); + break; + default: + /* SHOULD NOT BE REACHED */ + break; + } + + if (result != NULL) { + group_marshal_func(result, NULL, buffer_size); + *buffer = (char *)malloc(*buffer_size); + assert(*buffer != NULL); + group_marshal_func(result, *buffer, buffer_size); + } + + TRACE_OUT(group_lookup_func); + return (result == NULL ? NS_NOTFOUND : NS_SUCCESS); +} + +static void * +group_mp_init_func() +{ + TRACE_IN(group_mp_init_func); + setgrent(); + TRACE_OUT(group_mp_init_func); + + return (NULL); +} + +static int +group_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata) +{ + struct group *result; + + TRACE_IN(group_mp_lookup_func); + result = getgrent(); + if (result != NULL) { + group_marshal_func(result, NULL, buffer_size); + *buffer = (char *)malloc(*buffer_size); + assert(*buffer != NULL); + group_marshal_func(result, *buffer, buffer_size); + } + + TRACE_OUT(group_mp_lookup_func); + return (result == NULL ? NS_NOTFOUND : NS_SUCCESS); +} + +static void +group_mp_destroy_func(void *mdata) +{ + TRACE_IN(group_mp_destroy_func); + TRACE_OUT(group_mp_destroy_func); +} + +struct agent * +init_group_agent() +{ + struct common_agent *retval; + + TRACE_IN(init_group_agent); + retval = (struct common_agent *)malloc(sizeof(struct common_agent)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct common_agent)); + + retval->parent.name = strdup("group"); + assert(retval->parent.name != NULL); + + retval->parent.type = COMMON_AGENT; + retval->lookup_func = group_lookup_func; + + TRACE_OUT(init_group_agent); + return ((struct agent *)retval); +} + +struct agent * +init_group_mp_agent() +{ + struct multipart_agent *retval; + + TRACE_IN(init_group_mp_agent); + retval = (struct multipart_agent *)malloc( + sizeof(struct multipart_agent)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct multipart_agent)); + + retval->parent.name = strdup("group"); + retval->parent.type = MULTIPART_AGENT; + retval->mp_init_func = group_mp_init_func; + retval->mp_lookup_func = group_mp_lookup_func; + retval->mp_destroy_func = group_mp_destroy_func; + assert(retval->parent.name != NULL); + + TRACE_OUT(init_group_mp_agent); + return ((struct agent *)retval); +} diff --git a/usr.sbin/nscd/agents/group.h b/usr.sbin/nscd/agents/group.h new file mode 100644 index 000000000000..e6c73973f1d2 --- /dev/null +++ b/usr.sbin/nscd/agents/group.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include "../agent.h" + +extern struct agent *init_group_agent(); +extern struct agent *init_group_mp_agent(); diff --git a/usr.sbin/nscd/agents/passwd.c b/usr.sbin/nscd/agents/passwd.c new file mode 100644 index 000000000000..50c55babc633 --- /dev/null +++ b/usr.sbin/nscd/agents/passwd.c @@ -0,0 +1,269 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include "../debug.h" +#include "passwd.h" + +static int passwd_marshal_func(struct passwd *, char *, size_t *); +static int passwd_lookup_func(const char *, size_t, char **, size_t *); +static void *passwd_mp_init_func(); +static int passwd_mp_lookup_func(char **, size_t *, void *); +static void passwd_mp_destroy_func(void *mdata); + +static int +passwd_marshal_func(struct passwd *pwd, char *buffer, size_t *buffer_size) +{ + char *p; + struct passwd new_pwd; + size_t desired_size, size; + + TRACE_IN(passwd_marshal_func); + desired_size = sizeof(struct passwd) + sizeof(char *) + + strlen(pwd->pw_name) + 1; + if (pwd->pw_passwd != NULL) + desired_size += strlen(pwd->pw_passwd) + 1; + if (pwd->pw_class != NULL) + desired_size += strlen(pwd->pw_class) + 1; + if (pwd->pw_gecos != NULL) + desired_size += strlen(pwd->pw_gecos) + 1; + if (pwd->pw_dir != NULL) + desired_size += strlen(pwd->pw_dir) + 1; + if (pwd->pw_shell != NULL) + desired_size += strlen(pwd->pw_shell) + 1; + + if ((*buffer_size < desired_size) || (buffer == NULL)) { + *buffer_size = desired_size; + TRACE_OUT(passwd_marshal_func); + return (NS_RETURN); + } + + memcpy(&new_pwd, pwd, sizeof(struct passwd)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct passwd) + sizeof(char *); + memcpy(buffer + sizeof(struct passwd), &p, sizeof(char *)); + + if (new_pwd.pw_name != NULL) { + size = strlen(new_pwd.pw_name); + memcpy(p, new_pwd.pw_name, size); + new_pwd.pw_name = p; + p += size + 1; + } + + if (new_pwd.pw_passwd != NULL) { + size = strlen(new_pwd.pw_passwd); + memcpy(p, new_pwd.pw_passwd, size); + new_pwd.pw_passwd = p; + p += size + 1; + } + + if (new_pwd.pw_class != NULL) { + size = strlen(new_pwd.pw_class); + memcpy(p, new_pwd.pw_class, size); + new_pwd.pw_class = p; + p += size + 1; + } + + if (new_pwd.pw_gecos != NULL) { + size = strlen(new_pwd.pw_gecos); + memcpy(p, new_pwd.pw_gecos, size); + new_pwd.pw_gecos = p; + p += size + 1; + } + + if (new_pwd.pw_dir != NULL) { + size = strlen(new_pwd.pw_dir); + memcpy(p, new_pwd.pw_dir, size); + new_pwd.pw_dir = p; + p += size + 1; + } + + if (new_pwd.pw_shell != NULL) { + size = strlen(new_pwd.pw_shell); + memcpy(p, new_pwd.pw_shell, size); + new_pwd.pw_shell = p; + p += size + 1; + } + + memcpy(buffer, &new_pwd, sizeof(struct passwd)); + TRACE_OUT(passwd_marshal_func); + return (NS_SUCCESS); +} + +static int +passwd_lookup_func(const char *key, size_t key_size, char **buffer, + size_t *buffer_size) +{ + enum nss_lookup_type lookup_type; + char *login; + size_t size; + uid_t uid; + + struct passwd *result; + + TRACE_IN(passwd_lookup_func); + assert(buffer != NULL); + assert(buffer_size != NULL); + + if (key_size < sizeof(enum nss_lookup_type)) { + TRACE_OUT(passwd_lookup_func); + return (NS_UNAVAIL); + } + memcpy(&lookup_type, key, sizeof(enum nss_lookup_type)); + + switch (lookup_type) { + case nss_lt_name: + size = key_size - sizeof(enum nss_lookup_type) + 1; + login = (char *)malloc(size); + assert(login != NULL); + memset(login, 0, size); + memcpy(login, key + sizeof(enum nss_lookup_type), size - 1); + break; + case nss_lt_id: + if (key_size < sizeof(enum nss_lookup_type) + + sizeof(uid_t)) { + TRACE_OUT(passwd_lookup_func); + return (NS_UNAVAIL); + } + + memcpy(&uid, key + sizeof(enum nss_lookup_type), sizeof(uid_t)); + break; + default: + TRACE_OUT(passwd_lookup_func); + return (NS_UNAVAIL); + } + + switch (lookup_type) { + case nss_lt_name: + result = getpwnam(login); + free(login); + break; + case nss_lt_id: + result = getpwuid(uid); + break; + default: + /* SHOULD NOT BE REACHED */ + break; + } + + if (result != NULL) { + passwd_marshal_func(result, NULL, buffer_size); + *buffer = (char *)malloc(*buffer_size); + assert(*buffer != NULL); + passwd_marshal_func(result, *buffer, buffer_size); + } + + TRACE_OUT(passwd_lookup_func); + return (result == NULL ? NS_NOTFOUND : NS_SUCCESS); +} + +static void * +passwd_mp_init_func() +{ + TRACE_IN(passwd_mp_init_func); + setpwent(); + TRACE_OUT(passwd_mp_init_func); + + return (NULL); +} + +static int +passwd_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata) +{ + struct passwd *result; + + TRACE_IN(passwd_mp_lookup_func); + result = getpwent(); + if (result != NULL) { + passwd_marshal_func(result, NULL, buffer_size); + *buffer = (char *)malloc(*buffer_size); + assert(*buffer != NULL); + passwd_marshal_func(result, *buffer, buffer_size); + } + + TRACE_OUT(passwd_mp_lookup_func); + return (result == NULL ? NS_NOTFOUND : NS_SUCCESS); +} + +static void +passwd_mp_destroy_func(void *mdata) +{ + TRACE_IN(passwd_mp_destroy_func); + TRACE_OUT(passwd_mp_destroy_func); +} + +struct agent * +init_passwd_agent() +{ + struct common_agent *retval; + + TRACE_IN(init_passwd_agent); + retval = (struct common_agent *)malloc(sizeof(struct common_agent)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct common_agent)); + + retval->parent.name = strdup("passwd"); + assert(retval->parent.name != NULL); + + retval->parent.type = COMMON_AGENT; + retval->lookup_func = passwd_lookup_func; + + TRACE_OUT(init_passwd_agent); + return ((struct agent *)retval); +} + +struct agent * +init_passwd_mp_agent() +{ + struct multipart_agent *retval; + + TRACE_IN(init_passwd_mp_agent); + retval = (struct multipart_agent *)malloc( + sizeof(struct multipart_agent)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct multipart_agent)); + + retval->parent.name = strdup("passwd"); + retval->parent.type = MULTIPART_AGENT; + retval->mp_init_func = passwd_mp_init_func; + retval->mp_lookup_func = passwd_mp_lookup_func; + retval->mp_destroy_func = passwd_mp_destroy_func; + assert(retval->parent.name != NULL); + + TRACE_OUT(init_passwd_mp_agent); + return ((struct agent *)retval); +} diff --git a/usr.sbin/nscd/agents/passwd.h b/usr.sbin/nscd/agents/passwd.h new file mode 100644 index 000000000000..956a50d78008 --- /dev/null +++ b/usr.sbin/nscd/agents/passwd.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include "../agent.h" + +extern struct agent *init_passwd_agent(); +extern struct agent *init_passwd_mp_agent(); diff --git a/usr.sbin/nscd/agents/services.c b/usr.sbin/nscd/agents/services.c new file mode 100644 index 000000000000..36833960f892 --- /dev/null +++ b/usr.sbin/nscd/agents/services.c @@ -0,0 +1,286 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include "../debug.h" +#include "services.h" + +static int services_marshal_func(struct servent *, char *, size_t *); +static int services_lookup_func(const char *, size_t, char **, size_t *); +static void *services_mp_init_func(); +static int services_mp_lookup_func(char **, size_t *, void *); +static void services_mp_destroy_func(void *); + +static int +services_marshal_func(struct servent *serv, char *buffer, size_t *buffer_size) +{ + struct servent new_serv; + size_t desired_size; + char **alias; + char *p; + size_t size; + size_t aliases_size; + + TRACE_IN(services_marshal_func); + desired_size = ALIGNBYTES + sizeof(struct servent) + sizeof(char *); + if (serv->s_name != NULL) + desired_size += strlen(serv->s_name) + 1; + if (serv->s_proto != NULL) + desired_size += strlen(serv->s_proto) + 1; + + aliases_size = 0; + if (serv->s_aliases != NULL) { + for (alias = serv->s_aliases; *alias; ++alias) { + desired_size += strlen(*alias) + 1; + ++aliases_size; + } + + desired_size += ALIGNBYTES + sizeof(char *) * + (aliases_size + 1); + } + + if ((*buffer_size < desired_size) || (buffer == NULL)) { + *buffer_size = desired_size; + TRACE_OUT(services_marshal_func); + return (NS_RETURN); + } + + memcpy(&new_serv, serv, sizeof(struct servent)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct servent) + sizeof(char *); + memcpy(buffer + sizeof(struct servent), &p, sizeof(char *)); + p = (char *)ALIGN(p); + + if (new_serv.s_name != NULL) { + size = strlen(new_serv.s_name); + memcpy(p, new_serv.s_name, size); + new_serv.s_name = p; + p += size + 1; + } + + if (new_serv.s_proto != NULL) { + size = strlen(new_serv.s_proto); + memcpy(p, new_serv.s_proto, size); + new_serv.s_proto = p; + p += size + 1; + } + + if (new_serv.s_aliases != NULL) { + p = (char *)ALIGN(p); + memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size); + new_serv.s_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); + + for (alias = new_serv.s_aliases; *alias; ++alias) { + size = strlen(*alias); + memcpy(p, *alias, size); + *alias = p; + p += size + 1; + } + } + + memcpy(buffer, &new_serv, sizeof(struct servent)); + TRACE_OUT(services_marshal_func); + return (NS_SUCCESS); +} + +static int +services_lookup_func(const char *key, size_t key_size, char **buffer, + size_t *buffer_size) +{ + enum nss_lookup_type lookup_type; + char *name = NULL; + char *proto = NULL; + size_t size, size2; + int port; + + struct servent *result; + + TRACE_IN(services_lookup_func); + + assert(buffer != NULL); + assert(buffer_size != NULL); + + if (key_size < sizeof(enum nss_lookup_type)) { + TRACE_OUT(passwd_lookup_func); + return (NS_UNAVAIL); + } + memcpy(&lookup_type, key, sizeof(enum nss_lookup_type)); + + switch (lookup_type) { + case nss_lt_name: + size = key_size - sizeof(enum nss_lookup_type) + 1; + name = (char *)malloc(size); + assert(name != NULL); + memset(name, 0, size); + memcpy(name, key + sizeof(enum nss_lookup_type), size - 1); + + size2 = strlen(name) + 1; + if (size2 < size) { + proto = strchr(name, '\0'); + if (strrchr(name, '\0') > proto) + ++proto ; + else + proto = NULL; + } + break; + case nss_lt_id: + if (key_size < sizeof(enum nss_lookup_type) + + sizeof(int)) { + TRACE_OUT(passwd_lookup_func); + return (NS_UNAVAIL); + } + + memcpy(&port, key + sizeof(enum nss_lookup_type), + sizeof(int)); + + size = key_size - sizeof(enum nss_lookup_type) + sizeof(int); + if (size > 0) { + proto = (char *)malloc(size + 1); + assert(proto != NULL); + memset(proto, size + 1, 0); + memcpy(proto, key + sizeof(enum nss_lookup_type) + + sizeof(int), size); + } + break; + default: + TRACE_OUT(passwd_lookup_func); + return (NS_UNAVAIL); + } + + switch (lookup_type) { + case nss_lt_name: + result = getservbyname(name, proto); + free(name); + break; + case nss_lt_id: + result = getservbyport(port, proto); + free(proto); + break; + default: + /* SHOULD NOT BE REACHED */ + break; + } + + if (result != NULL) { + services_marshal_func(result, NULL, buffer_size); + *buffer = (char *)malloc(*buffer_size); + assert(*buffer != NULL); + services_marshal_func(result, *buffer, buffer_size); + } + + TRACE_OUT(services_lookup_func); + return (result == NULL ? NS_NOTFOUND : NS_SUCCESS); +} + +static void * +services_mp_init_func() +{ + TRACE_IN(services_mp_init_func); + setservent(0); + TRACE_OUT(services_mp_init_func); + + return (NULL); +} + +static int +services_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata) +{ + struct servent *result; + + TRACE_IN(services_mp_lookup_func); + result = getservent(); + if (result != NULL) { + services_marshal_func(result, NULL, buffer_size); + *buffer = (char *)malloc(*buffer_size); + assert(*buffer != NULL); + services_marshal_func(result, *buffer, buffer_size); + } + + TRACE_OUT(services_mp_lookup_func); + return (result == NULL ? NS_NOTFOUND : NS_SUCCESS); +} + +static void +services_mp_destroy_func(void *mdata) +{ + TRACE_IN(services_mp_destroy_func); + TRACE_OUT(services_mp_destroy_func); +} + +struct agent * +init_services_agent() +{ + struct common_agent *retval; + TRACE_IN(init_services_agent); + + retval = (struct common_agent *)malloc(sizeof(struct common_agent)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct common_agent)); + + retval->parent.name = strdup("services"); + assert(retval->parent.name != NULL); + + retval->parent.type = COMMON_AGENT; + retval->lookup_func = services_lookup_func; + + TRACE_OUT(init_services_agent); + return ((struct agent *)retval); +} + +struct agent * +init_services_mp_agent() +{ + struct multipart_agent *retval; + + TRACE_IN(init_services_mp_agent); + retval = (struct multipart_agent *)malloc( + sizeof(struct multipart_agent)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct multipart_agent)); + + retval->parent.name = strdup("services"); + retval->parent.type = MULTIPART_AGENT; + retval->mp_init_func = services_mp_init_func; + retval->mp_lookup_func = services_mp_lookup_func; + retval->mp_destroy_func = services_mp_destroy_func; + assert(retval->parent.name != NULL); + + TRACE_OUT(init_services_mp_agent); + return ((struct agent *)retval); +} diff --git a/usr.sbin/nscd/agents/services.h b/usr.sbin/nscd/agents/services.h new file mode 100644 index 000000000000..0b77c87a9979 --- /dev/null +++ b/usr.sbin/nscd/agents/services.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include "../agent.h" + +extern struct agent *init_services_agent(); +extern struct agent *init_services_mp_agent(); diff --git a/usr.sbin/nscd/cachelib.c b/usr.sbin/nscd/cachelib.c new file mode 100644 index 000000000000..4f771cc864c3 --- /dev/null +++ b/usr.sbin/nscd/cachelib.c @@ -0,0 +1,1234 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include "cachelib.h" +#include "debug.h" + +#define INITIAL_ENTRIES_CAPACITY 32 +#define ENTRIES_CAPACITY_STEP 32 + +#define STRING_SIMPLE_HASH_BODY(in_var, var, a, M) \ + for ((var) = 0; *(in_var) != '\0'; ++(in_var)) \ + (var) = ((a)*(var) + *(in_var)) % (M) + +#define STRING_SIMPLE_MP2_HASH_BODY(in_var, var, a, M) \ + for ((var) = 0; *(in_var) != 0; ++(in_var)) \ + (var) = ((a)*(var) + *(in_var)) & (M - 1) + +static int cache_elemsize_common_continue_func(struct cache_common_entry_ *, + struct cache_policy_item_ *); +static int cache_lifetime_common_continue_func(struct cache_common_entry_ *, + struct cache_policy_item_ *); +static void clear_cache_entry(struct cache_entry_ *); +static void destroy_cache_entry(struct cache_entry_ *); +static void destroy_cache_mp_read_session(struct cache_mp_read_session_ *); +static void destroy_cache_mp_write_session(struct cache_mp_write_session_ *); +static int entries_bsearch_cmp_func(const void *, const void *); +static int entries_qsort_cmp_func(const void *, const void *); +static struct cache_entry_ ** find_cache_entry_p(struct cache_ *, + const char *); +static void flush_cache_entry(struct cache_entry_ *); +static void flush_cache_policy(struct cache_common_entry_ *, + struct cache_policy_ *, struct cache_policy_ *, + int (*)(struct cache_common_entry_ *, + struct cache_policy_item_ *)); +static int ht_items_cmp_func(const void *, const void *); +static int ht_items_fixed_size_left_cmp_func(const void *, const void *); +static hashtable_index_t ht_item_hash_func(const void *, size_t); + +/* + * Hashing and comparing routines, that are used with the hash tables + */ +static int +ht_items_cmp_func(const void *p1, const void *p2) +{ + struct cache_ht_item_data_ *hp1, *hp2; + size_t min_size; + int result; + + hp1 = (struct cache_ht_item_data_ *)p1; + hp2 = (struct cache_ht_item_data_ *)p2; + + assert(hp1->key != NULL); + assert(hp2->key != NULL); + + if (hp1->key_size != hp2->key_size) { + min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size : + hp2->key_size; + result = memcmp(hp1->key, hp2->key, min_size); + + if (result == 0) + return ((hp1->key_size < hp2->key_size) ? -1 : 1); + else + return (result); + } else + return (memcmp(hp1->key, hp2->key, hp1->key_size)); +} + +static int +ht_items_fixed_size_left_cmp_func(const void *p1, const void *p2) +{ + struct cache_ht_item_data_ *hp1, *hp2; + size_t min_size; + int result; + + hp1 = (struct cache_ht_item_data_ *)p1; + hp2 = (struct cache_ht_item_data_ *)p2; + + assert(hp1->key != NULL); + assert(hp2->key != NULL); + + if (hp1->key_size != hp2->key_size) { + min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size : + hp2->key_size; + result = memcmp(hp1->key, hp2->key, min_size); + + if (result == 0) + if (min_size == hp1->key_size) + return (0); + else + return ((hp1->key_size < hp2->key_size) ? -1 : 1); + else + return (result); + } else + return (memcmp(hp1->key, hp2->key, hp1->key_size)); +} + +static hashtable_index_t +ht_item_hash_func(const void *p, size_t cache_entries_size) +{ + struct cache_ht_item_data_ *hp; + size_t i; + + hashtable_index_t retval; + + hp = (struct cache_ht_item_data_ *)p; + assert(hp->key != NULL); + + retval = 0; + for (i = 0; i < hp->key_size; ++i) + retval = (127 * retval + (unsigned char)hp->key[i]) % + cache_entries_size; + + return retval; +} + +HASHTABLE_GENERATE(cache_ht_, cache_ht_item_, struct cache_ht_item_data_, data, + ht_item_hash_func, ht_items_cmp_func); + +/* + * Routines to sort and search the entries by name + */ +static int +entries_bsearch_cmp_func(const void *key, const void *ent) +{ + + assert(key != NULL); + assert(ent != NULL); + + return (strcmp((char const *)key, + (*(struct cache_entry_ const **)ent)->name)); +} + +static int +entries_qsort_cmp_func(const void *e1, const void *e2) +{ + + assert(e1 != NULL); + assert(e2 != NULL); + + return (strcmp((*(struct cache_entry_ const **)e1)->name, + (*(struct cache_entry_ const **)e2)->name)); +} + +static struct cache_entry_ ** +find_cache_entry_p(struct cache_ *the_cache, const char *entry_name) +{ + + return ((struct cache_entry_ **)(bsearch(entry_name, the_cache->entries, + the_cache->entries_size, sizeof(struct cache_entry_ *), + entries_bsearch_cmp_func))); +} + +static void +destroy_cache_mp_write_session(struct cache_mp_write_session_ *ws) +{ + + struct cache_mp_data_item_ *data_item; + + TRACE_IN(destroy_cache_mp_write_session); + assert(ws != NULL); + while (!TAILQ_EMPTY(&ws->items)) { + data_item = TAILQ_FIRST(&ws->items); + TAILQ_REMOVE(&ws->items, data_item, entries); + free(data_item->value); + free(data_item); + } + + free(ws); + TRACE_OUT(destroy_cache_mp_write_session); +} + +static void +destroy_cache_mp_read_session(struct cache_mp_read_session_ *rs) +{ + + TRACE_IN(destroy_cache_mp_read_session); + assert(rs != NULL); + free(rs); + TRACE_OUT(destroy_cache_mp_read_session); +} + +static void +destroy_cache_entry(struct cache_entry_ *entry) +{ + struct cache_common_entry_ *common_entry; + struct cache_mp_entry_ *mp_entry; + struct cache_mp_read_session_ *rs; + struct cache_mp_write_session_ *ws; + struct cache_ht_item_ *ht_item; + struct cache_ht_item_data_ *ht_item_data; + + TRACE_IN(destroy_cache_entry); + assert(entry != NULL); + + if (entry->params->entry_type == CET_COMMON) { + common_entry = (struct cache_common_entry_ *)entry; + + HASHTABLE_FOREACH(&(common_entry->items), ht_item) { + HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data) + { + free(ht_item_data->key); + free(ht_item_data->value); + } + HASHTABLE_ENTRY_CLEAR(ht_item, data); + } + + HASHTABLE_DESTROY(&(common_entry->items), data); + + /* FIFO policy is always first */ + destroy_cache_fifo_policy(common_entry->policies[0]); + switch (common_entry->common_params.policy) { + case CPT_LRU: + destroy_cache_lru_policy(common_entry->policies[1]); + break; + case CPT_LFU: + destroy_cache_lfu_policy(common_entry->policies[1]); + break; + default: + break; + } + free(common_entry->policies); + } else { + mp_entry = (struct cache_mp_entry_ *)entry; + + while (!TAILQ_EMPTY(&mp_entry->ws_head)) { + ws = TAILQ_FIRST(&mp_entry->ws_head); + TAILQ_REMOVE(&mp_entry->ws_head, ws, entries); + destroy_cache_mp_write_session(ws); + } + + while (!TAILQ_EMPTY(&mp_entry->rs_head)) { + rs = TAILQ_FIRST(&mp_entry->rs_head); + TAILQ_REMOVE(&mp_entry->rs_head, rs, entries); + destroy_cache_mp_read_session(rs); + } + + if (mp_entry->completed_write_session != NULL) + destroy_cache_mp_write_session( + mp_entry->completed_write_session); + + if (mp_entry->pending_write_session != NULL) + destroy_cache_mp_write_session( + mp_entry->pending_write_session); + } + + free(entry->name); + free(entry); + TRACE_OUT(destroy_cache_entry); +} + +static void +clear_cache_entry(struct cache_entry_ *entry) +{ + struct cache_mp_entry_ *mp_entry; + struct cache_common_entry_ *common_entry; + struct cache_ht_item_ *ht_item; + struct cache_ht_item_data_ *ht_item_data; + struct cache_policy_ *policy; + struct cache_policy_item_ *item, *next_item; + size_t entry_size; + int i; + + if (entry->params->entry_type == CET_COMMON) { + common_entry = (struct cache_common_entry_ *)entry; + + entry_size = 0; + HASHTABLE_FOREACH(&(common_entry->items), ht_item) { + HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data) + { + free(ht_item_data->key); + free(ht_item_data->value); + } + entry_size += HASHTABLE_ENTRY_SIZE(ht_item, data); + HASHTABLE_ENTRY_CLEAR(ht_item, data); + } + + common_entry->items_size -= entry_size; + for (i = 0; i < common_entry->policies_size; ++i) { + policy = common_entry->policies[i]; + + next_item = NULL; + item = policy->get_first_item_func(policy); + while (item != NULL) { + next_item = policy->get_next_item_func(policy, + item); + policy->remove_item_func(policy, item); + policy->destroy_item_func(item); + item = next_item; + } + } + } else { + mp_entry = (struct cache_mp_entry_ *)entry; + + if (mp_entry->rs_size == 0) { + if (mp_entry->completed_write_session != NULL) { + destroy_cache_mp_write_session( + mp_entry->completed_write_session); + mp_entry->completed_write_session = NULL; + } + + memset(&mp_entry->creation_time, 0, + sizeof(struct timeval)); + memset(&mp_entry->last_request_time, 0, + sizeof(struct timeval)); + } + } +} + +/* + * When passed to the flush_cache_policy, ensures that all old elements are + * deleted. + */ +static int +cache_lifetime_common_continue_func(struct cache_common_entry_ *entry, + struct cache_policy_item_ *item) +{ + + return ((item->last_request_time.tv_sec - item->creation_time.tv_sec > + entry->common_params.max_lifetime.tv_sec) ? 1: 0); +} + +/* + * When passed to the flush_cache_policy, ensures that all elements, that + * exceed the size limit, are deleted. + */ +static int +cache_elemsize_common_continue_func(struct cache_common_entry_ *entry, + struct cache_policy_item_ *item) +{ + + return ((entry->items_size > entry->common_params.satisf_elemsize) ? 1 + : 0); +} + +/* + * Removes the elements from the cache entry, while the continue_func returns 1. + */ +static void +flush_cache_policy(struct cache_common_entry_ *entry, + struct cache_policy_ *policy, + struct cache_policy_ *connected_policy, + int (*continue_func)(struct cache_common_entry_ *, + struct cache_policy_item_ *)) +{ + struct cache_policy_item_ *item, *next_item, *connected_item; + struct cache_ht_item_ *ht_item; + struct cache_ht_item_data_ *ht_item_data, ht_key; + hashtable_index_t hash; + + assert(policy != NULL); + + next_item = NULL; + item = policy->get_first_item_func(policy); + while ((item != NULL) && (continue_func(entry, item) == 1)) { + next_item = policy->get_next_item_func(policy, item); + + connected_item = item->connected_item; + policy->remove_item_func(policy, item); + + memset(&ht_key, 0, sizeof(struct cache_ht_item_data_)); + ht_key.key = item->key; + ht_key.key_size = item->key_size; + + hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &entry->items, + &ht_key); + assert(hash >= 0); + assert(hash < HASHTABLE_ENTRIES_COUNT(&entry->items)); + + ht_item = HASHTABLE_GET_ENTRY(&(entry->items), hash); + ht_item_data = HASHTABLE_ENTRY_FIND(cache_ht_, ht_item, + &ht_key); + assert(ht_item_data != NULL); + free(ht_item_data->key); + free(ht_item_data->value); + HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item, ht_item_data); + --entry->items_size; + + policy->destroy_item_func(item); + + if (connected_item != NULL) { + connected_policy->remove_item_func(connected_policy, + connected_item); + connected_policy->destroy_item_func(connected_item); + } + + item = next_item; + } +} + +static void +flush_cache_entry(struct cache_entry_ *entry) +{ + struct cache_mp_entry_ *mp_entry; + struct cache_common_entry_ *common_entry; + struct cache_policy_ *policy, *connected_policy; + + connected_policy = NULL; + if (entry->params->entry_type == CET_COMMON) { + common_entry = (struct cache_common_entry_ *)entry; + if ((common_entry->common_params.max_lifetime.tv_sec != 0) || + (common_entry->common_params.max_lifetime.tv_usec != 0)) { + + policy = common_entry->policies[0]; + if (common_entry->policies_size > 1) + connected_policy = common_entry->policies[1]; + + flush_cache_policy(common_entry, policy, + connected_policy, + cache_lifetime_common_continue_func); + } + + + if ((common_entry->common_params.max_elemsize != 0) && + common_entry->items_size > + common_entry->common_params.max_elemsize) { + + if (common_entry->policies_size > 1) { + policy = common_entry->policies[1]; + connected_policy = common_entry->policies[0]; + } else { + policy = common_entry->policies[0]; + connected_policy = NULL; + } + + flush_cache_policy(common_entry, policy, + connected_policy, + cache_elemsize_common_continue_func); + } + } else { + mp_entry = (struct cache_mp_entry_ *)entry; + + if ((mp_entry->mp_params.max_lifetime.tv_sec != 0) + || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) { + + if (mp_entry->last_request_time.tv_sec - + mp_entry->last_request_time.tv_sec > + mp_entry->mp_params.max_lifetime.tv_sec) + clear_cache_entry(entry); + } + } +} + +struct cache_ * +init_cache(struct cache_params const *params) +{ + struct cache_ *retval; + + TRACE_IN(init_cache); + assert(params != NULL); + + retval = (struct cache_ *)malloc(sizeof(struct cache_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cache_)); + + assert(params != NULL); + memcpy(&retval->params, params, sizeof(struct cache_params)); + + retval->entries = (struct cache_entry_ **)malloc( + sizeof(struct cache_entry_ *) * INITIAL_ENTRIES_CAPACITY); + assert(retval->entries != NULL); + memset(retval->entries, 0, sizeof(sizeof(struct cache_entry_ *) + * INITIAL_ENTRIES_CAPACITY)); + + retval->entries_capacity = INITIAL_ENTRIES_CAPACITY; + retval->entries_size = 0; + + TRACE_OUT(init_cache); + return (retval); +} + +void +destroy_cache(struct cache_ *the_cache) +{ + + TRACE_IN(destroy_cache); + assert(the_cache != NULL); + + if (the_cache->entries != NULL) { + size_t i; + for (i = 0; i < the_cache->entries_size; ++i) + destroy_cache_entry(the_cache->entries[i]); + + free(the_cache->entries); + } + + free(the_cache); + TRACE_OUT(destroy_cache); +} + +int +register_cache_entry(struct cache_ *the_cache, + struct cache_entry_params const *params) +{ + int policies_size; + size_t entry_name_size; + struct cache_common_entry_ *new_common_entry; + struct cache_mp_entry_ *new_mp_entry; + + TRACE_IN(register_cache_entry); + assert(the_cache != NULL); + + if (find_cache_entry(the_cache, params->entry_name) != NULL) { + TRACE_OUT(register_cache_entry); + return (-1); + } + + if (the_cache->entries_size == the_cache->entries_capacity) { + struct cache_entry_ **new_entries; + size_t new_capacity; + + new_capacity = the_cache->entries_capacity + + ENTRIES_CAPACITY_STEP; + new_entries = (struct cache_entry_ **)malloc( + sizeof(struct cache_entry_ *) * new_capacity); + assert(new_entries != NULL); + + memset(new_entries, 0, sizeof(struct cache_entry_ *) * + new_capacity); + memcpy(new_entries, the_cache->entries, + sizeof(struct cache_entry_ *) + * the_cache->entries_size); + + free(the_cache->entries); + the_cache->entries = new_entries; + } + + entry_name_size = strlen(params->entry_name); + switch (params->entry_type) + { + case CET_COMMON: + new_common_entry = (struct cache_common_entry_ *)malloc( + sizeof(struct cache_common_entry_)); + assert(new_common_entry != NULL); + memset(new_common_entry, 0, sizeof(struct cache_common_entry_)); + + memcpy(&new_common_entry->common_params, params, + sizeof(struct common_cache_entry_params)); + new_common_entry->params = + (struct cache_entry_params *)&new_common_entry->common_params; + + new_common_entry->common_params.entry_name = (char *)malloc( + entry_name_size+1); + assert(new_common_entry->common_params.entry_name != NULL); + memset(new_common_entry->common_params.entry_name, 0, + entry_name_size + 1); + strncpy(new_common_entry->common_params.entry_name, + params->entry_name, entry_name_size); + new_common_entry->name = + new_common_entry->common_params.entry_name; + + HASHTABLE_INIT(&(new_common_entry->items), + struct cache_ht_item_data_, data, + new_common_entry->common_params.cache_entries_size); + + if (new_common_entry->common_params.policy == CPT_FIFO) + policies_size = 1; + else + policies_size = 2; + + new_common_entry->policies = (struct cache_policy_ **)malloc( + sizeof(struct cache_policy_ *) * policies_size); + assert(new_common_entry->policies != NULL); + memset(new_common_entry->policies, 0, + sizeof(struct cache_policy_ *) * policies_size); + + new_common_entry->policies_size = policies_size; + new_common_entry->policies[0] = init_cache_fifo_policy(); + + if (policies_size > 1) { + switch (new_common_entry->common_params.policy) { + case CPT_LRU: + new_common_entry->policies[1] = + init_cache_lru_policy(); + break; + case CPT_LFU: + new_common_entry->policies[1] = + init_cache_lfu_policy(); + break; + default: + break; + } + } + + new_common_entry->get_time_func = + the_cache->params.get_time_func; + the_cache->entries[the_cache->entries_size++] = + (struct cache_entry_ *)new_common_entry; + break; + case CET_MULTIPART: + new_mp_entry = (struct cache_mp_entry_ *)malloc( + sizeof(struct cache_mp_entry_)); + assert(new_mp_entry != NULL); + memset(new_mp_entry, 0, sizeof(struct cache_mp_entry_)); + + memcpy(&new_mp_entry->mp_params, params, + sizeof(struct mp_cache_entry_params)); + new_mp_entry->params = + (struct cache_entry_params *)&new_mp_entry->mp_params; + + new_mp_entry->mp_params.entry_name = (char *)malloc( + entry_name_size+1); + assert(new_mp_entry->mp_params.entry_name != NULL); + memset(new_mp_entry->mp_params.entry_name, 0, + entry_name_size + 1); + strncpy(new_mp_entry->mp_params.entry_name, params->entry_name, + entry_name_size); + new_mp_entry->name = new_mp_entry->mp_params.entry_name; + + TAILQ_INIT(&new_mp_entry->ws_head); + TAILQ_INIT(&new_mp_entry->rs_head); + + new_mp_entry->get_time_func = the_cache->params.get_time_func; + the_cache->entries[the_cache->entries_size++] = + (struct cache_entry_ *)new_mp_entry; + break; + } + + + qsort(the_cache->entries, the_cache->entries_size, + sizeof(struct cache_entry_ *), entries_qsort_cmp_func); + + TRACE_OUT(register_cache_entry); + return (0); +} + +int +unregister_cache_entry(struct cache_ *the_cache, const char *entry_name) +{ + struct cache_entry_ **del_ent; + + TRACE_IN(unregister_cache_entry); + assert(the_cache != NULL); + + del_ent = find_cache_entry_p(the_cache, entry_name); + if (del_ent != NULL) { + destroy_cache_entry(*del_ent); + --the_cache->entries_size; + + memmove(del_ent, del_ent + 1, + (&(the_cache->entries[--the_cache->entries_size]) - + del_ent) * sizeof(struct cache_entry_ *)); + + TRACE_OUT(unregister_cache_entry); + return (0); + } else { + TRACE_OUT(unregister_cache_entry); + return (-1); + } +} + +struct cache_entry_ * +find_cache_entry(struct cache_ *the_cache, const char *entry_name) +{ + struct cache_entry_ **result; + + TRACE_IN(find_cache_entry); + result = find_cache_entry_p(the_cache, entry_name); + + if (result == NULL) { + TRACE_OUT(find_cache_entry); + return (NULL); + } else { + TRACE_OUT(find_cache_entry); + return (*result); + } +} + +/* + * Tries to read the element with the specified key from the cache. If the + * value_size is too small, it will be filled with the proper number, and + * the user will need to call cache_read again with the value buffer, that + * is large enough. + * Function returns 0 on success, -1 on error, and -2 if the value_size is too + * small. + */ +int +cache_read(struct cache_entry_ *entry, const char *key, size_t key_size, + char *value, size_t *value_size) +{ + struct cache_common_entry_ *common_entry; + struct cache_ht_item_data_ item_data, *find_res; + struct cache_ht_item_ *item; + hashtable_index_t hash; + struct cache_policy_item_ *connected_item; + + TRACE_IN(cache_read); + assert(entry != NULL); + assert(key != NULL); + assert(value_size != NULL); + assert(entry->params->entry_type == CET_COMMON); + + common_entry = (struct cache_common_entry_ *)entry; + + memset(&item_data, 0, sizeof(struct cache_ht_item_data_)); + /* can't avoid the cast here */ + item_data.key = (char *)key; + item_data.key_size = key_size; + + hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items, + &item_data); + assert(hash >= 0); + assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items)); + + item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash); + find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data); + if (find_res == NULL) { + TRACE_OUT(cache_read); + return (-1); + } + + if ((common_entry->common_params.max_lifetime.tv_sec != 0) || + (common_entry->common_params.max_lifetime.tv_usec != 0)) { + + if (find_res->fifo_policy_item->last_request_time.tv_sec - + find_res->fifo_policy_item->creation_time.tv_sec > + common_entry->common_params.max_lifetime.tv_sec) { + + free(find_res->key); + free(find_res->value); + + connected_item = + find_res->fifo_policy_item->connected_item; + if (connected_item != NULL) { + common_entry->policies[1]->remove_item_func( + common_entry->policies[1], + connected_item); + common_entry->policies[1]->destroy_item_func( + connected_item); + } + + common_entry->policies[0]->remove_item_func( + common_entry->policies[0], + find_res->fifo_policy_item); + common_entry->policies[0]->destroy_item_func( + find_res->fifo_policy_item); + + HASHTABLE_ENTRY_REMOVE(cache_ht_, item, find_res); + --common_entry->items_size; + } + } + + if ((*value_size < find_res->value_size) || (value == NULL)) { + *value_size = find_res->value_size; + TRACE_OUT(cache_read); + return (-2); + } + + *value_size = find_res->value_size; + memcpy(value, find_res->value, find_res->value_size); + + ++find_res->fifo_policy_item->request_count; + common_entry->get_time_func( + &find_res->fifo_policy_item->last_request_time); + common_entry->policies[0]->update_item_func(common_entry->policies[0], + find_res->fifo_policy_item); + + if (find_res->fifo_policy_item->connected_item != NULL) { + connected_item = find_res->fifo_policy_item->connected_item; + memcpy(&connected_item->last_request_time, + &find_res->fifo_policy_item->last_request_time, + sizeof(struct timeval)); + connected_item->request_count = + find_res->fifo_policy_item->request_count; + + common_entry->policies[1]->update_item_func( + common_entry->policies[1], connected_item); + } + + TRACE_OUT(cache_read); + return (0); +} + +/* + * Writes the value with the specified key into the cache entry. + * Functions returns 0 on success, and -1 on error. + */ +int +cache_write(struct cache_entry_ *entry, const char *key, size_t key_size, + char const *value, size_t value_size) +{ + struct cache_common_entry_ *common_entry; + struct cache_ht_item_data_ item_data, *find_res; + struct cache_ht_item_ *item; + hashtable_index_t hash; + + struct cache_policy_ *policy, *connected_policy; + struct cache_policy_item_ *policy_item; + struct cache_policy_item_ *connected_policy_item; + + TRACE_IN(cache_write); + assert(entry != NULL); + assert(key != NULL); + assert(value != NULL); + assert(entry->params->entry_type == CET_COMMON); + + common_entry = (struct cache_common_entry_ *)entry; + + memset(&item_data, 0, sizeof(struct cache_ht_item_data_)); + /* can't avoid the cast here */ + item_data.key = (char *)key; + item_data.key_size = key_size; + + hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items, + &item_data); + assert(hash >= 0); + assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items)); + + item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash); + find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data); + if (find_res != NULL) { + TRACE_OUT(cache_write); + return (-1); + } + + item_data.key = (char *)malloc(key_size); + memcpy(item_data.key, key, key_size); + + item_data.value = (char *)malloc(value_size); + assert(item_data.value != NULL); + + memcpy(item_data.value, value, value_size); + item_data.value_size = value_size; + + policy_item = common_entry->policies[0]->create_item_func(); + policy_item->key = item_data.key; + policy_item->key_size = item_data.key_size; + common_entry->get_time_func(&policy_item->creation_time); + + if (common_entry->policies_size > 1) { + connected_policy_item = + common_entry->policies[1]->create_item_func(); + memcpy(&connected_policy_item->creation_time, + &policy_item->creation_time, + sizeof(struct timeval)); + connected_policy_item->key = policy_item->key; + connected_policy_item->key_size = policy_item->key_size; + + connected_policy_item->connected_item = policy_item; + policy_item->connected_item = connected_policy_item; + } + + item_data.fifo_policy_item = policy_item; + + common_entry->policies[0]->add_item_func(common_entry->policies[0], + policy_item); + if (common_entry->policies_size > 1) + common_entry->policies[1]->add_item_func( + common_entry->policies[1], connected_policy_item); + + HASHTABLE_ENTRY_STORE(cache_ht_, item, &item_data); + ++common_entry->items_size; + + if ((common_entry->common_params.max_elemsize != 0) && + (common_entry->items_size > + common_entry->common_params.max_elemsize)) { + if (common_entry->policies_size > 1) { + policy = common_entry->policies[1]; + connected_policy = common_entry->policies[0]; + } else { + policy = common_entry->policies[0]; + connected_policy = NULL; + } + + flush_cache_policy(common_entry, policy, connected_policy, + cache_elemsize_common_continue_func); + } + + TRACE_OUT(cache_write); + return (0); +} + +/* + * Initializes the write session for the specified multipart entry. This + * session then should be filled with data either committed or abandoned by + * using close_cache_mp_write_session or abandon_cache_mp_write_session + * respectively. + * Returns NULL on errors (when there are too many opened write sessions for + * the entry). + */ +struct cache_mp_write_session_ * +open_cache_mp_write_session(struct cache_entry_ *entry) +{ + struct cache_mp_entry_ *mp_entry; + struct cache_mp_write_session_ *retval; + + TRACE_IN(open_cache_mp_write_session); + assert(entry != NULL); + assert(entry->params->entry_type == CET_MULTIPART); + mp_entry = (struct cache_mp_entry_ *)entry; + + if ((mp_entry->mp_params.max_sessions > 0) && + (mp_entry->ws_size == mp_entry->mp_params.max_sessions)) { + TRACE_OUT(open_cache_mp_write_session); + return (NULL); + } + + retval = (struct cache_mp_write_session_ *)malloc( + sizeof(struct cache_mp_write_session_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cache_mp_write_session_)); + + TAILQ_INIT(&retval->items); + retval->parent_entry = mp_entry; + + TAILQ_INSERT_HEAD(&mp_entry->ws_head, retval, entries); + ++mp_entry->ws_size; + + TRACE_OUT(open_cache_mp_write_session); + return (retval); +} + +/* + * Writes data to the specified session. Return 0 on success and -1 on errors + * (when write session size limit is exceeded). + */ +int +cache_mp_write(struct cache_mp_write_session_ *ws, char *data, + size_t data_size) +{ + struct cache_mp_data_item_ *new_item; + + TRACE_IN(cache_mp_write); + assert(ws != NULL); + assert(ws->parent_entry != NULL); + assert(ws->parent_entry->params->entry_type == CET_MULTIPART); + + if ((ws->parent_entry->mp_params.max_elemsize > 0) && + (ws->parent_entry->mp_params.max_elemsize == ws->items_size)) { + TRACE_OUT(cache_mp_write); + return (-1); + } + + new_item = (struct cache_mp_data_item_ *)malloc( + sizeof(struct cache_mp_data_item_)); + assert(new_item != NULL); + memset(new_item, 0, sizeof(struct cache_mp_data_item_)); + + new_item->value = (char *)malloc(data_size); + assert(new_item->value != NULL); + memcpy(new_item->value, data, data_size); + new_item->value_size = data_size; + + TAILQ_INSERT_TAIL(&ws->items, new_item, entries); + ++ws->items_size; + + TRACE_OUT(cache_mp_write); + return (0); +} + +/* + * Abandons the write session and frees all the connected resources. + */ +void +abandon_cache_mp_write_session(struct cache_mp_write_session_ *ws) +{ + + TRACE_IN(abandon_cache_mp_write_session); + assert(ws != NULL); + assert(ws->parent_entry != NULL); + assert(ws->parent_entry->params->entry_type == CET_MULTIPART); + + TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries); + --ws->parent_entry->ws_size; + + destroy_cache_mp_write_session(ws); + TRACE_OUT(abandon_cache_mp_write_session); +} + +/* + * Commits the session to the entry, for which it was created. + */ +void +close_cache_mp_write_session(struct cache_mp_write_session_ *ws) +{ + + TRACE_IN(close_cache_mp_write_session); + assert(ws != NULL); + assert(ws->parent_entry != NULL); + assert(ws->parent_entry->params->entry_type == CET_MULTIPART); + + TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries); + --ws->parent_entry->ws_size; + + if (ws->parent_entry->completed_write_session == NULL) { + /* + * If there is no completed session yet, this will be the one + */ + ws->parent_entry->get_time_func( + &ws->parent_entry->creation_time); + ws->parent_entry->completed_write_session = ws; + } else { + /* + * If there is a completed session, then we'll save our session + * as a pending session. If there is already a pending session, + * it would be destroyed. + */ + if (ws->parent_entry->pending_write_session != NULL) + destroy_cache_mp_write_session( + ws->parent_entry->pending_write_session); + + ws->parent_entry->pending_write_session = ws; + } + TRACE_OUT(close_cache_mp_write_session); +} + +/* + * Opens read session for the specified entry. Returns NULL on errors (when + * there are no data in the entry, or the data are obsolete). + */ +struct cache_mp_read_session_ * +open_cache_mp_read_session(struct cache_entry_ *entry) +{ + struct cache_mp_entry_ *mp_entry; + struct cache_mp_read_session_ *retval; + + TRACE_IN(open_cache_mp_read_session); + assert(entry != NULL); + assert(entry->params->entry_type == CET_MULTIPART); + mp_entry = (struct cache_mp_entry_ *)entry; + + if (mp_entry->completed_write_session == NULL) { + TRACE_OUT(open_cache_mp_read_session); + return (NULL); + } + + if ((mp_entry->mp_params.max_lifetime.tv_sec != 0) + || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) { + if (mp_entry->last_request_time.tv_sec - + mp_entry->last_request_time.tv_sec > + mp_entry->mp_params.max_lifetime.tv_sec) { + flush_cache_entry(entry); + TRACE_OUT(open_cache_mp_read_session); + return (NULL); + } + } + + retval = (struct cache_mp_read_session_ *)malloc( + sizeof(struct cache_mp_read_session_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cache_mp_read_session_)); + + retval->parent_entry = mp_entry; + retval->current_item = TAILQ_FIRST( + &mp_entry->completed_write_session->items); + + TAILQ_INSERT_HEAD(&mp_entry->rs_head, retval, entries); + ++mp_entry->rs_size; + + mp_entry->get_time_func(&mp_entry->last_request_time); + TRACE_OUT(open_cache_mp_read_session); + return (retval); +} + +/* + * Reads the data from the read session - step by step. + * Returns 0 on success, -1 on error (when there are no more data), and -2 if + * the data_size is too small. In the last case, data_size would be filled + * the proper value. + */ +int +cache_mp_read(struct cache_mp_read_session_ *rs, char *data, size_t *data_size) +{ + + TRACE_IN(cache_mp_read); + assert(rs != NULL); + + if (rs->current_item == NULL) { + TRACE_OUT(cache_mp_read); + return (-1); + } + + if (rs->current_item->value_size > *data_size) { + *data_size = rs->current_item->value_size; + if (data == NULL) { + TRACE_OUT(cache_mp_read); + return (0); + } + + TRACE_OUT(cache_mp_read); + return (-2); + } + + *data_size = rs->current_item->value_size; + memcpy(data, rs->current_item->value, rs->current_item->value_size); + rs->current_item = TAILQ_NEXT(rs->current_item, entries); + + TRACE_OUT(cache_mp_read); + return (0); +} + +/* + * Closes the read session. If there are no more read sessions and there is + * a pending write session, it will be committed and old + * completed_write_session will be destroyed. + */ +void +close_cache_mp_read_session(struct cache_mp_read_session_ *rs) +{ + + TRACE_IN(close_cache_mp_read_session); + assert(rs != NULL); + assert(rs->parent_entry != NULL); + + TAILQ_REMOVE(&rs->parent_entry->rs_head, rs, entries); + --rs->parent_entry->rs_size; + + if ((rs->parent_entry->rs_size == 0) && + (rs->parent_entry->pending_write_session != NULL)) { + destroy_cache_mp_write_session( + rs->parent_entry->completed_write_session); + rs->parent_entry->completed_write_session = + rs->parent_entry->pending_write_session; + rs->parent_entry->pending_write_session = NULL; + } + + destroy_cache_mp_read_session(rs); + TRACE_OUT(close_cache_mp_read_session); +} + +int +transform_cache_entry(struct cache_entry_ *entry, + enum cache_transformation_t transformation) +{ + + TRACE_IN(transform_cache_entry); + switch (transformation) { + case CTT_CLEAR: + clear_cache_entry(entry); + TRACE_OUT(transform_cache_entry); + return (0); + case CTT_FLUSH: + flush_cache_entry(entry); + TRACE_OUT(transform_cache_entry); + return (0); + default: + TRACE_OUT(transform_cache_entry); + return (-1); + } +} + +int +transform_cache_entry_part(struct cache_entry_ *entry, + enum cache_transformation_t transformation, const char *key_part, + size_t key_part_size, enum part_position_t part_position) +{ + struct cache_common_entry_ *common_entry; + struct cache_ht_item_ *ht_item; + struct cache_ht_item_data_ *ht_item_data, ht_key; + + struct cache_policy_item_ *item, *connected_item; + + TRACE_IN(transform_cache_entry_part); + if (entry->params->entry_type != CET_COMMON) { + TRACE_OUT(transform_cache_entry_part); + return (-1); + } + + if (transformation != CTT_CLEAR) { + TRACE_OUT(transform_cache_entry_part); + return (-1); + } + + memset(&ht_key, 0, sizeof(struct cache_ht_item_data_)); + ht_key.key = (char *)key_part; /* can't avoid casting here */ + ht_key.key_size = key_part_size; + + common_entry = (struct cache_common_entry_ *)entry; + HASHTABLE_FOREACH(&(common_entry->items), ht_item) { + do { + ht_item_data = HASHTABLE_ENTRY_FIND_SPECIAL(cache_ht_, + ht_item, &ht_key, + ht_items_fixed_size_left_cmp_func); + + if (ht_item_data != NULL) { + item = ht_item_data->fifo_policy_item; + connected_item = item->connected_item; + + common_entry->policies[0]->remove_item_func( + common_entry->policies[0], + item); + + free(ht_item_data->key); + free(ht_item_data->value); + HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item, + ht_item_data); + --common_entry->items_size; + + common_entry->policies[0]->destroy_item_func( + item); + if (common_entry->policies_size == 2) { + common_entry->policies[1]->remove_item_func( + common_entry->policies[1], + connected_item); + common_entry->policies[1]->destroy_item_func( + connected_item); + } + } + } while (ht_item_data != NULL); + } + + TRACE_OUT(transform_cache_entry_part); + return (0); +} diff --git a/usr.sbin/nscd/cachelib.h b/usr.sbin/nscd/cachelib.h new file mode 100644 index 000000000000..d67e83063870 --- /dev/null +++ b/usr.sbin/nscd/cachelib.h @@ -0,0 +1,281 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_CACHELIB_H__ +#define __CACHED_CACHELIB_H__ + +#include +#include +#include +#include "hashtable.h" +#include "cacheplcs.h" + +enum cache_entry_t { + CET_COMMON = 0, /* cache item is atomic */ + CET_MULTIPART /* cache item is formed part by part */ +}; + +enum cache_transformation_t { + CTT_FLUSH = 0, /* flush the cache - delete all obsolete items */ + CTT_CLEAR = 1 /* delete all items in the cache */ +}; + +/* cache deletion policy type enum */ +enum cache_policy_t { + CPT_FIFO = 0, /* first-in first-out */ + CPT_LRU = 1, /* least recently used */ + CPT_LFU = 2 /* least frequently used */ +}; + +/* multipart sessions can be used for reading and writing */ +enum cache_mp_session_t { + CMPT_READ_SESSION, + CMPT_WRITE_SESSION +}; + +/* + * When doing partial transformations of entries (which are applied for + * elements with keys, that contain specified buffer in its left or + * right part), this enum will show the needed position of the key part. + */ +enum part_position_t { + KPPT_LEFT, + KPPT_RIGHT +}; + +/* num_levels attribute is obsolete, i think - user can always emulate it + * by using one entry. + * get_time_func is needed to have the clocks-independent counter + */ +struct cache_params +{ + void (*get_time_func)(struct timeval *); +}; + +/* + * base structure - normal_cache_entry_params and multipart_cache_entry_params + * are "inherited" from it + */ +struct cache_entry_params +{ + enum cache_entry_t entry_type; + char *entry_name; +}; + +/* params, used for most entries */ +struct common_cache_entry_params +{ + /* inherited fields */ + enum cache_entry_t entry_type; + + /* unique fields */ + char *entry_name; + size_t cache_entries_size; + + size_t max_elemsize; /* if 0 then no check is made */ + size_t satisf_elemsize; /* if entry size is exceeded, + * this number of elements will be left, + * others will be deleted */ + struct timeval max_lifetime; /* if 0 then no check is made */ + enum cache_policy_t policy; /* policy used for transformations */ +}; + +/* params, used for multipart entries */ +struct mp_cache_entry_params +{ + /* inherited fields */ + enum cache_entry_t entry_type; + char *entry_name; + + /* unique fields */ + size_t max_elemsize; /* if 0 then no check is made */ + size_t max_sessions; /* maximum number of active sessions */ + + struct timeval max_lifetime; /* maximum elements lifetime */ +}; + +struct cache_ht_item_data_ +{ + /* key is the bytes sequence only - not the null-terminated string */ + char *key; + size_t key_size; + + char *value; + size_t value_size; + + struct cache_policy_item_ *fifo_policy_item; +}; + +struct cache_ht_item_ +{ + HASHTABLE_ENTRY_HEAD(ht_item_, struct cache_ht_item_data_) data; +}; + +struct cache_entry_ +{ + char *name; + struct cache_entry_params *params; +}; + +struct cache_common_entry_ +{ + char *name; + struct cache_entry_params *params; + + struct common_cache_entry_params common_params; + + HASHTABLE_HEAD(cache_ht_, cache_ht_item_) items; + size_t items_size; + + /* + * Entry always has the FIFO policy, that is used to eliminate old + * elements (the ones, with lifetime more than max_lifetime). Besides, + * user can specify another policy to be applied, when there are too + * many elements in the entry. So policies_size can be 1 or 2. + */ + struct cache_policy_ **policies; + size_t policies_size; + + void (*get_time_func)(struct timeval *); +}; + +struct cache_mp_data_item_ { + char *value; + size_t value_size; + + TAILQ_ENTRY(cache_mp_data_item_) entries; +}; + +struct cache_mp_write_session_ +{ + struct cache_mp_entry_ *parent_entry; + + /* + * All items are accumulated in this queue. When the session is + * committed, they all will be copied to the multipart entry. + */ + TAILQ_HEAD(cache_mp_data_item_head, cache_mp_data_item_) items; + size_t items_size; + + TAILQ_ENTRY(cache_mp_write_session_) entries; +}; + +struct cache_mp_read_session_ +{ + struct cache_mp_entry_ *parent_entry; + struct cache_mp_data_item_ *current_item; + + TAILQ_ENTRY(cache_mp_read_session_) entries; +}; + +struct cache_mp_entry_ +{ + char *name; + struct cache_entry_params *params; + + struct mp_cache_entry_params mp_params; + + /* All opened write sessions */ + TAILQ_HEAD(write_sessions_head, cache_mp_write_session_) ws_head; + size_t ws_size; + + /* All opened read sessions */ + TAILQ_HEAD(read_sessions_head, cache_mp_read_session_) rs_head; + size_t rs_size; + + /* + * completed_write_session is the committed write sessions. All read + * sessions use data from it. If the completed_write_session is out of + * date, but still in use by some of the read sessions, the newly + * committed write session is stored in the pending_write_session. + * In such a case, completed_write_session will be substituted with + * pending_write_session as soon as it won't be used by any of + * the read sessions. + */ + struct cache_mp_write_session_ *completed_write_session; + struct cache_mp_write_session_ *pending_write_session; + struct timeval creation_time; + struct timeval last_request_time; + + void (*get_time_func)(struct timeval *); +}; + +struct cache_ +{ + struct cache_params params; + + struct cache_entry_ **entries; + size_t entries_capacity; + size_t entries_size; +}; + +/* simple abstractions - for not to write "struct" every time */ +typedef struct cache_ *cache; +typedef struct cache_entry_ *cache_entry; +typedef struct cache_mp_write_session_ *cache_mp_write_session; +typedef struct cache_mp_read_session_ *cache_mp_read_session; + +#define INVALID_CACHE (NULL) +#define INVALID_CACHE_ENTRY (NULL) +#define INVALID_CACHE_MP_WRITE_SESSION (NULL) +#define INVALID_CACHE_MP_READ_SESSION (NULL) + +/* + * NOTE: all cache operations are thread-unsafe. You must ensure thread-safety + * externally, by yourself. + */ + +/* cache initialization/destruction routines */ +extern cache init_cache(struct cache_params const *); +extern void destroy_cache(cache); + +/* cache entries manipulation routines */ +extern int register_cache_entry(cache, struct cache_entry_params const *); +extern int unregister_cache_entry(cache, const char *); +extern cache_entry find_cache_entry(cache, const char *); + +/* read/write operations used on common entries */ +extern int cache_read(cache_entry, const char *, size_t, char *, size_t *); +extern int cache_write(cache_entry, const char *, size_t, char const *, size_t); + +/* read/write operations used on multipart entries */ +extern cache_mp_write_session open_cache_mp_write_session(cache_entry); +extern int cache_mp_write(cache_mp_write_session, char *, size_t); +extern void abandon_cache_mp_write_session(cache_mp_write_session); +extern void close_cache_mp_write_session(cache_mp_write_session); + +extern cache_mp_read_session open_cache_mp_read_session(cache_entry); +extern int cache_mp_read(cache_mp_read_session, char *, size_t *); +extern void close_cache_mp_read_session(cache_mp_read_session); + +/* transformation routines */ +extern int transform_cache_entry(cache_entry, enum cache_transformation_t); +extern int transform_cache_entry_part(cache_entry, enum cache_transformation_t, + const char *, size_t, enum part_position_t); + +#endif diff --git a/usr.sbin/nscd/cacheplcs.c b/usr.sbin/nscd/cacheplcs.c new file mode 100644 index 000000000000..a7ee38de4801 --- /dev/null +++ b/usr.sbin/nscd/cacheplcs.c @@ -0,0 +1,590 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include "cacheplcs.h" +#include "debug.h" + +static void cache_fifo_policy_update_item(struct cache_policy_ *, + struct cache_policy_item_ *); +static void cache_lfu_policy_add_item(struct cache_policy_ *, + struct cache_policy_item_ *); +static struct cache_policy_item_ * cache_lfu_policy_create_item(void); +static void cache_lfu_policy_destroy_item(struct cache_policy_item_ *); +static struct cache_policy_item_ *cache_lfu_policy_get_first_item( + struct cache_policy_ *); +static struct cache_policy_item_ *cache_lfu_policy_get_last_item( + struct cache_policy_ *); +static struct cache_policy_item_ *cache_lfu_policy_get_next_item( + struct cache_policy_ *, struct cache_policy_item_ *); +static struct cache_policy_item_ *cache_lfu_policy_get_prev_item( + struct cache_policy_ *, struct cache_policy_item_ *); +static void cache_lfu_policy_remove_item(struct cache_policy_ *, + struct cache_policy_item_ *); +static void cache_lfu_policy_update_item(struct cache_policy_ *, + struct cache_policy_item_ *); +static void cache_lru_policy_update_item(struct cache_policy_ *, + struct cache_policy_item_ *); +static void cache_queue_policy_add_item(struct cache_policy_ *, + struct cache_policy_item_ *); +static struct cache_policy_item_ * cache_queue_policy_create_item(); +static void cache_queue_policy_destroy_item(struct cache_policy_item_ *); +static struct cache_policy_item_ *cache_queue_policy_get_first_item( + struct cache_policy_ *); +static struct cache_policy_item_ *cache_queue_policy_get_last_item( + struct cache_policy_ *); +static struct cache_policy_item_ *cache_queue_policy_get_next_item( + struct cache_policy_ *, struct cache_policy_item_ *); +static struct cache_policy_item_ *cache_queue_policy_get_prev_item( + struct cache_policy_ *, struct cache_policy_item_ *); +static void cache_queue_policy_remove_item(struct cache_policy_ *, + struct cache_policy_item_ *); +static void destroy_cache_queue_policy(struct cache_queue_policy_ *); +static struct cache_queue_policy_ *init_cache_queue_policy(void); + +/* + * All cache_queue_policy_XXX functions below will be used to fill + * the cache_queue_policy structure. They implement the most functionality of + * LRU and FIFO policies. LRU and FIFO policies are actually the + * cache_queue_policy_ with cache_update_item function changed. + */ +static struct cache_policy_item_ * +cache_queue_policy_create_item() +{ + struct cache_queue_policy_item_ *retval; + + TRACE_IN(cache_queue_policy_create_item); + retval = (struct cache_queue_policy_item_ *)malloc( + sizeof(struct cache_queue_policy_item_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cache_queue_policy_item_)); + + TRACE_OUT(cache_queue_policy_create_item); + return ((struct cache_policy_item_ *)retval); +} + +static void +cache_queue_policy_destroy_item(struct cache_policy_item_ *item) +{ + + TRACE_IN(cache_queue_policy_destroy_item); + assert(item != NULL); + free(item); + TRACE_OUT(cache_queue_policy_destroy_item); +} + +static void +cache_queue_policy_add_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_queue_policy_ *queue_policy; + struct cache_queue_policy_item_ *queue_item; + + TRACE_IN(cache_queue_policy_add_item); + queue_policy = (struct cache_queue_policy_ *)policy; + queue_item = (struct cache_queue_policy_item_ *)item; + TAILQ_INSERT_TAIL(&queue_policy->head, queue_item, entries); + TRACE_OUT(cache_queue_policy_add_item); +} + +static void +cache_queue_policy_remove_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_queue_policy_ *queue_policy; + struct cache_queue_policy_item_ *queue_item; + + TRACE_IN(cache_queue_policy_remove_item); + queue_policy = (struct cache_queue_policy_ *)policy; + queue_item = (struct cache_queue_policy_item_ *)item; + TAILQ_REMOVE(&queue_policy->head, queue_item, entries); + TRACE_OUT(cache_queue_policy_remove_item); +} + +static struct cache_policy_item_ * +cache_queue_policy_get_first_item(struct cache_policy_ *policy) +{ + struct cache_queue_policy_ *queue_policy; + + TRACE_IN(cache_queue_policy_get_first_item); + queue_policy = (struct cache_queue_policy_ *)policy; + TRACE_OUT(cache_queue_policy_get_first_item); + return ((struct cache_policy_item_ *)TAILQ_FIRST(&queue_policy->head)); +} + +static struct cache_policy_item_ * +cache_queue_policy_get_last_item(struct cache_policy_ *policy) +{ + struct cache_queue_policy_ *queue_policy; + + TRACE_IN(cache_queue_policy_get_last_item); + queue_policy = (struct cache_queue_policy_ *)policy; + TRACE_OUT(cache_queue_policy_get_last_item); + return ((struct cache_policy_item_ *)TAILQ_LAST(&queue_policy->head, + cache_queue_policy_head_)); +} + +static struct cache_policy_item_ * +cache_queue_policy_get_next_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_queue_policy_ *queue_policy; + struct cache_queue_policy_item_ *queue_item; + + TRACE_IN(cache_queue_policy_get_next_item); + queue_policy = (struct cache_queue_policy_ *)policy; + queue_item = (struct cache_queue_policy_item_ *)item; + + TRACE_OUT(cache_queue_policy_get_next_item); + return ((struct cache_policy_item_ *)TAILQ_NEXT(queue_item, entries)); +} + +static struct cache_policy_item_ * +cache_queue_policy_get_prev_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_queue_policy_ *queue_policy; + struct cache_queue_policy_item_ *queue_item; + + TRACE_IN(cache_queue_policy_get_prev_item); + queue_policy = (struct cache_queue_policy_ *)policy; + queue_item = (struct cache_queue_policy_item_ *)item; + + TRACE_OUT(cache_queue_policy_get_prev_item); + return ((struct cache_policy_item_ *)TAILQ_PREV(queue_item, + cache_queue_policy_head_, entries)); +} + +/* + * Initializes cache_queue_policy_ by filling the structure with the functions + * pointers, defined above + */ +static struct cache_queue_policy_ * +init_cache_queue_policy(void) +{ + struct cache_queue_policy_ *retval; + + TRACE_IN(init_cache_queue_policy); + retval = (struct cache_queue_policy_ *)malloc( + sizeof(struct cache_queue_policy_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cache_queue_policy_)); + + retval->parent_data.create_item_func = cache_queue_policy_create_item; + retval->parent_data.destroy_item_func = cache_queue_policy_destroy_item; + + retval->parent_data.add_item_func = cache_queue_policy_add_item; + retval->parent_data.remove_item_func = cache_queue_policy_remove_item; + + retval->parent_data.get_first_item_func = + cache_queue_policy_get_first_item; + retval->parent_data.get_last_item_func = + cache_queue_policy_get_last_item; + retval->parent_data.get_next_item_func = + cache_queue_policy_get_next_item; + retval->parent_data.get_prev_item_func = + cache_queue_policy_get_prev_item; + + TAILQ_INIT(&retval->head); + TRACE_OUT(init_cache_queue_policy); + return (retval); +} + +static void +destroy_cache_queue_policy(struct cache_queue_policy_ *queue_policy) +{ + struct cache_queue_policy_item_ *queue_item; + + TRACE_IN(destroy_cache_queue_policy); + while (!TAILQ_EMPTY(&queue_policy->head)) { + queue_item = TAILQ_FIRST(&queue_policy->head); + TAILQ_REMOVE(&queue_policy->head, queue_item, entries); + cache_queue_policy_destroy_item( + (struct cache_policy_item_ *)queue_item); + } + free(queue_policy); + TRACE_OUT(destroy_cache_queue_policy); +} + +/* + * Makes cache_queue_policy_ behave like FIFO policy - we don't do anything, + * when the cache element is updated. So it always stays in its initial + * position in the queue - that is exactly the FIFO functionality. + */ +static void +cache_fifo_policy_update_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + + TRACE_IN(cache_fifo_policy_update_item); + /* policy and item arguments are ignored */ + TRACE_OUT(cache_fifo_policy_update_item); +} + +struct cache_policy_ * +init_cache_fifo_policy() +{ + struct cache_queue_policy_ *retval; + + TRACE_IN(init_cache_fifo_policy); + retval = init_cache_queue_policy(); + retval->parent_data.update_item_func = cache_fifo_policy_update_item; + + TRACE_OUT(init_cache_fifo_policy); + return ((struct cache_policy_ *)retval); +} + +void +destroy_cache_fifo_policy(struct cache_policy_ *policy) +{ + struct cache_queue_policy_ *queue_policy; + + TRACE_IN(destroy_cache_fifo_policy); + queue_policy = (struct cache_queue_policy_ *)policy; + destroy_cache_queue_policy(queue_policy); + TRACE_OUT(destroy_cache_fifo_policy); +} + +/* + * Makes cache_queue_policy_ behave like LRU policy. On each update, cache + * element is moved to the end of the queue - so it would be deleted in last + * turn. That is exactly the LRU policy functionality. + */ +static void +cache_lru_policy_update_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_queue_policy_ *queue_policy; + struct cache_queue_policy_item_ *queue_item; + + TRACE_IN(cache_lru_policy_update_item); + queue_policy = (struct cache_queue_policy_ *)policy; + queue_item = (struct cache_queue_policy_item_ *)item; + + TAILQ_REMOVE(&queue_policy->head, queue_item, entries); + TAILQ_INSERT_TAIL(&queue_policy->head, queue_item, entries); + TRACE_OUT(cache_lru_policy_update_item); +} + +struct cache_policy_ * +init_cache_lru_policy() +{ + struct cache_queue_policy_ *retval; + + TRACE_IN(init_cache_lru_policy); + retval = init_cache_queue_policy(); + retval->parent_data.update_item_func = cache_lru_policy_update_item; + + TRACE_OUT(init_cache_lru_policy); + return ((struct cache_policy_ *)retval); +} + +void +destroy_cache_lru_policy(struct cache_policy_ *policy) +{ + struct cache_queue_policy_ *queue_policy; + + TRACE_IN(destroy_cache_lru_policy); + queue_policy = (struct cache_queue_policy_ *)policy; + destroy_cache_queue_policy(queue_policy); + TRACE_OUT(destroy_cache_lru_policy); +} + +/* + * LFU (least frequently used) policy implementation differs much from the + * LRU and FIFO (both based on cache_queue_policy_). Almost all cache_policy_ + * functions are implemented specifically for this policy. The idea of this + * policy is to represent frequency (real number) as the integer number and + * use it as the index in the array. Each array's element is + * the list of elements. For example, if we have the 100-elements + * array for this policy, the elements with frequency 0.1 (calls per-second) + * would be in 10th element of the array. + */ +static struct cache_policy_item_ * +cache_lfu_policy_create_item(void) +{ + struct cache_lfu_policy_item_ *retval; + + TRACE_IN(cache_lfu_policy_create_item); + retval = (struct cache_lfu_policy_item_ *)malloc( + sizeof(struct cache_lfu_policy_item_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cache_lfu_policy_item_)); + + TRACE_OUT(cache_lfu_policy_create_item); + return ((struct cache_policy_item_ *)retval); +} + +static void +cache_lfu_policy_destroy_item(struct cache_policy_item_ *item) +{ + + TRACE_IN(cache_lfu_policy_destroy_item); + assert(item != NULL); + free(item); + TRACE_OUT(cache_lfu_policy_destroy_item); +} + +/* + * When placed in the LFU policy queue for the first time, the maximum + * frequency is assigned to the element + */ +static void +cache_lfu_policy_add_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_lfu_policy_ *lfu_policy; + struct cache_lfu_policy_item_ *lfu_item; + + TRACE_IN(cache_lfu_policy_add_item); + lfu_policy = (struct cache_lfu_policy_ *)policy; + lfu_item = (struct cache_lfu_policy_item_ *)item; + + lfu_item->frequency = CACHELIB_MAX_FREQUENCY - 1; + TAILQ_INSERT_HEAD(&(lfu_policy->groups[CACHELIB_MAX_FREQUENCY - 1]), + lfu_item, entries); + TRACE_OUT(cache_lfu_policy_add_item); +} + +/* + * On each update the frequency of the element is recalculated and, if it + * changed, the element would be moved to the another place in the array. + */ +static void +cache_lfu_policy_update_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_lfu_policy_ *lfu_policy; + struct cache_lfu_policy_item_ *lfu_item; + int index; + + TRACE_IN(cache_lfu_policy_update_item); + lfu_policy = (struct cache_lfu_policy_ *)policy; + lfu_item = (struct cache_lfu_policy_item_ *)item; + + /* + * We calculate the square of the request_count to avoid grouping of + * all elements at the start of the array (for example, if array size is + * 100 and most of its elements has frequency below the 0.01, they + * all would be grouped in the first array's position). Other + * techniques should be used here later to ensure, that elements are + * equally distributed in the array and not grouped in its beginning. + */ + if (lfu_item->parent_data.last_request_time.tv_sec != + lfu_item->parent_data.creation_time.tv_sec) { + index = ((double)lfu_item->parent_data.request_count * + (double)lfu_item->parent_data.request_count / + (lfu_item->parent_data.last_request_time.tv_sec - + lfu_item->parent_data.creation_time.tv_sec + 1)) * + CACHELIB_MAX_FREQUENCY; + if (index >= CACHELIB_MAX_FREQUENCY) + index = CACHELIB_MAX_FREQUENCY - 1; + } else + index = CACHELIB_MAX_FREQUENCY - 1; + + TAILQ_REMOVE(&(lfu_policy->groups[lfu_item->frequency]), lfu_item, + entries); + lfu_item->frequency = index; + TAILQ_INSERT_HEAD(&(lfu_policy->groups[index]), lfu_item, entries); + + TRACE_OUT(cache_lfu_policy_update_item); +} + +static void +cache_lfu_policy_remove_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_lfu_policy_ *lfu_policy; + struct cache_lfu_policy_item_ *lfu_item; + + TRACE_IN(cache_lfu_policy_remove_item); + lfu_policy = (struct cache_lfu_policy_ *)policy; + lfu_item = (struct cache_lfu_policy_item_ *)item; + + TAILQ_REMOVE(&(lfu_policy->groups[lfu_item->frequency]), lfu_item, + entries); + TRACE_OUT(cache_lfu_policy_remove_item); +} + +static struct cache_policy_item_ * +cache_lfu_policy_get_first_item(struct cache_policy_ *policy) +{ + struct cache_lfu_policy_ *lfu_policy; + struct cache_lfu_policy_item_ *lfu_item; + int i; + + TRACE_IN(cache_lfu_policy_get_first_item); + lfu_item = NULL; + lfu_policy = (struct cache_lfu_policy_ *)policy; + for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i) + if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) { + lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i])); + break; + } + + TRACE_OUT(cache_lfu_policy_get_first_item); + return ((struct cache_policy_item_ *)lfu_item); +} + +static struct cache_policy_item_ * +cache_lfu_policy_get_last_item(struct cache_policy_ *policy) +{ + struct cache_lfu_policy_ *lfu_policy; + struct cache_lfu_policy_item_ *lfu_item; + int i; + + TRACE_IN(cache_lfu_policy_get_last_item); + lfu_item = NULL; + lfu_policy = (struct cache_lfu_policy_ *)policy; + for (i = CACHELIB_MAX_FREQUENCY - 1; i >= 0; --i) + if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) { + lfu_item = TAILQ_LAST(&(lfu_policy->groups[i]), + cache_lfu_policy_group_); + break; + } + + TRACE_OUT(cache_lfu_policy_get_last_item); + return ((struct cache_policy_item_ *)lfu_item); +} + +static struct cache_policy_item_ * +cache_lfu_policy_get_next_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_lfu_policy_ *lfu_policy; + struct cache_lfu_policy_item_ *lfu_item; + int i; + + TRACE_IN(cache_lfu_policy_get_next_item); + lfu_policy = (struct cache_lfu_policy_ *)policy; + lfu_item = TAILQ_NEXT((struct cache_lfu_policy_item_ *)item, entries); + if (lfu_item == NULL) + { + for (i = ((struct cache_lfu_policy_item_ *)item)->frequency + 1; + i < CACHELIB_MAX_FREQUENCY; ++i) { + if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) { + lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i])); + break; + } + } + } + + TRACE_OUT(cache_lfu_policy_get_next_item); + return ((struct cache_policy_item_ *)lfu_item); +} + +static struct cache_policy_item_ * +cache_lfu_policy_get_prev_item(struct cache_policy_ *policy, + struct cache_policy_item_ *item) +{ + struct cache_lfu_policy_ *lfu_policy; + struct cache_lfu_policy_item_ *lfu_item; + int i; + + TRACE_IN(cache_lfu_policy_get_prev_item); + lfu_policy = (struct cache_lfu_policy_ *)policy; + lfu_item = TAILQ_PREV((struct cache_lfu_policy_item_ *)item, + cache_lfu_policy_group_, entries); + if (lfu_item == NULL) + { + for (i = ((struct cache_lfu_policy_item_ *)item)->frequency - 1; + i >= 0; --i) + if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) { + lfu_item = TAILQ_LAST(&(lfu_policy->groups[i]), + cache_lfu_policy_group_); + break; + } + } + + TRACE_OUT(cache_lfu_policy_get_prev_item); + return ((struct cache_policy_item_ *)lfu_item); +} + +/* + * Initializes the cache_policy_ structure by filling it with appropriate + * functions pointers + */ +struct cache_policy_ * +init_cache_lfu_policy() +{ + int i; + struct cache_lfu_policy_ *retval; + + TRACE_IN(init_cache_lfu_policy); + retval = (struct cache_lfu_policy_ *)malloc( + sizeof(struct cache_lfu_policy_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cache_lfu_policy_)); + + retval->parent_data.create_item_func = cache_lfu_policy_create_item; + retval->parent_data.destroy_item_func = cache_lfu_policy_destroy_item; + + retval->parent_data.add_item_func = cache_lfu_policy_add_item; + retval->parent_data.update_item_func = cache_lfu_policy_update_item; + retval->parent_data.remove_item_func = cache_lfu_policy_remove_item; + + retval->parent_data.get_first_item_func = + cache_lfu_policy_get_first_item; + retval->parent_data.get_last_item_func = + cache_lfu_policy_get_last_item; + retval->parent_data.get_next_item_func = + cache_lfu_policy_get_next_item; + retval->parent_data.get_prev_item_func = + cache_lfu_policy_get_prev_item; + + for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i) + TAILQ_INIT(&(retval->groups[i])); + + TRACE_OUT(init_cache_lfu_policy); + return ((struct cache_policy_ *)retval); +} + +void +destroy_cache_lfu_policy(struct cache_policy_ *policy) +{ + int i; + struct cache_lfu_policy_ *lfu_policy; + struct cache_lfu_policy_item_ *lfu_item; + + TRACE_IN(destroy_cache_lfu_policy); + lfu_policy = (struct cache_lfu_policy_ *)policy; + for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i) { + while (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) { + lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i])); + TAILQ_REMOVE(&(lfu_policy->groups[i]), lfu_item, + entries); + cache_lfu_policy_destroy_item( + (struct cache_policy_item_ *)lfu_item); + } + } + free(policy); + TRACE_OUT(destroy_cache_lfu_policy); +} diff --git a/usr.sbin/nscd/cacheplcs.h b/usr.sbin/nscd/cacheplcs.h new file mode 100644 index 000000000000..36997f57a931 --- /dev/null +++ b/usr.sbin/nscd/cacheplcs.h @@ -0,0 +1,137 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_CACHEPLCS_H__ +#define __CACHED_CACHEPLCS_H__ + +#include +#include +#include + +/* common policy definitions */ +#define CACHELIB_MAX_FREQUENCY 100 + +/* + * cache_policy_item_ represents some abstract cache element in the policy + * queue. connected_item pointers to the corresponding cache_policy_item_ in + * another policy queue. + */ +struct cache_policy_item_ +{ + char *key; + size_t key_size; + + size_t request_count; + struct timeval last_request_time; + struct timeval creation_time; + + struct cache_policy_item_ *connected_item; +}; + +/* + * cache_policy_ represents an abstract policy queue. It can be customized by + * setting appropriate function pointers + */ +struct cache_policy_ +{ + struct cache_policy_item_* (*create_item_func)(); + void (*destroy_item_func)(struct cache_policy_item_ *); + + void (*add_item_func)(struct cache_policy_ *, + struct cache_policy_item_ *); + void (*remove_item_func)(struct cache_policy_ *, + struct cache_policy_item_ *); + void (*update_item_func)(struct cache_policy_ *, + struct cache_policy_item_ *); + + struct cache_policy_item_ *(*get_first_item_func)( + struct cache_policy_ *); + struct cache_policy_item_ *(*get_last_item_func)( + struct cache_policy_ *); + struct cache_policy_item_ *(*get_next_item_func)( + struct cache_policy_ *, struct cache_policy_item_ *); + struct cache_policy_item_ *(*get_prev_item_func)( + struct cache_policy_ *, struct cache_policy_item_ *); +}; + +/* + * LFU cache policy item "inherited" from cache_policy_item_ structure + */ +struct cache_lfu_policy_item_ +{ + struct cache_policy_item_ parent_data; + int frequency; + + TAILQ_ENTRY(cache_lfu_policy_item_) entries; +}; + +TAILQ_HEAD(cache_lfu_policy_group_, cache_lfu_policy_item_); + +/* + * LFU policy queue "inherited" from cache_policy_. + */ +struct cache_lfu_policy_ +{ + struct cache_policy_ parent_data; + struct cache_lfu_policy_group_ groups[CACHELIB_MAX_FREQUENCY]; +}; + +/* + * LRU and FIFO policies item "inherited" from cache_policy_item_ + */ +struct cache_queue_policy_item_ +{ + struct cache_policy_item_ parent_data; + TAILQ_ENTRY(cache_queue_policy_item_) entries; +}; + +/* + * LRU and FIFO policies "inherited" from cache_policy_ + */ +struct cache_queue_policy_ +{ + struct cache_policy_ parent_data; + TAILQ_HEAD(cache_queue_policy_head_, cache_queue_policy_item_) head; +}; + +typedef struct cache_queue_policy_ cache_fifo_policy_; +typedef struct cache_queue_policy_ cache_lru_policy_; + +/* fifo policy routines */ +extern struct cache_policy_ *init_cache_fifo_policy(); +extern void destroy_cache_fifo_policy(struct cache_policy_ *); + +/* lru policy routines */ +extern struct cache_policy_ *init_cache_lru_policy(); +extern void destroy_cache_lru_policy(struct cache_policy_ *); + +/* lfu policy routines */ +extern struct cache_policy_ *init_cache_lfu_policy(); +extern void destroy_cache_lfu_policy(struct cache_policy_ *); + +#endif diff --git a/usr.sbin/nscd/config.c b/usr.sbin/nscd/config.c new file mode 100644 index 000000000000..bc3eb49eeb71 --- /dev/null +++ b/usr.sbin/nscd/config.c @@ -0,0 +1,588 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include "config.h" +#include "debug.h" +#include "log.h" + +/* + * Default entries, which always exist in the configuration + */ +const char *c_default_entries[6] = { + NSDB_PASSWD, + NSDB_GROUP, + NSDB_HOSTS, + NSDB_SERVICES, + NSDB_PROTOCOLS, + NSDB_RPC + }; + +static int configuration_entry_cmp(const void *, const void *); +static int configuration_entry_sort_cmp(const void *, const void *); +static int configuration_entry_cache_mp_sort_cmp(const void *, const void *); +static int configuration_entry_cache_mp_cmp(const void *, const void *); +static int configuration_entry_cache_mp_part_cmp(const void *, const void *); +static struct configuration_entry *create_configuration_entry(const char *, + struct timeval const *, struct timeval const *, + struct common_cache_entry_params const *, + struct common_cache_entry_params const *, + struct mp_cache_entry_params const *); + +static int +configuration_entry_sort_cmp(const void *e1, const void *e2) +{ + return (strcmp((*((struct configuration_entry **)e1))->name, + (*((struct configuration_entry **)e2))->name + )); +} + +static int +configuration_entry_cmp(const void *e1, const void *e2) +{ + return (strcmp((const char *)e1, + (*((struct configuration_entry **)e2))->name + )); +} + +static int +configuration_entry_cache_mp_sort_cmp(const void *e1, const void *e2) +{ + return (strcmp((*((cache_entry *)e1))->params->entry_name, + (*((cache_entry *)e2))->params->entry_name + )); +} + +static int +configuration_entry_cache_mp_cmp(const void *e1, const void *e2) +{ + return (strcmp((const char *)e1, + (*((cache_entry *)e2))->params->entry_name + )); +} + +static int +configuration_entry_cache_mp_part_cmp(const void *e1, const void *e2) +{ + return (strncmp((const char *)e1, + (*((cache_entry *)e2))->params->entry_name, + strlen((const char *)e1) + )); +} + +static struct configuration_entry * +create_configuration_entry(const char *name, + struct timeval const *common_timeout, + struct timeval const *mp_timeout, + struct common_cache_entry_params const *positive_params, + struct common_cache_entry_params const *negative_params, + struct mp_cache_entry_params const *mp_params) +{ + struct configuration_entry *retval; + size_t size; + int res; + + TRACE_IN(create_configuration_entry); + assert(name != NULL); + assert(positive_params != NULL); + assert(negative_params != NULL); + assert(mp_params != NULL); + + retval = (struct configuration_entry *)malloc( + sizeof(struct configuration_entry)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct configuration_entry)); + + res = pthread_mutex_init(&retval->positive_cache_lock, NULL); + if (res != 0) { + free(retval); + LOG_ERR_2("create_configuration_entry", + "can't create positive cache lock"); + TRACE_OUT(create_configuration_entry); + return (NULL); + } + + res = pthread_mutex_init(&retval->negative_cache_lock, NULL); + if (res != 0) { + pthread_mutex_destroy(&retval->positive_cache_lock); + free(retval); + LOG_ERR_2("create_configuration_entry", + "can't create negative cache lock"); + TRACE_OUT(create_configuration_entry); + return (NULL); + } + + res = pthread_mutex_init(&retval->mp_cache_lock, NULL); + if (res != 0) { + pthread_mutex_destroy(&retval->positive_cache_lock); + pthread_mutex_destroy(&retval->negative_cache_lock); + free(retval); + LOG_ERR_2("create_configuration_entry", + "can't create negative cache lock"); + TRACE_OUT(create_configuration_entry); + return (NULL); + } + + memcpy(&retval->positive_cache_params, positive_params, + sizeof(struct common_cache_entry_params)); + memcpy(&retval->negative_cache_params, negative_params, + sizeof(struct common_cache_entry_params)); + memcpy(&retval->mp_cache_params, mp_params, + sizeof(struct mp_cache_entry_params)); + + size = strlen(name); + retval->name = (char *)malloc(size + 1); + assert(retval->name != NULL); + memset(retval->name, 0, size + 1); + memcpy(retval->name, name, size); + + memcpy(&retval->common_query_timeout, common_timeout, + sizeof(struct timeval)); + memcpy(&retval->mp_query_timeout, mp_timeout, + sizeof(struct timeval)); + + asprintf(&retval->positive_cache_params.entry_name, "%s+", name); + assert(retval->positive_cache_params.entry_name != NULL); + + asprintf(&retval->negative_cache_params.entry_name, "%s-", name); + assert(retval->negative_cache_params.entry_name != NULL); + + asprintf(&retval->mp_cache_params.entry_name, "%s*", name); + assert(retval->mp_cache_params.entry_name != NULL); + + TRACE_OUT(create_configuration_entry); + return (retval); +} + +/* + * Creates configuration entry and fills it with default values + */ +struct configuration_entry * +create_def_configuration_entry(const char *name) +{ + struct common_cache_entry_params positive_params, negative_params; + struct mp_cache_entry_params mp_params; + struct timeval default_common_timeout, default_mp_timeout; + + struct configuration_entry *res = NULL; + + TRACE_IN(create_def_configuration_entry); + memset(&positive_params, 0, + sizeof(struct common_cache_entry_params)); + positive_params.entry_type = CET_COMMON; + positive_params.cache_entries_size = DEFAULT_CACHE_HT_SIZE; + positive_params.max_elemsize = DEFAULT_POSITIVE_ELEMENTS_SIZE; + positive_params.satisf_elemsize = DEFAULT_POSITIVE_ELEMENTS_SIZE / 2; + positive_params.max_lifetime.tv_sec = DEFAULT_POSITIVE_LIFETIME; + positive_params.policy = CPT_LRU; + + memcpy(&negative_params, &positive_params, + sizeof(struct common_cache_entry_params)); + negative_params.max_elemsize = DEFAULT_NEGATIVE_ELEMENTS_SIZE; + negative_params.satisf_elemsize = DEFAULT_NEGATIVE_ELEMENTS_SIZE / 2; + negative_params.max_lifetime.tv_sec = DEFAULT_NEGATIVE_LIFETIME; + negative_params.policy = CPT_FIFO; + + memset(&default_common_timeout, 0, sizeof(struct timeval)); + default_common_timeout.tv_sec = DEFAULT_COMMON_ENTRY_TIMEOUT; + + memset(&default_mp_timeout, 0, sizeof(struct timeval)); + default_mp_timeout.tv_sec = DEFAULT_MP_ENTRY_TIMEOUT; + + memset(&mp_params, 0, + sizeof(struct mp_cache_entry_params)); + mp_params.entry_type = CET_MULTIPART; + mp_params.max_elemsize = DEFAULT_MULTIPART_ELEMENTS_SIZE; + mp_params.max_sessions = DEFAULT_MULITPART_SESSIONS_SIZE; + mp_params.max_lifetime.tv_sec = DEFAULT_MULITPART_LIFETIME; + + res = create_configuration_entry(name, &default_common_timeout, + &default_mp_timeout, &positive_params, &negative_params, + &mp_params); + + TRACE_OUT(create_def_configuration_entry); + return (res); +} + +void +destroy_configuration_entry(struct configuration_entry *entry) +{ + TRACE_IN(destroy_configuration_entry); + assert(entry != NULL); + pthread_mutex_destroy(&entry->positive_cache_lock); + pthread_mutex_destroy(&entry->negative_cache_lock); + pthread_mutex_destroy(&entry->mp_cache_lock); + free(entry->name); + free(entry->positive_cache_params.entry_name); + free(entry->negative_cache_params.entry_name); + free(entry->mp_cache_params.entry_name); + free(entry->mp_cache_entries); + free(entry); + TRACE_OUT(destroy_configuration_entry); +} + +int +add_configuration_entry(struct configuration *config, + struct configuration_entry *entry) +{ + TRACE_IN(add_configuration_entry); + assert(entry != NULL); + assert(entry->name != NULL); + if (configuration_find_entry(config, entry->name) != NULL) { + TRACE_OUT(add_configuration_entry); + return (-1); + } + + if (config->entries_size == config->entries_capacity) { + struct configuration_entry **new_entries; + + config->entries_capacity *= 2; + new_entries = (struct configuration_entry **)malloc( + sizeof(struct configuration_entry *) * + config->entries_capacity); + assert(new_entries != NULL); + memset(new_entries, 0, sizeof(struct configuration_entry *) * + config->entries_capacity); + memcpy(new_entries, config->entries, + sizeof(struct configuration_entry *) * + config->entries_size); + + free(config->entries); + config->entries = new_entries; + } + + config->entries[config->entries_size++] = entry; + qsort(config->entries, config->entries_size, + sizeof(struct configuration_entry *), + configuration_entry_sort_cmp); + + TRACE_OUT(add_configuration_entry); + return (0); +} + +size_t +configuration_get_entries_size(struct configuration *config) +{ + TRACE_IN(configuration_get_entries_size); + assert(config != NULL); + TRACE_OUT(configuration_get_entries_size); + return (config->entries_size); +} + +struct configuration_entry * +configuration_get_entry(struct configuration *config, size_t index) +{ + TRACE_IN(configuration_get_entry); + assert(config != NULL); + assert(index < config->entries_size); + TRACE_OUT(configuration_get_entry); + return (config->entries[index]); +} + +struct configuration_entry * +configuration_find_entry(struct configuration *config, + const char *name) +{ + struct configuration_entry **retval; + + TRACE_IN(configuration_find_entry); + + retval = bsearch(name, config->entries, config->entries_size, + sizeof(struct configuration_entry *), configuration_entry_cmp); + TRACE_OUT(configuration_find_entry); + + return ((retval != NULL) ? *retval : NULL); +} + +/* + * All multipart cache entries are stored in the configuration_entry in the + * sorted array (sorted by names). The 3 functions below manage this array. + */ + +int +configuration_entry_add_mp_cache_entry(struct configuration_entry *config_entry, + cache_entry c_entry) +{ + cache_entry *new_mp_entries, *old_mp_entries; + + TRACE_IN(configuration_entry_add_mp_cache_entry); + ++config_entry->mp_cache_entries_size; + new_mp_entries = (cache_entry *)malloc(sizeof(cache_entry) * + config_entry->mp_cache_entries_size); + assert(new_mp_entries != NULL); + new_mp_entries[0] = c_entry; + + if (config_entry->mp_cache_entries_size - 1 > 0) { + memcpy(new_mp_entries + 1, + config_entry->mp_cache_entries, + (config_entry->mp_cache_entries_size - 1) * + sizeof(cache_entry)); + } + + old_mp_entries = config_entry->mp_cache_entries; + config_entry->mp_cache_entries = new_mp_entries; + free(old_mp_entries); + + qsort(config_entry->mp_cache_entries, + config_entry->mp_cache_entries_size, + sizeof(cache_entry), + configuration_entry_cache_mp_sort_cmp); + + TRACE_OUT(configuration_entry_add_mp_cache_entry); + return (0); +} + +cache_entry +configuration_entry_find_mp_cache_entry( + struct configuration_entry *config_entry, const char *mp_name) +{ + cache_entry *result; + + TRACE_IN(configuration_entry_find_mp_cache_entry); + result = bsearch(mp_name, config_entry->mp_cache_entries, + config_entry->mp_cache_entries_size, + sizeof(cache_entry), configuration_entry_cache_mp_cmp); + + if (result == NULL) { + TRACE_OUT(configuration_entry_find_mp_cache_entry); + return (NULL); + } else { + TRACE_OUT(configuration_entry_find_mp_cache_entry); + return (*result); + } +} + +/* + * Searches for all multipart entries with names starting with mp_name. + * Needed for cache flushing. + */ +int +configuration_entry_find_mp_cache_entries( + struct configuration_entry *config_entry, const char *mp_name, + cache_entry **start, cache_entry **finish) +{ + cache_entry *result; + + TRACE_IN(configuration_entry_find_mp_cache_entries); + result = bsearch(mp_name, config_entry->mp_cache_entries, + config_entry->mp_cache_entries_size, + sizeof(cache_entry), configuration_entry_cache_mp_part_cmp); + + if (result == NULL) { + TRACE_OUT(configuration_entry_find_mp_cache_entries); + return (-1); + } + + *start = result; + *finish = result + 1; + + while (*start != config_entry->mp_cache_entries) { + if (configuration_entry_cache_mp_part_cmp(mp_name, *start - 1) == 0) + *start = *start - 1; + else + break; + } + + while (*finish != config_entry->mp_cache_entries + + config_entry->mp_cache_entries_size) { + + if (configuration_entry_cache_mp_part_cmp( + mp_name, *finish) == 0) + *finish = *finish + 1; + else + break; + } + + TRACE_OUT(configuration_entry_find_mp_cache_entries); + return (0); +} + +/* + * Configuration entry uses rwlock to handle access to its fields. + */ +void +configuration_lock_rdlock(struct configuration *config) +{ + TRACE_IN(configuration_lock_rdlock); + pthread_rwlock_rdlock(&config->rwlock); + TRACE_OUT(configuration_lock_rdlock); +} + +void +configuration_lock_wrlock(struct configuration *config) +{ + TRACE_IN(configuration_lock_wrlock); + pthread_rwlock_wrlock(&config->rwlock); + TRACE_OUT(configuration_lock_wrlock); +} + +void +configuration_unlock(struct configuration *config) +{ + TRACE_IN(configuration_unlock); + pthread_rwlock_unlock(&config->rwlock); + TRACE_OUT(configuration_unlock); +} + +/* + * Configuration entry uses 3 mutexes to handle cache operations. They are + * acquired by configuration_lock_entry and configuration_unlock_entry + * functions. + */ +void +configuration_lock_entry(struct configuration_entry *entry, + enum config_entry_lock_type lock_type) +{ + TRACE_IN(configuration_lock_entry); + assert(entry != NULL); + + switch (lock_type) { + case CELT_POSITIVE: + pthread_mutex_lock(&entry->positive_cache_lock); + break; + case CELT_NEGATIVE: + pthread_mutex_lock(&entry->negative_cache_lock); + break; + case CELT_MULTIPART: + pthread_mutex_lock(&entry->mp_cache_lock); + break; + default: + /* should be unreachable */ + break; + } + TRACE_OUT(configuration_lock_entry); +} + +void +configuration_unlock_entry(struct configuration_entry *entry, + enum config_entry_lock_type lock_type) +{ + TRACE_IN(configuration_unlock_entry); + assert(entry != NULL); + + switch (lock_type) { + case CELT_POSITIVE: + pthread_mutex_unlock(&entry->positive_cache_lock); + break; + case CELT_NEGATIVE: + pthread_mutex_unlock(&entry->negative_cache_lock); + break; + case CELT_MULTIPART: + pthread_mutex_unlock(&entry->mp_cache_lock); + break; + default: + /* should be unreachable */ + break; + } + TRACE_OUT(configuration_unlock_entry); +} + +struct configuration * +init_configuration(void) +{ + struct configuration *retval; + + TRACE_IN(init_configuration); + retval = (struct configuration *)malloc(sizeof(struct configuration)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct configuration)); + + retval->entries_capacity = INITIAL_ENTRIES_CAPACITY; + retval->entries = (struct configuration_entry **)malloc( + sizeof(struct configuration_entry *) * + retval->entries_capacity); + assert(retval->entries != NULL); + memset(retval->entries, 0, sizeof(struct configuration_entry *) * + retval->entries_capacity); + + pthread_rwlock_init(&retval->rwlock, NULL); + + TRACE_OUT(init_configuration); + return (retval); +} + +void +fill_configuration_defaults(struct configuration *config) +{ + size_t len, i; + + TRACE_IN(fill_configuration_defaults); + assert(config != NULL); + + if (config->socket_path != NULL) + free(config->socket_path); + + len = strlen(DEFAULT_SOCKET_PATH); + config->socket_path = (char *)malloc(len + 1); + assert(config->socket_path != NULL); + memset(config->socket_path, 0, len + 1); + memcpy(config->socket_path, DEFAULT_SOCKET_PATH, len); + + len = strlen(DEFAULT_PIDFILE_PATH); + config->pidfile_path = (char *)malloc(len + 1); + assert(config->pidfile_path != NULL); + memset(config->pidfile_path, 0, len + 1); + memcpy(config->pidfile_path, DEFAULT_PIDFILE_PATH, len); + + config->socket_mode = S_IFSOCK | S_IRUSR | S_IWUSR | + S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + config->force_unlink = 1; + + config->query_timeout = DEFAULT_QUERY_TIMEOUT; + config->threads_num = DEFAULT_THREADS_NUM; + + for (i = 0; i < config->entries_size; ++i) + destroy_configuration_entry(config->entries[i]); + config->entries_size = 0; + + TRACE_OUT(fill_configuration_defaults); +} + +void +destroy_configuration(struct configuration *config) +{ + int i; + TRACE_IN(destroy_configuration); + assert(config != NULL); + free(config->pidfile_path); + free(config->socket_path); + + for (i = 0; i < config->entries_size; ++i) + destroy_configuration_entry(config->entries[i]); + free(config->entries); + + pthread_rwlock_destroy(&config->rwlock); + free(config); + TRACE_OUT(destroy_configuration); +} diff --git a/usr.sbin/nscd/config.h b/usr.sbin/nscd/config.h new file mode 100644 index 000000000000..b54dc9bfbacf --- /dev/null +++ b/usr.sbin/nscd/config.h @@ -0,0 +1,156 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_CONFIG_H__ +#define __CACHED_CONFIG_H__ + +#include +#include +#include +#include +#include +#include "cachelib.h" + +#define DEFAULT_QUERY_TIMEOUT 8 +#define DEFAULT_THREADS_NUM 8 + +#define DEFAULT_COMMON_ENTRY_TIMEOUT 10 +#define DEFAULT_MP_ENTRY_TIMEOUT 60 +#define DEFAULT_CACHE_HT_SIZE 257 + +#define INITIAL_ENTRIES_CAPACITY 8 +#define DEFAULT_SOCKET_PATH "/var/run/cached" +#define DEFAULT_PIDFILE_PATH "/var/run/cached.pid" + +#define DEFAULT_POSITIVE_ELEMENTS_SIZE (2048) +#define DEFAULT_POSITIVE_LIFETIME (3600) + +#define DEFAULT_NEGATIVE_ELEMENTS_SIZE (2048) +#define DEFAULT_NEGATIVE_LIFETIME (60) + +#define DEFAULT_MULTIPART_ELEMENTS_SIZE (1024 * 8) +#define DEFAULT_MULITPART_SESSIONS_SIZE (1024) +#define DEFAULT_MULITPART_LIFETIME (3600) + +extern const char *c_default_entries[6]; + +/* + * Configuration entry represents the details of each cache entry in the + * config file (i.e. passwd or group). Its purpose also is to acquire locks + * of three different types (for usual read/write caching, for multipart + * caching and for caching of the negative results) for that cache entry. + */ +struct configuration_entry { + struct common_cache_entry_params positive_cache_params; + struct common_cache_entry_params negative_cache_params; + struct mp_cache_entry_params mp_cache_params; + + /* + * configuration_entry holds pointers for all actual cache_entries, + * which are used for it. There is one for positive caching, one for + * for negative caching, and several (one per each euid/egid) for + * multipart caching. + */ + cache_entry positive_cache_entry; + cache_entry negative_cache_entry; + + cache_entry *mp_cache_entries; + size_t mp_cache_entries_size; + + struct timeval common_query_timeout; + struct timeval mp_query_timeout; + + char *name; + pthread_mutex_t positive_cache_lock; + pthread_mutex_t negative_cache_lock; + pthread_mutex_t mp_cache_lock; + + int perform_actual_lookups; + int enabled; +}; + +/* + * Contains global configuration options and array of all configuration entries + */ +struct configuration { + char *pidfile_path; + char *socket_path; + + struct configuration_entry **entries; + size_t entries_capacity; + size_t entries_size; + + pthread_rwlock_t rwlock; + + mode_t socket_mode; + int force_unlink; + int query_timeout; + + int threads_num; +}; + +enum config_entry_lock_type { + CELT_POSITIVE, + CELT_NEGATIVE, + CELT_MULTIPART +}; + +extern struct configuration *init_configuration(void); +extern void destroy_configuration(struct configuration *); +extern void fill_configuration_defaults(struct configuration *); + +extern int add_configuration_entry(struct configuration *, + struct configuration_entry *); +extern struct configuration_entry *create_def_configuration_entry( + const char *); +extern void destroy_configuration_entry(struct configuration_entry *); +extern size_t configuration_get_entries_size(struct configuration *); +extern struct configuration_entry *configuration_get_entry( + struct configuration *, size_t); +extern struct configuration_entry *configuration_find_entry( + struct configuration *, const char *); + +extern int configuration_entry_add_mp_cache_entry(struct configuration_entry *, + cache_entry); +extern cache_entry configuration_entry_find_mp_cache_entry( + struct configuration_entry *, + const char *); +extern int configuration_entry_find_mp_cache_entries( + struct configuration_entry *, const char *, cache_entry **, + cache_entry **); + +extern void configuration_lock_rdlock(struct configuration *config); +extern void configuration_lock_wrlock(struct configuration *config); +extern void configuration_unlock(struct configuration *config); + +extern void configuration_lock_entry(struct configuration_entry *, + enum config_entry_lock_type); +extern void configuration_unlock_entry(struct configuration_entry *, + enum config_entry_lock_type); + +#endif diff --git a/usr.sbin/nscd/debug.c b/usr.sbin/nscd/debug.c new file mode 100644 index 000000000000..420c517c1159 --- /dev/null +++ b/usr.sbin/nscd/debug.c @@ -0,0 +1,149 @@ +/*- + * Copyright (c) 2004 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "debug.h" + +static int trace_level = 0; +static int trace_level_bk = 0; + +void +__trace_in(const char *s, const char *f, int l) +{ + int i; + if (trace_level < TRACE_WANTED) + { + for (i = 0; i < trace_level; ++i) + printf("\t"); + + printf("=> %s\n", s); + } + + ++trace_level; +} + +void +__trace_point(const char *f, int l) +{ + int i; + + if (trace_level < TRACE_WANTED) + { + for (i = 0; i < trace_level - 1; ++i) + printf("\t"); + + printf("= %s: %d\n", f, l); + } +} + +void +__trace_msg(const char *msg, const char *f, int l) +{ + int i; + + if (trace_level < TRACE_WANTED) + { + for (i = 0; i < trace_level - 1; ++i) + printf("\t"); + + printf("= MSG %s, %s: %d\n", msg, f, l); + } +} + +void +__trace_ptr(const char *desc, const void *p, const char *f, int l) +{ + int i; + + if (trace_level < TRACE_WANTED) + { + for (i = 0; i < trace_level - 1; ++i) + printf("\t"); + + printf("= PTR %s: %p, %s: %d\n", desc, p, f, l); + } +} + +void +__trace_int(const char *desc, int i, const char *f, int l) +{ + int j; + + if (trace_level < TRACE_WANTED) + { + for (j = 0; j < trace_level - 1; ++j) + printf("\t"); + + printf("= INT %s: %i, %s: %d\n",desc, i, f, l); + } +} + +void +__trace_str(const char *desc, const char *s, const char *f, int l) +{ + int i; + + if (trace_level < TRACE_WANTED) + { + for (i = 0; i < trace_level - 1; ++i) + printf("\t"); + + printf("= STR %s: '%s', %s: %d\n", desc, s, f, l); + } +} + +void +__trace_out(const char *s, const char *f, int l) +{ + int i; + + --trace_level; + if (trace_level < TRACE_WANTED) + { + for (i = 0; i < trace_level; ++i) + printf("\t"); + + printf("<= %s\n", s); + } +} + +void +__trace_on() +{ + trace_level = trace_level_bk; + trace_level_bk = 0; +} + +void +__trace_off() +{ + trace_level_bk = trace_level; + trace_level = 1024; +} diff --git a/usr.sbin/nscd/debug.h b/usr.sbin/nscd/debug.h new file mode 100644 index 000000000000..320e10f30a1b --- /dev/null +++ b/usr.sbin/nscd/debug.h @@ -0,0 +1,67 @@ +/*- + * Copyright (c) 2004 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_DEBUG_H__ +#define __CACHED_DEBUG_H__ + +#define TRACE_WANTED 32 + +/* #ifndef NDEBUG */ +#if 0 +#define TRACE_IN(x) __trace_in(#x, __FILE__, __LINE__) +#define TRACE_POINT() __trace_point(__FILE__, __LINE__) +#define TRACE_MSG(x) __trace_msg(x, __FILE__, __LINE__) +#define TRACE_PTR(p) __trace_ptr(#p, p, __FILE__, __LINE__) +#define TRACE_INT(i) __trace_int(#i, i, __FILE__, __LINE__) +#define TRACE_STR(s) __trace_str(#s, s, __FILE__, __LINE__) +#define TRACE_OUT(x) __trace_out(#x, __FILE__, __LINE__) +#define TRACE_ON() __trace_on() +#define TRACE_OFF() __trace_off() +#else +#define TRACE_IN(x) +#define TRACE_POINT() +#define TRACE_MSG(x) +#define TRACE_PTR(p) +#define TRACE_INT(i) +#define TRACE_STR(s) +#define TRACE_OUT(x) +#define TRACE_ON() +#define TRACE_OFF() +#endif + +extern void __trace_in(const char *, const char *, int); +extern void __trace_point(const char *, int); +extern void __trace_msg(const char *, const char *, int); +extern void __trace_ptr(const char *, const void *, const char *, int); +extern void __trace_int(const char *, int, const char *, int); +extern void __trace_str(const char *, const char *, const char *, int); +extern void __trace_out(const char *, const char *, int); +extern void __trace_on(); +extern void __trace_off(); + +#endif diff --git a/usr.sbin/nscd/hashtable.h b/usr.sbin/nscd/hashtable.h new file mode 100644 index 000000000000..86dad9f92b34 --- /dev/null +++ b/usr.sbin/nscd/hashtable.h @@ -0,0 +1,218 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHELIB_HASHTABLE_H__ +#define __CACHELIB_HASHTABLE_H__ + +#include +#include + +#define HASHTABLE_INITIAL_ENTRIES_CAPACITY 8 +typedef int hashtable_index_t; + +/* + * This file contains queue.h-like macro definitions for hash tables. + * Hash table is organized as an array of the specified size of the user + * defined (with HASTABLE_ENTRY_HEAD) structures. Each hash table + * entry (user defined structure) stores its elements in the sorted array. + * You can place elements into the hash table, retrieve elements with + * specified key, traverse through all elements, and delete them. + * New elements are placed into the hash table by using the compare and + * hashing functions, provided by the user. + */ + +/* + * Defines the hash table entry structure, that uses specified type of + * elements. + */ +#define HASHTABLE_ENTRY_HEAD(name, type) struct name { \ + type *values; \ + size_t capacity; \ + size_t size; \ +} + +/* + * Defines the hash table structure, which uses the specified type of entries. + * The only restriction for entries is that is that they should have the field, + * defined with HASHTABLE_ENTRY_HEAD macro. + */ +#define HASHTABLE_HEAD(name, entry) struct name { \ + struct entry *entries; \ + size_t entries_size; \ +} + +#define HASHTABLE_ENTRIES_COUNT(table) ((table)->entries_size) + +/* + * Unlike most of queue.h data types, hash tables can not be initialized + * statically - so there is no HASHTABLE_HEAD_INITIALIZED macro. + */ +#define HASHTABLE_INIT(table, type, field, _entries_size) \ + do { \ + hashtable_index_t var; \ + (table)->entries = (void *)malloc( \ + sizeof(*(table)->entries) * (_entries_size)); \ + memset((table)->entries, 0, \ + sizeof(*(table)->entries) * (_entries_size)); \ + (table)->entries_size = (_entries_size); \ + for (var = 0; var < HASHTABLE_ENTRIES_COUNT(table); ++var) {\ + (table)->entries[var].field.capacity = \ + HASHTABLE_INITIAL_ENTRIES_CAPACITY; \ + (table)->entries[var].field.size = 0; \ + (table)->entries[var].field.values = (type *)malloc(\ + sizeof(type) * \ + HASHTABLE_INITIAL_ENTRIES_CAPACITY); \ + assert((table)->entries[var].field.values != NULL);\ + } \ + } while (0) + +/* + * All initialized hashtables should be destroyed with this macro. + */ +#define HASHTABLE_DESTROY(table, field) \ + do { \ + hashtable_index_t var; \ + for (var = 0; var < HASHTABLE_ENTRIES_COUNT(table); ++var) {\ + free((table)->entries[var].field.values); \ + } \ + } while (0) + +#define HASHTABLE_GET_ENTRY(table, hash) (&((table)->entries[hash])) + +/* + * Traverses through all hash table entries + */ +#define HASHTABLE_FOREACH(table, var) \ + for ((var) = &((table)->entries[0]); \ + (var) < &((table)->entries[HASHTABLE_ENTRIES_COUNT(table)]);\ + ++(var)) + +/* + * Traverses through all elements of the specified hash table entry + */ +#define HASHTABLE_ENTRY_FOREACH(entry, field, var) \ + for ((var) = &((entry)->field.values[0]); \ + (var) < &((entry)->field.values[(entry)->field.size]); \ + ++(var)) + +#define HASHTABLE_ENTRY_CLEAR(entry, field) \ + ((entry)->field.size = 0) + +#define HASHTABLE_ENTRY_SIZE(entry, field) \ + ((entry)->field.size) + +#define HASHTABLE_ENTRY_CAPACITY(entry, field) \ + ((entry)->field.capacity) + +#define HASHTABLE_ENTRY_CAPACITY_INCREASE(entry, field, type) \ + (entry)->field.capacity *= 2; \ + (entry)->field.values = (type *)realloc((entry)->field.values, \ + (entry)->field.capacity * sizeof(type)); + +#define HASHTABLE_ENTRY_CAPACITY_DECREASE(entry, field, type) \ + (entry)->field.capacity /= 2; \ + (entry)->field.values = (type *)realloc((entry)->field.values, \ + (entry)->field.capacity * sizeof(type)); + +/* + * Generates prototypes for the hash table functions + */ +#define HASHTABLE_PROTOTYPE(name, entry_, type) \ +hashtable_index_t name##_CALCULATE_HASH(struct name *, type *); \ +void name##_ENTRY_STORE(struct entry_*, type *); \ +type *name##_ENTRY_FIND(struct entry_*, type *); \ +type *name##_ENTRY_FIND_SPECIAL(struct entry_ *, type *, \ + int (*) (const void *, const void *)); \ +void name##_ENTRY_REMOVE(struct entry_*, type *); + +/* + * Generates implementations of the hash table functions + */ +#define HASHTABLE_GENERATE(name, entry_, type, field, HASH, CMP) \ +hashtable_index_t name##_CALCULATE_HASH(struct name *table, type *data) \ +{ \ + \ + return HASH(data, table->entries_size); \ +} \ + \ +void name##_ENTRY_STORE(struct entry_ *the_entry, type *data) \ +{ \ + \ + if (the_entry->field.size == the_entry->field.capacity) \ + HASHTABLE_ENTRY_CAPACITY_INCREASE(the_entry, field, type);\ + \ + memcpy(&(the_entry->field.values[the_entry->field.size++]), \ + data, \ + sizeof(type)); \ + qsort(the_entry->field.values, the_entry->field.size, \ + sizeof(type), CMP); \ +} \ + \ +type *name##_ENTRY_FIND(struct entry_ *the_entry, type *key) \ +{ \ + \ + return ((type *)bsearch(key, the_entry->field.values, \ + the_entry->field.size, sizeof(type), CMP)); \ +} \ + \ +type *name##_ENTRY_FIND_SPECIAL(struct entry_ *the_entry, type *key, \ + int (*compar) (const void *, const void *)) \ +{ \ + return ((type *)bsearch(key, the_entry->field.values, \ + the_entry->field.size, sizeof(type), compar)); \ +} \ + \ +void name##_ENTRY_REMOVE(struct entry_ *the_entry, type *del_elm) \ +{ \ + \ + memmove(del_elm, del_elm + 1, \ + (&the_entry->field.values[--the_entry->field.size] - del_elm) *\ + sizeof(type)); \ +} + +/* + * Macro definitions below wrap the functions, generaed with + * HASHTABLE_GENERATE macro. You should use them and avoid using generated + * functions directly. + */ +#define HASHTABLE_CALCULATE_HASH(name, table, data) \ + (name##_CALCULATE_HASH((table), data)) + +#define HASHTABLE_ENTRY_STORE(name, entry, data) \ + name##_ENTRY_STORE((entry), data) + +#define HASHTABLE_ENTRY_FIND(name, entry, key) \ + (name##_ENTRY_FIND((entry), (key))) + +#define HASHTABLE_ENTRY_FIND_SPECIAL(name, entry, key, cmp) \ + (name##_ENTRY_FIND_SPECIAL((entry), (key), (cmp))) + +#define HASHTABLE_ENTRY_REMOVE(name, entry, del_elm) \ + name##_ENTRY_REMOVE((entry), (del_elm)) + +#endif diff --git a/usr.sbin/nscd/log.c b/usr.sbin/nscd/log.c new file mode 100644 index 000000000000..053930a233fe --- /dev/null +++ b/usr.sbin/nscd/log.c @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include "log.h" + +void +__log_msg(int level, const char *sender, const char *message, ...) +{ + va_list ap; + char *fmessage; + + fmessage = NULL; + va_start(ap, message); + vasprintf(&fmessage, message, ap); + va_end(ap); + assert(fmessage != NULL); + + printf("M%d from %s: %s\n", level, sender, fmessage); +#ifndef NO_SYSLOG + if (level == 0) + syslog(LOG_INFO, "cached message (from %s): %s", sender, + fmessage); +#endif + free(fmessage); +} + +void +__log_err(int level, const char *sender, const char *error, ...) +{ + va_list ap; + char *ferror; + + ferror = NULL; + va_start(ap, error); + vasprintf(&ferror, error, ap); + va_end(ap); + assert(ferror != NULL); + + printf("E%d from %s: %s\n", level, sender, ferror); + +#ifndef NO_SYSLOG + if (level == 0) + syslog(LOG_ERR, "cached error (from %s): %s", sender, ferror); +#endif + free(ferror); +} diff --git a/usr.sbin/nscd/log.h b/usr.sbin/nscd/log.h new file mode 100644 index 000000000000..8d665a492752 --- /dev/null +++ b/usr.sbin/nscd/log.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_LOG_H__ +#define __CACHED_LOG_H__ + +#define LOG_MSG_1(sender, msg, ...) __log_msg(1, sender, msg, ##__VA_ARGS__) +#define LOG_MSG_2(sender, msg, ...) __log_msg(2, sender, msg, ##__VA_ARGS__) +#define LOG_MSG_3(sender, msg, ...) __log_msg(3, sedner, msg, ##__VA_ARGS__) + +#define LOG_ERR_1(sender, err, ...) __log_err(1, sender, err, ##__VA_ARGS__) +#define LOG_ERR_2(sender, err, ...) __log_err(2, sender, err, ##__VA_ARGS__) +#define LOG_ERR_3(sender, err, ...) __log_err(3, sender, err, ##__VA_ARGS__) + +extern void __log_msg(int, const char *, const char *, ...); +extern void __log_err(int, const char *, const char *, ...); + +#endif diff --git a/usr.sbin/nscd/mp_rs_query. b/usr.sbin/nscd/mp_rs_query. new file mode 100644 index 000000000000..f468afae25ac --- /dev/null +++ b/usr.sbin/nscd/mp_rs_query. @@ -0,0 +1,34 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_MP_RS_QUERY_H__ +#define __CACHED_MP_RS_QUERY_H__ + +extern int on_mp_read_session_request_read1(struct query_state *); + +#endif diff --git a/usr.sbin/nscd/mp_rs_query.c b/usr.sbin/nscd/mp_rs_query.c new file mode 100644 index 000000000000..ae0f3bbbe98e --- /dev/null +++ b/usr.sbin/nscd/mp_rs_query.c @@ -0,0 +1,537 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cachelib.h" +#include "config.h" +#include "debug.h" +#include "log.h" +#include "query.h" +#include "mp_rs_query.h" +#include "mp_ws_query.h" +#include "singletons.h" + +static int on_mp_read_session_close_notification(struct query_state *); +static void on_mp_read_session_destroy(struct query_state *); +static int on_mp_read_session_mapper(struct query_state *); +/* int on_mp_read_session_request_read1(struct query_state *); */ +static int on_mp_read_session_request_read2(struct query_state *); +static int on_mp_read_session_request_process(struct query_state *); +static int on_mp_read_session_response_write1(struct query_state *); +static int on_mp_read_session_read_request_process(struct query_state *); +static int on_mp_read_session_read_response_write1(struct query_state *); +static int on_mp_read_session_read_response_write2(struct query_state *); + +/* + * This function is used as the query_state's destroy_func to make the + * proper cleanup in case of errors. + */ +static void +on_mp_read_session_destroy(struct query_state *qstate) +{ + TRACE_IN(on_mp_read_session_destroy); + finalize_comm_element(&qstate->request); + finalize_comm_element(&qstate->response); + + if (qstate->mdata != NULL) { + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + close_cache_mp_read_session( + (cache_mp_read_session)qstate->mdata); + configuration_unlock_entry(qstate->config_entry, + CELT_MULTIPART); + } + TRACE_OUT(on_mp_read_session_destroy); +} + +/* + * The functions below are used to process multipart read session initiation + * requests. + * - on_mp_read_session_request_read1 and on_mp_read_session_request_read2 read + * the request itself + * - on_mp_read_session_request_process processes it + * - on_mp_read_session_response_write1 sends the response + */ +int +on_mp_read_session_request_read1(struct query_state *qstate) +{ + struct cache_mp_read_session_request *c_mp_rs_request; + ssize_t result; + + TRACE_IN(on_mp_read_session_request_read1); + if (qstate->kevent_watermark == 0) + qstate->kevent_watermark = sizeof(size_t); + else { + init_comm_element(&qstate->request, + CET_MP_READ_SESSION_REQUEST); + c_mp_rs_request = get_cache_mp_read_session_request( + &qstate->request); + + result = qstate->read_func(qstate, + &c_mp_rs_request->entry_length, sizeof(size_t)); + + if (result != sizeof(size_t)) { + TRACE_OUT(on_mp_read_session_request_read1); + return (-1); + } + + if (BUFSIZE_INVALID(c_mp_rs_request->entry_length)) { + TRACE_OUT(on_mp_read_session_request_read1); + return (-1); + } + + c_mp_rs_request->entry = (char *)malloc( + c_mp_rs_request->entry_length + 1); + assert(c_mp_rs_request->entry != NULL); + memset(c_mp_rs_request->entry, 0, + c_mp_rs_request->entry_length + 1); + + qstate->kevent_watermark = c_mp_rs_request->entry_length; + qstate->process_func = on_mp_read_session_request_read2; + } + TRACE_OUT(on_mp_read_session_request_read1); + return (0); +} + +static int +on_mp_read_session_request_read2(struct query_state *qstate) +{ + struct cache_mp_read_session_request *c_mp_rs_request; + ssize_t result; + + TRACE_IN(on_mp_read_session_request_read2); + c_mp_rs_request = get_cache_mp_read_session_request(&qstate->request); + + result = qstate->read_func(qstate, c_mp_rs_request->entry, + c_mp_rs_request->entry_length); + + if (result != qstate->kevent_watermark) { + LOG_ERR_3("on_mp_read_session_request_read2", + "read failed"); + TRACE_OUT(on_mp_read_session_request_read2); + return (-1); + } + + qstate->kevent_watermark = 0; + qstate->process_func = on_mp_read_session_request_process; + TRACE_OUT(on_mp_read_session_request_read2); + return (0); +} + +static int +on_mp_read_session_request_process(struct query_state *qstate) +{ + struct cache_mp_read_session_request *c_mp_rs_request; + struct cache_mp_read_session_response *c_mp_rs_response; + cache_mp_read_session rs; + cache_entry c_entry; + char *dec_cache_entry_name; + + char *buffer; + size_t buffer_size; + cache_mp_write_session ws; + struct agent *lookup_agent; + struct multipart_agent *mp_agent; + void *mdata; + int res; + + TRACE_IN(on_mp_read_session_request_process); + init_comm_element(&qstate->response, CET_MP_READ_SESSION_RESPONSE); + c_mp_rs_response = get_cache_mp_read_session_response( + &qstate->response); + c_mp_rs_request = get_cache_mp_read_session_request(&qstate->request); + + qstate->config_entry = configuration_find_entry( + s_configuration, c_mp_rs_request->entry); + if (qstate->config_entry == NULL) { + c_mp_rs_response->error_code = ENOENT; + + LOG_ERR_2("read_session_request", + "can't find configuration entry '%s'." + " aborting request", c_mp_rs_request->entry); + goto fin; + } + + if (qstate->config_entry->enabled == 0) { + c_mp_rs_response->error_code = EACCES; + + LOG_ERR_2("read_session_request", + "configuration entry '%s' is disabled", + c_mp_rs_request->entry); + goto fin; + } + + if (qstate->config_entry->perform_actual_lookups != 0) + dec_cache_entry_name = strdup( + qstate->config_entry->mp_cache_params.entry_name); + else { +#ifdef NS_CACHED_EID_CHECKING + if (check_query_eids(qstate) != 0) { + c_mp_rs_response->error_code = EPERM; + goto fin; + } +#endif + + asprintf(&dec_cache_entry_name, "%s%s", qstate->eid_str, + qstate->config_entry->mp_cache_params.entry_name); + } + + assert(dec_cache_entry_name != NULL); + + configuration_lock_rdlock(s_configuration); + c_entry = find_cache_entry(s_cache, dec_cache_entry_name); + configuration_unlock(s_configuration); + + if ((c_entry == INVALID_CACHE) && + (qstate->config_entry->perform_actual_lookups != 0)) + c_entry = register_new_mp_cache_entry(qstate, + dec_cache_entry_name); + + free(dec_cache_entry_name); + + if (c_entry != INVALID_CACHE_ENTRY) { + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + rs = open_cache_mp_read_session(c_entry); + configuration_unlock_entry(qstate->config_entry, + CELT_MULTIPART); + + if ((rs == INVALID_CACHE_MP_READ_SESSION) && + (qstate->config_entry->perform_actual_lookups != 0)) { + lookup_agent = find_agent(s_agent_table, + c_mp_rs_request->entry, MULTIPART_AGENT); + + if ((lookup_agent != NULL) && + (lookup_agent->type == MULTIPART_AGENT)) { + mp_agent = (struct multipart_agent *) + lookup_agent; + mdata = mp_agent->mp_init_func(); + + /* + * Multipart agents read the whole snapshot + * of the data at one time. + */ + configuration_lock_entry(qstate->config_entry, + CELT_MULTIPART); + ws = open_cache_mp_write_session(c_entry); + configuration_unlock_entry(qstate->config_entry, + CELT_MULTIPART); + if (ws != NULL) { + do { + buffer = NULL; + res = mp_agent->mp_lookup_func(&buffer, + &buffer_size, + mdata); + + if ((res & NS_TERMINATE) && + (buffer != NULL)) { + configuration_lock_entry( + qstate->config_entry, + CELT_MULTIPART); + if (cache_mp_write(ws, buffer, + buffer_size) != 0) { + abandon_cache_mp_write_session(ws); + ws = NULL; + } + configuration_unlock_entry( + qstate->config_entry, + CELT_MULTIPART); + + free(buffer); + buffer = NULL; + } else { + configuration_lock_entry( + qstate->config_entry, + CELT_MULTIPART); + close_cache_mp_write_session(ws); + configuration_unlock_entry( + qstate->config_entry, + CELT_MULTIPART); + + free(buffer); + buffer = NULL; + } + } while ((res & NS_TERMINATE) && + (ws != NULL)); + } + + configuration_lock_entry(qstate->config_entry, + CELT_MULTIPART); + rs = open_cache_mp_read_session(c_entry); + configuration_unlock_entry(qstate->config_entry, + CELT_MULTIPART); + } + } + + if (rs == INVALID_CACHE_MP_READ_SESSION) + c_mp_rs_response->error_code = -1; + else { + qstate->mdata = rs; + qstate->destroy_func = on_mp_read_session_destroy; + + configuration_lock_entry(qstate->config_entry, + CELT_MULTIPART); + if ((qstate->config_entry->mp_query_timeout.tv_sec != 0) || + (qstate->config_entry->mp_query_timeout.tv_usec != 0)) + memcpy(&qstate->timeout, + &qstate->config_entry->mp_query_timeout, + sizeof(struct timeval)); + configuration_unlock_entry(qstate->config_entry, + CELT_MULTIPART); + } + } else + c_mp_rs_response->error_code = -1; + +fin: + qstate->process_func = on_mp_read_session_response_write1; + qstate->kevent_watermark = sizeof(int); + qstate->kevent_filter = EVFILT_WRITE; + + TRACE_OUT(on_mp_read_session_request_process); + return (0); +} + +static int +on_mp_read_session_response_write1(struct query_state *qstate) +{ + struct cache_mp_read_session_response *c_mp_rs_response; + ssize_t result; + + TRACE_IN(on_mp_read_session_response_write1); + c_mp_rs_response = get_cache_mp_read_session_response( + &qstate->response); + result = qstate->write_func(qstate, &c_mp_rs_response->error_code, + sizeof(int)); + + if (result != sizeof(int)) { + LOG_ERR_3("on_mp_read_session_response_write1", + "write failed"); + TRACE_OUT(on_mp_read_session_response_write1); + return (-1); + } + + if (c_mp_rs_response->error_code == 0) { + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_mp_read_session_mapper; + qstate->kevent_filter = EVFILT_READ; + } else { + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + } + TRACE_OUT(on_mp_read_session_response_write1); + return (0); +} + +/* + * Mapper function is used to avoid multiple connections for each session + * write or read requests. After processing the request, it does not close + * the connection, but waits for the next request. + */ +static int +on_mp_read_session_mapper(struct query_state *qstate) +{ + ssize_t result; + int elem_type; + + TRACE_IN(on_mp_read_session_mapper); + if (qstate->kevent_watermark == 0) { + qstate->kevent_watermark = sizeof(int); + } else { + result = qstate->read_func(qstate, &elem_type, sizeof(int)); + if (result != sizeof(int)) { + LOG_ERR_3("on_mp_read_session_mapper", + "read failed"); + TRACE_OUT(on_mp_read_session_mapper); + return (-1); + } + + switch (elem_type) { + case CET_MP_READ_SESSION_READ_REQUEST: + qstate->kevent_watermark = 0; + qstate->process_func = + on_mp_read_session_read_request_process; + break; + case CET_MP_READ_SESSION_CLOSE_NOTIFICATION: + qstate->kevent_watermark = 0; + qstate->process_func = + on_mp_read_session_close_notification; + break; + default: + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + LOG_ERR_3("on_mp_read_session_mapper", + "unknown element type"); + TRACE_OUT(on_mp_read_session_mapper); + return (-1); + } + } + TRACE_OUT(on_mp_read_session_mapper); + return (0); +} + +/* + * The functions below are used to process multipart read sessions read + * requests. User doesn't have to pass any kind of data, besides the + * request identificator itself. So we don't need any XXX_read functions and + * start with the XXX_process function. + * - on_mp_read_session_read_request_process processes it + * - on_mp_read_session_read_response_write1 and + * on_mp_read_session_read_response_write2 sends the response + */ +static int +on_mp_read_session_read_request_process(struct query_state *qstate) +{ + struct cache_mp_read_session_read_response *read_response; + + TRACE_IN(on_mp_read_session_response_process); + init_comm_element(&qstate->response, CET_MP_READ_SESSION_READ_RESPONSE); + read_response = get_cache_mp_read_session_read_response( + &qstate->response); + + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + read_response->error_code = cache_mp_read( + (cache_mp_read_session)qstate->mdata, NULL, + &read_response->data_size); + + if (read_response->error_code == 0) { + read_response->data = (char *)malloc(read_response->data_size); + assert(read_response != NULL); + read_response->error_code = cache_mp_read( + (cache_mp_read_session)qstate->mdata, + read_response->data, + &read_response->data_size); + } + configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART); + + if (read_response->error_code == 0) + qstate->kevent_watermark = sizeof(size_t) + sizeof(int); + else + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_mp_read_session_read_response_write1; + qstate->kevent_filter = EVFILT_WRITE; + + TRACE_OUT(on_mp_read_session_response_process); + return (0); +} + +static int +on_mp_read_session_read_response_write1(struct query_state *qstate) +{ + struct cache_mp_read_session_read_response *read_response; + ssize_t result; + + TRACE_IN(on_mp_read_session_read_response_write1); + read_response = get_cache_mp_read_session_read_response( + &qstate->response); + + result = qstate->write_func(qstate, &read_response->error_code, + sizeof(int)); + if (read_response->error_code == 0) { + result += qstate->write_func(qstate, &read_response->data_size, + sizeof(size_t)); + if (result != qstate->kevent_watermark) { + TRACE_OUT(on_mp_read_session_read_response_write1); + LOG_ERR_3("on_mp_read_session_read_response_write1", + "write failed"); + return (-1); + } + + qstate->kevent_watermark = read_response->data_size; + qstate->process_func = on_mp_read_session_read_response_write2; + } else { + if (result != qstate->kevent_watermark) { + LOG_ERR_3("on_mp_read_session_read_response_write1", + "write failed"); + TRACE_OUT(on_mp_read_session_read_response_write1); + return (-1); + } + + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + } + + TRACE_OUT(on_mp_read_session_read_response_write1); + return (0); +} + +static int +on_mp_read_session_read_response_write2(struct query_state *qstate) +{ + struct cache_mp_read_session_read_response *read_response; + ssize_t result; + + TRACE_IN(on_mp_read_session_read_response_write2); + read_response = get_cache_mp_read_session_read_response( + &qstate->response); + result = qstate->write_func(qstate, read_response->data, + read_response->data_size); + if (result != qstate->kevent_watermark) { + LOG_ERR_3("on_mp_read_session_read_response_write2", + "write failed"); + TRACE_OUT(on_mp_read_session_read_response_write2); + return (-1); + } + + finalize_comm_element(&qstate->request); + finalize_comm_element(&qstate->response); + + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_mp_read_session_mapper; + qstate->kevent_filter = EVFILT_READ; + + TRACE_OUT(on_mp_read_session_read_response_write2); + return (0); +} + +/* + * Handles session close notification by calling close_cache_mp_read_session + * function. + */ +static int +on_mp_read_session_close_notification(struct query_state *qstate) +{ + + TRACE_IN(on_mp_read_session_close_notification); + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + close_cache_mp_read_session((cache_mp_read_session)qstate->mdata); + configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART); + qstate->mdata = NULL; + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + TRACE_OUT(on_mp_read_session_close_notification); + return (0); +} diff --git a/usr.sbin/nscd/mp_rs_query.h b/usr.sbin/nscd/mp_rs_query.h new file mode 100644 index 000000000000..f468afae25ac --- /dev/null +++ b/usr.sbin/nscd/mp_rs_query.h @@ -0,0 +1,34 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_MP_RS_QUERY_H__ +#define __CACHED_MP_RS_QUERY_H__ + +extern int on_mp_read_session_request_read1(struct query_state *); + +#endif diff --git a/usr.sbin/nscd/mp_ws_query.c b/usr.sbin/nscd/mp_ws_query.c new file mode 100644 index 000000000000..d7aeb495d6fb --- /dev/null +++ b/usr.sbin/nscd/mp_ws_query.c @@ -0,0 +1,548 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cachelib.h" +#include "config.h" +#include "debug.h" +#include "log.h" +#include "query.h" +#include "mp_ws_query.h" +#include "singletons.h" + +static int on_mp_write_session_abandon_notification(struct query_state *); +static int on_mp_write_session_close_notification(struct query_state *); +static void on_mp_write_session_destroy(struct query_state *); +static int on_mp_write_session_mapper(struct query_state *); +/* int on_mp_write_session_request_read1(struct query_state *); */ +static int on_mp_write_session_request_read2(struct query_state *); +static int on_mp_write_session_request_process(struct query_state *); +static int on_mp_write_session_response_write1(struct query_state *); +static int on_mp_write_session_write_request_read1(struct query_state *); +static int on_mp_write_session_write_request_read2(struct query_state *); +static int on_mp_write_session_write_request_process(struct query_state *); +static int on_mp_write_session_write_response_write1(struct query_state *); + +/* + * This function is used as the query_state's destroy_func to make the + * proper cleanup in case of errors. + */ +static void +on_mp_write_session_destroy(struct query_state *qstate) +{ + + TRACE_IN(on_mp_write_session_destroy); + finalize_comm_element(&qstate->request); + finalize_comm_element(&qstate->response); + + if (qstate->mdata != NULL) { + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + abandon_cache_mp_write_session( + (cache_mp_write_session)qstate->mdata); + configuration_unlock_entry(qstate->config_entry, + CELT_MULTIPART); + } + TRACE_OUT(on_mp_write_session_destroy); +} + +/* + * The functions below are used to process multipart write session initiation + * requests. + * - on_mp_write_session_request_read1 and on_mp_write_session_request_read2 + * read the request itself + * - on_mp_write_session_request_process processes it + * - on_mp_write_session_response_write1 sends the response + */ +int +on_mp_write_session_request_read1(struct query_state *qstate) +{ + struct cache_mp_write_session_request *c_mp_ws_request; + ssize_t result; + + TRACE_IN(on_mp_write_session_request_read1); + if (qstate->kevent_watermark == 0) + qstate->kevent_watermark = sizeof(size_t); + else { + init_comm_element(&qstate->request, + CET_MP_WRITE_SESSION_REQUEST); + c_mp_ws_request = get_cache_mp_write_session_request( + &qstate->request); + + result = qstate->read_func(qstate, + &c_mp_ws_request->entry_length, sizeof(size_t)); + + if (result != sizeof(size_t)) { + LOG_ERR_3("on_mp_write_session_request_read1", + "read failed"); + TRACE_OUT(on_mp_write_session_request_read1); + return (-1); + } + + if (BUFSIZE_INVALID(c_mp_ws_request->entry_length)) { + LOG_ERR_3("on_mp_write_session_request_read1", + "invalid entry_length value"); + TRACE_OUT(on_mp_write_session_request_read1); + return (-1); + } + + c_mp_ws_request->entry = (char *)malloc( + c_mp_ws_request->entry_length + 1); + assert(c_mp_ws_request->entry != NULL); + memset(c_mp_ws_request->entry, 0, + c_mp_ws_request->entry_length + 1); + + qstate->kevent_watermark = c_mp_ws_request->entry_length; + qstate->process_func = on_mp_write_session_request_read2; + } + TRACE_OUT(on_mp_write_session_request_read1); + return (0); +} + +static int +on_mp_write_session_request_read2(struct query_state *qstate) +{ + struct cache_mp_write_session_request *c_mp_ws_request; + ssize_t result; + + TRACE_IN(on_mp_write_session_request_read2); + c_mp_ws_request = get_cache_mp_write_session_request(&qstate->request); + + result = qstate->read_func(qstate, c_mp_ws_request->entry, + c_mp_ws_request->entry_length); + + if (result != qstate->kevent_watermark) { + LOG_ERR_3("on_mp_write_session_request_read2", + "read failed"); + TRACE_OUT(on_mp_write_session_request_read2); + return (-1); + } + + qstate->kevent_watermark = 0; + qstate->process_func = on_mp_write_session_request_process; + + TRACE_OUT(on_mp_write_session_request_read2); + return (0); +} + +static int +on_mp_write_session_request_process(struct query_state *qstate) +{ + struct cache_mp_write_session_request *c_mp_ws_request; + struct cache_mp_write_session_response *c_mp_ws_response; + cache_mp_write_session ws; + cache_entry c_entry; + char *dec_cache_entry_name; + + TRACE_IN(on_mp_write_session_request_process); + init_comm_element(&qstate->response, CET_MP_WRITE_SESSION_RESPONSE); + c_mp_ws_response = get_cache_mp_write_session_response( + &qstate->response); + c_mp_ws_request = get_cache_mp_write_session_request(&qstate->request); + + qstate->config_entry = configuration_find_entry( + s_configuration, c_mp_ws_request->entry); + if (qstate->config_entry == NULL) { + c_mp_ws_response->error_code = ENOENT; + + LOG_ERR_2("write_session_request", + "can't find configuration entry '%s'. " + "aborting request", c_mp_ws_request->entry); + goto fin; + } + + if (qstate->config_entry->enabled == 0) { + c_mp_ws_response->error_code = EACCES; + + LOG_ERR_2("write_session_request", + "configuration entry '%s' is disabled", + c_mp_ws_request->entry); + goto fin; + } + + if (qstate->config_entry->perform_actual_lookups != 0) { + c_mp_ws_response->error_code = EOPNOTSUPP; + + LOG_ERR_2("write_session_request", + "entry '%s' performs lookups by itself: " + "can't write to it", c_mp_ws_request->entry); + goto fin; + } else { +#ifdef NS_CACHED_EID_CHECKING + if (check_query_eids(qstate) != 0) { + c_mp_ws_response->error_code = EPERM; + goto fin; + } +#endif + } + + /* + * All multipart entries are separated by their name decorations. + * For one configuration entry there will be a lot of multipart + * cache entries - each with its own decorated name. + */ + asprintf(&dec_cache_entry_name, "%s%s", qstate->eid_str, + qstate->config_entry->mp_cache_params.entry_name); + assert(dec_cache_entry_name != NULL); + + configuration_lock_rdlock(s_configuration); + c_entry = find_cache_entry(s_cache, + dec_cache_entry_name); + configuration_unlock(s_configuration); + + if (c_entry == INVALID_CACHE_ENTRY) + c_entry = register_new_mp_cache_entry(qstate, + dec_cache_entry_name); + + free(dec_cache_entry_name); + + assert(c_entry != NULL); + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + ws = open_cache_mp_write_session(c_entry); + if (ws == INVALID_CACHE_MP_WRITE_SESSION) + c_mp_ws_response->error_code = -1; + else { + qstate->mdata = ws; + qstate->destroy_func = on_mp_write_session_destroy; + + if ((qstate->config_entry->mp_query_timeout.tv_sec != 0) || + (qstate->config_entry->mp_query_timeout.tv_usec != 0)) + memcpy(&qstate->timeout, + &qstate->config_entry->mp_query_timeout, + sizeof(struct timeval)); + } + configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART); + +fin: + qstate->process_func = on_mp_write_session_response_write1; + qstate->kevent_watermark = sizeof(int); + qstate->kevent_filter = EVFILT_WRITE; + + TRACE_OUT(on_mp_write_session_request_process); + return (0); +} + +static int +on_mp_write_session_response_write1(struct query_state *qstate) +{ + struct cache_mp_write_session_response *c_mp_ws_response; + ssize_t result; + + TRACE_IN(on_mp_write_session_response_write1); + c_mp_ws_response = get_cache_mp_write_session_response( + &qstate->response); + result = qstate->write_func(qstate, &c_mp_ws_response->error_code, + sizeof(int)); + if (result != sizeof(int)) { + LOG_ERR_3("on_mp_write_session_response_write1", + "write failed"); + TRACE_OUT(on_mp_write_session_response_write1); + return (-1); + } + + if (c_mp_ws_response->error_code == 0) { + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_mp_write_session_mapper; + qstate->kevent_filter = EVFILT_READ; + } else { + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + } + TRACE_OUT(on_mp_write_session_response_write1); + return (0); +} + +/* + * Mapper function is used to avoid multiple connections for each session + * write or read requests. After processing the request, it does not close + * the connection, but waits for the next request. + */ +static int +on_mp_write_session_mapper(struct query_state *qstate) +{ + ssize_t result; + int elem_type; + + TRACE_IN(on_mp_write_session_mapper); + if (qstate->kevent_watermark == 0) { + qstate->kevent_watermark = sizeof(int); + } else { + result = qstate->read_func(qstate, &elem_type, sizeof(int)); + if (result != sizeof(int)) { + LOG_ERR_3("on_mp_write_session_mapper", + "read failed"); + TRACE_OUT(on_mp_write_session_mapper); + return (-1); + } + + switch (elem_type) { + case CET_MP_WRITE_SESSION_WRITE_REQUEST: + qstate->kevent_watermark = sizeof(size_t); + qstate->process_func = + on_mp_write_session_write_request_read1; + break; + case CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION: + qstate->kevent_watermark = 0; + qstate->process_func = + on_mp_write_session_abandon_notification; + break; + case CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION: + qstate->kevent_watermark = 0; + qstate->process_func = + on_mp_write_session_close_notification; + break; + default: + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + LOG_ERR_2("on_mp_write_session_mapper", + "unknown element type"); + TRACE_OUT(on_mp_write_session_mapper); + return (-1); + } + } + TRACE_OUT(on_mp_write_session_mapper); + return (0); +} + +/* + * The functions below are used to process multipart write sessions write + * requests. + * - on_mp_write_session_write_request_read1 and + * on_mp_write_session_write_request_read2 read the request itself + * - on_mp_write_session_write_request_process processes it + * - on_mp_write_session_write_response_write1 sends the response + */ +static int +on_mp_write_session_write_request_read1(struct query_state *qstate) +{ + struct cache_mp_write_session_write_request *write_request; + ssize_t result; + + TRACE_IN(on_mp_write_session_write_request_read1); + init_comm_element(&qstate->request, + CET_MP_WRITE_SESSION_WRITE_REQUEST); + write_request = get_cache_mp_write_session_write_request( + &qstate->request); + + result = qstate->read_func(qstate, &write_request->data_size, + sizeof(size_t)); + + if (result != sizeof(size_t)) { + LOG_ERR_3("on_mp_write_session_write_request_read1", + "read failed"); + TRACE_OUT(on_mp_write_session_write_request_read1); + return (-1); + } + + if (BUFSIZE_INVALID(write_request->data_size)) { + LOG_ERR_3("on_mp_write_session_write_request_read1", + "invalid data_size value"); + TRACE_OUT(on_mp_write_session_write_request_read1); + return (-1); + } + + write_request->data = (char *)malloc(write_request->data_size); + assert(write_request->data != NULL); + memset(write_request->data, 0, write_request->data_size); + + qstate->kevent_watermark = write_request->data_size; + qstate->process_func = on_mp_write_session_write_request_read2; + TRACE_OUT(on_mp_write_session_write_request_read1); + return (0); +} + +static int +on_mp_write_session_write_request_read2(struct query_state *qstate) +{ + struct cache_mp_write_session_write_request *write_request; + ssize_t result; + + TRACE_IN(on_mp_write_session_write_request_read2); + write_request = get_cache_mp_write_session_write_request( + &qstate->request); + + result = qstate->read_func(qstate, write_request->data, + write_request->data_size); + + if (result != qstate->kevent_watermark) { + LOG_ERR_3("on_mp_write_session_write_request_read2", + "read failed"); + TRACE_OUT(on_mp_write_session_write_request_read2); + return (-1); + } + + qstate->kevent_watermark = 0; + qstate->process_func = on_mp_write_session_write_request_process; + TRACE_OUT(on_mp_write_session_write_request_read2); + return (0); +} + +static int +on_mp_write_session_write_request_process(struct query_state *qstate) +{ + struct cache_mp_write_session_write_request *write_request; + struct cache_mp_write_session_write_response *write_response; + + TRACE_IN(on_mp_write_session_write_request_process); + init_comm_element(&qstate->response, + CET_MP_WRITE_SESSION_WRITE_RESPONSE); + write_response = get_cache_mp_write_session_write_response( + &qstate->response); + write_request = get_cache_mp_write_session_write_request( + &qstate->request); + + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + write_response->error_code = cache_mp_write( + (cache_mp_write_session)qstate->mdata, + write_request->data, + write_request->data_size); + configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART); + + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_mp_write_session_write_response_write1; + qstate->kevent_filter = EVFILT_WRITE; + + TRACE_OUT(on_mp_write_session_write_request_process); + return (0); +} + +static int +on_mp_write_session_write_response_write1(struct query_state *qstate) +{ + struct cache_mp_write_session_write_response *write_response; + ssize_t result; + + TRACE_IN(on_mp_write_session_write_response_write1); + write_response = get_cache_mp_write_session_write_response( + &qstate->response); + result = qstate->write_func(qstate, &write_response->error_code, + sizeof(int)); + if (result != sizeof(int)) { + LOG_ERR_3("on_mp_write_session_write_response_write1", + "write failed"); + TRACE_OUT(on_mp_write_session_write_response_write1); + return (-1); + } + + if (write_response->error_code == 0) { + finalize_comm_element(&qstate->request); + finalize_comm_element(&qstate->response); + + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_mp_write_session_mapper; + qstate->kevent_filter = EVFILT_READ; + } else { + qstate->kevent_watermark = 0; + qstate->process_func = 0; + } + + TRACE_OUT(on_mp_write_session_write_response_write1); + return (0); +} + +/* + * Handles abandon notifications. Destroys the session by calling the + * abandon_cache_mp_write_session. + */ +static int +on_mp_write_session_abandon_notification(struct query_state *qstate) +{ + TRACE_IN(on_mp_write_session_abandon_notification); + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + abandon_cache_mp_write_session((cache_mp_write_session)qstate->mdata); + configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART); + qstate->mdata = INVALID_CACHE_MP_WRITE_SESSION; + + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + TRACE_OUT(on_mp_write_session_abandon_notification); + return (0); +} + +/* + * Handles close notifications. Commits the session by calling + * the close_cache_mp_write_session. + */ +static int +on_mp_write_session_close_notification(struct query_state *qstate) +{ + TRACE_IN(on_mp_write_session_close_notification); + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + close_cache_mp_write_session((cache_mp_write_session)qstate->mdata); + configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART); + qstate->mdata = INVALID_CACHE_MP_WRITE_SESSION; + + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + TRACE_OUT(on_mp_write_session_close_notification); + return (0); +} + +cache_entry register_new_mp_cache_entry(struct query_state *qstate, + const char *dec_cache_entry_name) +{ + cache_entry c_entry; + char *en_bkp; + + TRACE_IN(register_new_mp_cache_entry); + c_entry = INVALID_CACHE_ENTRY; + configuration_lock_entry(qstate->config_entry, CELT_MULTIPART); + + configuration_lock_wrlock(s_configuration); + en_bkp = qstate->config_entry->mp_cache_params.entry_name; + qstate->config_entry->mp_cache_params.entry_name = + (char *)dec_cache_entry_name; + register_cache_entry(s_cache, (struct cache_entry_params *) + &qstate->config_entry->mp_cache_params); + qstate->config_entry->mp_cache_params.entry_name = en_bkp; + configuration_unlock(s_configuration); + + configuration_lock_rdlock(s_configuration); + c_entry = find_cache_entry(s_cache, + dec_cache_entry_name); + configuration_unlock(s_configuration); + + configuration_entry_add_mp_cache_entry(qstate->config_entry, + c_entry); + + configuration_unlock_entry(qstate->config_entry, + CELT_MULTIPART); + + TRACE_OUT(register_new_mp_cache_entry); + return (c_entry); +} diff --git a/usr.sbin/nscd/mp_ws_query.h b/usr.sbin/nscd/mp_ws_query.h new file mode 100644 index 000000000000..ba776656b833 --- /dev/null +++ b/usr.sbin/nscd/mp_ws_query.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_MP_WS_QUERY_H__ +#define __CACHED_MP_WS_QUERY_H__ + +extern int on_mp_write_session_request_read1(struct query_state *); +extern cache_entry register_new_mp_cache_entry(struct query_state *, + const char *); + +#endif diff --git a/usr.sbin/nscd/nscd.8 b/usr.sbin/nscd/nscd.8 new file mode 100644 index 000000000000..5a8693ac453a --- /dev/null +++ b/usr.sbin/nscd/nscd.8 @@ -0,0 +1,148 @@ +.\" Copyright (c) 2005 Michael Bushkov +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" + +.Dd October 20, 2005 +.Dt CACHED 8 +.Os +.Sh NAME +.Nm cached +.Nd caching server daemon +.Sh SYNOPSIS +.Nm +.Op Fl nst +.Op Fl i Ar cachename +.Op Fl I Ar cachename +.Sh DESCRIPTION +.Nm +- is the system caching daemon. +It can cache almost all types of data and is basically intended to be used +with +.Pa nsswitch +subsystem. +The cache is actually per-user. It means that each user can work only with the +cached data, that were cached by him and can't poison the cache of other users. +.Nm +supports 2 types of caching: +.Bl -tag -width Pair +.It Sy Type +.Sy Description +.It Common caching +Each cached element is the the key+value pair. +This type of caching supports policies, which are applied, when maximum +number of cached elements is exceeded. +3 policies are available: +.Pa FIFO +(first in - first out), +.Pa LRU +(least recently used) and +.Pa LFU +(least frequently used). +This type of caching is used with +.Fn getXXXbyname +- like functions. +.It Multipart caching +Each cached element is the part of the elements sequence. +This type of caching is intended to be used with +.Fn getXXXent +- like functions. +.El +.Pp +.Nm +is able not only to cache elements, but to perform the actual nsswitch +lookups by itself. To enable this feature use +.Pa perform-actual-lookups +parameter in the +.Xr cached.conf 5 +.Pp +.Nm +recognizes the following runtime options: +.Bl -tag -width indent +.It Fl n +Do not daemonize. +.Nm +doesn't fork and doesn't disconnect itself from the terminal. +.It Fl s +Single-threaded mode. +Forces using only one thread for all processing purposes (it overrides +the +.Pa threads +parameter in the +.Xr cached.conf 5 +file). +.It Fl t +Trace mode. +All trace messages would be written to the stdout. +This mode is usually used with +.Fl n +and +.Fl s +flags for debugging purposes. +.It Fl i Ar cachename +Invalidates personal cache. When specified, +.Nm +acts as the administration tool. It asks the already +running +.Nm +to invalidate the specified part of the cache of the +calling user. For example, you may want sometimes +to invalidate your +.Pa hosts +cache. You can specify +.Pa all +as the +.Pa cachename +to invalidate your personal cache as a whole. You +can't use this option for the cachename, for which +the +.Pa perform-actual-lookups +option is enabled. +.It Fl I Ar cachename +Invalidates the cache for every user. When specified, +.Nm +acts as the administration tool. It asks the already +running +.Nm +to invalidate the specified part of the cache for +every user. You can specify +.Pa all +as the +.Pa cachename +to invalidate the whole cache. Only root can use this +option. +.El +.Sh FILES +.Xr cached.conf 5 +.Sh SEE ALSO +.Xr cached.conf 5 +.Xr nsswitch.conf 5 +.Xr nsdispatch 3 +.Sh "AUTHORS" +Michael Bushkov +.Aq bushman@rsu.ru +.Sh "BUGS" +To report bugs or suggestions please mail me +.Aq bushman@rsu.ru diff --git a/usr.sbin/nscd/nscd.c b/usr.sbin/nscd/nscd.c new file mode 100644 index 000000000000..3219d22a7e1a --- /dev/null +++ b/usr.sbin/nscd/nscd.c @@ -0,0 +1,884 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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 thereg + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "agents/passwd.h" +#include "agents/group.h" +#include "agents/services.h" +#include "cachedcli.h" +#include "cachelib.h" +#include "config.h" +#include "debug.h" +#include "log.h" +#include "parser.h" +#include "query.h" +#include "singletons.h" + +#ifndef CONFIG_PATH +#define CONFIG_PATH "/etc/cached.conf" +#endif +#define DEFAULT_CONFIG_PATH "cached.conf" + +#define MAX_SOCKET_IO_SIZE 4096 + +struct processing_thread_args { + cache the_cache; + struct configuration *the_configuration; + struct runtime_env *the_runtime_env; +}; + +static void accept_connection(struct kevent *, struct runtime_env *, + struct configuration *); +static void destroy_cache_(cache); +static void destroy_runtime_env(struct runtime_env *); +static cache init_cache_(struct configuration *); +static struct runtime_env *init_runtime_env(struct configuration *); +static void print_version_info(void); +static void processing_loop(cache, struct runtime_env *, + struct configuration *); +static void process_socket_event(struct kevent *, struct runtime_env *, + struct configuration *); +static void process_timer_event(struct kevent *, struct runtime_env *, + struct configuration *); +static void *processing_thread(void *); +static void usage(void); + +void get_time_func(struct timeval *); + +static void +print_version_info(void) +{ + TRACE_IN(print_version_info); + printf("cached v0.2 (20 Oct 2005)\nwas developed during SoC 2005\n"); + TRACE_OUT(print_version_info); +} + +static void +usage(void) +{ + fprintf(stderr,"usage: cached [-nstiId]\n"); + exit(1); +} + +static cache +init_cache_(struct configuration *config) +{ + struct cache_params params; + cache retval; + + struct configuration_entry *config_entry; + size_t size, i; + int res; + + TRACE_IN(init_cache_); + + memset(¶ms, 0, sizeof(struct cache_params)); + params.get_time_func = get_time_func; + retval = init_cache(¶ms); + + size = configuration_get_entries_size(config); + for (i = 0; i < size; ++i) { + config_entry = configuration_get_entry(config, i); + /* + * We should register common entries now - multipart entries + * would be registered automatically during the queries. + */ + res = register_cache_entry(retval, (struct cache_entry_params *) + &config_entry->positive_cache_params); + config_entry->positive_cache_entry = find_cache_entry(retval, + config_entry->positive_cache_params.entry_name); + assert(config_entry->positive_cache_entry != + INVALID_CACHE_ENTRY); + + res = register_cache_entry(retval, (struct cache_entry_params *) + &config_entry->negative_cache_params); + config_entry->negative_cache_entry = find_cache_entry(retval, + config_entry->negative_cache_params.entry_name); + assert(config_entry->negative_cache_entry != + INVALID_CACHE_ENTRY); + } + + LOG_MSG_2("cache", "cache was successfully initialized"); + TRACE_OUT(init_cache_); + return (retval); +} + +static void +destroy_cache_(cache the_cache) +{ + TRACE_IN(destroy_cache_); + destroy_cache(the_cache); + TRACE_OUT(destroy_cache_); +} + +/* + * Socket and kqueues are prepared here. We have one global queue for both + * socket and timers events. + */ +static struct runtime_env * +init_runtime_env(struct configuration *config) +{ + int serv_addr_len; + struct sockaddr_un serv_addr; + + struct kevent eventlist; + struct timespec timeout; + + struct runtime_env *retval; + + TRACE_IN(init_runtime_env); + retval = (struct runtime_env *)malloc(sizeof(struct runtime_env)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct runtime_env)); + + retval->sockfd = socket(PF_LOCAL, SOCK_STREAM, 0); + + if (config->force_unlink == 1) + unlink(config->socket_path); + + memset(&serv_addr, 0, sizeof(struct sockaddr_un)); + serv_addr.sun_family = PF_LOCAL; + strncpy(serv_addr.sun_path, config->socket_path, + sizeof(serv_addr.sun_path)); + serv_addr_len = sizeof(serv_addr.sun_family) + + strlen(serv_addr.sun_path) + 1; + + if (bind(retval->sockfd, (struct sockaddr *)&serv_addr, + serv_addr_len) == -1) { + close(retval->sockfd); + free(retval); + + LOG_ERR_2("runtime environment", "can't bind socket to path: " + "%s", config->socket_path); + TRACE_OUT(init_runtime_env); + return (NULL); + } + LOG_MSG_2("runtime environment", "using socket %s", + config->socket_path); + + /* + * Here we're marking socket as non-blocking and setting its backlog + * to the maximum value + */ + chmod(config->socket_path, config->socket_mode); + listen(retval->sockfd, -1); + fcntl(retval->sockfd, F_SETFL, O_NONBLOCK); + + retval->queue = kqueue(); + assert(retval->queue != -1); + + EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD | EV_ONESHOT, + 0, 0, 0); + memset(&timeout, 0, sizeof(struct timespec)); + kevent(retval->queue, &eventlist, 1, NULL, 0, &timeout); + + LOG_MSG_2("runtime environment", "successfully initialized"); + TRACE_OUT(init_runtime_env); + return (retval); +} + +static void +destroy_runtime_env(struct runtime_env *env) +{ + TRACE_IN(destroy_runtime_env); + close(env->queue); + close(env->sockfd); + free(env); + TRACE_OUT(destroy_runtime_env); +} + +static void +accept_connection(struct kevent *event_data, struct runtime_env *env, + struct configuration *config) +{ + struct kevent eventlist[2]; + struct timespec timeout; + struct query_state *qstate; + + int fd; + int res; + + uid_t euid; + gid_t egid; + + TRACE_IN(accept_connection); + fd = accept(event_data->ident, NULL, NULL); + if (fd == -1) { + LOG_ERR_2("accept_connection", "error %d during accept()", + errno); + TRACE_OUT(accept_connection); + return; + } + + if (getpeereid(fd, &euid, &egid) != 0) { + LOG_ERR_2("accept_connection", "error %d during getpeereid()", + errno); + TRACE_OUT(accept_connection); + return; + } + + qstate = init_query_state(fd, sizeof(int), euid, egid); + if (qstate == NULL) { + LOG_ERR_2("accept_connection", "can't init query_state"); + TRACE_OUT(accept_connection); + return; + } + + memset(&timeout, 0, sizeof(struct timespec)); + EV_SET(&eventlist[0], fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, + 0, qstate->timeout.tv_sec * 1000, qstate); + EV_SET(&eventlist[1], fd, EVFILT_READ, EV_ADD | EV_ONESHOT, + NOTE_LOWAT, qstate->kevent_watermark, qstate); + res = kevent(env->queue, eventlist, 2, NULL, 0, &timeout); + if (res < 0) + LOG_ERR_2("accept_connection", "kevent error"); + + TRACE_OUT(accept_connection); +} + +static void +process_socket_event(struct kevent *event_data, struct runtime_env *env, + struct configuration *config) +{ + struct kevent eventlist[2]; + struct timeval query_timeout; + struct timespec kevent_timeout; + int nevents; + int eof_res, res; + ssize_t io_res; + struct query_state *qstate; + + TRACE_IN(process_socket_event); + eof_res = event_data->flags & EV_EOF ? 1 : 0; + res = 0; + + memset(&kevent_timeout, 0, sizeof(struct timespec)); + EV_SET(&eventlist[0], event_data->ident, EVFILT_TIMER, EV_DELETE, + 0, 0, NULL); + nevents = kevent(env->queue, eventlist, 1, NULL, 0, &kevent_timeout); + if (nevents == -1) { + if (errno == ENOENT) { + /* the timer is already handling this event */ + TRACE_OUT(process_socket_event); + return; + } else { + /* some other error happened */ + LOG_ERR_2("process_socket_event", "kevent error, errno" + " is %d", errno); + TRACE_OUT(process_socket_event); + return; + } + } + qstate = (struct query_state *)event_data->udata; + + /* + * If the buffer that is to be send/received is too large, + * we send it implicitly, by using query_io_buffer_read and + * query_io_buffer_write functions in the query_state. These functions + * use the temporary buffer, which is later send/received in parts. + * The code below implements buffer splitting/mergind for send/receive + * operations. It also does the actual socket IO operations. + */ + if (((qstate->use_alternate_io == 0) && + (qstate->kevent_watermark <= event_data->data)) || + ((qstate->use_alternate_io != 0) && + (qstate->io_buffer_watermark <= event_data->data))) { + if (qstate->use_alternate_io != 0) { + switch (qstate->io_buffer_filter) { + case EVFILT_READ: + io_res = query_socket_read(qstate, + qstate->io_buffer_p, + qstate->io_buffer_watermark); + if (io_res < 0) { + qstate->use_alternate_io = 0; + qstate->process_func = NULL; + } else { + qstate->io_buffer_p += io_res; + if (qstate->io_buffer_p == + qstate->io_buffer + + qstate->io_buffer_size) { + qstate->io_buffer_p = + qstate->io_buffer; + qstate->use_alternate_io = 0; + } + } + break; + default: + break; + } + } + + if (qstate->use_alternate_io == 0) { + do { + res = qstate->process_func(qstate); + } while ((qstate->kevent_watermark == 0) && + (qstate->process_func != NULL) && + (res == 0)); + + if (res != 0) + qstate->process_func = NULL; + } + + if ((qstate->use_alternate_io != 0) && + (qstate->io_buffer_filter == EVFILT_WRITE)) { + io_res = query_socket_write(qstate, qstate->io_buffer_p, + qstate->io_buffer_watermark); + if (io_res < 0) { + qstate->use_alternate_io = 0; + qstate->process_func = NULL; + } else + qstate->io_buffer_p += io_res; + } + } else { + /* assuming that socket was closed */ + qstate->process_func = NULL; + qstate->use_alternate_io = 0; + } + + if (((qstate->process_func == NULL) && + (qstate->use_alternate_io == 0)) || + (eof_res != 0) || (res != 0)) { + destroy_query_state(qstate); + close(event_data->ident); + TRACE_OUT(process_socket_event); + return; + } + + /* updating the query_state lifetime variable */ + get_time_func(&query_timeout); + query_timeout.tv_usec = 0; + query_timeout.tv_sec -= qstate->creation_time.tv_sec; + if (query_timeout.tv_sec > qstate->timeout.tv_sec) + query_timeout.tv_sec = 0; + else + query_timeout.tv_sec = qstate->timeout.tv_sec - + query_timeout.tv_sec; + + if ((qstate->use_alternate_io != 0) && (qstate->io_buffer_p == + qstate->io_buffer + qstate->io_buffer_size)) + qstate->use_alternate_io = 0; + + if (qstate->use_alternate_io == 0) { + /* + * If we must send/receive the large block of data, + * we should prepare the query_state's io_XXX fields. + * We should also substitute its write_func and read_func + * with the query_io_buffer_write and query_io_buffer_read, + * which will allow us to implicitly send/receive this large + * buffer later (in the subsequent calls to the + * process_socket_event). + */ + if (qstate->kevent_watermark > MAX_SOCKET_IO_SIZE) { + if (qstate->io_buffer != NULL) + free(qstate->io_buffer); + + qstate->io_buffer = (char *)malloc( + qstate->kevent_watermark); + assert(qstate->io_buffer != NULL); + memset(qstate->io_buffer, 0, qstate->kevent_watermark); + + qstate->io_buffer_p = qstate->io_buffer; + qstate->io_buffer_size = qstate->kevent_watermark; + qstate->io_buffer_filter = qstate->kevent_filter; + + qstate->write_func = query_io_buffer_write; + qstate->read_func = query_io_buffer_read; + + if (qstate->kevent_filter == EVFILT_READ) + qstate->use_alternate_io = 1; + + qstate->io_buffer_watermark = MAX_SOCKET_IO_SIZE; + EV_SET(&eventlist[1], event_data->ident, + qstate->kevent_filter, EV_ADD | EV_ONESHOT, + NOTE_LOWAT, MAX_SOCKET_IO_SIZE, qstate); + } else { + EV_SET(&eventlist[1], event_data->ident, + qstate->kevent_filter, EV_ADD | EV_ONESHOT, + NOTE_LOWAT, qstate->kevent_watermark, qstate); + } + } else { + if (qstate->io_buffer + qstate->io_buffer_size - + qstate->io_buffer_p < + MAX_SOCKET_IO_SIZE) { + qstate->io_buffer_watermark = qstate->io_buffer + + qstate->io_buffer_size - qstate->io_buffer_p; + EV_SET(&eventlist[1], event_data->ident, + qstate->io_buffer_filter, + EV_ADD | EV_ONESHOT, NOTE_LOWAT, + qstate->io_buffer_watermark, + qstate); + } else { + qstate->io_buffer_watermark = MAX_SOCKET_IO_SIZE; + EV_SET(&eventlist[1], event_data->ident, + qstate->io_buffer_filter, EV_ADD | EV_ONESHOT, + NOTE_LOWAT, MAX_SOCKET_IO_SIZE, qstate); + } + } + EV_SET(&eventlist[0], event_data->ident, EVFILT_TIMER, + EV_ADD | EV_ONESHOT, 0, query_timeout.tv_sec * 1000, qstate); + kevent(env->queue, eventlist, 2, NULL, 0, &kevent_timeout); + + TRACE_OUT(process_socket_event); +} + +/* + * This routine is called if timer event has been signaled in the kqueue. It + * just closes the socket and destroys the query_state. + */ +static void +process_timer_event(struct kevent *event_data, struct runtime_env *env, + struct configuration *config) +{ + struct query_state *qstate; + + TRACE_IN(process_timer_event); + qstate = (struct query_state *)event_data->udata; + destroy_query_state(qstate); + close(event_data->ident); + TRACE_OUT(process_timer_event); +} + +/* + * Processing loop is the basic processing routine, that forms a body of each + * procssing thread + */ +static void +processing_loop(cache the_cache, struct runtime_env *env, + struct configuration *config) +{ + struct timespec timeout; + const int eventlist_size = 1; + struct kevent eventlist[eventlist_size]; + int nevents, i; + + TRACE_MSG("=> processing_loop"); + memset(&timeout, 0, sizeof(struct timespec)); + memset(&eventlist, 0, sizeof(struct kevent) * eventlist_size); + + for (;;) { + nevents = kevent(env->queue, NULL, 0, eventlist, + eventlist_size, NULL); + /* + * we can only receive 1 event on success + */ + if (nevents == 1) { + struct kevent *event_data; + event_data = &eventlist[0]; + + if (event_data->ident == env->sockfd) { + for (i = 0; i < event_data->data; ++i) + accept_connection(event_data, env, config); + + EV_SET(eventlist, s_runtime_env->sockfd, + EVFILT_READ, EV_ADD | EV_ONESHOT, + 0, 0, 0); + memset(&timeout, 0, + sizeof(struct timespec)); + kevent(s_runtime_env->queue, eventlist, + 1, NULL, 0, &timeout); + + } else { + switch (event_data->filter) { + case EVFILT_READ: + case EVFILT_WRITE: + process_socket_event(event_data, + env, config); + break; + case EVFILT_TIMER: + process_timer_event(event_data, + env, config); + break; + default: + break; + } + } + } else { + /* this branch shouldn't be currently executed */ + } + } + + TRACE_MSG("<= processing_loop"); +} + +/* + * Wrapper above the processing loop function. It sets the thread signal mask + * to avoid SIGPIPE signals (which can happen if the client works incorrectly). + */ +static void * +processing_thread(void *data) +{ + struct processing_thread_args *args; + sigset_t new; + + TRACE_MSG("=> processing_thread"); + args = (struct processing_thread_args *)data; + + sigemptyset(&new); + sigaddset(&new, SIGPIPE); + if (pthread_sigmask(SIG_BLOCK, &new, NULL) != 0) + LOG_ERR_1("processing thread", + "thread can't block the SIGPIPE signal"); + + processing_loop(args->the_cache, args->the_runtime_env, + args->the_configuration); + free(args); + TRACE_MSG("<= processing_thread"); + + return (NULL); +} + +void +get_time_func(struct timeval *time) +{ + struct timespec res; + memset(&res, 0, sizeof(struct timespec)); + clock_gettime(CLOCK_MONOTONIC, &res); + + time->tv_sec = res.tv_sec; + time->tv_usec = 0; +} + +/* + * The idea of _nss_cache_cycle_prevention_function is that nsdispatch will + * search for this symbol in the executable. This symbol is the attribute of + * the caching daemon. So, if it exists, nsdispatch won't try to connect to + * the caching daemon and will just ignore the 'cache' source in the + * nsswitch.conf. This method helps to avoid cycles and organize + * self-performing requests. + */ +void +_nss_cache_cycle_prevention_function(void) +{ +} + +int +main(int argc, char *argv[]) +{ + struct processing_thread_args *thread_args; + pthread_t *threads; + + struct pidfh *pidfile; + pid_t pid; + + char const *config_file; + char const *error_str; + int error_line; + int i, res; + + int trace_mode_enabled; + int force_single_threaded; + int do_not_daemonize; + int clear_user_cache_entries, clear_all_cache_entries; + char *user_config_entry_name, *global_config_entry_name; + int show_statistics; + int daemon_mode, interactive_mode; + + + /* by default all debug messages are omitted */ + TRACE_OFF(); + + /* startup output */ + print_version_info(); + + /* parsing command line arguments */ + trace_mode_enabled = 0; + force_single_threaded = 0; + do_not_daemonize = 0; + clear_user_cache_entries = 0; + clear_all_cache_entries = 0; + show_statistics = 0; + user_config_entry_name = NULL; + global_config_entry_name = NULL; + while ((res = getopt(argc, argv, "nstdi:I:")) != -1) { + switch (res) { + case 'n': + do_not_daemonize = 1; + break; + case 's': + force_single_threaded = 1; + break; + case 't': + trace_mode_enabled = 1; + break; + case 'i': + clear_user_cache_entries = 1; + if (optarg != NULL) + if (strcmp(optarg, "all") != 0) + user_config_entry_name = strdup(optarg); + break; + case 'I': + clear_all_cache_entries = 1; + if (optarg != NULL) + if (strcmp(optarg, "all") != 0) + global_config_entry_name = + strdup(optarg); + break; + case 'd': + show_statistics = 1; + break; + case '?': + default: + usage(); + /* NOT REACHED */ + } + } + + daemon_mode = do_not_daemonize | force_single_threaded | + trace_mode_enabled; + interactive_mode = clear_user_cache_entries | clear_all_cache_entries | + show_statistics; + + if ((daemon_mode != 0) && (interactive_mode != 0)) { + LOG_ERR_1("main", "daemon mode and interactive_mode arguments " + "can't be used together"); + usage(); + } + + if (interactive_mode != 0) { + FILE *pidfin = fopen(DEFAULT_PIDFILE_PATH, "r"); + char pidbuf[256]; + + struct cached_connection_params connection_params; + cached_connection connection; + + int result; + + if (pidfin == NULL) + errx(EXIT_FAILURE, "There is no daemon running."); + + memset(pidbuf, 0, sizeof(pidbuf)); + fread(pidbuf, sizeof(pidbuf) - 1, 1, pidfin); + fclose(pidfin); + + if (ferror(pidfin) != 0) + errx(EXIT_FAILURE, "Can't read from pidfile."); + + if (sscanf(pidbuf, "%d", &pid) != 1) + errx(EXIT_FAILURE, "Invalid pidfile."); + LOG_MSG_1("main", "daemon PID is %d", pid); + + + memset(&connection_params, 0, + sizeof(struct cached_connection_params)); + connection_params.socket_path = DEFAULT_SOCKET_PATH; + connection = open_cached_connection__(&connection_params); + if (connection == INVALID_CACHED_CONNECTION) + errx(EXIT_FAILURE, "Can't connect to the daemon."); + + if (clear_user_cache_entries != 0) { + result = cached_transform__(connection, + user_config_entry_name, TT_USER); + if (result != 0) + LOG_MSG_1("main", + "user cache transformation failed"); + else + LOG_MSG_1("main", + "user cache_transformation " + "succeeded"); + } + + if (clear_all_cache_entries != 0) { + if (geteuid() != 0) + errx(EXIT_FAILURE, "Only root can initiate " + "global cache transformation."); + + result = cached_transform__(connection, + global_config_entry_name, TT_ALL); + if (result != 0) + LOG_MSG_1("main", + "global cache transformation " + "failed"); + else + LOG_MSG_1("main", + "global cache transformation " + "succeeded"); + } + + close_cached_connection__(connection); + + free(user_config_entry_name); + free(global_config_entry_name); + return (EXIT_SUCCESS); + } + + pidfile = pidfile_open(DEFAULT_PIDFILE_PATH, 0644, &pid); + if (pidfile == NULL) { + if (errno == EEXIST) + errx(EXIT_FAILURE, "Daemon already running, pid: %d.", + pid); + warn("Cannot open or create pidfile"); + } + + if (trace_mode_enabled == 1) + TRACE_ON(); + + /* blocking the main thread from receiving SIGPIPE signal */ + sigblock(sigmask(SIGPIPE)); + + /* daemonization */ + if (do_not_daemonize == 0) { + res = daemon(0, trace_mode_enabled == 0 ? 0 : 1); + if (res != 0) { + LOG_ERR_1("main", "can't daemonize myself: %s", + strerror(errno)); + pidfile_remove(pidfile); + goto fin; + } else + LOG_MSG_1("main", "successfully daemonized"); + } + + pidfile_write(pidfile); + + s_agent_table = init_agent_table(); + register_agent(s_agent_table, init_passwd_agent()); + register_agent(s_agent_table, init_passwd_mp_agent()); + register_agent(s_agent_table, init_group_agent()); + register_agent(s_agent_table, init_group_mp_agent()); + register_agent(s_agent_table, init_services_agent()); + register_agent(s_agent_table, init_services_mp_agent()); + LOG_MSG_1("main", "request agents registered successfully"); + + /* + * Hosts agent can't work properly until we have access to the + * appropriate dtab structures, which are used in nsdispatch + * calls + * + register_agent(s_agent_table, init_hosts_agent()); + */ + + /* configuration initialization */ + s_configuration = init_configuration(); + fill_configuration_defaults(s_configuration); + + error_str = NULL; + error_line = 0; + config_file = CONFIG_PATH; + + res = parse_config_file(s_configuration, config_file, &error_str, + &error_line); + if ((res != 0) && (error_str == NULL)) { + config_file = DEFAULT_CONFIG_PATH; + res = parse_config_file(s_configuration, config_file, + &error_str, &error_line); + } + + if (res != 0) { + if (error_str != NULL) { + LOG_ERR_1("main", "error in configuration file(%s, %d): %s\n", + config_file, error_line, error_str); + } else { + LOG_ERR_1("main", "no configuration file found " + "- was looking for %s and %s", + CONFIG_PATH, DEFAULT_CONFIG_PATH); + } + destroy_configuration(s_configuration); + return (-1); + } + + if (force_single_threaded == 1) + s_configuration->threads_num = 1; + + /* cache initialization */ + s_cache = init_cache_(s_configuration); + if (s_cache == NULL) { + LOG_ERR_1("main", "can't initialize the cache"); + destroy_configuration(s_configuration); + return (-1); + } + + /* runtime environment initialization */ + s_runtime_env = init_runtime_env(s_configuration); + if (s_runtime_env == NULL) { + LOG_ERR_1("main", "can't initialize the runtime environment"); + destroy_configuration(s_configuration); + destroy_cache_(s_cache); + return (-1); + } + + if (s_configuration->threads_num > 1) { + threads = (pthread_t *)malloc(sizeof(pthread_t) * + s_configuration->threads_num); + memset(threads, 0, sizeof(pthread_t) * + s_configuration->threads_num); + for (i = 0; i < s_configuration->threads_num; ++i) { + thread_args = (struct processing_thread_args *)malloc( + sizeof(struct processing_thread_args)); + thread_args->the_cache = s_cache; + thread_args->the_runtime_env = s_runtime_env; + thread_args->the_configuration = s_configuration; + + LOG_MSG_1("main", "thread #%d was successfully created", + i); + pthread_create(&threads[i], NULL, processing_thread, + thread_args); + + thread_args = NULL; + } + + for (i = 0; i < s_configuration->threads_num; ++i) + pthread_join(threads[i], NULL); + } else { + LOG_MSG_1("main", "working in single-threaded mode"); + processing_loop(s_cache, s_runtime_env, s_configuration); + } + +fin: + /* runtime environment destruction */ + destroy_runtime_env(s_runtime_env); + + /* cache destruction */ + destroy_cache_(s_cache); + + /* configuration destruction */ + destroy_configuration(s_configuration); + + /* agents table destruction */ + destroy_agent_table(s_agent_table); + + pidfile_remove(pidfile); + return (EXIT_SUCCESS); +} diff --git a/usr.sbin/nscd/nscd.conf.5 b/usr.sbin/nscd/nscd.conf.5 new file mode 100644 index 000000000000..de6d43a978d0 --- /dev/null +++ b/usr.sbin/nscd/nscd.conf.5 @@ -0,0 +1,102 @@ +.\" Copyright (c) 2005 Michael Bushkov +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" + +.Dd August 29, 2005 +.Dt CACHED.CONF 5 +.Os +.Sh NAME +.Nm cached.conf +.Nd "caching daemon configuration file manual page" +.Sh "DESCRIPTION" +.Nm +is used by the +.Xr cached 8 +daemon and is read on its startup. Its syntax is mostly similar to the +nscd.conf syntax in Linux and Solaris. It has some differences, though - +see them below. +.Pp +Each line specifies either an attribute and a value, or an attribute, +a cachename and a value. Usual cachenames are passwd, groups, hosts, +services, protocols and rpc. You can also use any other cachename +(for example, if some third-party application uses nsswitch). +.Pp +.Bl -tag -width Pair +.It Sy threads [value] +Number of threads, which would listen for connections and process requests. The +minimum is 1. The default value is 8. +.It Sy enable-cache [cachename] [yes|no] +Enables or disables the cache for specified cachename. +.It Sy positive-time-to-live [cachename] [value] +Sets the TTL (time-to-live) for the specified cache in seconds. Larger values +can increase system's performance, but they also can affect the cache +coherence. The default value is 3600. +.It Sy positive-policy [cachename] [fifo|lru|lfu] +The policy that is applied to erase some of the cache elements, when the +size limit of the given cachename is exceeded. Possible policies are: +fifo (first-in-first-out), lru (least-recently-used), +lfu (least-frequently-used). The default policy is +.It Sy negative-time-to-live [cachename] [value] +The TTL of the negative cached elements in seconds. The larger values can +significantly increase system performance in some environments +(when dealing with files with UIDs, which are not in system databases, +for example). This number should be kept low to avoid the cache +coherence problems. The default value is 60. +.It Sy negative-policy [cachename] [fifo|lru|lfu] +The same as the positive-policy, but this one is applied to the negative +elements of the given cachename. The default policy is +.It Sy suggested-size [cachename] [value] +This is the internal hash table size. The value should be a prime number +for optimum performance. You should only change this value when the number +of cached elements is significantly (in 5-10 times) greater then the default +hash table size (255). +.It Sy keep-hot-count [cachename] [value] +The size limit of the cache with the given cachename. When it is exceeded, +the policy will be applied. The default value is 2048. +.It Sy perform-actual-lookups [cachename] [yes|no] +If enabled, the +.Xr cached 8 +doesn't simply receive and cache the NSS-requests results, but performs +all the lookups by itself and only returns the responses. If this feature is +enabled, then for the given cachename +.Xr cached 8 +will act similarly to the NSCD. +.Pp +.Pa NOTE: +this feature is currently experimental - it supports only passwd, groups and +services cachenames. +.Sh "NOTES" +You can use +.Sq # +symbol at the beginning of the line for comments. +.Sh "SEE ALSO" +.Xr cached 8 +.Sh "AUTHORS" +Michael Bushkov +.Aq bushman@rsu.ru +.Sh "BUGS" +To report bugs or suggestions please mail me +.Aq bushman@rsu.ru diff --git a/usr.sbin/nscd/nscdcli.c b/usr.sbin/nscd/nscdcli.c new file mode 100644 index 000000000000..c78c875bc1ff --- /dev/null +++ b/usr.sbin/nscd/nscdcli.c @@ -0,0 +1,284 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "cachedcli.h" +#include "protocol.h" + +#define DEFAULT_CACHED_IO_TIMEOUT 4 + +static int safe_write(struct cached_connection_ *, const void *, size_t); +static int safe_read(struct cached_connection_ *, void *, size_t); +static int send_credentials(struct cached_connection_ *, int); + +static int +safe_write(struct cached_connection_ *connection, const void *data, + size_t data_size) +{ + struct kevent eventlist; + int nevents; + size_t result; + ssize_t s_result; + struct timespec timeout; + + if (data_size == 0) + return (0); + + timeout.tv_sec = DEFAULT_CACHED_IO_TIMEOUT; + timeout.tv_nsec = 0; + result = 0; + do { + nevents = kevent(connection->write_queue, NULL, 0, &eventlist, + 1, &timeout); + if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) { + s_result = write(connection->sockfd, data + result, + eventlist.data < data_size - result ? + eventlist.data : data_size - result); + if (s_result == -1) + return (-1); + else + result += s_result; + + if (eventlist.flags & EV_EOF) + return (result < data_size ? -1 : 0); + } else + return (-1); + } while (result < data_size); + + return (0); +} + +static int +safe_read(struct cached_connection_ *connection, void *data, size_t data_size) +{ + struct kevent eventlist; + size_t result; + ssize_t s_result; + struct timespec timeout; + int nevents; + + if (data_size == 0) + return (0); + + timeout.tv_sec = DEFAULT_CACHED_IO_TIMEOUT; + timeout.tv_nsec = 0; + result = 0; + do { + nevents = kevent(connection->read_queue, NULL, 0, &eventlist, 1, + &timeout); + if ((nevents == 1) && (eventlist.filter == EVFILT_READ)) { + s_result = read(connection->sockfd, data + result, + eventlist.data <= data_size - result ? eventlist.data : + data_size - result); + if (s_result == -1) + return (-1); + else + result += s_result; + + if (eventlist.flags & EV_EOF) + return (result < data_size ? -1 : 0); + } else + return (-1); + } while (result < data_size); + + return (0); +} + +static int +send_credentials(struct cached_connection_ *connection, int type) +{ + struct kevent eventlist; + int nevents; + ssize_t result; + int res; + + struct msghdr cred_hdr; + struct iovec iov; + + struct { + struct cmsghdr hdr; + struct cmsgcred creds; + } cmsg; + + TRACE_IN(send_credentials); + memset(&cmsg, 0, sizeof(cmsg)); + cmsg.hdr.cmsg_len = sizeof(cmsg); + cmsg.hdr.cmsg_level = SOL_SOCKET; + cmsg.hdr.cmsg_type = SCM_CREDS; + + memset(&cred_hdr, 0, sizeof(struct msghdr)); + cred_hdr.msg_iov = &iov; + cred_hdr.msg_iovlen = 1; + cred_hdr.msg_control = &cmsg; + cred_hdr.msg_controllen = sizeof(cmsg); + + iov.iov_base = &type; + iov.iov_len = sizeof(int); + + EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, + NOTE_LOWAT, sizeof(int), NULL); + res = kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); + + nevents = kevent(connection->write_queue, NULL, 0, &eventlist, 1, NULL); + if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) { + result = (sendmsg(connection->sockfd, &cred_hdr, 0) == -1) ? -1 + : 0; + EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, + 0, 0, NULL); + kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); + TRACE_OUT(send_credentials); + return (result); + } else { + TRACE_OUT(send_credentials); + return (-1); + } +} + +struct cached_connection_ * +open_cached_connection__(struct cached_connection_params const *params) +{ + struct cached_connection_ *retval; + struct kevent eventlist; + struct sockaddr_un client_address; + int client_address_len, client_socket; + int res; + + TRACE_IN(open_cached_connection); + assert(params != NULL); + + client_socket = socket(PF_LOCAL, SOCK_STREAM, 0); + client_address.sun_family = PF_LOCAL; + strncpy(client_address.sun_path, params->socket_path, + sizeof(client_address.sun_path)); + client_address_len = sizeof(client_address.sun_family) + + strlen(client_address.sun_path) + 1; + + res = connect(client_socket, (struct sockaddr *)&client_address, + client_address_len); + if (res == -1) { + close(client_socket); + TRACE_OUT(open_cached_connection); + return (NULL); + } + fcntl(client_socket, F_SETFL, O_NONBLOCK); + + retval = malloc(sizeof(struct cached_connection_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cached_connection_)); + + retval->sockfd = client_socket; + + retval->write_queue = kqueue(); + assert(retval->write_queue != -1); + + EV_SET(&eventlist, retval->sockfd, EVFILT_WRITE, EV_ADD, + 0, 0, NULL); + res = kevent(retval->write_queue, &eventlist, 1, NULL, 0, NULL); + + retval->read_queue = kqueue(); + assert(retval->read_queue != -1); + + EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD, + 0, 0, NULL); + res = kevent(retval->read_queue, &eventlist, 1, NULL, 0, NULL); + + TRACE_OUT(open_cached_connection); + return (retval); +} + +void +close_cached_connection__(struct cached_connection_ *connection) +{ + + TRACE_IN(close_cached_connection); + assert(connection != NULL); + + close(connection->sockfd); + close(connection->read_queue); + close(connection->write_queue); + free(connection); + TRACE_OUT(close_cached_connection); +} + +int +cached_transform__(struct cached_connection_ *connection, + const char *entry_name, int transformation_type) +{ + size_t name_size; + int error_code; + int result; + + TRACE_IN(cached_transform); + + error_code = -1; + result = 0; + result = send_credentials(connection, CET_TRANSFORM_REQUEST); + if (result != 0) + goto fin; + + if (entry_name != NULL) + name_size = strlen(entry_name); + else + name_size = 0; + + result = safe_write(connection, &name_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, &transformation_type, sizeof(int)); + if (result != 0) + goto fin; + + if (entry_name != NULL) { + result = safe_write(connection, entry_name, name_size); + if (result != 0) + goto fin; + } + + result = safe_read(connection, &error_code, sizeof(int)); + if (result != 0) + error_code = -1; + +fin: + TRACE_OUT(cached_transform); + return (error_code); +} diff --git a/usr.sbin/nscd/nscdcli.h b/usr.sbin/nscd/nscdcli.h new file mode 100644 index 000000000000..58d9ccc0ceef --- /dev/null +++ b/usr.sbin/nscd/nscdcli.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 2004 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_CACHEDCLI_H__ +#define __CACHED_CACHEDCLI_H__ + +struct cached_connection_params { + char *socket_path; + struct timeval timeout; +}; + +struct cached_connection_ { + int sockfd; + int read_queue; + int write_queue; +}; + +/* simple abstractions for not to write "struct" every time */ +typedef struct cached_connection_ *cached_connection; +typedef struct cached_connection_ *cached_mp_write_session; +typedef struct cached_connection_ *cached_mp_read_session; + +#define INVALID_CACHED_CONNECTION (NULL) + +/* initialization/destruction routines */ +extern cached_connection open_cached_connection__( + struct cached_connection_params const *); +extern void close_cached_connection__(cached_connection); + +extern int cached_transform__(cached_connection, const char *, int); + +#endif diff --git a/usr.sbin/nscd/parser.c b/usr.sbin/nscd/parser.c new file mode 100644 index 000000000000..b877efa6cf44 --- /dev/null +++ b/usr.sbin/nscd/parser.c @@ -0,0 +1,474 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "config.h" +#include "debug.h" +#include "log.h" +#include "parser.h" + +static void enable_cache(struct configuration *,const char *, int); +static struct configuration_entry *find_create_entry(struct configuration *, + const char *); +static int get_number(const char *, int, int); +static enum cache_policy_t get_policy(const char *); +static int get_yesno(const char *); +static int check_cachename(const char *); +static void check_files(struct configuration *, const char *, int); +static void set_keep_hot_count(struct configuration *, const char *, int); +static void set_negative_policy(struct configuration *, const char *, + enum cache_policy_t); +static void set_negative_time_to_live(struct configuration *, + const char *, int); +static void set_positive_policy(struct configuration *, const char *, + enum cache_policy_t); +static void set_perform_actual_lookups(struct configuration *, const char *, + int); +static void set_positive_time_to_live(struct configuration *, + const char *, int); +static void set_suggested_size(struct configuration *, const char *, + int size); +static void set_threads_num(struct configuration *, int); +static int strbreak(char *, char **, int); + +static int +strbreak(char *str, char **fields, int fields_size) +{ + char *c = str; + int i, num; + + TRACE_IN(strbreak); + num = 0; + for (i = 0; + ((*fields = + strsep(i < fields_size ? &c : NULL, "\n\t ")) != NULL); + ++i) + if ((*(*fields)) != '\0') { + ++fields; + ++num; + } + + TRACE_OUT(strbreak); + return (num); +} + +/* + * Tries to find the configuration entry with the specified name. If search + * fails, the new entry with the default parameters will be created. + */ +static struct configuration_entry * +find_create_entry(struct configuration *config, + const char *entry_name) +{ + struct configuration_entry *entry = NULL; + int res; + + TRACE_IN(find_create_entry); + entry = configuration_find_entry(config, entry_name); + if (entry == NULL) { + entry = create_def_configuration_entry(entry_name); + assert( entry != NULL); + res = add_configuration_entry(config, entry); + assert(res == 0); + } + + TRACE_OUT(find_create_entry); + return (entry); +} + +/* + * The vast majority of the functions below corresponds to the particular + * keywords in the configuration file. + */ +static void +enable_cache(struct configuration *config, const char *entry_name, int flag) +{ + struct configuration_entry *entry; + + TRACE_IN(enable_cache); + entry = find_create_entry(config, entry_name); + entry->enabled = flag; + TRACE_OUT(enable_cache); +} + +static void +set_positive_time_to_live(struct configuration *config, + const char *entry_name, int ttl) +{ + struct configuration_entry *entry; + struct timeval lifetime; + + TRACE_IN(set_positive_time_to_live); + assert(ttl >= 0); + assert(entry_name != NULL); + memset(&lifetime, 0, sizeof(struct timeval)); + lifetime.tv_sec = ttl; + + entry = find_create_entry(config, entry_name); + memcpy(&entry->positive_cache_params.max_lifetime, + &lifetime, sizeof(struct timeval)); + memcpy(&entry->mp_cache_params.max_lifetime, + &lifetime, sizeof(struct timeval)); + + TRACE_OUT(set_positive_time_to_live); +} + +static void +set_negative_time_to_live(struct configuration *config, + const char *entry_name, int nttl) +{ + struct configuration_entry *entry; + struct timeval lifetime; + + TRACE_IN(set_negative_time_to_live); + assert(nttl > 0); + assert(entry_name != NULL); + memset(&lifetime, 0, sizeof(struct timeval)); + lifetime.tv_sec = nttl; + + entry = find_create_entry(config, entry_name); + assert(entry != NULL); + memcpy(&entry->negative_cache_params.max_lifetime, + &lifetime, sizeof(struct timeval)); + + TRACE_OUT(set_negative_time_to_live); +} + +/* + * Hot count is actually the elements size limit. + */ +static void +set_keep_hot_count(struct configuration *config, + const char *entry_name, int count) +{ + struct configuration_entry *entry; + + TRACE_IN(set_keep_hot_count); + assert(count >= 0); + assert(entry_name != NULL); + + entry = find_create_entry(config, entry_name); + assert(entry != NULL); + entry->positive_cache_params.max_elemsize = count; + + entry = find_create_entry(config, entry_name); + assert(entry != NULL); + entry->negative_cache_params.max_elemsize = count; + + TRACE_OUT(set_keep_hot_count); +} + +static void +set_positive_policy(struct configuration *config, + const char *entry_name, enum cache_policy_t policy) +{ + struct configuration_entry *entry; + + TRACE_IN(set_positive_policy); + assert(entry_name != NULL); + + entry = find_create_entry(config, entry_name); + assert(entry != NULL); + entry->positive_cache_params.policy = policy; + + TRACE_OUT(set_positive_policy); +} + +static void +set_negative_policy(struct configuration *config, + const char *entry_name, enum cache_policy_t policy) +{ + struct configuration_entry *entry; + + TRACE_IN(set_negative_policy); + assert(entry_name != NULL); + + entry = find_create_entry(config, entry_name); + assert(entry != NULL); + entry->negative_cache_params.policy = policy; + + TRACE_OUT(set_negative_policy); +} + +static void +set_perform_actual_lookups(struct configuration *config, + const char *entry_name, int flag) +{ + struct configuration_entry *entry; + + TRACE_IN(set_perform_actual_lookups); + assert(entry_name != NULL); + + entry = find_create_entry(config, entry_name); + assert(entry != NULL); + entry->perform_actual_lookups = flag; + + TRACE_OUT(set_perform_actual_lookups); +} + +static void +set_suggested_size(struct configuration *config, + const char *entry_name, int size) +{ + struct configuration_entry *entry; + + TRACE_IN(set_suggested_size); + assert(config != NULL); + assert(entry_name != NULL); + assert(size > 0); + + entry = find_create_entry(config, entry_name); + assert(entry != NULL); + entry->positive_cache_params.cache_entries_size = size; + entry->negative_cache_params.cache_entries_size = size; + + TRACE_OUT(set_suggested_size); +} + +static void +check_files(struct configuration *config, const char *entry_name, int flag) +{ + + TRACE_IN(check_files); + assert(entry_name != NULL); + TRACE_OUT(check_files); +} + +static int +get_yesno(const char *str) +{ + + if (strcmp(str, "yes") == 0) + return (1); + else if (strcmp(str, "no") == 0) + return (0); + else + return (-1); +} + +static int +get_number(const char *str, int low, int max) +{ + + char *end = NULL; + int res = 0; + + if (str[0] == '\0') + return (-1); + + res = strtol(str, &end, 10); + if (*end != '\0') + return (-1); + else + if (((res >= low) || (low == -1)) && + ((res <= max) || (max == -1))) + return (res); + else + return (-2); +} + +static enum cache_policy_t +get_policy(const char *str) +{ + + if (strcmp(str, "fifo") == 0) + return (CPT_FIFO); + else if (strcmp(str, "lru") == 0) + return (CPT_LRU); + else if (strcmp(str, "lfu") == 0) + return (CPT_LFU); + + return (-1); +} + +static int +check_cachename(const char *str) +{ + + assert(str != NULL); + return ((strlen(str) > 0) ? 0 : -1); +} + +static void +set_threads_num(struct configuration *config, int value) +{ + + assert(config != NULL); + config->threads_num = value; +} + +/* + * The main configuration routine. Its implementation is hugely inspired by the + * the same routine implementation in Solaris NSCD. + */ +int +parse_config_file(struct configuration *config, + const char *fname, char const **error_str, int *error_line) +{ + FILE *fin; + char buffer[255]; + char *fields[128]; + int field_count, line_num, value; + int res; + + TRACE_IN(parse_config_file); + assert(config != NULL); + assert(fname != NULL); + + fin = fopen(fname, "r"); + if (fin == NULL) { + TRACE_OUT(parse_config_file); + return (-1); + } + + res = 0; + line_num = 0; + memset(buffer, 0, sizeof(buffer)); + while ((res == 0) && (fgets(buffer, sizeof(buffer) - 1, fin) != NULL)) { + field_count = strbreak(buffer, fields, sizeof(fields)); + ++line_num; + + if (field_count == 0) + continue; + + switch (fields[0][0]) { + case '#': + case '\0': + continue; + case 'e': + if ((field_count == 3) && + (strcmp(fields[0], "enable-cache") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_yesno(fields[2])) != -1)) { + enable_cache(config, fields[1], value); + continue; + } + break; + case 'd': + if ((field_count == 2) && + (strcmp(fields[0], "debug-level") == 0) && + ((value = get_number(fields[1], 0, 10)) != -1)) { + continue; + } + break; + case 'p': + if ((field_count == 3) && + (strcmp(fields[0], "positive-time-to-live") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_number(fields[2], 0, -1)) != -1)) { + set_positive_time_to_live(config, + fields[1], value); + continue; + } else if ((field_count == 3) && + (strcmp(fields[0], "positive-policy") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_policy(fields[2])) != -1)) { + set_positive_policy(config, fields[1], value); + continue; + } else if ((field_count == 3) && + (strcmp(fields[0], "perform-actual-lookups") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_yesno(fields[2])) != -1)) { + set_perform_actual_lookups(config, fields[1], + value); + continue; + } + break; + case 'n': + if ((field_count == 3) && + (strcmp(fields[0], "negative-time-to-live") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_number(fields[2], 0, -1)) != -1)) { + set_negative_time_to_live(config, + fields[1], value); + continue; + } else if ((field_count == 3) && + (strcmp(fields[0], "negative-policy") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_policy(fields[2])) != -1)) { + set_negative_policy(config, + fields[1], value); + continue; + } + break; + case 's': + if ((field_count == 3) && + (strcmp(fields[0], "suggested-size") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_number(fields[2], 1, -1)) != -1)) { + set_suggested_size(config, fields[1], value); + continue; + } + break; + case 't': + if ((field_count == 2) && + (strcmp(fields[0], "threads") == 0) && + ((value = get_number(fields[1], 1, -1)) != -1)) { + set_threads_num(config, value); + continue; + } + break; + case 'k': + if ((field_count == 3) && + (strcmp(fields[0], "keep-hot-count") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_number(fields[2], 0, -1)) != -1)) { + set_keep_hot_count(config, + fields[1], value); + continue; + } + break; + case 'c': + if ((field_count == 3) && + (strcmp(fields[0], "check-files") == 0) && + (check_cachename(fields[1]) == 0) && + ((value = get_yesno(fields[2])) != -1)) { + check_files(config, + fields[1], value); + continue; + } + break; + default: + break; + } + + LOG_ERR_2("config file parser", "error in file " + "%s on line %d", fname, line_num); + *error_str = "syntax error"; + *error_line = line_num; + res = -1; + } + fclose(fin); + + TRACE_OUT(parse_config_file); + return (res); +} diff --git a/usr.sbin/nscd/parser.h b/usr.sbin/nscd/parser.h new file mode 100644 index 000000000000..54cc898873cc --- /dev/null +++ b/usr.sbin/nscd/parser.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_PARSER_H__ +#define __CACHED_PARSER_H__ + +extern int parse_config_file(struct configuration *, + const char *, char const **, int *); + +#endif diff --git a/usr.sbin/nscd/protocol.c b/usr.sbin/nscd/protocol.c new file mode 100644 index 000000000000..08cea9230995 --- /dev/null +++ b/usr.sbin/nscd/protocol.c @@ -0,0 +1,550 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "debug.h" +#include "log.h" +#include "protocol.h" + +/* + * Initializes the comm_element with any given type of data + */ +void +init_comm_element(struct comm_element *element, enum comm_element_t type) +{ + + TRACE_IN(init_comm_element); + memset(element, 0, sizeof(struct comm_element)); + + switch (type) { + case CET_WRITE_REQUEST: + init_cache_write_request(&element->c_write_request); + break; + case CET_WRITE_RESPONSE: + init_cache_write_response(&element->c_write_response); + break; + case CET_READ_REQUEST: + init_cache_read_request(&element->c_read_request); + break; + case CET_READ_RESPONSE: + init_cache_read_response(&element->c_read_response); + break; + case CET_TRANSFORM_REQUEST: + init_cache_transform_request(&element->c_transform_request); + break; + case CET_TRANSFORM_RESPONSE: + init_cache_transform_response(&element->c_transform_response); + break; + case CET_MP_WRITE_SESSION_REQUEST: + init_cache_mp_write_session_request(&element->c_mp_ws_request); + break; + case CET_MP_WRITE_SESSION_RESPONSE: + init_cache_mp_write_session_response(&element->c_mp_ws_response); + break; + case CET_MP_WRITE_SESSION_WRITE_REQUEST: + init_cache_mp_write_session_write_request( + &element->c_mp_ws_write_request); + break; + case CET_MP_WRITE_SESSION_WRITE_RESPONSE: + init_cache_mp_write_session_write_response( + &element->c_mp_ws_write_response); + break; + case CET_MP_READ_SESSION_REQUEST: + init_cache_mp_read_session_request(&element->c_mp_rs_request); + break; + case CET_MP_READ_SESSION_RESPONSE: + init_cache_mp_read_session_response(&element->c_mp_rs_response); + break; + case CET_MP_READ_SESSION_READ_RESPONSE: + init_cache_mp_read_session_read_response( + &element->c_mp_rs_read_response); + break; + case CET_UNDEFINED: + break; + default: + LOG_ERR_2("init_comm_element", "invalid communication element"); + TRACE_OUT(init_comm_element); + return; + } + + element->type = type; + TRACE_OUT(init_comm_element); +} + +void +finalize_comm_element(struct comm_element *element) +{ + + TRACE_IN(finalize_comm_element); + switch (element->type) { + case CET_WRITE_REQUEST: + finalize_cache_write_request(&element->c_write_request); + break; + case CET_WRITE_RESPONSE: + finalize_cache_write_response(&element->c_write_response); + break; + case CET_READ_REQUEST: + finalize_cache_read_request(&element->c_read_request); + break; + case CET_READ_RESPONSE: + finalize_cache_read_response(&element->c_read_response); + break; + case CET_TRANSFORM_REQUEST: + finalize_cache_transform_request(&element->c_transform_request); + break; + case CET_TRANSFORM_RESPONSE: + finalize_cache_transform_response( + &element->c_transform_response); + break; + case CET_MP_WRITE_SESSION_REQUEST: + finalize_cache_mp_write_session_request( + &element->c_mp_ws_request); + break; + case CET_MP_WRITE_SESSION_RESPONSE: + finalize_cache_mp_write_session_response( + &element->c_mp_ws_response); + break; + case CET_MP_WRITE_SESSION_WRITE_REQUEST: + finalize_cache_mp_write_session_write_request( + &element->c_mp_ws_write_request); + break; + case CET_MP_WRITE_SESSION_WRITE_RESPONSE: + finalize_cache_mp_write_session_write_response( + &element->c_mp_ws_write_response); + break; + case CET_MP_READ_SESSION_REQUEST: + finalize_cache_mp_read_session_request( + &element->c_mp_rs_request); + break; + case CET_MP_READ_SESSION_RESPONSE: + finalize_cache_mp_read_session_response( + &element->c_mp_rs_response); + break; + case CET_MP_READ_SESSION_READ_RESPONSE: + finalize_cache_mp_read_session_read_response( + &element->c_mp_rs_read_response); + break; + case CET_UNDEFINED: + break; + default: + break; + } + + element->type = CET_UNDEFINED; + TRACE_OUT(finalize_comm_element); +} + +void +init_cache_write_request(struct cache_write_request *write_request) +{ + + TRACE_IN(init_cache_write_request); + memset(write_request, 0, sizeof(struct cache_write_request)); + TRACE_OUT(init_cache_write_request); +} + +void +finalize_cache_write_request(struct cache_write_request *write_request) +{ + + TRACE_IN(finalize_cache_write_request); + free(write_request->entry); + free(write_request->cache_key); + free(write_request->data); + TRACE_OUT(finalize_cache_write_request); +} + +struct cache_write_request * +get_cache_write_request(struct comm_element *element) +{ + + TRACE_IN(get_cache_write_request); + assert(element->type == CET_WRITE_REQUEST); + TRACE_OUT(get_cache_write_request); + return (&element->c_write_request); +} + +void +init_cache_write_response(struct cache_write_response *write_response) +{ + + TRACE_IN(init_cache_write_response); + memset(write_response, 0, sizeof(struct cache_write_response)); + TRACE_OUT(init_cache_write_response); +} + +void +finalize_cache_write_response(struct cache_write_response *write_response) +{ + + TRACE_IN(finalize_cache_write_response); + TRACE_OUT(finalize_cache_write_response); +} + +struct cache_write_response * +get_cache_write_response(struct comm_element *element) +{ + + TRACE_IN(get_cache_write_response); + assert(element->type == CET_WRITE_RESPONSE); + TRACE_OUT(get_cache_write_response); + return (&element->c_write_response); +} + +void +init_cache_read_request(struct cache_read_request *read_request) +{ + + TRACE_IN(init_cache_read_request); + memset(read_request, 0, sizeof(struct cache_read_request)); + TRACE_OUT(init_cache_read_request); +} + +void +finalize_cache_read_request(struct cache_read_request *read_request) +{ + + TRACE_IN(finalize_cache_read_request); + free(read_request->entry); + free(read_request->cache_key); + TRACE_OUT(finalize_cache_read_request); +} + +struct cache_read_request * +get_cache_read_request(struct comm_element *element) +{ + + TRACE_IN(get_cache_read_request); + assert(element->type == CET_READ_REQUEST); + TRACE_OUT(get_cache_read_request); + return (&element->c_read_request); +} + +void +init_cache_read_response(struct cache_read_response *read_response) +{ + + TRACE_IN(init_cache_read_response); + memset(read_response, 0, sizeof(struct cache_read_response)); + TRACE_OUT(init_cache_read_response); +} + +void +finalize_cache_read_response(struct cache_read_response *read_response) +{ + + TRACE_IN(finalize_cache_read_response); + free(read_response->data); + TRACE_OUT(finalize_cache_read_response); +} + +struct cache_read_response * +get_cache_read_response(struct comm_element *element) +{ + + TRACE_IN(get_cache_read_response); + assert(element->type == CET_READ_RESPONSE); + TRACE_OUT(get_cache_read_response); + return (&element->c_read_response); +} + +void +init_cache_transform_request(struct cache_transform_request *transform_request) +{ + + TRACE_IN(init_cache_transform_request); + memset(transform_request, 0, sizeof(struct cache_transform_request)); + TRACE_OUT(init_cache_transform_request); +} + +void +finalize_cache_transform_request( + struct cache_transform_request *transform_request) +{ + + TRACE_IN(finalize_cache_transform_request); + free(transform_request->entry); + TRACE_OUT(finalize_cache_transform_request); +} + +struct cache_transform_request * +get_cache_transform_request(struct comm_element *element) +{ + + TRACE_IN(get_cache_transform_request); + assert(element->type == CET_TRANSFORM_REQUEST); + TRACE_OUT(get_cache_transform_request); + return (&element->c_transform_request); +} + +void +init_cache_transform_response( + struct cache_transform_response *transform_response) +{ + + TRACE_IN(init_cache_transform_request); + memset(transform_response, 0, sizeof(struct cache_transform_response)); + TRACE_OUT(init_cache_transform_request); +} + +void +finalize_cache_transform_response( + struct cache_transform_response *transform_response) +{ + + TRACE_IN(finalize_cache_transform_response); + TRACE_OUT(finalize_cache_transform_response); +} + +struct cache_transform_response * +get_cache_transform_response(struct comm_element *element) +{ + + TRACE_IN(get_cache_transform_response); + assert(element->type == CET_TRANSFORM_RESPONSE); + TRACE_OUT(get_cache_transform_response); + return (&element->c_transform_response); +} + + +void +init_cache_mp_write_session_request( + struct cache_mp_write_session_request *mp_ws_request) +{ + + TRACE_IN(init_cache_mp_write_session_request); + memset(mp_ws_request, 0, + sizeof(struct cache_mp_write_session_request)); + TRACE_OUT(init_cache_mp_write_session_request); +} + +void +finalize_cache_mp_write_session_request( + struct cache_mp_write_session_request *mp_ws_request) +{ + + TRACE_IN(finalize_cache_mp_write_session_request); + free(mp_ws_request->entry); + TRACE_OUT(finalize_cache_mp_write_session_request); +} + +struct cache_mp_write_session_request * +get_cache_mp_write_session_request(struct comm_element *element) +{ + + TRACE_IN(get_cache_mp_write_session_request); + assert(element->type == CET_MP_WRITE_SESSION_REQUEST); + TRACE_OUT(get_cache_mp_write_session_request); + return (&element->c_mp_ws_request); +} + +void +init_cache_mp_write_session_response( + struct cache_mp_write_session_response *mp_ws_response) +{ + + TRACE_IN(init_cache_mp_write_session_response); + memset(mp_ws_response, 0, + sizeof(struct cache_mp_write_session_response)); + TRACE_OUT(init_cache_mp_write_session_response); +} + +void +finalize_cache_mp_write_session_response( + struct cache_mp_write_session_response *mp_ws_response) +{ + + TRACE_IN(finalize_cache_mp_write_session_response); + TRACE_OUT(finalize_cache_mp_write_session_response); +} + +struct cache_mp_write_session_response * +get_cache_mp_write_session_response(struct comm_element *element) +{ + + TRACE_IN(get_cache_mp_write_session_response); + assert(element->type == CET_MP_WRITE_SESSION_RESPONSE); + TRACE_OUT(get_cache_mp_write_session_response); + return (&element->c_mp_ws_response); +} + +void +init_cache_mp_write_session_write_request( + struct cache_mp_write_session_write_request *mp_ws_write_request) +{ + + TRACE_IN(init_cache_mp_write_session_write_request); + memset(mp_ws_write_request, 0, + sizeof(struct cache_mp_write_session_write_request)); + TRACE_OUT(init_cache_mp_write_session_write_response); +} + +void +finalize_cache_mp_write_session_write_request( + struct cache_mp_write_session_write_request *mp_ws_write_request) +{ + + TRACE_IN(finalize_cache_mp_write_session_write_request); + free(mp_ws_write_request->data); + TRACE_OUT(finalize_cache_mp_write_session_write_request); +} + +struct cache_mp_write_session_write_request * +get_cache_mp_write_session_write_request(struct comm_element *element) +{ + + TRACE_IN(get_cache_mp_write_session_write_request); + assert(element->type == CET_MP_WRITE_SESSION_WRITE_REQUEST); + TRACE_OUT(get_cache_mp_write_session_write_request); + return (&element->c_mp_ws_write_request); +} + +void +init_cache_mp_write_session_write_response( + struct cache_mp_write_session_write_response *mp_ws_write_response) +{ + + TRACE_IN(init_cache_mp_write_session_write_response); + memset(mp_ws_write_response, 0, + sizeof(struct cache_mp_write_session_write_response)); + TRACE_OUT(init_cache_mp_write_session_write_response); +} + +void +finalize_cache_mp_write_session_write_response( + struct cache_mp_write_session_write_response *mp_ws_write_response) +{ + + TRACE_IN(finalize_cache_mp_write_session_write_response); + TRACE_OUT(finalize_cache_mp_write_session_write_response); +} + +struct cache_mp_write_session_write_response * +get_cache_mp_write_session_write_response(struct comm_element *element) +{ + + TRACE_IN(get_cache_mp_write_session_write_response); + assert(element->type == CET_MP_WRITE_SESSION_WRITE_RESPONSE); + TRACE_OUT(get_cache_mp_write_session_write_response); + return (&element->c_mp_ws_write_response); +} + +void +init_cache_mp_read_session_request( + struct cache_mp_read_session_request *mp_rs_request) +{ + + TRACE_IN(init_cache_mp_read_session_request); + memset(mp_rs_request, 0, sizeof(struct cache_mp_read_session_request)); + TRACE_OUT(init_cache_mp_read_session_request); +} + +void +finalize_cache_mp_read_session_request( + struct cache_mp_read_session_request *mp_rs_request) +{ + + TRACE_IN(finalize_cache_mp_read_session_request); + free(mp_rs_request->entry); + TRACE_OUT(finalize_cache_mp_read_session_request); +} + +struct cache_mp_read_session_request * +get_cache_mp_read_session_request(struct comm_element *element) +{ + + TRACE_IN(get_cache_mp_read_session_request); + assert(element->type == CET_MP_READ_SESSION_REQUEST); + TRACE_OUT(get_cache_mp_read_session_request); + return (&element->c_mp_rs_request); +} + +void +init_cache_mp_read_session_response( + struct cache_mp_read_session_response *mp_rs_response) +{ + + TRACE_IN(init_cache_mp_read_session_response); + memset(mp_rs_response, 0, + sizeof(struct cache_mp_read_session_response)); + TRACE_OUT(init_cache_mp_read_session_response); +} + +void +finalize_cache_mp_read_session_response( + struct cache_mp_read_session_response *mp_rs_response) +{ + + TRACE_IN(finalize_cache_mp_read_session_response); + TRACE_OUT(finalize_cache_mp_read_session_response); +} + +struct cache_mp_read_session_response * +get_cache_mp_read_session_response(struct comm_element *element) +{ + + TRACE_IN(get_cache_mp_read_session_response); + assert(element->type == CET_MP_READ_SESSION_RESPONSE); + TRACE_OUT(get_cache_mp_read_session_response); + return (&element->c_mp_rs_response); +} + +void +init_cache_mp_read_session_read_response( + struct cache_mp_read_session_read_response *mp_ws_read_response) +{ + + TRACE_IN(init_cache_mp_read_session_read_response); + memset(mp_ws_read_response, 0, + sizeof(struct cache_mp_read_session_read_response)); + TRACE_OUT(init_cache_mp_read_session_read_response); +} + +void +finalize_cache_mp_read_session_read_response( + struct cache_mp_read_session_read_response *mp_rs_read_response) +{ + + TRACE_IN(finalize_cache_mp_read_session_read_response); + free(mp_rs_read_response->data); + TRACE_OUT(finalize_cache_mp_read_session_read_response); +} + +struct cache_mp_read_session_read_response * +get_cache_mp_read_session_read_response(struct comm_element *element) +{ + + TRACE_IN(get_cache_mp_read_session_read_response); + assert(element->type == CET_MP_READ_SESSION_READ_RESPONSE); + TRACE_OUT(get_cache_mp_read_session_read_response); + return (&element->c_mp_rs_read_response); +} diff --git a/usr.sbin/nscd/protocol.h b/usr.sbin/nscd/protocol.h new file mode 100644 index 000000000000..7fadbfc4c2fb --- /dev/null +++ b/usr.sbin/nscd/protocol.h @@ -0,0 +1,265 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_PROTOCOL_H__ +#define __CACHED_PROTOCOL_H__ + +#include + +/* maximum buffer size to receive - larger buffers are not allowed */ +#define MAX_BUFFER_SIZE (1 << 20) + +/* buffer size correctness checking routine */ +#define BUFSIZE_CORRECT(x) (((x) > 0) && ((x) < MAX_BUFFER_SIZE)) +#define BUFSIZE_INVALID(x) (!BUFSIZE_CORRECT(x)) + +/* structures below represent the data that are sent/received by the daemon */ +struct cache_write_request +{ + char *entry; + char *cache_key; + char *data; + + size_t entry_length; + size_t cache_key_size; + size_t data_size; +}; + +struct cache_write_response +{ + int error_code; +}; + +struct cache_read_request +{ + char *entry; + char *cache_key; + + size_t entry_length; + size_t cache_key_size; +}; + +struct cache_read_response +{ + char *data; // ignored if error_code is not 0 + size_t data_size; // ignored if error_code is not 0 + + int error_code; +}; + +enum transformation_type { + TT_USER = 0, // tranform only the entries of the caller + TT_ALL = 1 // transform all entries +}; + +struct cache_transform_request +{ + char *entry; // ignored if entry_length is 0 + size_t entry_length; + + int transformation_type; +}; + +struct cache_transform_response +{ + int error_code; +}; + +struct cache_mp_write_session_request { + char *entry; + size_t entry_length; +}; + +struct cache_mp_write_session_response { + int error_code; +}; + +struct cache_mp_write_session_write_request { + char *data; + size_t data_size; +}; + +struct cache_mp_write_session_write_response { + int error_code; +}; + +struct cache_mp_read_session_request { + char *entry; + size_t entry_length; +}; + +struct cache_mp_read_session_response { + int error_code; +}; + +struct cache_mp_read_session_read_response { + char *data; + size_t data_size; + + int error_code; +}; + + +enum comm_element_t { + CET_UNDEFINED = 0, + CET_WRITE_REQUEST = 1, + CET_WRITE_RESPONSE = 2, + CET_READ_REQUEST = 3, + CET_READ_RESPONSE = 4, + CET_TRANSFORM_REQUEST = 5, + CET_TRANSFORM_RESPONSE = 6, + CET_MP_WRITE_SESSION_REQUEST = 7, + CET_MP_WRITE_SESSION_RESPONSE = 8, + CET_MP_WRITE_SESSION_WRITE_REQUEST = 9, + CET_MP_WRITE_SESSION_WRITE_RESPONSE = 10, + CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION = 11, + CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION = 12, + CET_MP_READ_SESSION_REQUEST = 13, + CET_MP_READ_SESSION_RESPONSE = 14, + CET_MP_READ_SESSION_READ_REQUEST = 15, + CET_MP_READ_SESSION_READ_RESPONSE = 16, + CET_MP_READ_SESSION_CLOSE_NOTIFICATION = 17, + CET_MAX = 18 +}; + +/* + * The comm_element is used as the holder of any known (defined above) data + * type that is to be sent/received. + */ +struct comm_element +{ + union { + struct cache_write_request c_write_request; + struct cache_write_response c_write_response; + struct cache_read_request c_read_request; + struct cache_read_response c_read_response; + struct cache_transform_request c_transform_request; + struct cache_transform_response c_transform_response; + + struct cache_mp_write_session_request c_mp_ws_request; + struct cache_mp_write_session_response c_mp_ws_response; + struct cache_mp_write_session_write_request c_mp_ws_write_request; + struct cache_mp_write_session_write_response c_mp_ws_write_response; + + struct cache_mp_read_session_request c_mp_rs_request; + struct cache_mp_read_session_response c_mp_rs_response; + struct cache_mp_read_session_read_response c_mp_rs_read_response; + }; + enum comm_element_t type; +}; + +extern void init_comm_element(struct comm_element *, enum comm_element_t type); +extern void finalize_comm_element(struct comm_element *); + +/* + * For each type of data, there is three functions (init/finalize/get), that + * used with comm_element structure + */ +extern void init_cache_write_request(struct cache_write_request *); +extern void finalize_cache_write_request(struct cache_write_request *); +extern struct cache_write_request *get_cache_write_request( + struct comm_element *); + +extern void init_cache_write_response(struct cache_write_response *); +extern void finalize_cache_write_response(struct cache_write_response *); +extern struct cache_write_response *get_cache_write_response( + struct comm_element *); + +extern void init_cache_read_request(struct cache_read_request *); +extern void finalize_cache_read_request(struct cache_read_request *); +extern struct cache_read_request *get_cache_read_request( + struct comm_element *); + +extern void init_cache_read_response(struct cache_read_response *); +extern void finalize_cache_read_response(struct cache_read_response *); +extern struct cache_read_response *get_cache_read_response( + struct comm_element *); + +extern void init_cache_transform_request(struct cache_transform_request *); +extern void finalize_cache_transform_request(struct cache_transform_request *); +extern struct cache_transform_request *get_cache_transform_request( + struct comm_element *); + +extern void init_cache_transform_response(struct cache_transform_response *); +extern void finalize_cache_transform_response( + struct cache_transform_response *); +extern struct cache_transform_response *get_cache_transform_response( + struct comm_element *); + +extern void init_cache_mp_write_session_request( + struct cache_mp_write_session_request *); +extern void finalize_cache_mp_write_session_request( + struct cache_mp_write_session_request *); +extern struct cache_mp_write_session_request * + get_cache_mp_write_session_request( + struct comm_element *); + +extern void init_cache_mp_write_session_response( + struct cache_mp_write_session_response *); +extern void finalize_cache_mp_write_session_response( + struct cache_mp_write_session_response *); +extern struct cache_mp_write_session_response * + get_cache_mp_write_session_response(struct comm_element *); + +extern void init_cache_mp_write_session_write_request( + struct cache_mp_write_session_write_request *); +extern void finalize_cache_mp_write_session_write_request( + struct cache_mp_write_session_write_request *); +extern struct cache_mp_write_session_write_request * + get_cache_mp_write_session_write_request(struct comm_element *); + +extern void init_cache_mp_write_session_write_response( + struct cache_mp_write_session_write_response *); +extern void finalize_cache_mp_write_session_write_response( + struct cache_mp_write_session_write_response *); +extern struct cache_mp_write_session_write_response * + get_cache_mp_write_session_write_response(struct comm_element *); + +extern void init_cache_mp_read_session_request( + struct cache_mp_read_session_request *); +extern void finalize_cache_mp_read_session_request( + struct cache_mp_read_session_request *); +extern struct cache_mp_read_session_request *get_cache_mp_read_session_request( + struct comm_element *); + +extern void init_cache_mp_read_session_response( + struct cache_mp_read_session_response *); +extern void finalize_cache_mp_read_session_response( + struct cache_mp_read_session_response *); +extern struct cache_mp_read_session_response * + get_cache_mp_read_session_response( + struct comm_element *); + +extern void init_cache_mp_read_session_read_response( + struct cache_mp_read_session_read_response *); +extern void finalize_cache_mp_read_session_read_response( + struct cache_mp_read_session_read_response *); +extern struct cache_mp_read_session_read_response * + get_cache_mp_read_session_read_response(struct comm_element *); + +#endif diff --git a/usr.sbin/nscd/query.c b/usr.sbin/nscd/query.c new file mode 100644 index 000000000000..28882c3d01df --- /dev/null +++ b/usr.sbin/nscd/query.c @@ -0,0 +1,1278 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "debug.h" +#include "query.h" +#include "log.h" +#include "mp_ws_query.h" +#include "mp_rs_query.h" +#include "singletons.h" + +static const char negative_data[1] = { 0 }; + +extern void get_time_func(struct timeval *); + +static void clear_config_entry(struct configuration_entry *); +static void clear_config_entry_part(struct configuration_entry *, + const char *, size_t); + +static int on_query_startup(struct query_state *); +static void on_query_destroy(struct query_state *); + +static int on_read_request_read1(struct query_state *); +static int on_read_request_read2(struct query_state *); +static int on_read_request_process(struct query_state *); +static int on_read_response_write1(struct query_state *); +static int on_read_response_write2(struct query_state *); + +static int on_rw_mapper(struct query_state *); + +static int on_transform_request_read1(struct query_state *); +static int on_transform_request_read2(struct query_state *); +static int on_transform_request_process(struct query_state *); +static int on_transform_response_write1(struct query_state *); + +static int on_write_request_read1(struct query_state *); +static int on_write_request_read2(struct query_state *); +static int on_negative_write_request_process(struct query_state *); +static int on_write_request_process(struct query_state *); +static int on_write_response_write1(struct query_state *); + +/* + * Clears the specified configuration entry (clears the cache for positive and + * and negative entries) and also for all multipart entries. + */ +static void +clear_config_entry(struct configuration_entry *config_entry) +{ + size_t i; + + TRACE_IN(clear_config_entry); + configuration_lock_entry(config_entry, CELT_POSITIVE); + if (config_entry->positive_cache_entry != NULL) + transform_cache_entry( + config_entry->positive_cache_entry, + CTT_CLEAR); + configuration_unlock_entry(config_entry, CELT_POSITIVE); + + configuration_lock_entry(config_entry, CELT_NEGATIVE); + if (config_entry->negative_cache_entry != NULL) + transform_cache_entry( + config_entry->negative_cache_entry, + CTT_CLEAR); + configuration_unlock_entry(config_entry, CELT_NEGATIVE); + + configuration_lock_entry(config_entry, CELT_MULTIPART); + for (i = 0; i < config_entry->mp_cache_entries_size; ++i) + transform_cache_entry( + config_entry->mp_cache_entries[i], + CTT_CLEAR); + configuration_unlock_entry(config_entry, CELT_MULTIPART); + + TRACE_OUT(clear_config_entry); +} + +/* + * Clears the specified configuration entry by deleting only the elements, + * that are owned by the user with specified eid_str. + */ +static void +clear_config_entry_part(struct configuration_entry *config_entry, + const char *eid_str, size_t eid_str_length) +{ + cache_entry *start, *finish, *mp_entry; + TRACE_IN(clear_config_entry_part); + configuration_lock_entry(config_entry, CELT_POSITIVE); + if (config_entry->positive_cache_entry != NULL) + transform_cache_entry_part( + config_entry->positive_cache_entry, + CTT_CLEAR, eid_str, eid_str_length, KPPT_LEFT); + configuration_unlock_entry(config_entry, CELT_POSITIVE); + + configuration_lock_entry(config_entry, CELT_NEGATIVE); + if (config_entry->negative_cache_entry != NULL) + transform_cache_entry_part( + config_entry->negative_cache_entry, + CTT_CLEAR, eid_str, eid_str_length, KPPT_LEFT); + configuration_unlock_entry(config_entry, CELT_NEGATIVE); + + configuration_lock_entry(config_entry, CELT_MULTIPART); + if (configuration_entry_find_mp_cache_entries(config_entry, + eid_str, &start, &finish) == 0) { + for (mp_entry = start; mp_entry != finish; ++mp_entry) + transform_cache_entry(*mp_entry, CTT_CLEAR); + } + configuration_unlock_entry(config_entry, CELT_MULTIPART); + + TRACE_OUT(clear_config_entry_part); +} + +/* + * This function is assigned to the query_state structue on its creation. + * It's main purpose is to receive credentials from the client. + */ +static int +on_query_startup(struct query_state *qstate) +{ + struct msghdr cred_hdr; + struct iovec iov; + int elem_type; + + struct { + struct cmsghdr hdr; + struct cmsgcred creds; + } cmsg; + + TRACE_IN(on_query_startup); + assert(qstate != NULL); + + memset(&cred_hdr, 0, sizeof(struct msghdr)); + cred_hdr.msg_iov = &iov; + cred_hdr.msg_iovlen = 1; + cred_hdr.msg_control = &cmsg; + cred_hdr.msg_controllen = sizeof(cmsg); + + memset(&iov, 0, sizeof(struct iovec)); + iov.iov_base = &elem_type; + iov.iov_len = sizeof(int); + + if (recvmsg(qstate->sockfd, &cred_hdr, 0) == -1) { + TRACE_OUT(on_query_startup); + return (-1); + } + + if (cmsg.hdr.cmsg_len != sizeof cmsg + || cmsg.hdr.cmsg_level != SOL_SOCKET + || cmsg.hdr.cmsg_type != SCM_CREDS) { + TRACE_OUT(on_query_startup); + return (-1); + } + + qstate->uid = cmsg.creds.cmcred_uid; + qstate->gid = cmsg.creds.cmcred_gid; + +#if defined(NS_CACHED_EID_CHECKING) || defined(NS_STRICT_CACHED_EID_CHECKING) +/* + * This check is probably a bit redundant - per-user cache is always separated + * by the euid/egid pair + */ + if (check_query_eids(qstate) != 0) { +#ifdef NS_STRICT_CACHED_EID_CHECKING + TRACE_OUT(on_query_startup); + return (-1); +#else + if ((elem_type != CET_READ_REQUEST) && + (elem_type != CET_MP_READ_SESSION_REQUEST) && + (elem_type != CET_WRITE_REQUEST) && + (elem_type != CET_MP_WRITE_SESSION_REQUEST)) { + TRACE_OUT(on_query_startup); + return (-1); + } +#endif + } +#endif + + switch (elem_type) { + case CET_WRITE_REQUEST: + qstate->process_func = on_write_request_read1; + break; + case CET_READ_REQUEST: + qstate->process_func = on_read_request_read1; + break; + case CET_TRANSFORM_REQUEST: + qstate->process_func = on_transform_request_read1; + break; + case CET_MP_WRITE_SESSION_REQUEST: + qstate->process_func = on_mp_write_session_request_read1; + break; + case CET_MP_READ_SESSION_REQUEST: + qstate->process_func = on_mp_read_session_request_read1; + break; + default: + TRACE_OUT(on_query_startup); + return (-1); + } + + qstate->kevent_watermark = 0; + TRACE_OUT(on_query_startup); + return (0); +} + +/* + * on_rw_mapper is used to process multiple read/write requests during + * one connection session. It's never called in the beginning (on query_state + * creation) as it does not process the multipart requests and does not + * receive credentials + */ +static int +on_rw_mapper(struct query_state *qstate) +{ + ssize_t result; + int elem_type; + + TRACE_IN(on_rw_mapper); + if (qstate->kevent_watermark == 0) { + qstate->kevent_watermark = sizeof(int); + } else { + result = qstate->read_func(qstate, &elem_type, sizeof(int)); + if (result != sizeof(int)) { + TRACE_OUT(on_rw_mapper); + return (-1); + } + + switch (elem_type) { + case CET_WRITE_REQUEST: + qstate->kevent_watermark = sizeof(size_t); + qstate->process_func = on_write_request_read1; + break; + case CET_READ_REQUEST: + qstate->kevent_watermark = sizeof(size_t); + qstate->process_func = on_read_request_read1; + break; + default: + TRACE_OUT(on_rw_mapper); + return (-1); + break; + } + } + TRACE_OUT(on_rw_mapper); + return (0); +} + +/* + * The default query_destroy function + */ +static void +on_query_destroy(struct query_state *qstate) +{ + + TRACE_IN(on_query_destroy); + finalize_comm_element(&qstate->response); + finalize_comm_element(&qstate->request); + TRACE_OUT(on_query_destroy); +} + +/* + * The functions below are used to process write requests. + * - on_write_request_read1 and on_write_request_read2 read the request itself + * - on_write_request_process processes it (if the client requests to + * cache the negative result, the on_negative_write_request_process is used) + * - on_write_response_write1 sends the response + */ +static int +on_write_request_read1(struct query_state *qstate) +{ + struct cache_write_request *write_request; + ssize_t result; + + TRACE_IN(on_write_request_read1); + if (qstate->kevent_watermark == 0) + qstate->kevent_watermark = sizeof(size_t) * 3; + else { + init_comm_element(&qstate->request, CET_WRITE_REQUEST); + write_request = get_cache_write_request(&qstate->request); + + result = qstate->read_func(qstate, &write_request->entry_length, + sizeof(size_t)); + result += qstate->read_func(qstate, + &write_request->cache_key_size, sizeof(size_t)); + result += qstate->read_func(qstate, + &write_request->data_size, sizeof(size_t)); + + if (result != sizeof(size_t) * 3) { + TRACE_OUT(on_write_request_read1); + return (-1); + } + + if (BUFSIZE_INVALID(write_request->entry_length) || + BUFSIZE_INVALID(write_request->cache_key_size) || + (BUFSIZE_INVALID(write_request->data_size) && + (write_request->data_size != 0))) { + TRACE_OUT(on_write_request_read1); + return (-1); + } + + write_request->entry = (char *)malloc( + write_request->entry_length + 1); + assert(write_request->entry != NULL); + memset(write_request->entry, 0, + write_request->entry_length + 1); + + write_request->cache_key = (char *)malloc( + write_request->cache_key_size + + qstate->eid_str_length); + assert(write_request->cache_key != NULL); + memcpy(write_request->cache_key, qstate->eid_str, + qstate->eid_str_length); + memset(write_request->cache_key + qstate->eid_str_length, 0, + write_request->cache_key_size); + + if (write_request->data_size != 0) { + write_request->data = (char *)malloc( + write_request->data_size); + assert(write_request->data != NULL); + memset(write_request->data, 0, + write_request->data_size); + } + + qstate->kevent_watermark = write_request->entry_length + + write_request->cache_key_size + + write_request->data_size; + qstate->process_func = on_write_request_read2; + } + + TRACE_OUT(on_write_request_read1); + return (0); +} + +static int +on_write_request_read2(struct query_state *qstate) +{ + struct cache_write_request *write_request; + ssize_t result; + + TRACE_IN(on_write_request_read2); + write_request = get_cache_write_request(&qstate->request); + + result = qstate->read_func(qstate, write_request->entry, + write_request->entry_length); + result += qstate->read_func(qstate, write_request->cache_key + + qstate->eid_str_length, write_request->cache_key_size); + if (write_request->data_size != 0) + result += qstate->read_func(qstate, write_request->data, + write_request->data_size); + + if (result != qstate->kevent_watermark) { + TRACE_OUT(on_write_request_read2); + return (-1); + } + write_request->cache_key_size += qstate->eid_str_length; + + qstate->kevent_watermark = 0; + if (write_request->data_size != 0) + qstate->process_func = on_write_request_process; + else + qstate->process_func = on_negative_write_request_process; + TRACE_OUT(on_write_request_read2); + return (0); +} + +static int +on_write_request_process(struct query_state *qstate) +{ + struct cache_write_request *write_request; + struct cache_write_response *write_response; + cache_entry c_entry; + + TRACE_IN(on_write_request_process); + init_comm_element(&qstate->response, CET_WRITE_RESPONSE); + write_response = get_cache_write_response(&qstate->response); + write_request = get_cache_write_request(&qstate->request); + + qstate->config_entry = configuration_find_entry( + s_configuration, write_request->entry); + + if (qstate->config_entry == NULL) { + write_response->error_code = ENOENT; + + LOG_ERR_2("write_request", "can't find configuration" + " entry '%s'. aborting request", write_request->entry); + goto fin; + } + + if (qstate->config_entry->enabled == 0) { + write_response->error_code = EACCES; + + LOG_ERR_2("write_request", + "configuration entry '%s' is disabled", + write_request->entry); + goto fin; + } + + if (qstate->config_entry->perform_actual_lookups != 0) { + write_response->error_code = EOPNOTSUPP; + + LOG_ERR_2("write_request", + "entry '%s' performs lookups by itself: " + "can't write to it", write_request->entry); + goto fin; + } + + configuration_lock_rdlock(s_configuration); + c_entry = find_cache_entry(s_cache, + qstate->config_entry->positive_cache_params.entry_name); + configuration_unlock(s_configuration); + if (c_entry != NULL) { + configuration_lock_entry(qstate->config_entry, CELT_POSITIVE); + qstate->config_entry->positive_cache_entry = c_entry; + write_response->error_code = cache_write(c_entry, + write_request->cache_key, + write_request->cache_key_size, + write_request->data, + write_request->data_size); + configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE); + + if ((qstate->config_entry->common_query_timeout.tv_sec != 0) || + (qstate->config_entry->common_query_timeout.tv_usec != 0)) + memcpy(&qstate->timeout, + &qstate->config_entry->common_query_timeout, + sizeof(struct timeval)); + + } else + write_response->error_code = -1; + +fin: + qstate->kevent_filter = EVFILT_WRITE; + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_write_response_write1; + + TRACE_OUT(on_write_request_process); + return (0); +} + +static int +on_negative_write_request_process(struct query_state *qstate) +{ + struct cache_write_request *write_request; + struct cache_write_response *write_response; + cache_entry c_entry; + + TRACE_IN(on_negative_write_request_process); + init_comm_element(&qstate->response, CET_WRITE_RESPONSE); + write_response = get_cache_write_response(&qstate->response); + write_request = get_cache_write_request(&qstate->request); + + qstate->config_entry = configuration_find_entry ( + s_configuration, write_request->entry); + + if (qstate->config_entry == NULL) { + write_response->error_code = ENOENT; + + LOG_ERR_2("negative_write_request", + "can't find configuration" + " entry '%s'. aborting request", write_request->entry); + goto fin; + } + + if (qstate->config_entry->enabled == 0) { + write_response->error_code = EACCES; + + LOG_ERR_2("negative_write_request", + "configuration entry '%s' is disabled", + write_request->entry); + goto fin; + } + + if (qstate->config_entry->perform_actual_lookups != 0) { + write_response->error_code = EOPNOTSUPP; + + LOG_ERR_2("negative_write_request", + "entry '%s' performs lookups by itself: " + "can't write to it", write_request->entry); + goto fin; + } else { +#ifdef NS_CACHED_EID_CHECKING + if (check_query_eids(qstate) != 0) { + write_response->error_code = EPERM; + goto fin; + } +#endif + } + + configuration_lock_rdlock(s_configuration); + c_entry = find_cache_entry(s_cache, + qstate->config_entry->negative_cache_params.entry_name); + configuration_unlock(s_configuration); + if (c_entry != NULL) { + configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE); + qstate->config_entry->negative_cache_entry = c_entry; + write_response->error_code = cache_write(c_entry, + write_request->cache_key, + write_request->cache_key_size, + negative_data, + sizeof(negative_data)); + configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE); + + if ((qstate->config_entry->common_query_timeout.tv_sec != 0) || + (qstate->config_entry->common_query_timeout.tv_usec != 0)) + memcpy(&qstate->timeout, + &qstate->config_entry->common_query_timeout, + sizeof(struct timeval)); + } else + write_response->error_code = -1; + +fin: + qstate->kevent_filter = EVFILT_WRITE; + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_write_response_write1; + + TRACE_OUT(on_negative_write_request_process); + return (0); +} + +static int +on_write_response_write1(struct query_state *qstate) +{ + struct cache_write_response *write_response; + ssize_t result; + + TRACE_IN(on_write_response_write1); + write_response = get_cache_write_response(&qstate->response); + result = qstate->write_func(qstate, &write_response->error_code, + sizeof(int)); + if (result != sizeof(int)) { + TRACE_OUT(on_write_response_write1); + return (-1); + } + + finalize_comm_element(&qstate->request); + finalize_comm_element(&qstate->response); + + qstate->kevent_watermark = sizeof(int); + qstate->kevent_filter = EVFILT_READ; + qstate->process_func = on_rw_mapper; + + TRACE_OUT(on_write_response_write1); + return (0); +} + +/* + * The functions below are used to process read requests. + * - on_read_request_read1 and on_read_request_read2 read the request itself + * - on_read_request_process processes it + * - on_read_response_write1 and on_read_response_write2 send the response + */ +static int +on_read_request_read1(struct query_state *qstate) +{ + struct cache_read_request *read_request; + ssize_t result; + + TRACE_IN(on_read_request_read1); + if (qstate->kevent_watermark == 0) + qstate->kevent_watermark = sizeof(size_t) * 2; + else { + init_comm_element(&qstate->request, CET_READ_REQUEST); + read_request = get_cache_read_request(&qstate->request); + + result = qstate->read_func(qstate, + &read_request->entry_length, sizeof(size_t)); + result += qstate->read_func(qstate, + &read_request->cache_key_size, sizeof(size_t)); + + if (result != sizeof(size_t) * 2) { + TRACE_OUT(on_read_request_read1); + return (-1); + } + + if (BUFSIZE_INVALID(read_request->entry_length) || + BUFSIZE_INVALID(read_request->cache_key_size)) { + TRACE_OUT(on_read_request_read1); + return (-1); + } + + read_request->entry = (char *)malloc( + read_request->entry_length + 1); + assert(read_request->entry != NULL); + memset(read_request->entry, 0, read_request->entry_length + 1); + + read_request->cache_key = (char *)malloc( + read_request->cache_key_size + + qstate->eid_str_length); + assert(read_request->cache_key != NULL); + memcpy(read_request->cache_key, qstate->eid_str, + qstate->eid_str_length); + memset(read_request->cache_key + qstate->eid_str_length, 0, + read_request->cache_key_size); + + qstate->kevent_watermark = read_request->entry_length + + read_request->cache_key_size; + qstate->process_func = on_read_request_read2; + } + + TRACE_OUT(on_read_request_read1); + return (0); +} + +static int +on_read_request_read2(struct query_state *qstate) +{ + struct cache_read_request *read_request; + ssize_t result; + + TRACE_IN(on_read_request_read2); + read_request = get_cache_read_request(&qstate->request); + + result = qstate->read_func(qstate, read_request->entry, + read_request->entry_length); + result += qstate->read_func(qstate, + read_request->cache_key + qstate->eid_str_length, + read_request->cache_key_size); + + if (result != qstate->kevent_watermark) { + TRACE_OUT(on_read_request_read2); + return (-1); + } + read_request->cache_key_size += qstate->eid_str_length; + + qstate->kevent_watermark = 0; + qstate->process_func = on_read_request_process; + + TRACE_OUT(on_read_request_read2); + return (0); +} + +static int +on_read_request_process(struct query_state *qstate) +{ + struct cache_read_request *read_request; + struct cache_read_response *read_response; + cache_entry c_entry, neg_c_entry; + + struct agent *lookup_agent; + struct common_agent *c_agent; + int res; + + TRACE_IN(on_read_request_process); + init_comm_element(&qstate->response, CET_READ_RESPONSE); + read_response = get_cache_read_response(&qstate->response); + read_request = get_cache_read_request(&qstate->request); + + qstate->config_entry = configuration_find_entry( + s_configuration, read_request->entry); + if (qstate->config_entry == NULL) { + read_response->error_code = ENOENT; + + LOG_ERR_2("read_request", + "can't find configuration " + "entry '%s'. aborting request", read_request->entry); + goto fin; + } + + if (qstate->config_entry->enabled == 0) { + read_response->error_code = EACCES; + + LOG_ERR_2("read_request", + "configuration entry '%s' is disabled", + read_request->entry); + goto fin; + } + + /* + * if we perform lookups by ourselves, then we don't need to separate + * cache entries by euid and egid + */ + if (qstate->config_entry->perform_actual_lookups != 0) + memset(read_request->cache_key, 0, qstate->eid_str_length); + else { +#ifdef NS_CACHED_EID_CHECKING + if (check_query_eids(qstate) != 0) { + /* if the lookup is not self-performing, we check for clients euid/egid */ + read_response->error_code = EPERM; + goto fin; + } +#endif + } + + configuration_lock_rdlock(s_configuration); + c_entry = find_cache_entry(s_cache, + qstate->config_entry->positive_cache_params.entry_name); + neg_c_entry = find_cache_entry(s_cache, + qstate->config_entry->negative_cache_params.entry_name); + configuration_unlock(s_configuration); + if ((c_entry != NULL) && (neg_c_entry != NULL)) { + configuration_lock_entry(qstate->config_entry, CELT_POSITIVE); + qstate->config_entry->positive_cache_entry = c_entry; + read_response->error_code = cache_read(c_entry, + read_request->cache_key, + read_request->cache_key_size, NULL, + &read_response->data_size); + + if (read_response->error_code == -2) { + read_response->data = (char *)malloc( + read_response->data_size); + assert(read_response != NULL); + read_response->error_code = cache_read(c_entry, + read_request->cache_key, + read_request->cache_key_size, + read_response->data, + &read_response->data_size); + } + configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE); + + configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE); + qstate->config_entry->negative_cache_entry = neg_c_entry; + if (read_response->error_code == -1) { + read_response->error_code = cache_read(neg_c_entry, + read_request->cache_key, + read_request->cache_key_size, NULL, + &read_response->data_size); + + if (read_response->error_code == -2) { + read_response->error_code = 0; + read_response->data = NULL; + read_response->data_size = 0; + } + } + configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE); + + if ((read_response->error_code == -1) && + (qstate->config_entry->perform_actual_lookups != 0)) { + free(read_response->data); + read_response->data = NULL; + read_response->data_size = 0; + + lookup_agent = find_agent(s_agent_table, + read_request->entry, COMMON_AGENT); + + if ((lookup_agent != NULL) && + (lookup_agent->type == COMMON_AGENT)) { + c_agent = (struct common_agent *)lookup_agent; + res = c_agent->lookup_func( + read_request->cache_key + + qstate->eid_str_length, + read_request->cache_key_size - + qstate->eid_str_length, + &read_response->data, + &read_response->data_size); + + if (res == NS_SUCCESS) { + read_response->error_code = 0; + configuration_lock_entry( + qstate->config_entry, + CELT_POSITIVE); + cache_write(c_entry, + read_request->cache_key, + read_request->cache_key_size, + read_response->data, + read_response->data_size); + configuration_unlock_entry( + qstate->config_entry, + CELT_POSITIVE); + } else if ((res == NS_NOTFOUND) || + (res == NS_RETURN)) { + configuration_lock_entry( + qstate->config_entry, + CELT_NEGATIVE); + cache_write(neg_c_entry, + read_request->cache_key, + read_request->cache_key_size, + negative_data, + sizeof(negative_data)); + configuration_unlock_entry( + qstate->config_entry, + CELT_NEGATIVE); + + read_response->error_code = 0; + read_response->data = NULL; + read_response->data_size = 0; + } + } + } + + if ((qstate->config_entry->common_query_timeout.tv_sec != 0) || + (qstate->config_entry->common_query_timeout.tv_usec != 0)) + memcpy(&qstate->timeout, + &qstate->config_entry->common_query_timeout, + sizeof(struct timeval)); + } else + read_response->error_code = -1; + +fin: + qstate->kevent_filter = EVFILT_WRITE; + if (read_response->error_code == 0) + qstate->kevent_watermark = sizeof(int) + sizeof(size_t); + else + qstate->kevent_watermark = sizeof(int); + qstate->process_func = on_read_response_write1; + + TRACE_OUT(on_read_request_process); + return (0); +} + +static int +on_read_response_write1(struct query_state *qstate) +{ + struct cache_read_response *read_response; + ssize_t result; + + TRACE_IN(on_read_response_write1); + read_response = get_cache_read_response(&qstate->response); + + result = qstate->write_func(qstate, &read_response->error_code, + sizeof(int)); + + if (read_response->error_code == 0) { + result += qstate->write_func(qstate, &read_response->data_size, + sizeof(size_t)); + if (result != qstate->kevent_watermark) { + TRACE_OUT(on_read_response_write1); + return (-1); + } + + qstate->kevent_watermark = read_response->data_size; + qstate->process_func = on_read_response_write2; + } else { + if (result != qstate->kevent_watermark) { + TRACE_OUT(on_read_response_write1); + return (-1); + } + + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + } + + TRACE_OUT(on_read_response_write1); + return (0); +} + +static int +on_read_response_write2(struct query_state *qstate) +{ + struct cache_read_response *read_response; + ssize_t result; + + TRACE_IN(on_read_response_write2); + read_response = get_cache_read_response(&qstate->response); + if (read_response->data_size > 0) { + result = qstate->write_func(qstate, read_response->data, + read_response->data_size); + if (result != qstate->kevent_watermark) { + TRACE_OUT(on_read_response_write2); + return (-1); + } + } + + finalize_comm_element(&qstate->request); + finalize_comm_element(&qstate->response); + + qstate->kevent_watermark = sizeof(int); + qstate->kevent_filter = EVFILT_READ; + qstate->process_func = on_rw_mapper; + TRACE_OUT(on_read_response_write2); + return (0); +} + +/* + * The functions below are used to process write requests. + * - on_transform_request_read1 and on_transform_request_read2 read the + * request itself + * - on_transform_request_process processes it + * - on_transform_response_write1 sends the response + */ +static int +on_transform_request_read1(struct query_state *qstate) +{ + struct cache_transform_request *transform_request; + ssize_t result; + + TRACE_IN(on_transform_request_read1); + if (qstate->kevent_watermark == 0) + qstate->kevent_watermark = sizeof(size_t) + sizeof(int); + else { + init_comm_element(&qstate->request, CET_TRANSFORM_REQUEST); + transform_request = + get_cache_transform_request(&qstate->request); + + result = qstate->read_func(qstate, + &transform_request->entry_length, sizeof(size_t)); + result += qstate->read_func(qstate, + &transform_request->transformation_type, sizeof(int)); + + if (result != sizeof(size_t) + sizeof(int)) { + TRACE_OUT(on_transform_request_read1); + return (-1); + } + + if ((transform_request->transformation_type != TT_USER) && + (transform_request->transformation_type != TT_ALL)) { + TRACE_OUT(on_transform_request_read1); + return (-1); + } + + if (transform_request->entry_length != 0) { + if (BUFSIZE_INVALID(transform_request->entry_length)) { + TRACE_OUT(on_transform_request_read1); + return (-1); + } + + transform_request->entry = (char *)malloc( + transform_request->entry_length + 1); + assert(transform_request->entry != NULL); + memset(transform_request->entry, 0, + transform_request->entry_length + 1); + + qstate->process_func = on_transform_request_read2; + } else + qstate->process_func = on_transform_request_process; + + qstate->kevent_watermark = transform_request->entry_length; + } + + TRACE_OUT(on_transform_request_read1); + return (0); +} + +static int +on_transform_request_read2(struct query_state *qstate) +{ + struct cache_transform_request *transform_request; + ssize_t result; + + TRACE_IN(on_transform_request_read2); + transform_request = get_cache_transform_request(&qstate->request); + + result = qstate->read_func(qstate, transform_request->entry, + transform_request->entry_length); + + if (result != qstate->kevent_watermark) { + TRACE_OUT(on_transform_request_read2); + return (-1); + } + + qstate->kevent_watermark = 0; + qstate->process_func = on_transform_request_process; + + TRACE_OUT(on_transform_request_read2); + return (0); +} + +static int +on_transform_request_process(struct query_state *qstate) +{ + struct cache_transform_request *transform_request; + struct cache_transform_response *transform_response; + struct configuration_entry *config_entry; + size_t i, size; + + TRACE_IN(on_transform_request_process); + init_comm_element(&qstate->response, CET_TRANSFORM_RESPONSE); + transform_response = get_cache_transform_response(&qstate->response); + transform_request = get_cache_transform_request(&qstate->request); + + switch (transform_request->transformation_type) { + case TT_USER: + if (transform_request->entry == NULL) { + size = configuration_get_entries_size(s_configuration); + for (i = 0; i < size; ++i) { + config_entry = configuration_get_entry( + s_configuration, i); + + if (config_entry->perform_actual_lookups == 0) + clear_config_entry_part(config_entry, + qstate->eid_str, qstate->eid_str_length); + } + } else { + qstate->config_entry = configuration_find_entry( + s_configuration, transform_request->entry); + + if (qstate->config_entry == NULL) { + LOG_ERR_2("transform_request", + "can't find configuration" + " entry '%s'. aborting request", + transform_request->entry); + transform_response->error_code = -1; + goto fin; + } + + if (qstate->config_entry->perform_actual_lookups != 0) { + LOG_ERR_2("transform_request", + "can't transform the cache entry %s" + ", because it ised for actual lookups", + transform_request->entry); + transform_response->error_code = -1; + goto fin; + } + + clear_config_entry_part(qstate->config_entry, + qstate->eid_str, qstate->eid_str_length); + } + break; + case TT_ALL: + if (qstate->euid != 0) + transform_response->error_code = -1; + else { + if (transform_request->entry == NULL) { + size = configuration_get_entries_size( + s_configuration); + for (i = 0; i < size; ++i) { + clear_config_entry( + configuration_get_entry( + s_configuration, i)); + } + } else { + qstate->config_entry = configuration_find_entry( + s_configuration, + transform_request->entry); + + if (qstate->config_entry == NULL) { + LOG_ERR_2("transform_request", + "can't find configuration" + " entry '%s'. aborting request", + transform_request->entry); + transform_response->error_code = -1; + goto fin; + } + + clear_config_entry(qstate->config_entry); + } + } + break; + default: + transform_response->error_code = -1; + } + +fin: + qstate->kevent_watermark = 0; + qstate->process_func = on_transform_response_write1; + TRACE_OUT(on_transform_request_process); + return (0); +} + +static int +on_transform_response_write1(struct query_state *qstate) +{ + struct cache_transform_response *transform_response; + ssize_t result; + + TRACE_IN(on_transform_response_write1); + transform_response = get_cache_transform_response(&qstate->response); + result = qstate->write_func(qstate, &transform_response->error_code, + sizeof(int)); + if (result != sizeof(int)) { + TRACE_OUT(on_transform_response_write1); + return (-1); + } + + finalize_comm_element(&qstate->request); + finalize_comm_element(&qstate->response); + + qstate->kevent_watermark = 0; + qstate->process_func = NULL; + TRACE_OUT(on_transform_response_write1); + return (0); +} + +/* + * Checks if the client's euid and egid do not differ from its uid and gid. + * Returns 0 on success. + */ +int +check_query_eids(struct query_state *qstate) +{ + + return ((qstate->uid != qstate->euid) || (qstate->gid != qstate->egid) ? -1 : 0); +} + +/* + * Uses the qstate fields to process an "alternate" read - when the buffer is + * too large to be received during one socket read operation + */ +ssize_t +query_io_buffer_read(struct query_state *qstate, void *buf, size_t nbytes) +{ + ssize_t result; + + TRACE_IN(query_io_buffer_read); + if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL)) + return (-1); + + if (nbytes < qstate->io_buffer + qstate->io_buffer_size - + qstate->io_buffer_p) + result = nbytes; + else + result = qstate->io_buffer + qstate->io_buffer_size - + qstate->io_buffer_p; + + memcpy(buf, qstate->io_buffer_p, result); + qstate->io_buffer_p += result; + + if (qstate->io_buffer_p == qstate->io_buffer + qstate->io_buffer_size) { + free(qstate->io_buffer); + qstate->io_buffer = NULL; + + qstate->write_func = query_socket_write; + qstate->read_func = query_socket_read; + } + + TRACE_OUT(query_io_buffer_read); + return (result); +} + +/* + * Uses the qstate fields to process an "alternate" write - when the buffer is + * too large to be sent during one socket write operation + */ +ssize_t +query_io_buffer_write(struct query_state *qstate, const void *buf, + size_t nbytes) +{ + ssize_t result; + + TRACE_IN(query_io_buffer_write); + if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL)) + return (-1); + + if (nbytes < qstate->io_buffer + qstate->io_buffer_size - + qstate->io_buffer_p) + result = nbytes; + else + result = qstate->io_buffer + qstate->io_buffer_size - + qstate->io_buffer_p; + + memcpy(qstate->io_buffer_p, buf, result); + qstate->io_buffer_p += result; + + if (qstate->io_buffer_p == qstate->io_buffer + qstate->io_buffer_size) { + qstate->use_alternate_io = 1; + qstate->io_buffer_p = qstate->io_buffer; + + qstate->write_func = query_socket_write; + qstate->read_func = query_socket_read; + } + + TRACE_OUT(query_io_buffer_write); + return (result); +} + +/* + * The default "read" function, which reads data directly from socket + */ +ssize_t +query_socket_read(struct query_state *qstate, void *buf, size_t nbytes) +{ + ssize_t result; + + TRACE_IN(query_socket_read); + if (qstate->socket_failed != 0) { + TRACE_OUT(query_socket_read); + return (-1); + } + + result = read(qstate->sockfd, buf, nbytes); + if ((result == -1) || (result < nbytes)) + qstate->socket_failed = 1; + + TRACE_OUT(query_socket_read); + return (result); +} + +/* + * The default "write" function, which writes data directly to socket + */ +ssize_t +query_socket_write(struct query_state *qstate, const void *buf, size_t nbytes) +{ + ssize_t result; + + TRACE_IN(query_socket_write); + if (qstate->socket_failed != 0) { + TRACE_OUT(query_socket_write); + return (-1); + } + + result = write(qstate->sockfd, buf, nbytes); + if ((result == -1) || (result < nbytes)) + qstate->socket_failed = 1; + + TRACE_OUT(query_socket_write); + return (result); +} + +/* + * Initializes the query_state structure by filling it with the default values. + */ +struct query_state * +init_query_state(int sockfd, size_t kevent_watermark, uid_t euid, gid_t egid) +{ + struct query_state *retval; + + TRACE_IN(init_query_state); + retval = (struct query_state *)malloc(sizeof(struct query_state)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct query_state)); + + retval->sockfd = sockfd; + retval->kevent_filter = EVFILT_READ; + retval->kevent_watermark = kevent_watermark; + + retval->euid = euid; + retval->egid = egid; + retval->uid = retval->gid = -1; + + if (asprintf(&retval->eid_str, "%d_%d_", retval->euid, + retval->egid) == -1) { + free(retval); + return (NULL); + } + retval->eid_str_length = strlen(retval->eid_str); + + init_comm_element(&retval->request, CET_UNDEFINED); + init_comm_element(&retval->response, CET_UNDEFINED); + retval->process_func = on_query_startup; + retval->destroy_func = on_query_destroy; + + retval->write_func = query_socket_write; + retval->read_func = query_socket_read; + + get_time_func(&retval->creation_time); + memcpy(&retval->timeout, &s_configuration->query_timeout, + sizeof(struct timeval)); + + TRACE_OUT(init_query_state); + return (retval); +} + +void +destroy_query_state(struct query_state *qstate) +{ + + TRACE_IN(destroy_query_state); + if (qstate->eid_str != NULL) + free(qstate->eid_str); + + if (qstate->io_buffer != NULL) + free(qstate->io_buffer); + + qstate->destroy_func(qstate); + free(qstate); + TRACE_OUT(destroy_query_state); +} diff --git a/usr.sbin/nscd/query.h b/usr.sbin/nscd/query.h new file mode 100644 index 000000000000..5c4e918ef9f4 --- /dev/null +++ b/usr.sbin/nscd/query.h @@ -0,0 +1,110 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_QUERY_H__ +#define __CACHED_QUERY_H__ + +#include +#include +#include +#include "cachelib.h" +#include "config.h" +#include "protocol.h" + +struct query_state; +struct configuration; +struct configuration_entry; + +typedef int (*query_process_func)(struct query_state *); +typedef void (*query_destroy_func)(struct query_state *); +typedef ssize_t (*query_read_func)(struct query_state *, void *, size_t); +typedef ssize_t (*query_write_func)(struct query_state *, const void *, + size_t); + +/* + * The query state structure contains the information to process all types of + * requests and to send all types of responses. + */ +struct query_state { + struct timeval creation_time; + struct timeval timeout; + + struct comm_element request; + struct comm_element response; + struct configuration_entry *config_entry; + void *mdata; + + query_process_func process_func; /* called on each event */ + query_destroy_func destroy_func; /* called on destroy */ + + /* + * By substituting these functions we can opaquely send and received + * very large buffers + */ + query_write_func write_func; /* data write function */ + query_read_func read_func; /* data read function */ + + char *eid_str; /* the user-identifying string (euid_egid_) */ + size_t eid_str_length; + + uid_t euid; /* euid of the caller, received via getpeereid */ + uid_t uid; /* uid of the caller, received via credentials */ + gid_t egid; /* egid of the caller, received via getpeereid */ + gid_t gid; /* gid of the caller received via credentials */ + + size_t io_buffer_size; + size_t io_buffer_watermark; + size_t kevent_watermark; /* bytes to be sent/received */ + int sockfd; /* the unix socket to read/write */ + int kevent_filter; /* EVFILT_READ or EVFILT_WRITE */ + int socket_failed; /* set to 1 if the socket doesn't work correctly */ + + /* + * These fields are used to opaquely proceed sending/receiving of + * the large buffers + */ + char *io_buffer; + char *io_buffer_p; + int io_buffer_filter; + int use_alternate_io; +}; + +extern int check_query_eids(struct query_state *); + +extern ssize_t query_io_buffer_read(struct query_state *, void *, size_t); +extern ssize_t query_io_buffer_write(struct query_state *, const void *, + size_t); + +extern ssize_t query_socket_read(struct query_state *, void *, size_t); +extern ssize_t query_socket_write(struct query_state *, const void *, + size_t); + +extern struct query_state *init_query_state(int, size_t, uid_t, gid_t); +extern void destroy_query_state(struct query_state *); + +#endif diff --git a/usr.sbin/nscd/singletons.c b/usr.sbin/nscd/singletons.c new file mode 100644 index 000000000000..669d12be2abd --- /dev/null +++ b/usr.sbin/nscd/singletons.c @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "singletons.h" + +struct configuration *s_configuration = NULL; +cache s_cache = INVALID_CACHE; +struct runtime_env *s_runtime_env = NULL; +struct agent_table *s_agent_table = NULL; diff --git a/usr.sbin/nscd/singletons.h b/usr.sbin/nscd/singletons.h new file mode 100644 index 000000000000..918519c15cb8 --- /dev/null +++ b/usr.sbin/nscd/singletons.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2005 Michael Bushkov + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __CACHED_SINGLETONS_H__ +#define __CACHED_SINGLETONS_H__ + +#include "cachelib.h" +#include "config.h" +#include "agent.h" + +struct runtime_env { + int queue; + int sockfd; + int finished; /* for future use */ +}; + +extern struct configuration *s_configuration; +extern cache s_cache; +extern struct runtime_env *s_runtime_env; +extern struct agent_table *s_agent_table; + +#endif